使用NumPy在Python中执行矩阵运算很方便。
可以使用标准的Python列表类型实现二维数组(列表列表),但是NumPy可以用于轻松计算矩阵乘积,逆矩阵,行列式和特征值。
NumPy具有通用多维数组类numpy.ndarray和矩阵(二维数组)专用类numpy.matrix。
ndarray和matrix都可以执行矩阵(二维数组)操作(矩阵乘积,逆矩阵等),但是使用矩阵可以更轻松地编写代码。
如果您经常计算矩阵乘积和逆矩阵,则矩阵可能更易于描述,但是在其他情况下,您无需使用矩阵。
注意,可以在ndarray中使用的大多数函数和方法,例如max()和min()以获得最大值和最小值,也可以在矩阵中使用。
这里将对以下内容与示例代码一起解释。
如果将列表指定为列表的元素,则它将成为二维数组。
import numpy as np
l_2d = [[0, 1, 2], [3, 4, 5]]
print(l_2d)
# [[0, 1, 2], [3, 4, 5]]
print(type(l_2d))
#
NumPy配列numpy.ndarray numpy.array()生成できる。
将Python列表等作为第一个参数传递。
arr = np.array([[0, 1, 2], [3, 4, 5]])
print(arr)
# [[0 1 2]
# [3 4 5]]
print(type(arr))
#
除了numpy.array(),ndarray也可以由生成算术序列的函数(如np.arange())生成。您也可以使用reshape()方法更改形状。
arr = np.arange(6)
print(arr)
# [0 1 2 3 4 5]
arr = np.arange(6).reshape((2, 3))
print(arr)
# [[0 1 2]
# [3 4 5]]
此外,还有numpy.zeros(),numpy.ones(),numpy.full()等用于初始化具有相同值的任何形状。
可以使用构造函数numpy.matrix()创建umPy矩阵numpy.matrix。
传递Python列表或NumPy数组ndarray作为第一个参数。
mat = np.matrix([[0, 1, 2], [3, 4, 5]])
print(mat)
# [[0 1 2]
# [3 4 5]]
print(type(mat))
#
mat = np.matrix(arr)
print(mat)
# [[0 1 2]
# [3 4 5]]
print(type(mat))
#
如上所述,NumPy矩阵numpy.matrix是专门用于矩阵(二维数组)的类。
如果将一维数组作为构造函数的参数传递,它将扩展为二维,并且如果传递三个或更多个维的数组,则会发生错误。
mat_1d = np.matrix([0, 1, 2])
print(mat_1d)
# [[0 1 2]]
print(type(mat_1d))
#
print(mat_1d.shape)
# (1, 3)
# mat_3d = np.matrix([[[0, 1, 2]]])
# ValueError: matrix must be 2-dimensional
可以使list[行号] [列号]访问Python列表类型二维数组的元素的值。行号和列号从0开始。您可以获取值或对其进行更改(替代)。
print(l_2d)
# [[0, 1, 2], [3, 4, 5]]
print(l_2d[0][1])
# 1
l_2d[0][1] = 100
print(l_2d)
# [[0, 100, 2], [3, 4, 5]]
numpy.ndarray,numpy.matrix可以通过arr [行号,列号]访问。行号和列号从0开始。您可以获取值或更改(替代)它。
下面的示例是ndarray,但对于矩阵也是如此。
print(arr)
# [[0 1 2]
# [3 4 5]]
print(arr[0, 1])
# 1
arr[0, 1] = 100
print(arr)
# [[ 0 100 2]
# [ 3 4 5]]
此外,可以使用切片来选择要获取值的范围,或分配新值。
Python列表类型二维数组的转置矩阵可以按如下方式获得。
l_2d = [[0, 1, 2], [3, 4, 5]]
print(l_2d)
# [[0, 1, 2], [3, 4, 5]]
print([list(x) for x in list(zip(*l_2d))])
# [[0, 3], [1, 4], [2, 5]]
numpy.ndarray和numpy.matrix可以使用.T获得转置矩阵。
下面的示例是ndarray,但对于矩阵也是如此。
arr = np.arange(6).reshape((2, 3))
print(arr)
# [[0 1 2]
# [3 4 5]]
print(arr.T)
# [[0 3]
# [1 4]
# [2 5]]
另外,也可以使用ndarray的transpose()方法和numpy.transpose()函数进行转置。
在Python列表类型中,+运算符是列表串联,并且未定义操作符的操作,从而导致错误TypeError。
l_2d_1 = [[0, 1, 2], [3, 4, 5]]
l_2d_2 = [[0, 2, 4], [6, 8, 10]]
print(l_2d_1 + l_2d_2)
# [[0, 1, 2], [3, 4, 5], [0, 2, 4], [6, 8, 10]]
# print(l_2d_1 - l_2d_2)
# TypeError: unsupported operand type(s) for -: 'list' and 'list'
在numpy.ndarray和numpy.matrix中,每个元素都由+运算符和-运算符进行加减。
arr1 = np.arange(6).reshape((2, 3))
print(arr1)
# [[0 1 2]
# [3 4 5]]
arr2 = np.arange(0, 12, 2).reshape((2, 3))
print(arr2)
# [[ 0 2 4]
# [ 6 8 10]]
print(arr1 + arr2)
# [[ 0 3 6]
# [ 9 12 15]]
print(arr1 - arr2)
# [[ 0 -1 -2]
# [-3 -4 -5]]
mat1 = np.matrix(arr1)
mat2 = np.matrix(arr2)
print(mat1 + mat2)
# [[ 0 3 6]
# [ 9 12 15]]
print(mat1 - mat2)
# [[ 0 -1 -2]
# [-3 -4 -5]]
在Python列表类型中,带数字的运算是重复的数组,并且由于未定义列表之间的运算而发生错误。
print(l_2d_1 * 2)
# [[0, 1, 2], [3, 4, 5], [0, 1, 2], [3, 4, 5]]
# print(l_2d_1 * l_2d_2)
# TypeError: can't multiply sequence by non-int of type 'list'
在numpy.ndarray和numpy.matrix中,带有数字值(标量值)的*运算是每个元素的标量倍数。
print(arr1 * 2)
# [[ 0 2 4]
# [ 6 8 10]]
print(mat1 * 2)
# [[ 0 2 4]
# [ 6 8 10]]
使用numpy.multiply()获取矩阵之间的按元素乘积(Hadamard乘积)。
print(np.multiply(arr1, arr2))
# [[ 0 2 8]
# [18 32 50]]
print(np.multiply(mat1, mat2))
# [[ 0 2 8]
# [18 32 50]]
在numpy.ndarray中,*运算符等效于numpy.multiply(),这是逐元素的乘法。
print(arr1 * arr2)
# [[ 0 2 8]
# [18 32 50]]
要计算矩阵乘积,请使用numpy.dot(),numpy.matmul(),@运算符。提供dot()作为方法和函数。
@运算符自Python 3.5 NumPy 1.10.0起可用,等效于numpy.matmul()。
numpy.dot()和numpy.matmul()在3维或更多维的多维数组中的处理方式有所不同,但在此我们将不做深入介绍。对于矩阵(二维数组)可以获得相同的结果。
一维数组的dot()和matmul()返回向量的内积。见下文。
arr1 = np.arange(4).reshape((2, 2))
print(arr1)
# [[0 1]
# [2 3]]
arr2 = np.arange(6).reshape((2, 3))
print(arr2)
# [[0 1 2]
# [3 4 5]]
print(np.dot(arr1, arr2))
# [[ 3 4 5]
# [ 9 14 19]]
print(arr1.dot(arr2))
# [[ 3 4 5]
# [ 9 14 19]]
print(np.matmul(arr1, arr2))
# [[ 3 4 5]
# [ 9 14 19]]
print(arr1 @ arr2)
# [[ 3 4 5]
# [ 9 14 19]]
在numpy.matrix中,*运算符还会计算矩阵乘积。
mat1 = np.matrix(arr1)
mat2 = np.matrix(arr2)
print(np.dot(mat1, mat2))
# [[ 3 4 5]
# [ 9 14 19]]
print(mat1.dot(mat2))
# [[ 3 4 5]
# [ 9 14 19]]
print(np.matmul(mat1, mat2))
# [[ 3 4 5]
# [ 9 14 19]]
print(mat1 @ mat2)
# [[ 3 4 5]
# [ 9 14 19]]
print(mat1 * mat2)
# [[ 3 4 5]
# [ 9 14 19]]
如上所述,对于*运算符,numpy.ndarray和numpy.matrix的行为有所不同,因此对幂运算符**的结果也不同。
numpy.ndarray对每个元素执行幂运算,并且numpy.matrix重复矩阵乘法。
arr = np.arange(1, 5).reshape(2, 2)
print(arr)
# [[1 2]
# [3 4]]
print(arr**2)
# [[ 1 4]
# [ 9 16]]
mat = np.matrix(arr)
print(mat)
# [[1 2]
# [3 4]]
print(mat**2)
# [[ 7 10]
# [15 22]]
print(mat**2 == mat * mat)
# [[ True True]
# [ True True]]
print(mat**3 == mat * mat * mat)
# [[ True True]
# [ True True]]
关于负值的幂运算,numpy.ndarray还会对每个元素执行幂运算。此时,如果原始数组的数据类型dtype为int,则会发生错误。如果数据类型为预先浮动,则可以。
# print(arr**-1)
# ValueError: Integers to negative integer powers are not allowed.
arr_f = np.array(arr, dtype=float)
print(arr_f**-1)
# [[1. 0.5 ]
# [0.33333333 0.25 ]]
numpy.matrix的**-1是逆矩阵运算。负幂是逆矩阵乘积的重复。
print(mat**-1)
# [[-2. 1. ]
# [ 1.5 -0.5]]
print(mat**-2)
# [[ 5.5 -2.5 ]
# [-3.75 1.75]]
print(mat**-2 == mat**-1 * mat**-1)
# [[ True True]
# [ True True]]
print(mat**-3 == mat**-1 * mat**-1 * mat**-1)
# [[ True True]
# [ True True]]
numpy.ndarray的逆矩阵将在后面描述。
尽管它偏离矩阵(二维数组)的主题,但请使用numpy.inner()计算向量(一维数组)的内积。返回值是标量值。
如上所述,numpy.matrix是专门研究矩阵(二维数组)的类,如果将一维数组传递给构造函数,它将被扩展为二维数组。使用.ndarray。
v = np.array([0, 1, 2])
print(v)
# [0 1 2]
print(v.shape)
# (3,)
print(np.inner(v, v))
# 5
print(type(np.inner(v, v)))
#
同样在numpy.dot()和numpy.matmul()中,如果两个参数都是一维数组,则返回内积。 @运算符也是如此。
print(np.dot(v, v))
# 5
print(np.matmul(v, v))
# 5
print(v @ v)
# 5
仅具有一行的矩阵称为行向量,具有一列的矩阵称为列向量,但是在ndarray的一维数组中行与列之间没有区别。二维数组用于清楚地指示只有行或只有列。
arr_row = np.arange(3).reshape(1, 3)
print(arr_row)
# [[0 1 2]]
print(arr_row.shape)
# (1, 3)
arr_col = np.arange(3).reshape(3, 1)
print(arr_col)
# [[0]
# [1]
# [2]]
print(arr_col.shape)
# (3, 1)
使用numpy.dot(),numpy.matmul(),@运算符计算一维数组和二维数组的乘积时,如果第一个参数(左侧)是一维数组,则行向量,第二个参数(右侧) )是一维数组,它作为列向量计算,结果作为一维数组返回。
使用二维数组时,自然会将它们计算为普通矩阵的乘积,然后将结果作为二维数组返回。
arr = np.arange(9).reshape(3, 3)
print(arr)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
print(v @ arr)
# [15 18 21]
print(arr_row @ arr)
# [[15 18 21]]
print(arr @ v)
# [ 5 14 23]
print(arr @ arr_col)
# [[ 5]
# [14]
# [23]]
使用numpy.linalg.inv()计算逆矩阵。
arr = np.array([[2, 5], [1, 3]])
arr_inv = np.linalg.inv(arr)
print(arr_inv)
# [[ 3. -5.]
# [-1. 2.]]
mat = np.matrix([[2, 5], [1, 3]])
mat_inv = np.linalg.inv(mat)
print(mat_inv)
# [[ 3. -5.]
# [-1. 2.]]
如上所述,在numpy.matrix中,即使使用**-1(-1幂),也可以计算逆矩阵。
mat_inv = mat**-1
print(mat_inv)
# [[ 3. -5.]
# [-1. 2.]]
还可以使用I属性获得逆矩阵。
mat_inv = mat.I
print(mat_inv)
# [[ 3. -5.]
# [-1. 2.]]
在numpy.matrix中,可以使用*运算符来计算矩阵的乘积,因此,例如,您可以通过以下简单的书写来确定原始矩阵和逆矩阵的乘积是单位矩阵。
result = mat * mat.I
print(result)
# [[1. 0.]
# [0. 1.]]
请注意,numpy.ndarray没有I属性。
# print(arr.I)
# AttributeError: 'numpy.ndarray' object has no attribute 'I'
如果逆矩阵不存在(奇异),则numpy.linalg.inv()将生成错误。
arr_s = np.array([[0, 0], [1, 3]])
# print(np.linalg.inv(arr_s))
# LinAlgError: Singular matrix
对于此类矩阵,可以使用numpy.linalg.pinv()计算Moore-Penrose伪逆矩阵。
arr_pinv = np.linalg.pinv(arr_s)
print(arr_pinv)
# [[0. 0.1]
# [0. 0.3]]
奇异矩阵与其伪逆矩阵的矩阵乘积不成为单位矩阵,但是伪逆矩阵的伪逆矩阵具有与原始矩阵相等的性质。
print(arr_s @ arr_inv)
# [[0. 0.]
# [0. 1.]]
print(np.linalg.pinv(arr_pinv))
# [[0. 0.]
# [1. 3.]]
なお、元の行列に逆行列が存在する(正則行列、非特異行列である)場合、numpy.linalg.inv()とnumpy.linalg.pinv()はどちらも逆行列を返す。
print(np.linalg.inv(arr))
# [[ 3. -5.]
# [-1. 2.]]
print(np.linalg.pinv(arr))
# [[ 3. -5.]
# [-1. 2.]]
与numpy.matrix相同。在奇异矩阵numpy.linalg.inv()的情况下,-1和I属性的幂将导致错误,但是numpy.linalg.pinv()可以计算伪逆矩阵。
mat_s = np.mat([[0, 0], [1, 3]])
# print(np.linalg.inv(mat_s))
# LinAlgError: Singular matrix
# print(mat_s**-1)
# LinAlgError: Singular matrix
# print(mat_s.I)
# LinAlgError: Singular matrix
print(np.linalg.pinv(mat_s))
# [[0. 0.1]
# [0. 0.3]]
使用numpy.linalg.det()计算行列式。
该示例为numpy.ndarray,但对于numpy.matrix也是如此。
arr = np.array([[0, 1], [2, 3]])
det = np.linalg.det(arr)
print(det)
# -2.0
使用numpy.linalg.eig()计算特征值和特征向量。
对于n阶方阵,返回以n个特征值为元素的一维数组w和以每列为特征向量的二维数组v对应于每个特征值。
第i个特征值w [i]对应于第i个特征向量v [:,i]。特征向量是归一化为1。
该示例为numpy.ndarray,但对于numpy.matrix也是如此。
arr = np.array([[8, 1], [4, 5]])
w, v = np.linalg.eig(arr)
print(w)
# [9. 4.]
print(v)
# [[ 0.70710678 -0.24253563]
# [ 0.70710678 0.9701425 ]]
print('value: ', w[0])
print('vector: ', v[:, 0] / v[0, 0])
# value: 9.0
# vector: [1. 1.]
如果要查找最大特征值,请使用numpy.argmax(),它返回最大值的索引。
print(w[np.argmax(w)])
print(v[:, np.argmax(w)])
# 9.0
# [0.70710678 0.70710678]
可以通过定义以下函数来获取对应的特征值和特征向量对的元组列表。由于特征向量不知道是标准化为1的值,因此将其除以每列中最小的非零绝对值。
def get_eigenpairs(arr):
w, v = np.linalg.eig(arr)
eigenpairs = []
for i, val in enumerate(w):
vec = v[:, i] / np.min(np.abs(v[:, i][v[:, i] != 0]))
eigenpairs.append((val, vec))
return eigenpairs
eigenpairs = get_eigenpairs(arr)
for val, vec in eigenpairs:
print('value: {}, vector: {}'.format(val, vec))
# value: 9.0, vector: [1. 1.]
# value: 4.0, vector: [-1. 4.]
3x3示例。
arr = np.array([[1, 1, 2], [0, 2, -1], [0, 0, 3]])
eigenpairs = get_eigenpairs(arr)
for val, vec in eigenpairs:
print('value: {}, vector: {}'.format(val, vec))
# value: 1.0, vector: [1. 0. 0.]
# value: 2.0, vector: [1. 1. 0.]
# value: 3.0, vector: [ 1. -2. 2.]
它可以处理复数。虚数单位用j表示。
arr = np.array([[3, 2], [-2, 3]])
eigenpairs = get_eigenpairs(arr)
for val, vec in eigenpairs:
print('value: {}, vector: {}'.format(val, vec))
# value: (3+2.0000000000000004j), vector: [0.-1.j 1.+0.j]
# value: (3-2.0000000000000004j), vector: [0.+1.j 1.-0.j]
最后,我将总结numpy.ndarray和numpy.matrix之间的区别。
numpy.ndarray | numpy.matrix | |
---|---|---|
维度数 | 任意 | 2D |
*运算符 | 各要素的乘积 | 矩阵乘法 |
**运算符 | 每个元素的幂 | 重复矩阵乘积 |
**-1 | 每个元素的-1幂 | 逆矩阵 |
**-n | 每个元素的-n幂 | 重复逆矩阵的乘积 |
.I属性 | 没有 | 逆矩阵 |
任何其他+,-,/运算符都是每个元素的操作。
正如我在开始时所写的那样,如果您经常计算矩阵乘积和逆矩阵,则矩阵可能更易于描述,但否则无需使用矩阵。
在Python3.5,NumPy1.10.0或更高版本中,即使在ndarray中,也可以使用@运算符来计算矩阵的乘积,因此降低了使用矩阵的优势。