文章目录
- 1. 广播(Broadcasting)规则
- 2. 使用索引数组索引
- 3. 使用布尔值作为数组索引
- 4. ix_()函数
- 5. 线性代数 简单数组操作
- 6. 技巧和提示
- 6.1 “自动”整形
- 6.2 矢量堆叠
1. 广播(Broadcasting)规则
Broadcasting允许通用函数以有意义的方式处理具有不完全相同形状的输入。
-
第一个规则,如果所有输入数组不具有相同数量的维度,则“1”将被重复地添加到较小数组的形状,直到所有数组具有相同数量的维度。
-
第二个规则,确保沿着特定维度具有大小为1的数组表现得好像它们具有沿着该维度具有最大形状的数组的大小。假定数组元素的值沿“Broadcasting”数组的该维度相同。
在应用广播规则之后,所有阵列的大小必须匹配。
(以上不是特别明白)
2. 使用索引数组索引
>>> a = np.arange(12)**2
>>> a
array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121],dtype=int32)
>>> i = np.array([1,1,3,8,5])
>>> a[i]
array([ 1, 1, 9, 64, 25], dtype=int32)
>>>
>>> j = np.array([[3,4],[9,7]])
>>> a[j] # a[j] 形状与 j 一致
array([[ 9, 16],[81, 49]], dtype=int32)
- 当被索引的数组
a
是一个多维数组,单个索引数组指的是a
的第一个维度。以下示例通过使用调色板将标签图像转换为彩色图像来作为举例。
>>> palette = np.array([[0,0,0],[255,0,0],[0,255,0],[0,0,255],[255,255,255]])
>>> image = np.array([[0,1,2,0],[0,3,4,0]])
>>> palette[image]
array([[[ 0, 0, 0],[255, 0, 0],[ 0, 255, 0],[ 0, 0, 0]],[[ 0, 0, 0],[ 0, 0, 255],[255, 255, 255],[ 0, 0, 0]]])
- 也可以给出多个维度的索引。每个维度的索引数组必须具有相同的形状。
>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]])
>>> i = np.array([[0,1],[1,2]]) #行
>>> j = np.array([[2,1],[3,3]]) #列
>>> a[i,j] # i,j 必须有相同的shape
array([[ 2, 5],[ 7, 11]])
>>>
>>> a[i,2]
array([[ 2, 6],[ 6, 10]])
>>>
>>> a[:,j] #行和列的组合(0行所有的j...)
array([[[ 2, 1],[ 3, 3]],[[ 6, 5],[ 7, 7]],[[10, 9],[11, 11]]])
- 当然,我们可以把
i
和j
放在一个序列中(比如一个列表), 然后用列表进行索引。
>>> l = [i,j]
>>> a[l]
array([[ 2, 5], # 等效于 a[i,j][ 7, 11]])
- 不能将
i
和j
放入一个数组中,因为这个数组将被解释为索引第一个维度。
>>> s = np.array([i,j])
>>> s
array([[[0, 1],[1, 2]],[[2, 1],[3, 3]]])
>>> a[s]
Traceback (most recent call last):File "<pyshell#36>", line 1, in <module>a[s]
IndexError: index 3 is out of bounds for axis 0 with size 3
>>> a[tuple(s)] # 与 a[i,j] 一样
array([[ 2, 5],[ 7, 11]])
- 索引数组的另一个常见用途是搜索时间相关序列的最大值
>>> time_max = time[idx]
>>> data_max = data[idx, range(data.shape[1])] # => data[idx[0],0], data[idx[1],1]...
>>> time_max
array([ 82.5 , 20. , 113.75, 51.25])
>>> data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
>>> np.all(data_max == data.max(axis=0))
True
- 还可以使用数组索引作为目标来赋值
>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[1,2,3]] = 0
>>> a
array([0, 0, 0, 0, 4])
- 当索引列表包含重复时,赋值多次,只留最后一个值
>>> a = np.arange(5)
>>> a[[0,0,2]] = [1,2,3] # a[0]=1,a[0]=2,a[2]=3
>>> a
array([2, 1, 3, 3, 4])
- 使用Python的
+=
构造要小心,可能得不到你想要的效果
>>> a
array([0, 1, 2, 3, 4])
>>> a[[0,0,2]] += 1 # a[0] 只加了1次
>>> a
array([1, 1, 3, 3, 4])
即使0在索引列表中出现2次,第0个元素只会增加一次。这是因为Python要求“a + = 1”等同于“a = a + 1”
3. 使用布尔值作为数组索引
使用布尔值作为索引时,我们明确地选择数组中的哪些元素我们想要的,哪些不是。
我们可以想到的布尔索引最自然的方式是使用与原始数组具有相同形状的布尔数组
>>> a = np.arange(12).reshape(3,4)
>>> a
array([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]])
>>> b = a > 4
>>> b
array([[False, False, False, False],[False, True, True, True],[ True, True, True, True]])
>>> a[b] # 取出true的元素
array([ 5, 6, 7, 8, 9, 10, 11])
- 此属性在赋值时非常有用
>>> a[b] = 0 # a中大于4的元素赋值为0
>>> a
array([[0, 1, 2, 3],[4, 0, 0, 0],[0, 0, 0, 0]])
使用布尔索引生成 Mandelbrot 集的图像(不懂)
import numpy as np
import matplotlib.pyplot as plt
def mandelbrot(h,w,maxit=20):# Returns an image of the Mandelbrot fractal of size (h,w)y,x = np.ogrid[-1.4:1.4:h*1j, -2:0.8:w*1j]c = x+y*1jz = cdivtime = maxit + np.zeros(z.shape, dtype=int)for i in range(maxit):z = z**2 + cdiverge = z*np.conj(z) > 2**2div_now = diverge & (divtime==maxit)divtime[div_now] = iz[diverge] = 2return divtimeplt.imshow(mandelbrot(400,400))
plt.show()
第二种使用布尔索引的方法更类似于整数索引; 对于数组的每个维度,给出一个一维布尔数组,选择我们想要的切片
>>> a = np.arange(12).reshape(3,4)
>>> b1 = np.array([False,True,True]) # 第一轴长度3=a中的3
>>> b2 = np.array([True,False,True,False]) # 第二轴长度4=a中的4
>>>
>>> a[b1,:] # 选择b1内true的行
array([[ 4, 5, 6, 7],[ 8, 9, 10, 11]])
>>>
>>> a[b1] # 同上
array([[ 4, 5, 6, 7],[ 8, 9, 10, 11]])
>>>
>>> a[:,b2] # 选择b2内true的列
array([[ 0, 2],[ 4, 6],[ 8, 10]])
>>>
>>> a[b1,b2] # 奇怪的结果!!!
array([ 4, 10])
1D布尔数组的长度必须与你要切片的维度(或轴)的长度一致。b1 是rank为1的数组,其长度为3( a 中行的数量), b2 (长度4)适合于索引 a 的第二个rank(列)。
4. ix_()函数
可以使用 ix_ 函数来组合不同的向量以获得每个n-uplet的结果。例如,如果要计算从向量a、b和c中的取得的所有三元组的所有a + b * c
>>> a = np.array([2,3,4,5])
>>> b = np.array([8,5,4])
>>> c = np.array([5,4,6,8,3])>>> ax,bx,cx = np.ix_(a,b,c) # 分别取对应的,组合>>> ax
array([[[2]],[[3]],[[4]],[[5]]])
>>> bx
array([[[8],[5],[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))>>> result = ax+bx*cx
>>> result
array([[[42, 34, 50, 66, 26],[27, 22, 32, 42, 17],[22, 18, 26, 34, 14]],[[43, 35, 51, 67, 27],[28, 23, 33, 43, 18],[23, 19, 27, 35, 15]],[[44, 36, 52, 68, 28],[29, 24, 34, 44, 19],[24, 20, 28, 36, 16]],[[45, 37, 53, 69, 29],[30, 25, 35, 45, 20],[25, 21, 29, 37, 17]]])
>>> result[3,2,4]
17
>>> a[3]+b[2]*c[4]
17
以下版本的reduce的优点是它使用Broadcasting规则,以避免创建参数数组输出的大小乘以向量的数量。
>>> def func(f, *vectors):vs = np.ix_(*vectors)r = f.identityfor v in vs:r = f(r,v)return r>>> func(np.add,a,b,c)
array([[[15, 14, 16, 18, 13],[12, 11, 13, 15, 10],[11, 10, 12, 14, 9]],[[16, 15, 17, 19, 14],[13, 12, 14, 16, 11],[12, 11, 13, 15, 10]],[[17, 16, 18, 20, 15],[14, 13, 15, 17, 12],[13, 12, 14, 16, 11]],[[18, 17, 19, 21, 16],[15, 14, 16, 18, 13],[14, 13, 15, 17, 12]]])
5. 线性代数 简单数组操作
>>> a = np.array([[1.0,2.0],[3.0,4.0]])
>>> print(a)
[[1. 2.][3. 4.]]
>>> a.transpose() #转置
array([[1., 3.],[2., 4.]])
>>> np.linalg.inv(a) #矩阵求逆
array([[-2. , 1. ],[ 1.5, -0.5]])>>> u = np.eye(2) #对角线是1
>>> u
array([[1., 0.],[0., 1.]])
>>> j = np.array([[0.0,-1.0],[1.0,0.0]])
>>> np.dot(j,j) #矩阵点积
array([[-1., 0.],[ 0., -1.]])
>>> np.trace(u)
2.0
>>> y = np.array([[5.0],[7.0]])
>>> np.linalg.solve(a,y) # 求解矩阵方程
array([[-3.],[ 4.]])
>>> np.linalg.eig(j)
(array([0.+1.j, 0.-1.j]), array([[0.70710678+0.j , 0.70710678-0.j ],[0. -0.70710678j, 0. +0.70710678j]]))
6. 技巧和提示
6.1 “自动”整形
可以省略一个维度的尺寸,由其自动计算出来
>>> a = np.arange(30)
>>> a.shape = 2,-1,3 # -1 表示自行推导,省略
>>> a.shape
(2, 5, 3)
>>> a
array([[[ 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]]])
6.2 矢量堆叠
column_stack,dstack,hstack,vstack
>>> x = np.arange(0,10,2) #array([0, 2, 4, 6, 8])
>>> y = np.arange(5) #array([0, 1, 2, 3, 4])
>>> m = np.vstack([x,y])
>>> m
array([[0, 2, 4, 6, 8],[0, 1, 2, 3, 4]])
>>> xy = np.hstack([x,y])
>>> xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])