虽然列表list可以完成数组操作,但不是真正意义上的数组,当数据量很大时, 其速度很慢,故提供了 NumPy扩展库完成数组操作.很多高级扩展库也依赖于它, 比如 Scipy, Pandas 和 Matplotlib 等.
NumPy 提供了两种基本的对象:ndarray(n-dimensional array object)和 ufunc (universal function object). ndarray(称为array数组,下文统一称为数组)是存储单 一数据类型的多维数组,而ufunc则是能够对数组进行处理的通用函数.
通过NumPy库的array函数实现数组的创建,如果向array函数中传入了一 个列表或元组,将构造简单的一维数组;如果传入多个嵌套的列表或元组,则可以 构造一个二维数组.构成数组的元素都具有相同的数据类型.下面分别构造一维数 组和二维数组.
将列表或元组转换为一个数组.在第二个数组b中,输 入的元素含有整型和浮点型两种数据类型,但输出的数组元素都转化为相同的浮点型.
# 利用array函数创建数组示例:
import numpy as np # 导入模块并命名为np
a = np.array([2, 4, 8, 20, 16, 30]) # 单个列表创建一维数组
# 嵌套元组创建二维数组
b = np.array(((1, 2, 3, 4, 5), (6, 7, 8, 9, 10),
(10, 9, 1, 2, 3), (4, 5, 6, 8, 9.0)))
print("一维数组:", a)
print("二维数组:\n", b)
一维数组: [ 2 4 8 20 16 30] 二维数组: [[ 1. 2. 3. 4. 5.] [ 6. 7. 8. 9. 10.] [10. 9. 1. 2. 3.] [ 4. 5. 6. 8. 9.]]
# 利用arange, empty, linspace等函数生成数组示例:
import numpy as np
a = np.arange(4, dtype=float) # 创建浮点型数组:[0., 1. ,2., 3.]
b = np.arange(0, 10, 2, dtype=int) # 创建整型数组:[0, 2, 4, 6, 8]
c = np.empty((2, 3), dtype=int) # 创建2X3的整型空矩阵
d = np.linspace(-1, 2, 5) # 创建数组:[-1., -0.25, 0.5, 1.25, 2.]
e = np.random. randint(0, 3, (2, 3)) # 生成[0,3)上的2行3列的随机整数数组
print("a=", a)
print("b=", b)
print("c=", c)
print("d=", d)
print("e=", e)
a= [0. 1. 2. 3.] b= [0 2 4 6 8] c= [[ 2 4 8] [20 16 30]] d= [-1. -0.25 0.5 1.25 2. ] e= [[1 1 1] [1 1 0]]
# 使用虚数单位“j”生成数组
import numpy as np
a = np.linspace(0, 2, 5) # 生成数组:[0., 0.5, 1., 1.5, 2.]
b = np.mgrid[0:2:5j] # 等价于 np. linspace (0,2,5)
x, y = np.mgrid[0:2:4j, 10:20:5j] # 生成[0,2」X [10,20]上的 4X5 的二维数组
print(f"x={x}\ny={y}")
x=[[0. 0. 0. 0. 0. ] [0.66666667 0.66666667 0.66666667 0.66666667 0.66666667] [1.33333333 1.33333333 1.33333333 1.33333333 1.33333333] [2. 2. 2. 2. 2. ]] y=[[10. 12.5 15. 17.5 20. ] [10. 12.5 15. 17.5 20. ] [10. 12.5 15. 17.5 20. ] [10. 12.5 15. 17.5 20. ]]
属性 | 说明 |
---|---|
ndim | 返回int,数组的维数 |
shape | 返回元组,表示数组的尺寸,对于m行n列的矩阵,返回值为(m,n) |
size | 返回int,表示数组的元素总数,等于shape属性返回元组中所有元素的乘积 |
dtype | 返回数据类型 |
itemsize | 返回int,表示数组每个元素的大小(以字节为单位) |
# 生成一个3x5的[1,10]上取值的随机整数矩阵,并显示它的各个属性
import numpy as np
a = np.random.randint(1, 11, (3, 5)) # 生成[1,10]区间上3行5列的随机整数数组
print("维数:", a.ndim)
print("维度:", a. shape)
print("元素总数:", a.size)
print("类型:", a.dtype)
print("每个元素字节数:", a.itemsize) # 字节数:4
维数: 2 维度: (3, 5) 元素总数: 15 类型: int32 每个元素字节数: 4
# 生成数学上一维向量的三种模式.
#程序文件Pex2_5 . py
import numpy as np
a = np.array([1, 2, 3])
print("维度为:", a.shape)
b = np.array([[1, 2, 3]])
print("维度为:", b.shape)
c = np.array([[1], [2], [3]])
print("维度为:", c.shape)
维度为: (3,) 维度为: (1, 3) 维度为: (3, 1)
NumPy中的array数组与Python基础数据结构列表(list)的区别是:列表中 的元素可以是不同的数据类型,而array数组只允许存储相同的数据类型.①对于 一维数组来说,Python原生的列表和NumPy的数组的切片操作都是相同的,无非 是记住一个规则:列表名(或数组名)$[start:end:step]$,但不包括索引end对应的值. ②二维数据列表元素的引用方式为a[i][j]; array数组元素的引用方式为a[ij],
NumPy比一般的Python序列提供更多的索引方式.除了用整数和切片的一般索引
外,数组还可以用布尔索引
及花式索引
.
# 一般索引
import numpy as np
a = np.array([2, 4, 8, 20, 16, 30])
b = np.array(((1, 2, 3, 4, 5), (6, 7, 8, 9, 10),
(10, 9, 1, 2, 3), (4, 5, 6, 8, 9.0)))
print(a[[2, 3, 5]]) # 输出 [8 20 30]
print(a[[-1, -2, -3]])
print(b[1, 2])
print(b[2])
print(b[2, :])
print(b[:, 1])
print(b[[2, 3], 1:4]) # 输出第3、4行,第2、3、4列的元素
print(b[1:3, 1:3]) # 输出第2、3行,第2、3列的元素
[ 8 20 30] [30 16 20] 8.0 [10. 9. 1. 2. 3.] [10. 9. 1. 2. 3.] [2. 7. 9. 5.] [[9. 1. 2.] [5. 6. 8.]] [[7. 8.] [9. 1.]]
# 布尔索引
from numpy import array, nan, isnan
a = array([[1, nan, 2], [4, nan, 3]])
b = a[~isnan(a)] # 提取a 中非 Nan 的数
print("b=", b)
print("b中大于2的元素有:", b[b > 2])
b= [1. 2. 4. 3.] b中大于2的元素有: [4. 3.]
# 花式索引
from numpy import array
x = array([[1, 2], [3, 4], [5, 6]])
print("前两行元素为:\n", x[[0, 1]]) # 输出:[[1,2] , [3,4]]
print("x[0][0]和x[1][1]为:", x[[0, 1], [0, 1]]) # 输出:[1 4]
print("以下两种格式是一样的:")
print(x[[0, 1]][:, [0, 1]]) # 输出:[[1,2] , [3,4]],
print(x[0:2, 0:2]) # 同上,输出第1行、2行,第1列、2列的元素
前两行元素为: [[1 2] [3 4]] x[0][0]和x[1][1]为: [1 4] 以下两种格式是一样的: [[1 2] [3 4]] [[1 2] [3 4]]
这里数组的修改是指数组元素的修改和数组维数的扩大或缩小
import numpy as np
x = np.array([[1, 2], [3, 4], [5, 6]])
print("x=\n", x)
x[2, 0] = -1 # 修改第3行、第1列元素为T
y = np.delete(x, 2, axis=0) # 删除数组的第3行
z = np.delete(y, 0, axis=1) # 删除数组的第 1 列
t1 = np.append(x, [[7, 8]], axis=0) # 增加一行
t2 = np.append(x, [[9], [10], [11]], axis=1) # 增加一列
print("x=\n", x)
print("y=\n", y)
print("z=\n", z)
print("t1=\n", t1)
print("t2=\n", t2)
x= [[1 2] [3 4] [5 6]] x= [[ 1 2] [ 3 4] [-1 6]] y= [[1 2] [3 4]] z= [[2] [4]] t1= [[ 1 2] [ 3 4] [-1 6] [ 7 8]] t2= [[ 1 2 9] [ 3 4 10] [-1 6 11]]
在对数组进行操作时,经常要改变数组的维度.在NumPy中,常用reshape函 数改变数据的形状,也就是改变数组的维度.其参数为一个正整数元组,分别指定 数组在每个维度上的大小.reshape函数在改变原始数据的形状的同时不改变原始数据的值.如果指定的维度和数组的元素数目不吻合,则函数将抛出异常.
数组变形和转换的一些函数(方法也统称函数).
函数 | 功能 | 调用方式 |
---|---|---|
reshape | 改变数组的维度 | a.reshape(m.n,s)把 a 数组,返回的是视图,a 变成m个n行s列的 本身不变 |
resize | 改变数组的维度 | a.resize(ni,nt8)把a变成in个zi行s列的数 组,没有返回,改变的是a数组 |
c- | 列组合 | c-[a,b],构造分块数组 $\begin{bmatrix} a&b \end{bmatrix}$ |
r_ | 行组合 | r_[a,b],构造分块数组 $\begin{bmatrix} a\\b \end{bmatrix}$ |
ravel | 水平展开数组 | a.ravel()返回的是a昌 J视图 |
flatten | 水平展开数组 | a.flatten()返回的是真实数组,需要分配新的内 存空间 |
hstack | 数组横向组合 | hstack((a,b)),输入参数为元组(a,b) |
vstack | 数组纵向组合 | vstack((a,b)) |
concatenate | 数组横向或纵向组合 | concatenate( (a,b) ,axis= 1), 同 hstack concatenate((a,b),axis=0),同 vs tack |
dstack | 深度组合,如在一幅图像数据的二维数组 上组合另一幅图像数据 | dstack((a,b)) |
hsplit | 数组横向分割 | hsplit(a,n)把a平均分成n个列数组 |
vsplit | 数组纵向分割 | vsplit(a,m)把a平均分成m个行数组 |
split | 数组横向或纵向分割 | split(a,n,aocis=l)同 hsplit(a,n) split(a,n,axis=0)同 vsplit(a,n) |
dsplit | 沿深度方向分割数组 | dsplit(a,n)沿深度方向平均分成n个数组 |
tolist | 把数组转换成Python列表 | a.tolist() |
import numpy as np
a = np.arange(4).reshape(2, 2) # 生成数组[[0,1] , [2,3]]
b = np.arange(4).reshape(2, 2) # 生成数组[[0,1] , [2,3]]
print(a.reshape(4,), '\n', a) # 输出 :[0 1 2 3]和[[0,1],[2,3]]
print(b.resize(4,), '\n', b) # 输出 :None和[0 123]
[0 1 2 3] [[0 1] [2 3]] None [0 1 2 3]
import numpy as np
a = np.arange(4).reshape(2, 2) # 生成数组[[0,1], [2,3]]
b = np.arange(4).reshape(2, 2) # 生成数组[[0,1], [2,3]]
c = np.arange(4).reshape(2, 2) # 生成数组[[0,1], [2,3]]
print(a.reshape(-1), '\n', a) # 输出:[0 1 2 3]和[[0,1], [2,3]]
print(b.ravel(), '\n', b) # 输出:[0 12 3]和[[0,1] , [2,3]]
print(c.flatten(), '\n', c) # 输出:[0 12 3]和[[0,1], [2,3]]
[0 1 2 3] [[0 1] [2 3]] [0 1 2 3] [[0 1] [2 3]] [0 1 2 3] [[0 1] [2 3]]
import numpy as np
a = np.arange(4).reshape(2, 2) # 生成数组[[0,1] , [2,3]]
b = np.arange(4, 8).reshape(2, 2) # 生成数组[[4,5] , [6,7]]
c1 = np.vstack([a, b])
c2 = np.r_[a, b]
dl = np.hstack([a, b])
d2 = np.c_[a, b]
在NumPy库中,实现四则运算既可以使用运算符号, *, /,也可以使用函 数add, substract, multiply, divide.需要注意的是,函数只能接受两个对象的运算, 如果需要多个对象的运算,就得使用嵌套方法.
另外还有三个数学运算符,分别是余数、整除和慕次,可以使用符号%, 〃, **, 也可以使用函数fmod, modf和power.但是整除的函数应用会稍微复杂一点,需要 写成np.modf(a/b)[l]的格式,因为modf可以返回数值的小数部分和整数部分,而 整数部分就是要取的整数值.
# 数组简单运算示例.
import numpy as np
a = np.arange(10, 15)
b = np.arange(5, 10)
c = a+b
d = a*b # 对应元素相加和相乘
e1 = np.modf(a/b)[0] # 对应元素相除的小数部分
e2 = np.modf(a/b)[1] # 对应元素相除的整数部分
print(f"a=\n{a}")
print(f"b=\n{b}")
print(f"c=\n{c}")
print(f"d=\n{d}")
print(f"e1=\n{e1}")
print(f"e2=\n{e2}")
a= [10 11 12 13 14] b= [5 6 7 8 9] c= [15 17 19 21 23] d= [ 50 66 84 104 126] e1= [0. 0.83333333 0.71428571 0.625 0.55555556] e2= [2. 1. 1. 1. 1.]
数组间的比较运算有六种 符号| 函数| 含义 :-: |:- | :- > |greater (a,b) | 判断a的元素是否大于b的元素 >= |greater_equal (a, b) | 判断a的元素是否大于等于b的元素 < | less(a,b) | 判断a的元素是否小于b的元素 <= |less_equal(a,b) | 判断a的元素是否小于等于b的元素 == |equal (a,b) | 判断a的元素是否等于b的元素 != |not equal(a,b) | 判断a的元素是否不等于b的元素
多维数组通过bool索引返回的都是一维数组; np.where返回的数组保持原来的形状.
# 比较运算示例.
import numpy as np
a = np.array([[3, 4, 9], [12, 15, 1]])
b = np.array([[2, 6, 3], [7, 8, 12]])
print(f'a大于b的所有元素:\n{a[a>b]}\n') # 取出a大于b的所有元素,输出:[3 9 12 15]
print(f'a大于b的所有元素:\n{a[a>10]}\n') # 取出a大于10的所有元素,输出:[12 15]
print(np.where(a > 10, -1, a)) # a中大于 10的元素改为-1
print(np.where(a > 10, -1, 0)) # a中大于 10 的元素改为-1,否则为 0
a大于b的所有元素: [ 3 9 12 15] a大于b的所有元素: [12 15] [[ 3 4 9] [-1 -1 1]] [[ 0 0 0] [-1 -1 0]]
ufunc函数全称为通用函数,是一种能够对数组中的元素逐个进行操作的函数. ufunc函数是针对数组进行操作的,并且都以NumPy数组作为输出.使用ufunc函 数比使用math库中的函数效率要高很多.目前NumPy支持超过60多种的通用 函数.这些函数包括广泛的操作,如四则运算、求模、取绝对值、幕函数、指数函 数、三角函数、位运算、比较运算和逻辑运算等.
# ufunc函数效率示例.
import numpy as np
import time
import math
x = [i*0.01 for i in range(1000000)]
start = time.time() # 1970纪元后经过的浮点秒数
for (i, t) in enumerate(x):
x[i] = math.sin(t)
print("math.sin: ", time.time()-start)
y = np.array([i*0.01 for i in range(1000000)])
start = time.time()
y = np.sin(y)
print("numpy.sin:", time.time()-start)
math.sin: 0.2890808582305908 numpy.sin: 0.011999368667602539
广播(broadcasting)是指不同形状的数组之间执行算术运算的方式.当使用 ufunc函数进行数组计算时,ufunc函数会对两个数组的对应元素进行计算.进行这 种计算的前提是两个数组的维度相容.若两个数组的维度不相容时,则NumPy会 实行广播机制.但是数组的广播功能是有规则的,如果不满足这些规则,运算时就 会出错.数组的主要广播规则为:
import numpy as np
a = np.arange(0, 20, 10).reshape(-1, 1)
# 变形为1列的数组,行数自动计算
b = np.arange(0, 3)
print(a+b)
[[ 0 1 2] [10 11 12]]
虽然在Python内置的random模块中可以生成随机数,但是每次只能随机生 成一个随机数,而且随机数的种类也不够丰富.建议使用NumPy.random模块的随 机数生成函数,一方面可以生成随机向量,另一方面函数丰富.关于各种常见的随 机数生成函数
函数 | 说明 |
---|---|
seed(n) | 设置随机数种子 |
beta(a,b,size=None) | 生成Beta分布随机数 |
chisquare(df,size=None) | 生成自由度为df的$\chi^{2}$分布随机数 |
choice (a,size=None, replace=N one ,p=None) | 从a中有放回地随机挑选指定数量的样本 |
exponential (scale=1.0,size=N one) | 生成指数分布随机数 |
f(dfnum,dfden,size=None) | 生成$F$分布随机数 |
gamma(shape,scale=1.0,size=None) | 生成伽马分布随机数 |
geometric(p,size=None) | 生成几何分布随机数 |
hypergeometric(ngood,nbad,nsample,size=None) | 生成超几何分布随机数 |
laplace(loc=0.0,scale=1.0,size=None) | 生成$Laplace$分布随机数 |
logistic(loc=0.0,scale= 1.0,size=None) | 生成$Logistic$分布随机数 |
lognormal(mean=0.0,signia=1.0,size=None) | 生成对数正态分布随机数 |
negative.bi nomial (n,p,size=None) | 生成负二项分布随机数 |
multinomial(p,pvals,size=None) | 生成多项分布随机数 |
multivariate_normal(mean,cov[,size]) | 生成多元正态分布随机数 |
normal (loc=0.0,scale= 1.0,size=None) | 生成正态分布随机数 |
pareto (a, size=None) | 生成帕累托分布随机数 |
poisson (lam=1.0,size=None) | 生成泊松分布随机数 |
rand(dO,dl,···,dn) | 生成n+1维的$ [0,1) $上均匀分布随机数 |
randn(dO,dl,··· ,dn) | 生成n + 1维的标准正态分布随机数 |
randint(low, high=None, size=None, dtype=,l,) | 生成区间$[low, high)$上的随机整数 |
random_sample(size=None) | 生成$[0, 1)$上的随机数 |
standard_t(df,size=None) | 生成标准的 $t$ 分布随机数 |
uniform(low=0.0,hign=1.0,size=None) | 生成区间 $[low, high)$上均匀分布随机数 |
wald (mean, scale,size=N one) | 生成 $Wald$ 分布随机数 |
weibull(a,size=None) | 生成 $Weibull$ 分布随机数 |
NumPy提供了多种文件操作函数以方便用户存取数组内容.文件存取的格式 分为两类:二进制和文本.而二进制格式的文件又分为NumPy专用的格式化二进 制类型和无格式类型.
savetxt()可以把1维和2维数组保存到文本文件中 </br> loadtxt()可以把文本文件中的数据加载到1维和2维数组中
# 文本文件存取示例.
import numpy as np
a = np.arange(0, 3, 0.5) .reshape(2, 3) # 生成2X3的数组
np.savetxt("Pdata2_18_l.txt", a)
# 缺省按照'X.18e'格式保存数值,以空格分隔
b = np. loadtxt("Pdata2_18_l.txt") # 返回浮点型数组
print("b=", b)
np.savetxt("CPdata2_18_2.txt", a, fmt="%d", delimiter=",")
# 保存为整型数据,以逗号分隔
c = np.loadtxt("CPdata2_18_2.txt", delimiter=",")
# 读入的时候也需要指定逗号分隔
print("c=", c)
b= [[0. 0.5 1. ] [1.5 2. 2.5]] c= [[0. 0. 1.] [1. 2. 2.]]
文本文件Pdata2_19.txt中存放如下格式的数据:
把其中的数据读入到数组a,并提取数组a的前2行、第2列到第4列的元素,构 造一个2行3列的数组b.
# 程序
import numpy as np
a = np. loadtxt("Pdata2_19.txt") # 返回值a 为浮点型数据
b = a[0:2, 1:4] # 获取a的第 1,2行,第2,3,4列
print("b=", b)
b= [[2. 6. 7.] [9. 5. 3.]]
例 2.20 文本文件Pdata2_20.txt中存放如下格式的数据</br>
姓名,年龄, 体重, 身高</br> 张三, 30, 75, 165</br> 李四, 45, 60, 179</br> 王五, 15, 39, 120</br>
提取其中的数值数据.
import numpy as np
a = np. loadtxt("Pdata2_20.txt", dtype=str, delimiter=",")
b = a[1:, 1:] .astype(float) # 提取a矩阵的数值行和数值列,并转换类型
print("b=\n", b)
b= [[ 30. 75. 165.] [ 45. 60. 179.] [ 15. 39. 120.]]
如果需要处理复杂的数据结构,比如处理缺失数据等情况,可以使用genfromtxt.
genfromtxt(fname,dtype=float,comments='#', delimiter=None,skip_header=0,skip_footer=0,converters=None, missing_values=None,filling_values=None,usecols=None,names=None, excludelist=None,deletechars=None,replace_space='_', autostrip=False,case_sensitive=True,defaultfmt='f%i',unpack=None, usemask=False,loose=True,invalid_raise=True,max_rows=None, encoding='bytes')
纯文本文件Pdata2_21.txt中存放如下数据.分别读取其中的前6 行前8列数据、第9列的数值数据、最后一行数据.
6 2 6 7 4 2 5 9 60kg</br> 4 9 5 3 8 5 8 2 55kg</br> 5 2 1 9 7 4 3 3 51kg</br> 7 6 7 3 9 2 7 1 43kg</br> 2 3 9 5 7 2 6 5 41kg</br> 5 5 2 2 8 1 4 -999 52kg</br> 35 37 22 32 41 32 43 38 </br>
import numpy as np
# 读前6行前8列数据
a = np.genfromtxt("Pdata2_21.txt", max_rows=6, usecols=range(8))
b = np.genfromtxt("Pdata2_21.txt", dtype=str, max_rows=6, usecols=[8])
# 读第9列数值数据
b = [float(v.rstrip('kg')) for (i, v) in enumerate(b)]
# 删除kg,并转换为浮点型数据
c = np.genfromtxt("Pdata2_21.txt", skip_header=6) # 读最后一行数据
print(a, '\n', b, '\n', c)
[[ 6. 2. 6. 7. 4. 2. 5. 9.] [ 4. 9. 5. 3. 8. 5. 8. 2.] [ 5. 2. 1. 9. 7. 4. 3. 3.] [ 7. 6. 7. 3. 9. 2. 7. 1.] [ 2. 3. 9. 5. 7. 2. 6. 5.] [ 5. 5. 2. 2. 8. 1. 4. -999.]] [60.0, 55.0, 51.0, 43.0, 41.0, 52.0] [35. 37. 22. 32. 41. 32. 43. 38.]