无法播放?请 点击这里 跳转到Youtube
切换视频源:

经过前几章的学习,我们已经掌握了NumPy中最常用的一些功能,比如如何创建NumPy Array,如何在不同维度的矩阵中进行索引,还有一些常用的数学函数,本章是NumPy入门教程的最后一章,我们就来学习一下NumPy中的一些高级的操作函数。

Transposing Array 数组变位

我们可以使用NumPy的transpose函数将矩阵各个维度进行对换:

m1 = np.arange(6).reshape((2, 3))
print(m1)
[[0 1 2]
 [3 4 5]]
print(np.transpose(m1)) # 将矩阵的维度进行对换
[[0 3]
 [1 4]
 [2 5]]

m2 = np.ones((1, 2, 3))
print(np.transpose(m2, (1, 0, 2)).shape) # 将原数组的维度(0, 1, 2)换成(1, 0, 2)

m3 = np.ones((2, 3, 4, 5))
print(np.transpose(m3).shape) # 对换m3矩阵所有的维度(0, 1, 2, 3)换成(3, 2, 1, 0)

m4 = np.arange(24).reshape(2, 3, 4)
print(m4)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

m5 = m4.transpose((2, 0, 1)) # 维度对换:(2, 3, 4) => (4, 2, 3)
print(m5)
[[[ 0  4  8]
  [12 16 20]]

 [[ 1  5  9]
  [13 17 21]]

 [[ 2  6 10]
  [14 18 22]]

 [[ 3  7 11]
  [15 19 23]]]

m6 = m4.swapaxes(0, 1) # 置换特定的维度:(2, 3, 4) => (3, 2, 4)
print(m6)

Stacking Arrays 合并

接下来我们来学习一些如何合并数组。

array1 = np.full((1, 5), 1.0)
array2 = np.full((1, 5), 2.0)
stacked_array = np.vstack((array1, array2)) # 竖直合并序列中的数组(行方向)
print(stacked_array)
[[1. 1. 1. 1. 1.]
 [2. 2. 2. 2. 2.]]
matrix1 = np.full((3, 2), 1.0)
matrix2 = np.full((3, 2), 2.0)
stacked_matrix = np.hstack((matrix1, matrix2)) # 水平合并序列中的数组(列方向)
print(stacked_matrix)
[[1. 1. 2. 2.]
 [1. 1. 2. 2.]
 [1. 1. 2. 2.]]

我们还能使用concatenate对多个矩阵或序列进行合并,通过自定义axis的位置,则能更方便地合并矩阵:

cmatrix1 = np.concatenate((matrix1, matrix2), axis = 0) # 和vstack相同
cmatrix2 = np.concatenate((matrix1, matrix2), axis = 1) # 和hstack相同
print(cmatrix1)
[[1. 1.]
 [1. 1.]
 [1. 1.]
 [2. 2.]
 [2. 2.]
 [2. 2.]]
print(cmatrix2)
[[1. 1. 2. 2.]
 [1. 1. 2. 2.]
 [1. 1. 2. 2.]]
matrix1 = np.full((3, 3), 1)
matrix2 = np.full((3, 3), 2)

matrix3 = np.stack((matrix1, matrix2)) # 沿着新轴将矩阵合并,要保持两个矩阵形状一致
print(matrix3)
[[[1 1 1]
  [1 1 1]
  [1 1 1]]

 [[2 2 2]
  [2 2 2]
  [2 2 2]]]

Splitting Array 分割

NumPy还能使用相似的功能分割数组:

big_matrix = np.arange(36).reshape(9, 4)
print(big_matrix)
[[ 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]
 [32 33 34 35]]
m1, m2, m3 = np.vsplit(big_matrix, 3) # 将一个数组垂直分割成多个子数组(按行)
print(m1)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
print(m2)
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
print(m3)
[[24 25 26 27]
 [28 29 30 31]
 [32 33 34 35]]
m4, m5 = np.hsplit(big_matrix, 2) # 将一个数组水平分割为多个子数组(按列)
print(m4)
[[ 0  1]
 [ 4  5]
 [ 8  9]
 [12 13]
 [16 17]
 [20 21]
 [24 25]
 [28 29]
 [32 33]]
print(m5)
[[ 2  3]
 [ 6  7]
 [10 11]
 [14 15]
 [18 19]
 [22 23]
 [26 27]
 [30 31]
 [34 35]]

Tile

Tile函数可以重复某个数组以构成一个新的数组:

m1 = np.arange(4)
print(m1)
print()
m2 = np.tile(m1, (4, 1)) # 重复4个m2矩阵,并将它们按行合并
print(m2)
[[0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]
 [0 1 2 3]]
m3 = np.tile(m1, (4, 2)) # 重复8个矩阵,4个按行合并,4个按列合并
print(m3)
[[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 0 1 2 3]]

重要机制

最后我们学习一下NumPy中重要的机制,虽然这个比较枯燥,但是也是需要了解的。

Broadcasting 广播

广播原则的前提:两个数组必须可以转化成维度大小一模一样才能进行运算。

规则1:如果两个数组的维度不同,那么小维度数组的形状将会在最左边补1

m1 = np.arange(4).reshape(1, 1, 4)
print(m1)
[[[0 1 2 3]]]
print(m1 + [10, 10, 20, 20]) # 等于:m1 + [[[10, 10, 20, 20]]](形状(1,1,4))
[[[10 11 22 23]]]

规则2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状则会沿着维度为1扩展,以匹配另一个数组的形状。

m2 = np.arange(6).reshape(2, 3)
print(m2)
[[0 1 2]
 [3 4 5]]
print(m2 + [[10], [20]]) # 等于: m2 + [[10, 10, 10], [20, 20, 20]]
[[10 11 12]
 [23 24 25]]
print(m2 + [10, 20, 30]) # 规则1:[[10,20,30]] => 规则2:[[10,20,30],[10,20,30]]
[[10 21 32]
 [13 24 35]]
print(m2 + 100) # 与之相同的逻辑: m2 + [[100, 100, 100], [100, 100, 100]]
[[100 101 102]
 [103 104 105]]
m3 = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
m4 = np.array([1, 0, 1])
[1 0 1]
m5 = m3 + m4  # 使用广播将 m4 加到 m3 的每一行上
print(m5)
[[ 2  2  4]
 [ 5  5  7]
 [ 8  8 10]
 [11 11 13]]

规则3:如果两个数组的形状在任何一个维度上都不匹配,并且没有任何一个维度为1,那么就会引起异常。

try:
  m1 = np.arange(4).reshape(1, 1, 4) 
  m1 + [33, 44] # 规则3会引发异常
except ValueError as e:
  print(e) 

Upcasting

将不同数据类型的数组进行运算时,运算结果会自动将数据类型转变为更为精确的数据类型:

m1 = np.arange(0, 5, dtype=np.int8)
print(m1.dtype, m1) # uint8

m2 = m1 + np.array([5, 6, 7, 8, 9], dtype=np.int16)
print(m2.dtype, m2) # int16

m3 = m1 + 1.5
print(m3.dtype, m3) # float64

Homework

将下面两个形状为(2, 4)的矩阵沿着第一个维度(axis=0)合并,然后将结果按列分割成两个形状为(4,2)的矩阵,最后将其中任何一个矩阵按着列(第二个维度)重复一遍,得到一个(4,4)的矩阵。

m1 = np.arange(8).reshape(2, 4)
m2 = np.arange(8).reshape(2, 4)

答案:

m3 = np.vstack((m1, m2))
print(m3)
print('********')
m4, m5 = np.hsplit(m3, 2)
print(m4)
print('********')
m6 = np.tile(m4, (1, 2))
print(m6)