一、NumPy数组属性
首先我们先了解一下NumPy 数组属性。NumPy 的数组中比较重要 ndarray 对象属性有:
属性 | 说明 |
---|---|
ndarray.ndim | 秩,即轴的数量或维度的数量 |
ndarray.shape | 数组的维度,对于矩阵,n 行 m 列 |
ndarray.size | 数组元素的总个数,相当于 .shape 中 n*m 的值 |
ndarray.dtype | ndarray 对象的元素类型 |
ndarray.itemsize | ndarray 对象中每个元素的大小,以字节为单位 |
ndarray.flags | ndarray 对象的内存信息 |
ndarray.real | ndarray元素的实部 |
ndarray.imag | ndarray 元素的虚部 |
ndarray.data | 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性。 |
二、NumPy创建数组
1.numpy.empty:
numpy.empty 方法用来创建一个指定形状(shape)、数据类型(dtype)且未初始化的数组:
numpy.empty(shape, dtype = float, order = 'C')
# x = np.empty([3,2], dtype = int, order = 'C')
shape表示数组形状,dtype表示数组类型,order有“C”和“F”两个选项,分别表示行优先和列优先。
2.numpy.zeros:
创建指定大小的数组,数组元素以 0 来填充:
numpy.zeros(shape, dtype = float, order = 'C')
# x = np.zeros([5,2], dtype = int)
"""
[[0 0]
[0 0]
[0 0]
[0 0]
[0 0]]
"""
3.numpy.ones:
创建指定大小的数组,数组元素以 1 来填充:
numpy.ones(shape, dtype = None, order = 'C')
# x = np.ones([2,2], dtype = int)
"""
[[1 1]
[1 1]]
"""
三、NumPy从数值范围创建数组
1.numpy.arange:
numpy 包中的使用 arange 函数创建数值范围并返回 ndarray 对象,函数格式如下:
numpy.arange(start, stop, step, dtype)
# x = np.arange(5) # [0 1 2 3 4]
# x = np.arange(10,20,2) # [10 12 14 16 18]
根据 start 与 stop 指定的范围以及 step 设定的步长,生成一个 ndarray。
2.numpy.linspace:
numpy.linspace 函数用于创建一个一维数组,数组是一个等差数列构成的,格式如下:
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
# a = np.linspace(1,10,10) # [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
# a = np.linspace(1,1,10) # [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
# a = np.linspace(10, 20, 5, endpoint = False) # [10. 12. 14. 16. 18.]
参数简要说明一下:
start:序列的起始值
stop:序列的终止值,如果endpoint为true,该值包含于数列中
num:要生成的等步长的样本数量,默认为50
endpoint:该值为 true 时,数列中包含stop值,反之不包含,默认是True。
retstep:如果为 True 时,生成的数组中会显示间距,反之不显示。
dtype:ndarray 的数据类型
3.numpy.logspace
numpy.logspace 函数用于创建一个于等比数列。格式如下:
np.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)
# a = np.logspace(0,9,10,base=2) # [ 1. 2. 4. 8. 16. 32. 64. 128. 256. 512.]
start 序列的起始值为:basestart。
stop序列的终止值为:basestop。如果endpoint为true,该值包含于数列中
num要生成的等步长的样本数量,默认为50
endpoint该值为 true 时,数列中中包含stop值,反之不包含,默认是True。
base表示对数 log 的底数。
dtype表示ndarray 的数据类型。
四、NumPy切片和索引
1.numpy.slice:
ndarray 数组可以基于 0 - n 的下标进行索引,切片对象可以通过内置的 slice 函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组。
a = np.arange(10)
s = slice(2,7,2) # [2 4 6] 从索引 2 开始到索引 7 停止,间隔为2
2.冒号:
我们也可以通过冒号分隔切片参数 start:stop:step 来进行切片操作:
a = np.arange(10)
b = a[2:7:2] # [2 4 6] 从索引 2 开始到索引 7 停止,间隔为 2
冒号 : 的解释:如果只放置一个参数,如 [2],将返回与该索引相对应的单个元素。如果为 [2:],表示从该索引开始以后的所有项都将被提取。如果使用了两个参数,如 [2:7],那么则提取两个索引(不包括停止索引)之间的项。多维数组同样适用上述索引提取方法:
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print(a[1:])
"""
[[3 4 5]
[4 5 6]]
"""
3.省略号…
切片还可以包括省略号 …,来使选择元组的长度与数组的维度相同。 如果在行位置使用省略号,它将返回包含行中元素的 ndarray。
a = np.array([[1,2,3],[3,4,5],[4,5,6]])
print (a[...,1]) # 第2列元素 [2 4 5]
print (a[1,...]) # 第2行元素 [3 4 5]
print (a[...,1:]) # 第2列及剩下的所有元素
"""
[[2 3]
[4 5]
[5 6]]
"""
五、NumPy高级索引
1.整数数组索引
以下实例获取数组中 (0,0),(1,1) 和 (2,0) 位置处的元素。
x = np.array([[1, 2], [3, 4], [5, 6]])
y = x[[0,1,2], [0,1,0]] # [1 4 5]
以下实例获取了 4X3 数组中的四个角的元素。 行索引是 [0,0] 和 [3,3],而列索引是 [0,2] 和 [0,2]。
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
rows = np.array([[0,0],[3,3]])
cols = np.array([[0,2],[0,2]])
y = x[rows,cols]
"""
[[ 0 2]
[ 9 11]]
"""
可以借助切片 : 或 … 与索引数组组合。如下面例子:
a = np.array([[1,2,3], [4,5,6],[7,8,9]])
b = a[1:3, 1:3] # 矩阵索引的一到二行,一到二列形成一个新的矩阵
"""
[[5 6]
[8 9]]
"""
c = a[1:3,[1,0,2]] # 矩阵索引的一到二行,第一列,第零列,第二列形成一个新的矩阵
"""
[[5 4 6]
[8 7 9]]
"""
d = a[...,1:] # 矩阵索引第一列往后形成一个新的矩阵
"""
[[2 3]
[5 6]
[8 9]]
"""
2.布尔索引
我们可以通过一个布尔数组来索引目标数组。布尔索引通过布尔运算(如:比较运算符)来获取符合指定条件的元素的数组。以下实例获取大于 5 的元素:
x = np.array([[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]])
print (x[x > 5]) # [ 6 7 8 9 10 11]
以下实例使用了 ~(取补运算符)来过滤 NaN(nan(NAN,Nan):not a number 表示不是一个数字,np.nan是一个float类型的数据,当我们读取本地文件为float时,如果有缺失,或者做了不合适的计算会出现nan):
a = np.array([np.nan, 1,2,np.nan,3,4,5])
print (a[~np.isnan(a)]) # [ 1. 2. 3. 4. 5.]
以下实例演示如何从数组中过滤掉非复数元素:
a = np.array([1, 2+6j, 5, 3.5+5j])
print (a[np.iscomplex(a)]) # [2.0+6.j 3.5+5.j]
3.花式索引
花式索引指的是利用整数数组进行索引。花式索引根据索引数组的值作为目标数组的某个轴的下标来取值。对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素,如果目标是二维数组,那么就是对应下标的行。花式索引跟切片不一样,它总是将数据复制到新数组中。
一维数组只有一个轴 axis = 0,所以一维数组就在 axis = 0 这个轴上取值:
x = np.arange(9)
x2 = x[[0, 6]] # [0 6]
print(x2[0]) # 0
print(x2[1]) # 6
二维数组传入顺序索引数组:
x=np.arange(32).reshape((8,4))
print(x)
"""
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
[20 21 22 23]
[24 25 26 27]
[28 29 30 31]]
"""
print (x[[4,2,1,7]]) # 输出下标为 4, 2, 1, 7 对应的行
"""
[[16 17 18 19]
[ 8 9 10 11]
[ 4 5 6 7]
[28 29 30 31]]
"""
二维数组传入倒序索引数组:
x=np.arange(32).reshape((8,4))
print (x[[-4,-2,-1,-7]])
"""
[[16 17 18 19]
[24 25 26 27]
[28 29 30 31]
[ 4 5 6 7]]
"""
传入多个索引数组(要使用 np.ix_)。np.ix_ 函数就是输入两个数组,产生笛卡尔积的映射关系。笛卡尔乘积是指在数学中,两个集合 X 和 Y 的笛卡尔积(Cartesian product),又称直积,表示为 X×Y,第一个对象是X的成员而第二个对象是 Y 的所有可能有序对的其中一个成员。
例如 A={a,b}, B={0,1,2},则:
A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}
x=np.arange(32).reshape((8,4))
print (x[np.ix_([1,5,7,2],[0,3,1,2])])
"""
笛卡尔乘积为
(1,0),(1,3),(1,1),(1,2);
(5,0),(5,3),(5,1),(5,2);
(7,0),(7,3),(7,1),(7,2);
(2,0),(2,3),(2,1),(2,2);
输出为:
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
"""
六、NumPy 广播(Broadcast)
广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行。如果两个数组 a 和 b 形状相同,即满足 a.shape == b.shape,那么 a*b 的结果就是 a 与 b 数组对应位相乘。这要求维数相同,且各维度的长度相同:
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])
c = a * b # [ 10 40 90 160]
当运算中的 2 个数组的形状不同时,numpy 将自动触发广播机制。如:
a = np.array([[ 0, 0, 0],
[10,10,10],
[20,20,20],
[30,30,30]])
b = np.array([0,1,2])
print(a + b)
"""
[[ 0 1 2]
[10 11 12]
[20 21 22]
[30 31 32]]
"""
下面的图片展示了数组 b 如何通过广播来与数组 a 兼容。
广播的规则:
1.让所有输入数组都向其中形状最长的数组看齐,形状中不足的部分都通过在前面加 1 补齐。
2.输出数组的形状是输入数组形状的各个维度上的最大值。
3.如果输入数组的某个维度和输出数组的对应维度的长度相同或者其长度为 1 时,这个数组能够用来计算,否则出错。
4.当输入数组的某个维度的长度为 1 时,沿着此维度运算时都用此维度上的第一组值。
七、NumPy迭代数组
NumPy 迭代器对象 numpy.nditer 提供了一种灵活访问一个或者多个数组元素的方式。迭代器最基本的任务的可以完成对数组元素的访问。接下来我们使用 arange() 函数创建一个 2X3 数组,并使用 nditer 对它进行迭代:
a = np.arange(6).reshape(2,3)
for x in np.nditer(a):
print (x, end=", " )
# 0, 1, 2, 3, 4, 5,
以上实例不是使用标准 C 或者 Fortran 顺序,选择的顺序是和数组内存布局一致的,这样做是为了提升访问的效率,默认是行序优先(row-major order,或者说是 C-order)。
这反映了默认情况下只需访问每个元素,而无需考虑其特定顺序。我们可以通过迭代上述数组的转置来看到这一点,并与以 C 顺序访问数组转置的 copy 方式做对比,如下实例:
a = np.arange(6).reshape(2,3)
for x in np.nditer(a.T):
print (x, end=", " )
print ('\n')
# 0, 1, 2, 3, 4, 5,
for x in np.nditer(a.T.copy(order='C')):
print (x, end=", " )
# 0, 3, 1, 4, 2, 5,
从上述例子可以看出,a 和 a.T 的遍历顺序是一样的,也就是他们在内存中的存储顺序也是一样的,但是 a.T.copy(order = ‘C’) 的遍历结果是不同的,那是因为它和前两种的存储方式是不一样的,默认是按行访问。
1.控制遍历顺序
for x in np.nditer(a, order='F'):Fortran order,即是列序优先;
for x in np.nditer(a.T, order='C'):C order,即是行序优先;
2.修改数组中元素的值
nditer 对象有另一个可选参数 op_flags。 默认情况下,nditer 将视待迭代遍历的数组为只读对象(read-only),为了在遍历数组的同时,实现对数组元素值得修改,必须指定 readwrite 或者 writeonly 的模式:
a = np.arange(0,60,5)
a = a.reshape(3,4)
print (a)
"""
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
"""
for x in np.nditer(a, op_flags=['readwrite']):
x[...]=2*x
print (a)
"""
[[ 0 10 20 30]
[ 40 50 60 70]
[ 80 90 100 110]]
"""
3.使用外部循环
nditer 类的构造器拥有 flags 参数,它可以接受下列值:
c_index 可以跟踪 C 顺序的索引
f_index 可以跟踪 Fortran 顺序的索引
multi_index 每次迭代可以跟踪一种索引类型
external_loop 给出的值是具有多个值的一维数组,而不是零维数组
在下面的实例中,迭代器遍历对应于每列,并组合为一维数组:
a = np.arange(0,60,5)
a = a.reshape(3,4)
for x in np.nditer(a, flags = ['external_loop'], order = 'F'):
print (x, end=", " )
# [ 0 20 40], [ 5 25 45], [10 30 50], [15 35 55],
4.广播迭代
如果两个数组是可广播的,nditer 组合对象能够同时迭代它们。 假设数组 a 的维度为 3X4,数组 b 的维度为 1X4 ,则使用以下迭代器(数组 b 被广播到 a 的大小):
a = np.arange(0,60,5)
a = a.reshape(3,4)
b = np.array([1, 2, 3, 4], dtype = int)
for x,y in np.nditer([a,b]):
print ("%d:%d" % (x,y), end=", " )
# 0:1, 5:2, 10:3, 15:4, 20:1, 25:2, 30:3, 35:4, 40:1, 45:2, 50:3, 55:4,