【Python库系列】超详细的NumPy入门

2023-11-12

目录

一、数组的创建

1.1 创建数组

1.2 数组的性质

二、数组的存载

2.1 numpy 自身的 .npy 格式

2.2 文本 .txt 格式

2.3 文本 .csv 格式

三、数组的获取

3.1 正规索引

3.2 布尔索引

3.3 花式索引

四、数组的变形

4.1 重塑和打平

4.2 合并和分裂

4.3 重复和拼接

4.4 其他操作

五、数组的计算

5.1 元素层面计算

5.2 线性代数计算

5.3 元素整合计算

5.4 广播机制计算


前提:导入numpy库。

import numpy as np

一、数组的创建

1.1 创建数组

创建numpy数组的三种方式:

  • 按步就班法:np.array() 用在列表和元组上 
  • 定隔定点法:np.arange() 和 np.linspace()
  • 一步登天法:np.ones(),np.zeros(),np.eye() 和 np.random.random()

1. 按步就班法

list = [1, 2, 3.5, 5.5, 8]
np.array(list)
array([1. , 2. , 3.5, 5.5, 8. ])
tuple = (1, 2, 3.5, 5.5, 8)
print(np.array(tuple))
np.array(tuple)
[1.  2.  3.5 5.5 8. ]
array([1. , 2. , 3.5, 5.5, 8. ])

注意,numpy数组的输出都带有 array() 的字样,里面的元素用“中括号 []”框住。用函数 print 打印 numpy 数组就没有 array() 的字样了。

2. 定隔定点法

  • 定隔的 arange():  固定元素大小间隔
  • 定点的 linspace():固定元素个数

函数 arange 的参数为起点,终点,间隔:arange(start , stop , step)。其中 stop 必须要有,start 和 step 没有的话默认为 1。

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

函数 linspace 的参数为起点,终点,点数:linspace (start , stop , num)。其中 start 和 stop 必须要有,num 没有的话默认为 50。

print(np.linspace(2, 6, 3))
print(np.linspace(2, 8, 4))
print(np.linspace(0, 49))
[2. 4. 6.]
[2. 4. 6. 8.]
[ 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.]

3. 一步登天法

  • zeros():    创建全是 0 的 n 维数组
  • ones():     创建全是 1 的 n 维数组
  • random():创建随机 n 维数组
  • eye():       创建对角矩阵

前三种,由于输出是 n 为数组,它们的参数是一个「标量」或「元组类型的形状」。

print(np.zeros(5)) # 标量5代表形状(5,)
print(np.ones((2, 3)))
print(np.random.random((2, 3, 4)))
[0. 0. 0. 0. 0.]
[[1. 1. 1.]
 [1. 1. 1.]]
[[[0.42180299 0.84854733 0.115058   0.65318331]
  [0.03357884 0.05289622 0.19615066 0.22481889]
  [0.05550249 0.2390295  0.24496322 0.83995308]]

 [[0.73575253 0.19963972 0.3201794  0.75923466]
  [0.54934324 0.6029062  0.39607958 0.9521938 ]
  [0.10393022 0.71187835 0.04202606 0.85566354]]]

函数 eye(),它的参数就是一个标量,控制矩阵的行数或列数。

eye() 里面的参数 k

  • k = 0: 默认设置,代表 1 落在对角线上
  • k = 1: 代表 1 落在对角线右上方
  • k = -1:代表 1 落在对角线左下方
print(np.eye(4))
print(np.eye(4, k=1))
[[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]]
[[0. 1. 0. 0.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]
 [0. 0. 0. 0.]]

1.2 数组的性质

先用按部就班的 np.array() 带列表生成二维数组 arr。

arr = np.array([1, 2, 3.5, 5.5, 8])

再用 dir(arr) 来查看数组的属性,之后我们对 type,ndim,len(),size,shape,stride,dtype 这几个常用函数分析一波。

  • type:    数组类型,当然是 numpy.ndarray
  • ndim:   数组维度个数
  • len():   数组长度 
  • size:    数组元素个数
  • shape: 数组形状,即每个维度的元素个数 (用元组来表示),只有一维,元素个数为 5,写成元组形式是 (5,)
  • strides:数组跨度,即在某一维度下为了获取到下一个元素需要「跨过」的字节数 (用元组来表示),float64 是 8 个字节数 (bytes),因此跨度为 8
  • dtype:  数组元素类型 (注意和 type 区分)
print('类型:', type(arr))
print('维度:', arr.ndim)
print('长度:', len(arr))
print('个数:', arr.size)
print('形状:', arr.shape)
print('跨度:', arr.strides)
print('类型:', arr.dtype)
类型: <class 'numpy.ndarray'>
维度: 
长度: 5
个数: 5
形状: (5,)
跨度: (8,)
类型: float64

二维数组,甚至多维数组,大家可自行测试体会。

二、数组的存载

2.1 numpy 自身的 .npy 格式

  • save:np.save( ‘’文件名”,数组 ) 即可保存为 .npy 格式
  • load:np.load( "文件名" ) 即可加载该文件
arr_disk = np.arange(8)
np.save("arr_disk", arr_disk) # 保存在当前目录下

np.load("arr_disk.npy")
array([0, 1, 2, 3, 4, 5, 6, 7])

2.2 文本 .txt 格式

  • savetxt:用 np.savetxt( ‘’文件名”,数组 ) 即可保存为 .txt 格式
  • loadtxt: 用 np.loadtxt( "文件名" ) 即可加载该文件
arr_text = np.array([[1., 2., 3.], [4., 5., 6.]])
np.savetxt("arr_text.txt", arr_text) # 保存为当前目录下

np.loadtxt("arr_text.txt")
array([[1., 2., 3.],
       [4., 5., 6.]])

2.3 文本 .csv 格式

arr_csv = np.array([[1, 2, 3], [4, 5, 6]])
np.savetxt("arr_csv.csv", arr_csv) # 保存为当前目录下

np.loadtxt("arr_csv.csv")

# 或 np.genfromtxt("arr_csv.csv")
array([[1., 2., 3.],
       [4., 5., 6.]])

三、数组的获取

获取数组是通过索引 (indexing) 和切片 (slicing) 来完成的。

  • 索引:获取一个特定位置的元素,写法是 arr[index]
  • 切片:获取一段特定位置的元素,写法是 arr[start : stop : step]

切片的操作是可以用索引操作来实现的 (一个一个总能凑成一段),只是没必要罢了。为了简化,我们在本章三节标题里把切片和索引都叫做索引。索引数组有三种形式,正规索引 (normal indexing)、布尔索引 (boolean indexing) 和花式索引 (fancy indexing)。

3.1 正规索引

一维数组:

arr1 = np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr1[6])   # 索引
print(arr1[1:5]) # 切片
6
[1 2 3 4]

二维数组:

arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2)
print(arr2[2])    # 索引第三行
print(arr2[0][2]) # 索引第一行第三列
print(arr2[0, 2]) # 索引第一行第三列
[[1 2 3]
 [4 5 6]
 [7 8 9]]
[7 8 9]
3
3
print(arr2[:2])         # 切片前两行
print(arr2[:, [0, 2]])  # 切片第一列和第三列
print(arr2[1, :2])      # 切片第二行的前两个元素    
print(arr2[:2, 2])      # 切片第三列的前两个元素
[[1 2 3]
 [4 5 6]]
[[1 3]
 [4 6]
 [7 9]]
[4 5]
[3 6]

3.2 布尔索引

布尔索引,就是用一个由布尔 (boolean) 类型值组成的数组来选择元素的方法。

code = np.array(['BABA', 'FB', 'JD', 'BABA', 'JD', 'FB'])
price = np.array([[170,177,169], [150,159,153],
                  [24,27,26], [165,170,167],
                  [22,23,20], [155,116,157]])
print(code == 'BABA')
print(price[code == 'BABA'])
print(price[code == 'BABA', :1])

注:这种布尔索引的操作在 Pandas 更常用也更方便。

3.3 花式索引

花式索引是获取数组中想要的特定元素的有效方法。

arr = np.arange(32).reshape(8,4)
print(arr)
[[ 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(arr[[4,3,6]], '\n')   # 按特定顺序来获取第 5,4 和 7 行
print(arr[[-4,-3,-6]], '\n')  # 按特定顺序来获取倒数第 4,3 和 6 行
print(arr[[1,5,7,2], [0,3,1,2]]) # 获取第二行第一列、第六行第四列、第八行第二列、第三行第三列的元素
# print(np.array([arr[1,0], arr[5,3], arr[7,1], arr[2,2]]))
[[16 17 18 19]
 [12 13 14 15]
 [24 25 26 27]] 

[[16 17 18 19]
 [20 21 22 23]
 [ 8  9 10 11]] 

[ 4 23 29 10]
print(arr[:,[0,3,1,2]]) # 把原先的 [0,1,2,3] 的列换成 [0,3,1,2]
[[ 0  3  1  2]
 [ 4  7  5  6]
 [ 8 11  9 10]
 [12 15 13 14]
 [16 19 17 18]
 [20 23 21 22]
 [24 27 25 26]
 [28 31 29 30]]

四、数组的变形

本节介绍四大类数组层面上的操作,具体有:

  1. 重塑 (reshape) 和打平 (ravel, flatten)
  2. 合并 (concatenate, stack) 和分裂 (split)
  3. 重复 (repeat) 和拼接 (tile)
  4. 其他操作 (sort, insert, delete, copy)

4.1 重塑和打平

重塑 (reshape) 和打平 (ravel, flatten) 这两个操作仅仅只改变数组的维度:

  • 重塑:是从低维到高维
  • 打平:是从高维到低维

1. 重塑

用reshape()函数将一维数组 arr 重塑成二维数组。

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

当你重塑高维矩阵时,不想花时间算某一维度的元素个数时,可以用「-1」取代,程序会自动帮你计算出来。比如把 12 个元素重塑成 (2,  6),你可以写成 (2, -1) 或者 (-1, 6)。

print(arr.reshape((2, -1)))
print(arr.reshape((-1, 6)))
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]]
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]]

注意:

在众多计算机语言中,

  • 默认行主序的有 C 语言( order=‘C’ 等价于行主序)

  • 默认列主序的有 Fortran 语言( order=‘F’ 等价于列主序)

在 numpy 数组中,默认的是行主序,即 order ='C'。如果你真的想在「重塑」和「打平」时用列主序,只用把 order 设为 'F',以重塑举例:

arr = np.arange(12)
print(arr.reshape((4, 3)))
print(arr.reshape((4,3), order='F'))
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
[[ 0  4  8]
 [ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]]

2. 打平

用 ravel() 或flatten() 函数将二维数组 arr 打平成一维数组。

arr = np.arange(12).reshape((4,3))
print(arr)

ravel_arr = arr.ravel()
print(ravel_arr)

flatten_arr = arr.flatten()
print(flatten_arr)
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[ 0  1  2  3  4  5  6  7  8  9 10 11]

函数 ravel() 或 flatten() 的不同之处是:

  1. ravel() 按「行主序」打平时没有复制原数组,按「列主序」在打平时复制了原数组
  2. flatten() 在打平时复制了原数组

4.2 合并和分裂

合并 (concatenate, stack) 和分裂 (split) 这两个操作仅仅只改变数组的分合:

  • 合并:是多合一
  • 分裂:是一分多

1. 合并

使用「合并」函数有三种选择:

  1. 有通用的 concatenate
  2. 有专门的 vstack, hstack, dstack
  3. 有极简的 r_, c_

用下面两个数组来举例:

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

在 concatenate() 函数里通过设定轴,来对数组进行竖直方向合并 (轴 0) 和水平方向合并 (轴 1)。  

print(np.concatenate([arr1, arr2], axis=0))
print(np.concatenate([arr1, arr2], axis=1))
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
[[ 1  2  3  7  8  9]
 [ 4  5  6 10 11 12]]

在 NumPy 里还有专门合并的函数

  • vstack:v 代表 vertical,竖直合并,等价于 concatenate(axis=0)
  • hstack:h 代表 horizontal,水平合并,等价于 concatenate(axis=1)
  • dstack:d 代表 depth-wise,按深度合并,深度有点像彩色照片的 RGB 通道

print(np.vstack((arr1, arr2)))
print(np.hstack((arr1, arr2)))
print(np.dstack((arr1, arr2)))
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
[[ 1  2  3  7  8  9]
 [ 4  5  6 10 11 12]]
[[[ 1  7]
  [ 2  8]
  [ 3  9]]

 [[ 4 10]
  [ 5 11]
  [ 6 12]]]

和 vstack, hstack 不同,dstack 将原数组的维度增加了一维。

np.dstack((arr1, arr2)).shape
(2, 3, 2)

此外,还有一种更简单的在竖直和水平方向合并的函数,r_() 和 c_()。

print(np.r_[arr1,arr2])
print(np.c_[arr1,arr2])
[[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]]
[[ 1  2  3  7  8  9]
 [ 4  5  6 10 11 12]]

2. 分裂

使用「分裂」函数有两种选择

  1. 有通用的 split
  2. 有专门的 hsplit, vsplit

用下面数组来举例:

arr = np.arange(25).reshape((5,5))
print(arr)
[[ 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]]

和 concatenate() 函数一样,我们可以在 split() 函数里通过设定轴,来对数组沿着竖直方向分裂 (轴 0) 和沿着水平方向分裂 (轴 1)。 

first, second, third = np.split(arr, [1,3])
print('The first split is', first)    # 第 1 行
print('The second split is', second)  # 第 2 到 3 行
print('The third split is', third)    # 第 4 到 5 行
The first split is [[0 1 2 3 4]]
The second split is [[ 5  6  7  8  9]
 [10 11 12 13 14]]
The third split is [[15 16 17 18 19]
 [20 21 22 23 24]]

split() 默认沿着轴 0 分裂,其第二个参数 [1, 3] 相当于是个切片操作,将数组分成三部分。

vsplit() 和 split(axis=0) 等价,hsplit() 和 split(axis=1) 等价。

first, second, third = np.hsplit(arr, [1, 3])
print('The first split is', first)
print('The second split is', second)
print('The third split is', third)
The first split is [[ 0]
 [ 5]
 [10]
 [15]
 [20]]
The second split is [[ 1  2]
 [ 6  7]
 [11 12]
 [16 17]
 [21 22]]
The third split is [[ 3  4]
 [ 8  9]
 [13 14]
 [18 19]
 [23 24]]

4.3 重复和拼接

重复 (repeat) 和拼接 (tile) 这两个操作本质都是复制。

  • 重复:是在元素层面复制
  • 拼接:是在数组层面复制

1. 重复

函数 repeat() 复制的是数组的每一个元素,参数有几种设定方法:

  • 一维数组:用标量和列表来复制元素的个数
  • 多维数组:用标量和列表来复制元素的个数,用轴来控制复制的行和列
arr = np.arange(5)
print(arr)
print(arr.repeat(3))   # 数组 arr 中每个元素复制 3 遍
print(arr.repeat([1, 2, 3, 4, 5]))  # 数组 arr 中每个元素分别复制 1, 2, 3, 4, 5 遍
[0 1 2 3 4]
[0 0 0 1 1 1 2 2 2 3 3 3 4 4 4]
[0 1 1 2 2 2 3 3 3 3 4 4 4 4 4]
arr2 = np.arange(6).reshape((2,3))
print(arr2)
print(arr2.repeat(2, axis=0))        # 数组 arr2 中每个元素沿着轴 0 复制 2 遍
print(arr2.repeat([2,3,4], axis=1))  # 数组 arr2 中每个元素沿着轴 1 分别复制 2, 3, 4 遍
[[0 1 2]
 [3 4 5]]
[[0 1 2]
 [0 1 2]
 [3 4 5]
 [3 4 5]]
[[0 0 1 1 1 2 2 2 2]
 [3 3 4 4 4 5 5 5 5]]

2. 拼接

函数 tile() 复制的是数组本身,参数有几种设定方法:

  • 标量:把数组当成一个元素,一列一列复制
  • 形状:把数组当成一个元素,按形状复制
arr2 = np.arange(6).reshape((2,3))
print(arr2)
print(np.tile(arr2, 2))       # 数组 arr2 按列复制 2 遍
print(np.tile(arr2, (2, 3)))  # 数组 arr2 按形状复制 6 (2×3) 遍,并以 (2,3) 的形式展现
[[0 1 2]
 [3 4 5]]
[[0 1 2 0 1 2]
 [3 4 5 3 4 5]]
[[0 1 2 0 1 2 0 1 2]
 [3 4 5 3 4 5 3 4 5]
 [0 1 2 0 1 2 0 1 2]
 [3 4 5 3 4 5 3 4 5]]

4.4 其他操作

本节讨论数组的其他操作,包括排序 (sort),插入 (insert),删除 (delete) 和复制 (copy)。

1. 排序

排序包括直接排序 (direct sort) 和间接排序 (indirect sort)。

直接排序

arr = np.array([5, 3, 2, 6, 1, 4])
arr.sort()
print(arr)
[1 2 3 4 5 6]

sort()函数是按升序 (ascending order) 排列的,该函数里没有参数可以控制 order,因此你想要按降序排列的数组,只需:

print(arr[::-1])
[6 5 4 3 2 1]

注意:

用来排序 numpy 用两种方式:

  1. arr.sort():sort 会改变 arr
  2. np.sort(arr):sort 在排序时创建了 arr 的一个复制品,不会改变 arr

间接排序

有时候我们不仅仅只想排序数组,还想在排序过程中提取每个元素在原数组对应的索引(index),这时 argsort() 就派上用场了。

score = np.array([100, 60, 99, 80, 91])
idx = score.argsort()
print(idx)  # 打印数据由小到大的地址
print(score[idx])
[1 3 4 2 0]
[ 60  80  91  99 100]
arr = np.random.randint(40, size=(3,4))
print(arr)
print(arr[:, arr[0].argsort()]) # 对其第一行 arr[0] 排序,获取索引,在应用到所用行上
[[24 16 39 34]
 [10  5 29 35]
 [39 22 17 15]]
[[16 24 34 39]
 [ 5 10 35 29]
 [22 39 15 17]]

2. 插入和删除

和列表一样,我们可以给 numpy 数组:

  • 用insert()函数在某个特定位置之前插入元素
  • 用delete()函数删除某些特定元素
a = np.arange(6)
print(a)
print(np.insert(a, 1, 100))
print(np.delete(a, [1, 3]))
[0 1 2 3 4 5]
[  0 100   1   2   3   4   5]
[0 2 4 5]

3. 复制

用copy()函数来复制数组 a 得到 a_copy,很明显,改变 a_copy 里面的元素不会改变 a。

a = np.arange(6)
a_copy = a.copy()
print('Before changing value, a is', a)
print('Before changing value, a_copy is', a_copy)
a_copy[-1] = 99
print('After changing value, a_copy is', a_copy)
print('After changing value, a is', a)
Before changing value, a is [0 1 2 3 4 5]
Before changing value, a_copy is [0 1 2 3 4 5]
After changing value, a_copy is [ 0  1  2  3  4 99]
After changing value, a is [0 1 2 3 4 5]

五、数组的计算

本节介绍四大类数组计算,具体有:

  1. 元素层面 (element-wise) 计算
  2. 线性代数 (linear algebra) 计算
  3. 元素整合 (element aggregation) 计算
  4. 广播机制 (broadcasting) 计算

5.1 元素层面计算

Numpy 数组元素层面计算包括:

  1. 二元运算 (binary operation):加减乘除
  2. 数学函数:倒数、平方、指数、对数
  3. 比较运算 (comparison)

先定义两个数组 arr1 和 arr2。

arr1 = np.array([[1., 2., 3.], [4., 5., 6.]])
arr2 = np.ones((2,3)) * 2
print(arr1)
print(arr2)
[[1. 2. 3.]
 [4. 5. 6.]]
[[2. 2. 2.]
 [2. 2. 2.]]
# 加减乘除
print(arr1 + arr2 + 1)
print(arr1 - arr2)
print(arr1 * arr2)
print(arr1 / arr2)
# 倒数、平方、指数、对数
print(1 / arr1)
print(arr1 ** 2)
print(np.exp(arr1))
print(np.log(arr1))
# 比较
print(arr1 > arr2)
print(arr1 > 3)
[[4. 5. 6.]
 [7. 8. 9.]]
[[-1.  0.  1.]
 [ 2.  3.  4.]]
[[ 2.  4.  6.]
 [ 8. 10. 12.]]
[[0.5 1.  1.5]
 [2.  2.5 3. ]]
[[1.         0.5        0.33333333]
 [0.25       0.2        0.16666667]]
[[ 1.  4.  9.]
 [16. 25. 36.]]
[[  2.71828183   7.3890561   20.08553692]
 [ 54.59815003 148.4131591  403.42879349]]
[[0.         0.69314718 1.09861229]
 [1.38629436 1.60943791 1.79175947]]
[[False False  True]
 [ True  True  True]]
[[False False False]
 [ True  True  True]]

5.2 线性代数计算

在 NumPy 默认不采用矩阵运算,而是数组 (ndarray) 运算。矩阵只是二维,而数组可以是任何维度,因此数组运算更通用些。

注:arr 的形状是 (2,),只含一个元素的元组只说明 arr 是一维,数组是不分行数组或列数组的。

下面我们分别对「数组」和「矩阵」从创建、转置、求逆和相乘四个方面看看它们的同异。

1. 创建

创建数组 arr2d 和矩阵 A,注意它们的输出有 array 和 matrix 的关键词。

arr2 = np.array([[1, 2], [3, 1]])
arr2
array([[1, 2],
       [3, 1]])
A = np.asmatrix(arr2)
matrix([[1, 2],
        [3, 1]])

2. 转置

数组用 arr2.T 操作或 arr.tranpose() 函数,而矩阵用 A.T 操作。主要原因就是 .T 只适合二维数据。

print(arr2.T)
print(arr2.transpose())
print(A.T)
[[1 3]
 [2 1]]
[[1 3]
 [2 1]]
[[1 3]
 [2 1]]

3. 求逆

数组用 np.linalg.inv() 函数,而矩阵用 A.I 和 A**-1 操作。

print(np.linalg.inv(arr2))
print(A.I)
print(A**-1)
[[-0.2  0.4]
 [ 0.6 -0.2]]
[[-0.2  0.4]
 [ 0.6 -0.2]]
[[-0.2  0.4]
 [ 0.6 -0.2]]

4. 相乘

相乘是个很模棱两可的概念

  • 数组相乘是在元素层面进行,
  • 矩阵相乘要就是数学定义的矩阵相乘 (比如第一个矩阵的列要和第二个矩阵的行一样)
arr2 = np.array([[1, 2], [3, 1]])
A = np.asmatrix(arr2)
print(arr2*arr2)  # 数组相乘
print(A*A)        # 矩阵相乘
[[1 4]
 [9 1]]
[[7 4]
 [6 7]]

点乘函数 dot()

点乘左右两边最常见的数组就是:

  • 向量 (1D) 和向量 (1D)
  • 矩阵 (2D) 和向量 (1D)
  • 矩阵 (2D) 和矩阵 (2D)

分别看看三个简单例子。

例一:np.dot(向量, 向量) 实际上做的就是内积,即把两个向量每个元素相乘,最后再加总。点乘结果 10 是个标量 (0D 数组),形状 = ()。

x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
z = np.dot(x, y)
print(z.shape)
print(z)
()
10

例二:np.dot(矩阵, 向量) 实际上做的就是普通的矩阵乘以向量。点乘结果是个向量 (1D 数组),形状 = (2, )。

x = np.array([1, 2, 3])
y = np.array([[3, 2, 1], [1, 1, 1]])
z = np.dot(y, x)
print(z.shape)
print(z)
(2,)
[10  6]

例三:np.dot(矩阵, 矩阵) 实际上做的就是普通的矩阵乘以矩阵。点乘结果是个矩阵 (2D 数组),形状 = (2, 3)。

x = np.array([[1, 2, 3], [1, 2, 3], [1, 2, 3]])
y = np.array([[3, 2, 1], [1, 1, 1]])
z = np.dot(y, x)
print(z.shape)
print(z)
(2, 3)
[[ 6 12 18]
 [ 3  6  9]]

当 x 第二个维度的元素 (x.shape[1]) 和 y 第一个维度的元素 (y.shape[0]) 个数相等时,np.dot(X, Y) 才有意义。

5.3 元素整合计算

在数组中,元素可以以不同方式整合 (aggregation)。拿求和 (sum) 函数来说,我们可以对数组:

  • 所有的元素求和
  • 在某个轴 (axis) 上的元素求和
arr = np.arange(1, 7).reshape((2, 3))
print(arr)
print( '总和:', arr.sum() )
print( '跨行求和', arr.sum(axis=0) )
print( '跨列求和', arr.sum(axis=1) )
[[1 2 3]
 [4 5 6]]
总和: 21
跨行求和 [5 7 9]
跨列求和 [ 6 15]

行和列这些概念对矩阵 (二维矩阵) 才适用,高维矩阵还是要用轴 (axis) 来区分每个维度。

除了 sum 函数,整合函数还包括 min, max, mean, std 和 cumsum,分别是求最小值、最大值、均值、标准差和累加,这些函数对数组里的元素整合方式和 sum 函数相同,就不多讲了。

5.4 广播机制计算

当对两个形状不同的数组按元素操作时,可能会触发「广播机制」。具体做法,先适当复制元素使得这两个数组形状相同后再按元素操作,两个步骤:

  1. 广播轴 (broadcast axis):比对两个数组的维度,将形状小的数组的维度 (轴) 补齐
  2. 复制元素:顺着补齐的轴,将形状小的数组里的元素复制,使得最终形状和另一个数组吻合
arr = np.arange(12).reshape((4,3))
print(arr)
print(arr.mean(axis=0))
print(arr - arr.mean(axis=0))
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]]
[4.5 5.5 6.5]
[[-4.5 -4.5 -4.5]
 [-1.5 -1.5 -1.5]
 [ 1.5  1.5  1.5]
 [ 4.5  4.5  4.5]]

沿轴 0 的均值的一维数组被广播到数组 arr 的所有的行上。

广播机制的规则

当我们对两个数组操作时,如果它们的形状

  • 不相容 (incompatible),广播机制不能进行
  • 相容 (compatible),广播机制可以进行

因此,进行广播机制分两步

  1. 检查两个数组形状是否兼容,即从两个形状元组最后一个元素,来检查(它们是否相等,是否有一个等于 1)。
  2. 一旦它们形状兼容,确定两个数组的最终形状。

例一:维度一样,形状不一样

a = np.array([[1, 2, 3]])
b = np.array([[4], [5], [6]])
print('The shape of a is', a.shape)
print('The shape of b is', b.shape)
The shape of a is (1, 3)
The shape of b is (3, 1)

回顾进行广播机制的两步

  1. 检查数组 a 和 b 形状是否兼容,从两个形状元组 (1, 3) 和 (3, 1)最后一个元素开始检查,发现它们都满足『有一个等于 1』的条件。
  2. 因此它们形状兼容,两个数组的最终形状为 (max(1,3), max(3,1)) = (3, 3)

到此,a 和 b 被扩展成 (3, 3) 的数组,让我们看看 a + b 等于多少?

c = a + b
print('The shape of c is', c.shape)
print('a is', a)
print('b is', b)
print('c = a + b =', c)
The shape of c is (3, 3)
a is [[1 2 3]]
b is [[4]
 [5]
 [6]]
c = a + b = [[5 6 7]
 [6 7 8]
 [7 8 9]]

例二:维度不一样

a = np.arange(5)
b = np.array(2)
print('The dimension of a is', a.ndim, 'and the shape of a is', a.shape)
print('The dimension of b is', b.ndim, 'and the shape of b is', b.shape)
The dimension of a is 1 and the shape of a is (5,)
The dimension of b is 0 and the shape of b is ()

数组 a 和 b 形状分别为 (5,) 和 (),首先我们把缺失的维度用 1 补齐得到 (5,) 和 (1,),再根据广播机制那套流程得到这两个形状是兼容的,而且最终形状为 (5,)。

c = a + b
print('The dimension of c is', c.ndim, 'and the shape of c is', c.shape, '\n')
print('a is', a)
print('b is', b)
print('c = a + b =', c)
The dimension of c is 1 and the shape of c is (5,) 

a is [0 1 2 3 4]
b is 2
c = a + b = [2 3 4 5 6]

例五:

a = np.array([[[1,2,3], [4,5,6]]])
b1 = np.array([[1,1,1], [2,2,2], [3,3,3]])
b2 = np.arange(3).reshape((1, 3))
b3 = np.arange(6).reshape((2, 3))
b4 = np.arange(12).reshape((2, 2, 3))
b5 = np.arange(6).reshape((2, 1, 3))

print('a的维度:', a.ndim, 'a的形状:', a.shape)
print('b1的维度:', b1.ndim, 'b1的形状:', b1.shape)
print('b2的维度:', b2.ndim, 'b2的形状:', b2.shape)
print('b3的维度:', b3.ndim, 'b3的形状:', b3.shape)
print('b4的维度:', b4.ndim, 'b4的形状:', b4.shape)
print('b5的维度:', b5.ndim, 'b5的形状:', b5.shape)
a的维度: 3 a的形状: (1, 2, 3)
b1的维度: 2 b1的形状: (3, 3)
b2的维度: 2 b2的形状: (1, 3)
b3的维度: 2 b3的形状: (2, 3)
b4的维度: 3 b4的形状: (2, 2, 3)
b5的维度: 3 b5的形状: (2, 1, 3)

对于数组 a 和 b1,它们形状是 (1, 2, 3) 和 (3, 3)。元组最后一个都是 3,兼容;倒数第二个是 3 和 2,即不相等,也没有一个是 1,不兼容a 和 b1 不能进行广播机制。不行就看看下面代码:

c1 = a + b1
print(c1)
print(c1.shape)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-159-224e02cbb1e0> in <module>
----> 1 c1 = a + b1
      2 print(c1)
      3 print(c1.shape)

ValueError: operands could not be broadcast together with shapes (1,2,3) (3,3) 

a 和其他 b2, b3, b4, b5 都可以进行广播机制。

 

c2 = a + b2
print(c2)
print(c2.shape)

c3 = a + b3
print(c3)
print(c3.shape)

c4 = a + b4
print(c4)
print(c4.shape)

c5 = a + b5
print(c5)
print(c5.shape)
[[[1 3 5]
  [4 6 8]]]
(1, 2, 3)
[[[ 1  3  5]
  [ 7  9 11]]]
(1, 2, 3)
[[[ 1  3  5]
  [ 7  9 11]]

 [[ 7  9 11]
  [13 15 17]]]
(2, 2, 3)
[[[ 1  3  5]
  [ 4  6  8]]

 [[ 4  6  8]
  [ 7  9 11]]]
(2, 2, 3)

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Python库系列】超详细的NumPy入门 的相关文章

  • DJANGO:如何列出_显示反向外键属性?

    我正在构建一个网络应用程序来跟踪一个人借阅的图书馆书籍 我有以下型号 class Person models Model name models CharField max length 100 def unicode self retur
  • facebook graph api 调用 python 中的 appsecret_proof

    在 python 中使用 appsecret proof 参数进行图形 api 调用的正确方法是什么 有没有允许这样的图书馆 我试图使用 python for facebook 库 但文档实际上不存在 所以我无法弄清楚 您可以使用以下方法来
  • Python 中的“断言”有什么用?

    什么是assert意思是 它是如何使用的 The assert语句几乎存在于所有编程语言中 它有两个主要用途 它有助于在程序早期发现问题 原因明确 而不是等到其他操作失败时才发现问题 例如 Python 中的类型错误在实际引发错误之前可能会
  • 如果我不重置 Python 的 ContextVars 会发生什么?

    这是Python中的内存泄漏吗 import contextvars contextvar contextvars ContextVar example while True string hello world token context
  • pytest动态生成测试方法

    您好 我如何为列表或文件数量动态生成测试方法 假设我有 file1 file2 和 filen 其输入值为 json 现在我需要对多个值运行相同的测试 如下所示 class Test File unittest TestCase def t
  • 如何在 python 中从相机(或网络摄像头)捕获视频(和音频)

    我正在寻找一个解决方案 无论是在Linux还是在Windows中 它都可以让我 同时从我的网络摄像头和麦克风录制视频 音频 将其另存为文件 AVI 或 mpg 或其他文件 录制时在屏幕上显示视频 就我而言 压缩不是问题 实际上我更喜欢捕获
  • 制作一个可以接受各种形状参数的函数

    Q1 Numpy 函数可以采用不同形状的参数 例如 np sum V 可以采用以下两个之一并返回具有不同形状的输出 x1 np array 1 3 1 x2 np array 1 2 3 4 5 6 7 8 2 我正在制作自己的函数 如下所
  • 无法在 VS Code 中导入

    我是 python 新手 一直在使用 VS code 现在我正在研究汤普森采样问题 需要 numpy 和 matplotlib 我已经导入了这两个库 但 VS code 给出了无法导入的错误 我知道我必须使用 PIP 进行安装 并且我已经看
  • 基本的 Python OpenCV 裁剪和调整大小

    有人可以帮我一些裁剪算法吗 它的 openCV 我想弄清楚这一点 我知道方法是crop image y y1 x x1 如果我有一个带有 new dimensionXxnew dimensionY 像素的图像 并且我想将其裁剪为相同的宽度
  • PyInstaller,规范文件,导入错误:没有名为“blah”的模块

    我正在尝试通过构建 python 脚本py安装程序 http www pyinstaller org 我使用以下命令来配置 生成规范文件并构建 wget pyinstaller zip extracted it python Configu
  • 根据另一个参数的值添加参数

    根据输入之一 我想初始化某些对象 这些对象的值将是其余参数的默认值 因此 即使在 parser parse args 之前 我也需要参数之一的值 我如何使用 python argparse 模块来实现这一点 所有选项都将作为一个命令行给出
  • Django的注释框架和CSRF

    据我了解 Django的评论框架 https docs djangoproject com en 1 4 ref contrib comments 专为匿名公众评论而设计 就像您通常在博客或文章下面看到的那样 换句话说 任何人都可以发表评论
  • 如何为 PyDev 制作文件模板?

    我希望在我创建的每个新文件的顶部都有一些有关许可证 作者等的样板信息 但我找不到要勾选的正确框 基本上 我想创建一个新文件 并已将其填充 在顶部 author Me license something copyright something
  • 避免 Python 3 的多处理队列中的竞争条件

    我正在尝试找到大约 61 亿 自定义 物品的最大重量 并且我想通过并行处理来完成此操作 对于我的特定应用程序 有更好的算法不需要我迭代超过 61 亿个项目 但解释它们的教科书超出了我的能力范围 我的老板希望在 4 天内完成此任务 我认为我公
  • 使用 tf.keras.Models.Sequential 构建的架构是否比使用 Tensorflow 的功能 API 构建的架构运行得更慢、更准确?

    我只是比较了 2 个 我认为 等效的 VGG ish 架构 一个是使用构建的tf keras Models Sequential 另一个用了Tensorflow 的函数式 API 每个人都试图解决cats vs dogs 数据集 经过 10
  • PyQt4 QPalette 不工作

    btn QtGui QPushButton Button self palettes btn palette palettes setColor btn backgroundRole QtCore Qt green btn setPalet
  • Python递归限制与堆栈大小?

    我了解递归中每个递归调用如何堆栈在堆栈上 如果超出堆栈限制 则会出现堆栈溢出 那么为什么Python的sys getrecursionlimit 返回一个数字 递归调用的最大深度 这不取决于我在该递归函数中所做的事情吗 或者它是否以某种方式
  • 如何获取当前的 IPython / Jupyter Notebook 名称

    我试图在运行 IPython 笔记本时获取当前的笔记本名称 我知道我可以在笔记本顶部看到它 我在追求什么 currentNotebook IPython foo bar notebookname 我需要在变量中获取名称 添加到以前的答案 要
  • 以任意深度嵌套 defaultdict

    我想嵌套任意数量的默认字典 如下所示 from collections import defaultdict D defaultdict lambda defaultdict int 正如所描述的那样工作正常earlier https st
  • 缩放插图中不同的 x 和 y 比例,matplotlib

    我正在尝试使用 matplotlib 制作插图 目前我有类似最后一个答案的内容如何缩放图像的一部分并插入到 matplotlib 中的同一图中 https stackoverflow com questions 13583153 how t

随机推荐