1647385980
上一章我们学习了如何使用一些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]]
切片操作中还能使用省略号 …,使得子数组维度的长度与原数组的维度相同,使用…可以包含未显性定义维度的所有内容。
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]]
学习了基本的索引操作,接下来我们来学学如何使用布尔数组来定位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]]
学完如何索引数组之后,最后我们来学习一下如何迭代数组
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
1647385980
上一章我们学习了如何使用一些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]]
切片操作中还能使用省略号 …,使得子数组维度的长度与原数组的维度相同,使用…可以包含未显性定义维度的所有内容。
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]]
学习了基本的索引操作,接下来我们来学学如何使用布尔数组来定位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]]
学完如何索引数组之后,最后我们来学习一下如何迭代数组
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
1647396840
通过前两章的学习,我们已经学会了创建NumPy数组常用的函数,以及和索引相关的内容。这一章我们来更深入地学习NumPy中和数学相关的函数,帮助我们更好地掌握如何实现NumPy数组中的数学运算。
首先我们来学习一下 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]]
除了简单的算数计算,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]
除了上面提到的基本数学计算,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自带的函数解决线性标量方程,比如我们可以使用下面的代码,给以下两个方程求解:
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]))
将以下的两个不同形状的矩阵,进行矩阵的相乘,得到最终形状为(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
1647364320
随着现在市场对数据岗位的需求越来越多,数据和人工智能领域中的工具也百花齐发。而其中Python生态最为活跃,因语言本身的简洁性和其丰富的社区环境,Python毫无争议地登上了最受欢迎的宝座,所以如今Python成了入门数据领域的基本功。
而在你学习各种各种机器学习算法框架(Tensorflow, Pytorch 等等)之前,有三大基本工具是必须要掌握的,那就是NumPy, Pandas和Matplotlib。NumPy最主要的用途是用来计算数组的,而 Pandas 是建立在 NumPy 之上,用来处理更多形式的数据类型(比如Dataframe和Series),Matplotlib则负责实现数据可视化。
那本系列就带大家掌握这三个工具中最基础的NumPy,帮助你随意玩转Array。
为什么使用 NumPy?因为 NumPy 能帮助我们更灵活、更快速地处理数组(Array)计算啊。那为什么要处理数组计算呢?主要是因为现在机器学习的方法本质上都是在进行矩阵(Matrix)计算,而矩阵本质上就是多维数组。而大部分复杂数据的组成形态也是矩阵,所以学会Numpy还是很有必要的。
那大家就好奇了,那我为什么不可以使用 Python 自带的 List 来处理数组呢,主要是因为以下两点原因:
总而言之,NumPy使用了更优秀的实现方式来处理数组,和Python自带的数据类型相比,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 创建数组还挺方便的,那么 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对数组一顿操作猛如虎。
视频纲要:
01:49 - 什么是NumPy?
05:21 - NumPy代码演示
16:10 - 《NumPy入门教程》大纲
#numpy
1647375120
上一章我们介绍了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]]
"""
在接下去学习之前,我们也来了解一下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]
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]
创建由随机数生成的数组:
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
1647407700
经过前几章的学习,我们已经掌握了NumPy中最常用的一些功能,比如如何创建NumPy Array,如何在不同维度的矩阵中进行索引,还有一些常用的数学函数,本章是NumPy入门教程的最后一章,我们就来学习一下NumPy中的一些高级的操作函数。
我们可以使用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)
接下来我们来学习一些如何合并数组。
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]]]
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函数可以重复某个数组以构成一个新的数组:
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中重要的机制,虽然这个比较枯燥,但是也是需要了解的。
广播原则的前提:两个数组必须可以转化成维度大小一模一样才能进行运算。
规则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)
将不同数据类型的数组进行运算时,运算结果会自动将数据类型转变为更为精确的数据类型:
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
将下面两个形状为(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