NumPy Matrix与Array类的乘法有何不同?

ele*_*bby 129 python arrays numpy matrix matrix-multiplication

numpy文档建议使用数组而不是矩阵来处理矩阵.但是,与octave(我直到最近使用)不同,*不执行矩阵乘法,你需要使用函数matrixmultipy().我觉得这使得代码非常难以理解.

有人分享我的观点,并找到了解决方案吗?

Joe*_*ton 127

避免使用matrix该类的主要原因是a)它本身就是二维的,并且b)与"普通"numpy数组相比,存在额外的开销.如果您所做的只是线性代数,那么无论如何,请随意使用矩阵类......但我个人觉得它比它的价值更麻烦.

对于数组(在Python 3.5之前),请使用dot而不是matrixmultiply.

例如

import numpy as np
x = np.arange(9).reshape((3,3))
y = np.arange(3)

print np.dot(x,y)
Run Code Online (Sandbox Code Playgroud)

或者在较新版本的numpy中,只需使用即可 x.dot(y)

就个人而言,我发现它比*运营商暗示矩阵乘法更具可读性......

对于Python 3.5中的数组,请使用x @ y.

  • @elexhobby - `xTdot(AT).dot(A).dot(x)`并不是那么难以理解,但对于他自己的每个人来说都是如此.如果你主要做矩阵乘法,那么一定要使用`numpy.matrix`! (14认同)
  • 当你有一叠乘法时它是不可读的,例如x'*A'*A*x. (10认同)
  • @amcnabb - 矩阵乘法在教科书中有时被称为"点积"(在那些书中,你想到的点积被称为"标量积"或"标量点积").毕竟,标量点积只是两个向量的矩阵乘法,因此使用"点"来表示矩阵乘法通常不是很大的延伸.这种特殊符号在工程和科学文本中比在数学中更常见,至少在我的经验中如此.它在numpy中的流行主要是因为`numpy.matrixmultiply`很难打字. (8认同)
  • 那么,为什么矩阵乘法称为"点"?在什么意义上它是一个点积? (7认同)
  • @amcnabb的重点是dot [概括为任意维度](http://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html#numpy.dot),没有含糊之处.正是这使得`numpy.dot`等效于矩阵乘法.如果你真的不喜欢这种表示法,请使用`matrix`类. (7认同)
  • 希望你不介意我更新你的Python 3.5的答案 (3认同)

dou*_*oug 80

NumPy 阵列上的操作与NumPy 矩阵上的操作有关的关键事项是:

  • NumPy矩阵是NumPy数组的子类

  • NumPy 数组操作是逐个元素的(一旦广播被占用)

  • NumPy 矩阵运算遵循线性代数的普通规则

一些代码片段来说明:

>>> from numpy import linalg as LA
>>> import numpy as NP

>>> a1 = NP.matrix("4 3 5; 6 7 8; 1 3 13; 7 21 9")
>>> a1
matrix([[ 4,  3,  5],
        [ 6,  7,  8],
        [ 1,  3, 13],
        [ 7, 21,  9]])

>>> a2 = NP.matrix("7 8 15; 5 3 11; 7 4 9; 6 15 4")
>>> a2
matrix([[ 7,  8, 15],
        [ 5,  3, 11],
        [ 7,  4,  9],
        [ 6, 15,  4]])

>>> a1.shape
(4, 3)

>>> a2.shape
(4, 3)

>>> a2t = a2.T
>>> a2t.shape
(3, 4)

>>> a1 * a2t         # same as NP.dot(a1, a2t) 
matrix([[127,  84,  85,  89],
        [218, 139, 142, 173],
        [226, 157, 136, 103],
        [352, 197, 214, 393]])
Run Code Online (Sandbox Code Playgroud)

但是如果将这两个NumPy矩阵转换为数组,则此操作将失败:

>>> a1 = NP.array(a1)
>>> a2t = NP.array(a2t)

>>> a1 * a2t
Traceback (most recent call last):
   File "<pyshell#277>", line 1, in <module>
   a1 * a2t
   ValueError: operands could not be broadcast together with shapes (4,3) (3,4) 
Run Code Online (Sandbox Code Playgroud)

虽然使用NP.dot语法适用于数组 ; 这个操作像矩阵乘法一样工作:

>> NP.dot(a1, a2t)
array([[127,  84,  85,  89],
       [218, 139, 142, 173],
       [226, 157, 136, 103],
       [352, 197, 214, 393]])
Run Code Online (Sandbox Code Playgroud)

所以你需要一个NumPy矩阵吗?也就是说,NumPy数组是否足以进行线性代数计算(前提是你知道正确的语法,即NP.dot)?

规则似乎是如果参数(数组)的形状(mxn)与给定的线性代数运算兼容,那么你没问题,否则,NumPy抛出.

我遇到的唯一例外(有可能是其他的)是计算矩阵逆.

下面是我称之为纯线性代数运算的片段(事实上,来自Numpy的线性代数模块)并传入NumPy数组

数组的决定因素:

>>> m = NP.random.randint(0, 10, 16).reshape(4, 4)
>>> m
array([[6, 2, 5, 2],
       [8, 5, 1, 6],
       [5, 9, 7, 5],
       [0, 5, 6, 7]])

>>> type(m)
<type 'numpy.ndarray'>

>>> md = LA.det(m)
>>> md
1772.9999999999995
Run Code Online (Sandbox Code Playgroud)

特征向量/特征值对:

>>> LA.eig(m)
(array([ 19.703+0.j   ,   0.097+4.198j,   0.097-4.198j,   5.103+0.j   ]), 
array([[-0.374+0.j   , -0.091+0.278j, -0.091-0.278j, -0.574+0.j   ],
       [-0.446+0.j   ,  0.671+0.j   ,  0.671+0.j   , -0.084+0.j   ],
       [-0.654+0.j   , -0.239-0.476j, -0.239+0.476j, -0.181+0.j   ],
       [-0.484+0.j   , -0.387+0.178j, -0.387-0.178j,  0.794+0.j   ]]))
Run Code Online (Sandbox Code Playgroud)

矩阵规范:

>>>> LA.norm(m)
22.0227
Run Code Online (Sandbox Code Playgroud)

qr分解:

>>> LA.qr(a1)
(array([[ 0.5,  0.5,  0.5],
        [ 0.5,  0.5, -0.5],
        [ 0.5, -0.5,  0.5],
        [ 0.5, -0.5, -0.5]]), 
 array([[ 6.,  6.,  6.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]]))
Run Code Online (Sandbox Code Playgroud)

矩阵排名:

>>> m = NP.random.rand(40).reshape(8, 5)
>>> m
array([[ 0.545,  0.459,  0.601,  0.34 ,  0.778],
       [ 0.799,  0.047,  0.699,  0.907,  0.381],
       [ 0.004,  0.136,  0.819,  0.647,  0.892],
       [ 0.062,  0.389,  0.183,  0.289,  0.809],
       [ 0.539,  0.213,  0.805,  0.61 ,  0.677],
       [ 0.269,  0.071,  0.377,  0.25 ,  0.692],
       [ 0.274,  0.206,  0.655,  0.062,  0.229],
       [ 0.397,  0.115,  0.083,  0.19 ,  0.701]])
>>> LA.matrix_rank(m)
5
Run Code Online (Sandbox Code Playgroud)

矩阵条件:

>>> a1 = NP.random.randint(1, 10, 12).reshape(4, 3)
>>> LA.cond(a1)
5.7093446189400954
Run Code Online (Sandbox Code Playgroud)

反演需要NumPy矩阵:

>>> a1 = NP.matrix(a1)
>>> type(a1)
<class 'numpy.matrixlib.defmatrix.matrix'>

>>> a1.I
matrix([[ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028]])
>>> a1 = NP.array(a1)
>>> a1.I

Traceback (most recent call last):
   File "<pyshell#230>", line 1, in <module>
   a1.I
   AttributeError: 'numpy.ndarray' object has no attribute 'I'
Run Code Online (Sandbox Code Playgroud)

Moore-Penrose pseudoinverse似乎运作得很好

>>> LA.pinv(m)
matrix([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
        [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
        [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
        [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
        [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])

>>> m = NP.array(m)

>>> LA.pinv(m)
array([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
       [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
       [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
       [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
       [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])
Run Code Online (Sandbox Code Playgroud)

  • mInv = NP.linalg.inv(m)计算数组的倒数 (3认同)

Pet*_*rin 20

在3.5中,Python最终得到了一个矩阵乘法运算符.语法是a @ b.

  • 谢谢!是的,很高兴看到我不是唯一觉得当前符号不可读的人. (2认同)

Jad*_*mas 15

在处理数组时,点运算符会给出不同的答案,就像处理矩阵一样.例如,假设以下内容:

>>> a=numpy.array([1, 2, 3])
>>> b=numpy.array([1, 2, 3])
Run Code Online (Sandbox Code Playgroud)

让我们将它们转换为矩阵:

>>> am=numpy.mat(a)
>>> bm=numpy.mat(b)
Run Code Online (Sandbox Code Playgroud)

现在,我们可以看到两种情况的不同输出:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Run Code Online (Sandbox Code Playgroud)


小智 8

参考http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

...,不鼓励使用numpy.matrix类,因为它不会添加2D numpy.ndarray对象无法实现的任何内容,并且可能导致混淆使用哪个类.例如,

>>> import numpy as np
>>> from scipy import linalg
>>> A = np.array([[1,2],[3,4]])
>>> A
    array([[1, 2],
           [3, 4]])
>>> linalg.inv(A)
array([[-2. ,  1. ],
      [ 1.5, -0.5]])
>>> b = np.array([[5,6]]) #2D array
>>> b
array([[5, 6]])
>>> b.T
array([[5],
      [6]])
>>> A*b #not matrix multiplication!
array([[ 5, 12],
      [15, 24]])
>>> A.dot(b.T) #matrix multiplication
array([[17],
      [39]])
>>> b = np.array([5,6]) #1D array
>>> b
array([5, 6])
>>> b.T  #not matrix transpose!
array([5, 6])
>>> A.dot(b)  #does not matter for multiplication
array([17, 39])
Run Code Online (Sandbox Code Playgroud)

scipy.linalg操作可以同等地应用于numpy.matrix或2D numpy.ndarray对象.


Bit*_*ise 7

这个技巧可能就是你要找的东西.这是一种简单的操作员过载.

然后,您可以使用类似于建议的Infix类的内容:

a = np.random.rand(3,4)
b = np.random.rand(4,3)
x = Infix(lambda x,y: np.dot(x,y))
c = a |x| b
Run Code Online (Sandbox Code Playgroud)


cod*_*k3y 5

来自PEP 465的相关报价- @ petr-viktorin提到的用于矩阵乘法的专用中缀运算符,阐明了OP遇到的问题:

numpy提供了两种具有不同__mul__方法的不同类型。对于numpy.ndarray对象,*执行元素乘法,矩阵乘法必须使用函数调用(numpy.dot)。对于numpy.matrix对象,*执行矩阵乘法,而元素乘法则需要函数语法。使用编写代码numpy.ndarray效果很好。使用编写代码numpy.matrix也可以。但是,一旦我们尝试将这两段代码集成在一起,麻烦就会开始。预期为ndarray并得到的代码,matrix反之亦然,可能会崩溃或返回错误的结果

@infix运算符的引入应有助于统一和简化python矩阵代码。