import numpy as np
NumPy 矩阵运算的基本机制
在 NumPy 中,numpy
模块的方法和 ndarray
对象的方法几乎是一一对应的。绝大多数方法都具有两种调用方式,并且它们是等价的。例如:
A = np.arange(6).reshape(2, 3)
ndarray.func(*args, **kwargs)
形式:
A.transpose([1, 0])
array([[0, 3],
[1, 4],
[2, 5]])
numpy.func(ndarray, *args, **kwargs)
形式:
np.transpose(A, [1, 0])
array([[0, 3],
[1, 4],
[2, 5]])
在 NumPy 中,一般支持这两种调用方式的方法都具有如下特点:
- 不会修改调用方法的
ndarray
对象本身 - 返回的计算结果是副本
NumPy 数学运算
一元运算
取绝对值
A = np.linspace(-1, 1, num=5)
np.abs(A)
array([1. , 0.5, 0. , 0.5, 1. ])
取倒数
A = np.arange(1, 6).astype(np.float)
np.reciprocal(A)
array([1. , 0.5 , 0.33333333, 0.25 , 0.2 ])
平方
A = np.arange(1, 6)
np.square(A)
array([ 1, 4, 9, 16, 25])
开方
A = np.arange(1, 6)
A = A * A
np.sqrt(A)
array([1., 2., 3., 4., 5.])
指数函数
A = np.linspace(-1, 1, num=5)
np.exp(A)
array([0.36787944, 0.60653066, 1. , 1.64872127, 2.71828183])
对数函数
A = np.array([1, 2, 2.71, 10])
np.log(A), np.log2(A), np.log10(A)
(array([0. , 0.69314718, 0.99694863, 2.30258509]),
array([0. , 1. , 1.43829285, 3.32192809]),
array([0. , 0.30103 , 0.43296929, 1. ]))
三角函数
A = np.linspace(0, 2*np.pi, num=12)
np.sin(A), np.cos(A), np.tan(A)
(array([ 0.00000000e+00, 5.40640817e-01, 9.09631995e-01, 9.89821442e-01,
7.55749574e-01, 2.81732557e-01, -2.81732557e-01, -7.55749574e-01,
-9.89821442e-01, -9.09631995e-01, -5.40640817e-01, -2.44929360e-16]),
array([ 1. , 0.84125353, 0.41541501, -0.14231484, -0.65486073,
-0.95949297, -0.95949297, -0.65486073, -0.14231484, 0.41541501,
0.84125353, 1. ]),
array([ 0.00000000e+00, 6.42660977e-01, 2.18969456e+00, -6.95515277e+00,
-1.15406152e+00, -2.93626493e-01, 2.93626493e-01, 1.15406152e+00,
6.95515277e+00, -2.18969456e+00, -6.42660977e-01, -2.44929360e-16]))
反三角函数
A = np.linspace(-1, 1, num=5)
np.arcsin(A), np.arccos(A), np.arctan(A)
(array([-1.57079633, -0.52359878, 0. , 0.52359878, 1.57079633]),
array([3.14159265, 2.0943951 , 1.57079633, 1.04719755, 0. ]),
array([-0.78539816, -0.46364761, 0. , 0.46364761, 0.78539816]))
A = np.random.randn(2, 5)
A
array([[ 0.50865384, -0.22082557, -0.38529808, 2.17211963, 0.32188451],
[-1.97660967, -0.08196995, -0.10862636, -0.69184639, 1.67270543]])
符号函数
np.sign(A)
array([[ 1., -1., -1., 1., 1.],
[-1., -1., -1., -1., 1.]])
向上取整(天花板函数)
np.ceil(A)
array([[ 1., -0., -0., 3., 1.],
[-1., -0., -0., -0., 2.]])
向下取整(地板函数)
np.floor(A)
array([[ 0., -1., -1., 2., 0.],
[-2., -1., -1., -1., 1.]])
四舍五入到整数
np.rint(A)
array([[ 1., -0., -0., 2., 0.],
[-2., -0., -0., -1., 2.]])
舍入函数,其中参数 decimals
表示小数的位数。
np.around(A, decimals=2)
array([[ 0.51, -0.22, -0.39, 2.17, 0.32],
[-1.98, -0.08, -0.11, -0.69, 1.67]])
整数部分和小数部分分离,返回两个 ndarray
数组:
np.modf(A)
(array([[ 0.50865384, -0.22082557, -0.38529808, 0.17211963, 0.32188451],
[-0.97660967, -0.08196995, -0.10862636, -0.69184639, 0.67270543]]),
array([[ 0., -0., -0., 2., 0.],
[-1., -0., -0., -0., 1.]]))
二元运算
A = np.random.randn(4)
B = np.random.randn(4)
A, B
(array([-1.39684605, 1.13610876, 0.37116582, 0.11624161]),
array([ 1.41286043, -1.52254143, 0.41928741, 1.09645406]))
取最大值
np.maximum(A, B)
array([1.41286043, 1.13610876, 0.41928741, 1.09645406])
取最小值
np.minimum(A, B)
array([-1.39684605, -1.52254143, 0.37116582, 0.11624161])
下面的函数和上面等价:
np.fmax(A, B), np.fmin(A, B)
(array([1.41286043, 1.13610876, 0.41928741, 1.09645406]),
array([-1.39684605, -1.52254143, 0.37116582, 0.11624161]))
A = np.arange(6).reshape(2, 3)
B = np.full((2, 3), 2)
A, B
(array([[0, 1, 2],
[3, 4, 5]]),
array([[2, 2, 2],
[2, 2, 2]]))
四则运算
np.add(A, B), np.subtract(A, B), np.multiply(A, B), np.divide(A, B)
(array([[2, 3, 4],
[5, 6, 7]]),
array([[-2, -1, 0],
[ 1, 2, 3]]),
array([[ 0, 2, 4],
[ 6, 8, 10]]),
array([[0. , 0.5, 1. ],
[1.5, 2. , 2.5]]))
等价的重载运算符的写法:
A + B, A - B, A * B, A / B
(array([[2, 3, 4],
[5, 6, 7]]),
array([[-2, -1, 0],
[ 1, 2, 3]]),
array([[ 0, 2, 4],
[ 6, 8, 10]]),
array([[0. , 0.5, 1. ],
[1.5, 2. , 2.5]]))
乘方(幂)
np.power(A, B)
array([[ 0, 1, 4],
[ 9, 16, 25]])
等价的重载运算符的写法:
A ** B
array([[ 0, 1, 4],
[ 9, 16, 25]])
取模
np.mod(A, B)
array([[0, 1, 0],
[1, 0, 1]])
等价的重载运算符的写法:
A % B
array([[0, 1, 0],
[1, 0, 1]])
将 $B$ 中各元素的符号赋值给 $A$ 中对应元素:
np.copysign(A, B)
array([[0., 1., 2.],
[3., 4., 5.]])
比较运算,返回布尔矩阵:
A > B, A < B, A >= B, A <= B, A == B, A != B
(array([[False, False, False],
[ True, True, True]]),
array([[ True, True, False],
[False, False, False]]),
array([[False, False, True],
[ True, True, True]]),
array([[ True, True, True],
[False, False, False]]),
array([[False, False, True],
[False, False, False]]),
array([[ True, True, False],
[ True, True, True]]))
应用:利用布尔矩阵索引机制,将矩阵 $A$ 小于矩阵 $B$ 的元素置 $1$,将矩阵 $A$ 大于矩阵 $B$ 的元素置 $0$:
a = A < B
b = A > B
A[a] = 1
A[b] = 0
A
array([[1, 1, 2],
[0, 0, 0]])
将 $x$ 轴和 $y$ 轴分别往纵向和横向扩展成 2D 数组:
x = np.array(np.arange(10))
y = np.array(np.arange(10))
xx, yy = np.meshgrid(x, y)
xx, yy
(array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]),
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]]))
$(xx, yy)$ 组成了平面直角坐标系:
for i in range(10):
for j in range(10):
point = (xx[i,j], yy[i,j])
print(point, end=' ')
print()
(0, 0) (1, 0) (2, 0) (3, 0) (4, 0) (5, 0) (6, 0) (7, 0) (8, 0) (9, 0)
(0, 1) (1, 1) (2, 1) (3, 1) (4, 1) (5, 1) (6, 1) (7, 1) (8, 1) (9, 1)
(0, 2) (1, 2) (2, 2) (3, 2) (4, 2) (5, 2) (6, 2) (7, 2) (8, 2) (9, 2)
(0, 3) (1, 3) (2, 3) (3, 3) (4, 3) (5, 3) (6, 3) (7, 3) (8, 3) (9, 3)
(0, 4) (1, 4) (2, 4) (3, 4) (4, 4) (5, 4) (6, 4) (7, 4) (8, 4) (9, 4)
(0, 5) (1, 5) (2, 5) (3, 5) (4, 5) (5, 5) (6, 5) (7, 5) (8, 5) (9, 5)
(0, 6) (1, 6) (2, 6) (3, 6) (4, 6) (5, 6) (6, 6) (7, 6) (8, 6) (9, 6)
(0, 7) (1, 7) (2, 7) (3, 7) (4, 7) (5, 7) (6, 7) (7, 7) (8, 7) (9, 7)
(0, 8) (1, 8) (2, 8) (3, 8) (4, 8) (5, 8) (6, 8) (7, 8) (8, 8) (9, 8)
(0, 9) (1, 9) (2, 9) (3, 9) (4, 9) (5, 9) (6, 9) (7, 9) (8, 9) (9, 9)
NumPy 变维运算
通过 ndarray 的属性和方法实现
用 ndarray
数组的 reshape
方法可以将普通的一维数组变成多维数组。
np.arange(24).reshape(2, 3, 4)
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]]])
用 resize
方法会直接改变原矩阵的 shape
,不返回任何值。
A = np.arange(24)
A.resize(2, 3, 4)
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]]])
如果我们已经有了一个多维数组,我们想要改变其 shape
,可以直接设置其 shape
属性。
A = np.arange(24).reshape(2, 3, 4)
A.shape = (4, 3, 2)
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]]])
轴的置换
我们可以用广义的多维矩阵转置的方法实现变维。其中参数 axes
表示新的轴顺序。
A = np.arange(24).reshape(2, 3, 4)
np.transpose(A, axes=[1, 2, 0])
array([[[ 0, 12],
[ 1, 13],
[ 2, 14],
[ 3, 15]],
[[ 4, 16],
[ 5, 17],
[ 6, 18],
[ 7, 19]],
[[ 8, 20],
[ 9, 21],
[10, 22],
[11, 23]]])
如果是普通的二维矩阵,可以直接用 ndarray
的 T
属性来得到转置的结果。
A = np.arange(12).reshape(3, 4)
A.T
array([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
我们还可以通过多维矩阵维度交换的方法实现变维。传入的参数 0
和 2
同样表示轴。
A = np.arange(24).reshape(2, 3, 4)
np.swapaxes(A, 0, 2)
array([[[ 0, 12],
[ 4, 16],
[ 8, 20]],
[[ 1, 13],
[ 5, 17],
[ 9, 21]],
[[ 2, 14],
[ 6, 18],
[10, 22]],
[[ 3, 15],
[ 7, 19],
[11, 23]]])
降维与拉直
A = np.arange(6).reshape(2, 1, 3)
A
array([[[0, 1, 2]],
[[3, 4, 5]]])
squeeze
方法将 shape
为 $1$ 的部分去除。
np.squeeze(A)
array([[0, 1, 2],
[3, 4, 5]])
如果我们希望将任意多维数组都化为一维,可以通过下面的方法。
ravel
方法可以“拉直”多维数组,它返回的是数组的视图 view
。
A.ravel()
array([0, 1, 2, 3, 4, 5])
flatten
方法同样可以“拉直”多维数组,但是它返回的是数组的副本。
A.flatten()
array([0, 1, 2, 3, 4, 5])
在“拉直”的一维数组和多维数组之间我们常常需要进行下标和索引的计算,下面的方法根据对应的 shape
将一维数组下的整型下标转化为多维数组下对应的下标:
np.unravel_index(9, shape=(3, 4))
(2, 1)
NumPy 数组排序
NumPy 提供了 sort
方法来实现对多维矩阵的排序。
A = np.random.randn(2, 5)
A
array([[ 0.64501916, 0.27037382, -0.19212451, 1.64902317, 0.25770498],
[-1.42529996, 1.78813871, -1.13524005, -1.42806611, 1.1828902 ]])
默认情况下,排序算法为快速排序 quicksort
。平均情况下,该排序算法是最快的。不过我们也可以指定排序算法,即设置 kind
参数。可选的排序算法有 ‘mergesort’ 归并排序和 ‘heapsort’ 堆排序。
np.sort(A)
array([[-0.19212451, 0.25770498, 0.27037382, 0.64501916, 1.64902317],
[-1.42806611, -1.42529996, -1.13524005, 1.1828902 , 1.78813871]])
对于多维数组,我们建议指定其 axis
参数。
np.sort(A, axis=0)
array([[-1.42529996, 0.27037382, -1.13524005, -1.42806611, 0.25770498],
[ 0.64501916, 1.78813871, -0.19212451, 1.64902317, 1.1828902 ]])
np.sort(A, axis=1)
array([[-0.19212451, 0.25770498, 0.27037382, 0.64501916, 1.64902317],
[-1.42806611, -1.42529996, -1.13524005, 1.1828902 , 1.78813871]])
argsort
则返回排序后的索引值:
np.argsort(A)
array([[2, 4, 1, 0, 3],
[3, 0, 2, 4, 1]])
np.argsort(A, axis=0)
array([[1, 0, 1, 1, 0],
[0, 1, 0, 0, 1]])
np.argsort(A, axis=1)
array([[2, 4, 1, 0, 3],
[3, 0, 2, 4, 1]])
NumPy 适用于纯数据的排序,并且能够方便地得到各个数据的“名次”。然而,对于较为复杂的多关键字排序,我们更建议使用数据分析包 Pandas。