许 志强

许 志强

1647407700

【NumPy快速入门】NumPy高级操作: 合并, 分割, 广播

经过前几章的学习,我们已经掌握了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)

 视频纲要:
00:56 - Transposing Array
05:00 - Stacking Array
08:27 - Splitting Array
09:53 - Tile
11:43 - Broadcasting
 

#numpy 

What is GEEK

Buddha Community

【NumPy快速入门】NumPy高级操作: 合并, 分割, 广播
许 志强

许 志强

1647407700

【NumPy快速入门】NumPy高级操作: 合并, 分割, 广播

经过前几章的学习,我们已经掌握了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)

 视频纲要:
00:56 - Transposing Array
05:00 - Stacking Array
08:27 - Splitting Array
09:53 - Tile
11:43 - Broadcasting
 

#numpy 

许 志强

许 志强

1647385980

【NumPy快速入门】NumPy切片和索引

上一章我们学习了如何使用一些NumPy常用的方法创建数组,今天我们来学习操作数组非常重要的一个内容:索引(indexing)。

数组的索引指的是定位数据元素,不仅仅是定位单个元素,也可以定位多个元素。

一维数组索引

基本的索引是通过方括号 [] 实现的,如果括号中放置一个参数,则返回与该索引相对应的单个元素。如果是 [2: ],表示从该索引开始以后的所有元素都被提取。如果使用了两个参数,比如 [2:10],表示提取两个索引位置 (不包括右边索引元素) 之间的所有元素。

array = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
print(array)

print(array[2:5]) # 第3个元素到第5个元素(不包括第五个元素) [2 3 4]

print(array[:5]) # 第1个元素到第5个元素 [0 1 2 3 4]

print(array[:-3]) # 第1个元素到倒数第4个元素(不包括倒数第3个元素) [0 1 2 3 4 5 6]

print(array[3:-1]) # 第4个元素到倒数第2个元素(不包括倒数第1个元素)[3 4 5 6 7 8]

print(array[3:-3]) # 第4个元素到倒数第4个元素 [3 4 5 6]

print(array[::-1]) # 颠倒NumPy的数组 [9 8 7 6 5 4 3 2 1 0]

print(array[2:7:3]) # 从第3个元素到7个元素(不包括第7个元素),间隔为3 [2 5]

print(array[-3:3:-1]) # 从倒数第3个元素(7),到第5个元素(4),回跳一个位置 [7 6 5 4]

二维矩阵索引

matrix_1 = np.arrange(20).reshape(4, 5) # 创建一个 4 * 5 的矩阵,取值范围在 0~19
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]]

matrix_1[0, 3] # 第1行第4列的元素:3

matrix_1[0, 1] = 100 # 第1行第2列的元素变成 100
[[  0 100   2   3   4]
 [  5   6   7   8   9]
 [ 10  11  12  13  14]
 [ 15  16  17  18  19]]

matrix_1[2, :] # 第3行的所有元素 [10 11 12 13 14]

matrix_1[:, 2] # 第3列的所有元素 [ 2  7 12 17]

matrix_1[1:3, 2:4] # 第2行到第3行,第3列到第4列的子矩阵
[[ 7  8]
 [12 13]]

我们还能使用多个数组,来定位各个维度上的位置,拿到特定位置的元素:

matrix_1[[0, 2, 2], [0, 1, 0]] # 返回3个元素 [0 11 10] 第1个元素位置是[0,0],第2个元素位置是[2,1],第3个元素位置是[2,0]

matrix_1[[1, 0, 2], [1, 1, 1]] # 返回3个元素 [6 100 11] 第1个元素位置是[1,1],第2个元素位置是[0,1],第3个元素位置是[2,1]

matrix_1[(0, 3), 2:5] # 第1行和第4行,列数3到5
[[2, 3, 4],
 [17, 18, 19]]

多维数组索引 Ellipsis(…)

切片操作中还能使用省略号 ,使得子数组维度的长度与原数组的维度相同,使用…可以包含未显性定义维度的所有内容。

matrix_3d = np.arange(80).reshape(4, 5, 4)
[[[ 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]
  [36 37 38 39]]

 [[40 41 42 43]
  [44 45 46 47]
  [48 49 50 51]
  [52 53 54 55]
  [56 57 58 59]]

 [[60 61 62 63]
  [64 65 66 67]
  [68 69 70 71]
  [72 73 74 75]
  [76 77 78 79]]]

matrix_3d[2, ...] # 矩阵3中所有的元素
[[40 41 42 43]
 [44 45 46 47]
 [48 49 50 51]
 [52 53 54 55]
 [56 57 58 59]]

matrix_3d[0, 2, ...] # 矩阵1中,第3行所有的列
[ 8  9 10 11]

matrix_3d[3, ..., 2] # 矩阵4中,所有行,第3列
 
matrix_3d[3, :, 2] # 矩阵4中,所有行,第3列

对于多维数组,我们也能使用多个数组,来定位各个维度上的位置,定位到特定位置的元素:

matrix2 = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])
print(matrix2)
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]

vector = np.array([0, 2, 0, 1])
print(matrix2[np.arange(4), vector]) # 拿到以下4个位置的元素: [0,0],[1,2],[2,0],[3,1]
[ 1  6  7 11]

matrix2[np.arange(4), vector] += 10 # 给特定位置上的元素加10
print(matrix2)
[[11  2  3]
 [ 4  5 16]
 [17  8  9]
 [10 21 12]]

布尔索引(Boolean Indexing)

学习了基本的索引操作,接下来我们来学学如何使用布尔数组来定位numpy数组。

matrix3 = np.array([[1, 2], [3, 4], [5, 6]])
bool_idx = matrix3 > 2 # 拿到一个布尔数组,其中元素代表相应位置的元素是否大与2
[[False False]
 [ True  True]
 [ True  True]]

matrix3[bool_idx] # 使用布尔数组,获取矩阵中大于2的值
[3 4 5 6]

matrix3 = np.arange(0, 15).reshape(3, 5)
matrix3[matrix3 > 2] # 拿到矩阵中所有大于2的元素
[ 3  4  5  6  7  8  9 10 11 12 13 14]

print(matrix3[matrix3 % 2 == 1]) # 拿到矩阵中所有不能整除2的元素(奇数)
[ 1  3  5  7  9 11 13]

我们也能使用多个布尔数组,通过 ix_ 函数来索引不同维度上的数据

matrix4 = np.arange(36).reshape(3, 12)
[[ 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]]

rows_on = [True, False, True]
cols_on = np.arange([False, True, False] * 4)

matrix4[np.ix_(rows_on, cols_on)]) # 拿到一个特定的子矩阵:第一个维度位置是1和3,第二个维度位置是1,4,7,10
[[ 1  4  7 10]
 [25 28 31 34]]

迭代 (Iterating)

学完如何索引数组之后,最后我们来学习一下如何迭代数组

two_d_matrix = np.arange(24).reshape(2, 12)

for row in two_d_matrix: # 迭代row
    print(row)
[ 0  1  2  3  4  5  6  7  8  9 10 11]
...

for column in two_d_matrix.T: # .T会转置矩阵,然后迭代row,就会迭代原来的col
    print(column)
[ 0 12]
[ 1 13]
...

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

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
for item in three_d_matrix: # 迭代每个矩阵
    print(item)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
...
for item in three_d_matrix.flat: # 迭代所有的元素
    print(item)

课后练习答案

创建一个函数处理以下这个包含0-44的三维数组,将第2个矩阵中,第1行和第3行,第2~4列的元素变为1。然后在把数组中所有的奇数变为1,所有偶数变成0。最后再把矩阵的形状变成(9,5)。

np_array = np.arange(45).reshape((3,3,5))

答案

def np_operator(input_array):
  input_array[1, (0, 2), 1:4] = 1
  print(input_array)
  input_array[input_array % 2 == 1] = 1
  input_array[input_array % 2 == 0] = 0
  print(input_array)
  input_array.shape = (9, 5)
  print(input_array)
  return input_array

new_np_array = np_operator(np_array)

视频纲要:
00:49 - 1D Array Indexing
05:34 - 2D Matrix Indexing
10:49 - Higher Dimensions Indexing
17:29 - Boolean Indexing
24:40 - Iterating 
26:59 - Homework
 

#numpy 

许 志强

许 志强

1647396840

【NumPy快速入门】NumPy常用数学函数

通过前两章的学习,我们已经学会了创建NumPy数组常用的函数,以及和索引相关的内容。这一章我们来更深入地学习NumPy中和数学相关的函数,帮助我们更好地掌握如何实现NumPy数组中的数学运算。

Arithmetic operations

首先我们来学习一下 NumPy中基本的算数运算,除了使用计算符号实现这类操作之外,我们还能直接使用NumPy自带的一些函数。

matrix1 = np.array([[1,2],[3,4]], dtype=np.float64)
matrix2 = np.array([[5,6],[7,8]], dtype=np.float64)

matrix1 + matrix2 # 加法
np.add(matrix1, matrix2)
[[ 6.  8.]
 [10. 12.]]

matrix1 - matrix2 # 减法
np.add(matrix1, matrix2)
[[-4. -4.]
 [-4. -4.]]

matrix1 * matrix2 # 相乘
np.multiply(matrix1, matrix2)
[[ 5. 12.]
 [21. 32.]]

matrix1 / matrix2 # 相除
np.divide(matrix1, matrix2)
[[0.2        0.33333333]
 [0.42857143 0.5       ]]

matrix2 // matrix1 # 整除
np.floor_divide(matrix2, matrix1)
[[5. 3.]
 [2. 2.]]

matrix2 % matrix1 # 取余
np.mod(matrix2, matrix1)
[[0. 0.]
 [1. 0.]]

matrix1 ** matrix2 # 幂运算
np.power(matrix1, matrix2)
[[1.0000e+00 6.4000e+01]
 [2.1870e+03 6.5536e+04]]

Mathematical and statistical functions

除了简单的算数计算,NumPy也提供了许多和数学与统计相关的函数,帮助我们方便地获取相关的运算结果。

rand_matrix = (np.random.randint(10, 1000, size=(2,5)) - 500) / 10.0
[[ 20.   15.4 -12.8  -1.1  -8.8]
 [-18.3 -19.6  20.2 -15.5  43.1]]

print(np.isnan(rand_matrix)) # 检测是否为nan
[[False False False False False]
 [False False False False False]]

print(np.sign(rand_matrix)) # 检测每个元素的正负,正是1,负是-1
[[ 1.  1. -1. -1. -1.]
 [-1. -1.  1. -1.  1.]]

print(np.nonzero(rand_matrix)) # 拿到数组中非零元素的位置,会返回非零元素的索引值位置
(array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), array([0, 1, 2, 3, 4, 0, 1, 2, 3, 4]))

print(np.ceil(rand_matrix)) # 拿到大于元素的最小整数
[[ 20.  16. -12.  -1.  -8.]
 [-18. -19.  21. -15.  44.]]

print(np.cumsum(rand_matrix)) # 计算数组的累计和
[ 20.   35.4  22.6  21.5  12.7  -5.6 -25.2  -5.  -20.5  22.6]

print(np.diff(rand_matrix)) # 计算数组的离散差值
[[ -4.6 -28.2  11.7  -7.7]
 [ -1.3  39.8 -35.7  58.6]]

print(np.sqrt(matrix1)) # 计算数组的正平方根
[[4.47213595 3.92428337        nan        nan        nan]
 [       nan        nan 4.49444101        nan 6.56505902]]

print(np.square(rand_matrix)) # 计算数组的平方数
[[4.00000e+02 2.37160e+02 1.63840e+02 1.21000e+00 7.74400e+01]
 [3.34890e+02 3.84160e+02 4.08040e+02 2.40250e+02 1.85761e+03]]

m2 = np.arange(10) * 2
np.sin(m2) # 计算数组的三角函数 sin
[ 0.          0.90929743 -0.7568025  -0.2794155   0.98935825 -0.54402111
 -0.53657292  0.99060736 -0.28790332 -0.75098725]

print(np.sort(rand_matrix)) # 将数组进行排序
[[-12.8  -8.8  -1.1  15.4  20. ]
 [-19.6 -18.3 -15.5  20.2  43.1]]

还有以下常用的统计函数:

matrix = np.arange(10).reshape(2, 5)
[[-5 -4 -3 -2 -1]
 [ 0  1  2  3  4]]

matrix.mean() # 数组中的平均数: -0.5
np.median(matrix) # 数组中的中位数: -0.5
np.argmin(matrix) # 数组中最大数的索引值: 0
np.argmax(matrix) # 数组中最大数的索引值: 9

np.sum(matrix) # 计算所有元素相加的和: -5
np.abs(matrix) # 计算数组元素的绝对值
[[5 4 3 2 1]
 [0 1 2 3 4]]
np.max(matrix) # 数组中最大的数: 4

np.sum(matrix) # 计算所有元素的和: -5
np.sum(matrix, axis=0) # 按列相加之和的数组
[-5 -3 -1  1  3]
np.sum(matrix, axis=1) # 按行相加之和的数组
[-15  10]

Linear Algebra

除了上面提到的基本数学计算,NumPy还支持和线性代数相关的计算操作,可以帮助我们非常方便地对矩阵进行相关操作。

matrix1 = np.arange(8).reshape(2, 4)
print(matrix)
[[0 1 2 3]
 [4 5 6 7]]
print(matrix.T) # 转置矩阵 Tranpose
[[0 4]
 [1 5]
 [2 6]
 [3 7]]
matrix2 = np.arange(5)
print(matrix2)
print(matrix2.T)
[[0 1 2 3 4]]

matrix3 = matrix2.reshape(1, 5)
print(matrix3)
print(matrix3.T)
[[0]
 [1]
 [2]
 [3]
 [4]]

矩阵乘法

matrix1 = np.arange(8).reshape(2, 4)
print(matrix1)
[[0 1 2 3]
 [4 5 6 7]]

matrix2 = np.arange(8).reshape(4, 2)
print(matrix2)
[[0 1]
 [2 3]
 [4 5]
 [6 7]]

print(matrix1.dot(matrix2)) # 矩阵积
[[28 34]
 [76 98]]

numpy.linalg 模块中有很多线性代数相关函数。 使用这个模块,可以计算逆矩阵、求特征值、解线性方程组以及求解行列式等。

matrix = np.arange(9).reshape(3, 3)
inverse_matrix = np.linalg.inv(matrix) # 获得逆矩阵: 此语句会返回错误,因为该矩阵是Singular Matrix,所有没有相对性的逆矩阵

valid_matrix = np.array([[1,2,3],[5,7,11],[21,29,31]])
print(valid_matrix)
inverse_matrix = np.linalg.inv(valid_matrix) # 获得逆矩阵
print(inverse_matrix)
[[-2.31818182  0.56818182  0.02272727]
 [ 1.72727273 -0.72727273  0.09090909]
 [-0.04545455  0.29545455 -0.06818182]]

print(valid_matrix.dot(inverse_matrix)) # The product of a matrix by its inverse returns the identity matrix
[[ 1.00000000e+00 -1.66533454e-16  0.00000000e+00]
 [ 6.31439345e-16  1.00000000e+00 -1.38777878e-16]
 [ 5.21110932e-15 -2.38697950e-15  1.00000000e+00]]

print(np.eye(3)) # eye函数可以输出对角线为1的矩阵,函数内的参数代表1的个数

获得矩阵的行列式:

np.linalg.det(valid_matrix) # 获得矩阵的行列式: 43.9999

计算矩阵的特征值和右特征向量(Eigenvalues and eigenvectors)

eigenvalues, eigenvectors = np.linalg.eig(matrix) # 计算方阵的特征值和右特征向量
print(eigenvalues)
print(eigenvectors)
print(matrix.dot(eigenvectors) - eigenvalues * eigenvectors) # M.E1 - E2.E1 = 0

我们甚至还能直接使用NumPy自带的函数解决线性标量方程,比如我们可以使用下面的代码,给以下两个方程求解:

  • 2x + 6y = 6
  • 5x + 3y = -9
coeffs = np.array([[2,6],[5,3]])
depvars = np.array([6,-9])
solution = np.linalg.solve(coeffs, depvars) # 以矩阵形式解一个线性矩阵方程
print(solution) # array([-3., 2.])

coeffs.dot(solution), depvars # (array([ 6., -9.]), array([ 6, -9]))

Homework

将以下的两个不同形状的矩阵,进行矩阵的相乘,得到最终形状为(3,3)的矩阵。然后计算出数组中各个元素的正平方根,最后再将计算之后所有元素相加,得到一个和。

matrix1 = np.arange(15).reshape(3, 5)
matrix2 = np.arange(15).reshape(5, 3)

答案:

matrix3 = np.dot(matrix1, matrix2)
matrix3 = np.sqrt(matrix3)
total_sum =np.sum(matrix3)
print(total_sum) # 143.2015963354727

 视频纲要:
00:54 - Arithmetic operations
02:30 - Math/Stat functions
11:23 - Linear Algebra
18:47 - Homework
 

#numpy 

许 志强

许 志强

1647375120

【NumPy快速入门】如何创建NumPy数组?创建NumPy数组常用函数

上一章我们介绍了NumPy的重要性,并通过代码演示了一些NumPy好用的功能。从这一章起就正式开始NumPy的学习,这一章先来学习创建NumPy数组常用的函数。

首先复习一下如何创建NumPy数组:

import numpy as np
np_array = np.array([1, 2, 3])
print(type(np_array)) # <class 'numpy.ndarray'>
print(np_array.shape) # (3,)

用来创建NumPy数组常用的函数:

zero_array = np.zeros((5)) # [0. 0. 0. 0. 0.]
one_array = np.ones((4,4))
"""
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
"""
full_array = np.full((3,3), 7)
"""
[[7 7 7]
 [7 7 7]
 [7 7 7]]
"""
empty_array = np.empty((2, 3)) # 无法预测其数值,内存里有什么就是什么
"""
[[1.94830084e-316 0.00000000e+000 0.00000000e+000]
 [0.00000000e+000 0.00000000e+000 0.00000000e+000]]
"""

Axis and Data Type

在接下去学习之前,我们也来了解一下NumPy中一些重要的概念,其中一个重要的名词叫 axis,也就是NumPy数组中的维度。axis 的数量叫做 rank,比如一个矩阵的尺寸是3×4,那么这个数组的rank就是2,代表这是个二维的矩阵,而一维的数组rank自然就是1。NumPy数组的shape则是用一个包含了所有axis长度的数组组成,而一个数组的大小(size)指的是一个数组中有多少元素,将所有axis的长度相乘即可。

zero_array = np.zeros((3, 4))
zero_array 
"""
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
"""
zero_array.shape # (3, 4)
zero_array.ndim # 2
zero_array.size # 12

每个NumPy数组由一串相同数据类型的元素组成,NumPy可用构建多种数据类型的数组。当你在创建NumPy数组时,NumPy会自动为你安排特定的数据类型,而你也能显性地定义数据类型:

int_array = np.array([1, 2])
int_array.dtype # int64
float_array = np.array([1.0, 2.0])
float_array.dtype # float64
float_array.itemsize # 每个元素的大小 8
float32_array = np.array([1, 2], dtype=np.float32)
float32_array.dtype # float64
float32_array.itemsize # 4

一个数组的形状(shape)是能被改变的,但是要保证改变之后的大小(size 元素的个数)不能变:

x = np.array([[1, 2, 3], [4, 5, 6]])
x.ndim # 2
x.shape = (6,) # Change its shape in place
x.ndim # 1

np.reshape和ravel可以创建出指向相同数据的新数组:

y = np.reshape(x, (3, 2)) # 创建形状不同的新数组,此数组指向相同的数据
y[0, 0] = 1000
print(x) # [1000, 2, 3, 4, 5, 6]
z = y.ravel() # 指向相同数组的新数组
z[0] = 2000
print(x) # [2000, 2, 3, 4, 5, 6]

如果要复制NumPy数组,可以使用copy函数创建新数组,新数据的改变不会影响原数组:

array1 = np.arange(4)
array2 = array1.copy()
array2[0] = 2000
array1 # [0, 1, 2, 3]

Arithmetic Operations

NumPy数组也支持一些常用的算数运算:

array1 = np.array([1, 2, 3, 4])
array2 = np.array([5, 6, 7, 8])
print(array1 + array2) # [6, 8, 10, 12]
print(array1 - array2) # [-4, -4, -4, -4]
print(array1 * array2) # [5, 12, 21, 32]
print(array1 / array2) # [0.2, 0.33333333, 0.42857143, 0.5]
print(array1 // array2) # [0, 0, 0, 0]
print(array1 % array2) # [1, 2, 3, 4]
print(array1 ** array2) # [1, 64, 2187, 65536]

Useful Functions

创建由随机数生成的数组:

np.random.random((5, 5)) # 创建数值在0~1之间的随机数数组
[[9.90294681e-01 6.51182438e-02 5.48551064e-02 4.13554785e-01 2.77916852e-02]
 [2.82902321e-01 3.82854818e-01 9.72110582e-01 6.41764840e-02 2.29831744e-01]
 [4.23048152e-01 8.07057740e-01 5.42399825e-01 4.56243935e-01 5.35099209e-01]
 [9.25518420e-01 6.01543526e-02 5.92246756e-01 5.86704977e-01 2.16448787e-01]
 [1.69499508e-01 2.13986897e-01 4.70516150e-04 9.32854954e-01 6.37419153e-01]]
np.random.randint(100, size=(5)) # 创建5个数值范围在0~100之间的随机整数
np.random.rand(3, 4) # 创建一个形状为(3,4),数值范围在0~1之间的矩阵
np.random.randn(10) # 创建一个长度为10,数值范围能构成正态分布(normal distribution)的数组

可以用eye函数创建一个对角线为1其他为0的矩阵:

eye_array = np.eye(4)
"""
[[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]]
"""

使用arange创建等差的数组:

np.arange(1,5) # [1, 2, 3, 4]
np.arange(1.0, 5.0) # [1.0, 2.0, 3.0, 4.0]
np.arnage(1, 5, 0.5) # (最小值, 最大值, 步长) [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]

linspace 可以用来创建等分线段型的数据:

np.linspace(0, 5, 10) # 起点是0,终点是5,将0~5的区间内平均分为10个数据,从小到大存到一个数组中

除了用这些常见的NumPy函数创建数组,我们还能使用fromfunction函数,自定义NumPy数据的创建:

np.fromfunction(lambda i, j: i == j, (2, 3), dtype=int)
[[True, False, False],
 [False, True, False]]

np.fromfunction(lambda i, j: i + j, (2, 3), dtype=int)
[[0, 1, 2],
 [1, 2, 3]]

def my_function(z, y, x):
    return x * y + z
np.fromfunction(my_function, (3, 2, 10))
array([[[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
        [ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.]],

       [[ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
        [ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.]],

       [[ 2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.],
        [ 2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11.]]])

本章函数整理

np.array # 创建NumPy数组
np.zeros(array_shape) # 创建只包含0的数组
np.ones(array_shape) # 创建只包含1的数组
np.full(array_shape, number) # 创建只包含一个数值的数组
np.empty(array_shape) # 创建一个不赋值的数组

np_array.shape # 包含各个维度大小的Tuple
np_array.ndim # 数据的rank数量
np_array.size # 包含元素的数量

np.array(python_list, dtype=<numpy_data_type>) # 创建指定数据类型的NumPy数组
np.reshape(original_array, new_shape) # 创建一个不同形状的数组
numpy_array.ravel() # 指向相同数据的新数组
numpy_array.copy() # 复制一个NumPy数组

np_array1 + np_array2 # 相加
np_array1 - np_array2 # 相减
np_array1 * np_array2 # 相乘
np_array1 / np_array2 # 相除
np_array1 // np_array2 # 取整除
np_array1 % np_array2 # 取余
np_array1 ** np_array2 # 幂运算

np.random.randint(number_range, size=shape) # 创建一个取值范围在0~number_range之间的数组
np.random.rand(shape) # 创建一个数值范围在0~1之间,形状为shape的数组
np.random.randn(shape) # 创建一个数值范围能形成正态分布,形状为shape的数组

np.eye(matrix_length) # 创建对角线是1,形状是(matrix_length, matrix_length)的矩阵
np.arange(min_value, max_value, step) # 创建有固定步长,取值范围在min_value和max_value之间的数组
np.linspace(start, end, items_num) # 创建一个起点是start, 终点是end,被等分成items_num数量的数组
np.fromfunction(function, numpy_array_shape) # 使用自定义函数创建numpy数组

 视频纲要:
00:53 - 创建数组常见函数
12:14 - 常用算术运算函数
13:31 - 其他创建数组有用的函数
#numpy 

许 志强

许 志强

1647364320

【NumPy快速入门】什么是NumPy?如何快速学会NumPy?

什么是Numpy?

随着现在市场对数据岗位的需求越来越多,数据和人工智能领域中的工具也百花齐发。而其中Python生态最为活跃,因语言本身的简洁性和其丰富的社区环境,Python毫无争议地登上了最受欢迎的宝座,所以如今Python成了入门数据领域的基本功。

而在你学习各种各种机器学习算法框架(Tensorflow, Pytorch 等等)之前,有三大基本工具是必须要掌握的,那就是NumPy, Pandas和Matplotlib。NumPy最主要的用途是用来计算数组的,而 Pandas 是建立在 NumPy 之上,用来处理更多形式的数据类型(比如Dataframe和Series),Matplotlib则负责实现数据可视化。

那本系列就带大家掌握这三个工具中最基础的NumPy,帮助你随意玩转Array。

到底为什么使用NumPy呢?

为什么使用 NumPy?因为 NumPy 能帮助我们更灵活、更快速地处理数组(Array)计算啊。那为什么要处理数组计算呢?主要是因为现在机器学习的方法本质上都是在进行矩阵(Matrix)计算,而矩阵本质上就是多维数组。而大部分复杂数据的组成形态也是矩阵,所以学会Numpy还是很有必要的。

Python Array 和 Numpy Array 的区别

那大家就好奇了,那我为什么不可以使用 Python 自带的 List 来处理数组呢,主要是因为以下两点原因:

  1. 处理数组更快:NumPy和Pandas的底层逻辑是用C语言写的,C这种底层语言自然能更快更有效率地调用电脑资源。
  2. 处理数组更灵活:NumPy 库中提供更多处理 Array 的方式,比如合并分割数组、对数组进行统计计算等等。

总而言之,NumPy使用了更优秀的实现方式来处理数组,和Python自带的数据类型相比,NumPy能用更少的空间存储相同的数据量,并用更快的方式进行数组计算。

NumPy基本功能演示

为了更方便并直观地使用NumPy,我建议大家使用 Jupyter Notebook 运行 Python 代码,另外再推荐一个谷歌开发的工具 CoLab,能在游览器上直接运行 Python 代码,CoLab中自带了很多库,还能使用谷歌免费提供的GPU实现更快速的数组计算。

首先我们先导入 numpy:

import numpy as np

然后使用以下的代码创建 numpy 数组:

np_array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(np_array) # 打印出numpy数组
print(np_array.shape) # 打印每个维度上的数组量:(2, 4)

numpy中还有一些很便捷的方式,帮助我们创建出特定的数组,比如下面这段代码,就会创建出一个10行10列的数组,其中的数字都是数据类型为float的1.0:

np_array = np.ones((10, 10), dtype = np.float)
print(np_array)

numpy array 的形状还能被改变,可以简单理解为一个数组的行数和列数被改变,但是其中的数字不变:

reshaped_np_array = np_array.reshape((2, 50))

Numpy vs Python List

看来用 NumPy 创建数组还挺方便的,那么 NumPy 在存储空间和计算效率上真的会碾压 Python List 吗,我们来运行下面的代码看一看:

import sys
python_list = range(1000)
print(f'Python list size: {sys.getsizeof(python_list) * len(python_list)}') # 48000
np_array = np.arange(1000)
print(f'Numpy Array size: {np_array.size * np_array.itemsize}') # 8000

可以看到 Python List 用了整整6倍的空间,存储和 numpy 数组相同的内容。

那我们再比较一下计算效率好了:

import time
array_size = 100000
python_list1 = range(array_size)
python_list2 = range(array_size)

start_time = time.time()
python_multiplication = [(n1 * n2) for n1, n2 in zip(python_list1, python_list2)]
print(f'Python multiplication time used: {time.time() - start_time}') # 0.012480020523071289

np_array1 = np.arange(array_size)
np_array2 = np.arange(array_size)

start_time = time.time()
numpy_multiplication = np_array1 * np_array2
print(f'Numpy multiplication time used: {time.time() - start_time}') # 0.0008220672607421875

计算就更夸张了,同样是实现数组之间的相乘,Python List 所用的时间至少是 Numpy 的上百倍。可见 Numpy 真的是在空间和时间上完爆 Python 自带的 List。

numpy array 还自带一些统计函数,帮助我们很方便地从数组中获得有用的统计数据:

np_array = np.arange(1, 21).reshape((4, 5))
print(np.argmin(np_array)) # 0
print(np.argmax(np_array)) # 19
print(np.mean(np_array)) # 10.5
print(np.median(np_array)) # 10.5

除了这些方便的数学函数,我们还可以很方便地将 numpy 存储在文件中,也很容易使用 numpy 从文件中读取数组:

from numpy import savetxt
np_array = np.array([1, 2, 3, 4, 5])
savetxt('data.csv', np_array, delimiter=',')
print(np_array)

from numpy import loadtxt
data = loadtxt('data.csv', delimiter=',')
print(data.astype(np.int))

NumPy课程大纲

上面演示的NumPy功能只是冰山一角,NumPy支持的功能非常丰富,如果只看官方文档肯定会让你头昏脑涨。但其实 NumPy 常用的功能是能在短时间内掌握的,那些不常用的奇技淫巧只需要在用到时候查查文档即可。为了帮助大家在短时间内掌握NumPy最核心的常用功能,我会在【NumPy入门教程】系列涵盖但不限于以下内容:

  1. Numpy Array 创建
  2. Array Indexing 索引
  3. Array Math 数学
  4. 高级操作(Broadcasting, Transposing)
  5. 合并和分割 Numpy Array

希望在完成这个教程后,大家能自信地操起NumPy对数组一顿操作猛如虎。

 视频纲要:
01:49​ - 什么是NumPy?
05:21​ - NumPy代码演示
16:10 - 《NumPy入门教程》大纲
#numpy