Чем отличается умножение для классов NumPy Matrix и Array?

130

Документы numpy рекомендуют использовать массив вместо матрицы для работы с матрицами. Однако, в отличие от октавы (которую я использовал до недавнего времени), * не выполняет умножение матриц, вам нужно использовать функцию matrixmultipy (). Я чувствую, что это делает код очень нечитаемым.

Кто-нибудь разделяет мои взгляды и нашел ли решение?

elexhobby
источник
8
Вы спрашиваете мнение, а не вопрос. Есть ли что-то более конкретное, с чем мы могли бы вам помочь или, возможно, помочь вам сделать его более читабельным?
Wheaties 08
2
На самом деле документы рекомендуют использовать матрицу, если вы занимаетесь линейной алгеброй и не хотите использовать multiply (), так в чем проблема?
Матти Пастелл
1
Я подробно не изучал документацию. Просто любопытно, какие преимущества у массивов перед матричным классом? Я обнаружил, что массивы не различают строки и столбцы. Это потому, что массивы должны рассматриваться как тензоры, а не как матрицы? Как заметил Джо, тот факт, что матричный класс является двумерным, весьма ограничивает. Какова идея такого дизайна, например, почему бы не иметь единственный матричный класс, такой как matlab / octave?
elexhobby
Я предполагаю, что основная проблема в том, что python не имеет .*синтаксиса vs '*' для поэлементного умножения матриц. Если бы это было так, все было бы проще, хотя я удивлен, что они выбрали *поэлементное умножение, а не матричное умножение.
Чарли Паркер

Ответы:

127

Основная причина избегать использования matrix класса, заключается в том, что а) он по своей природе двумерен, и б) есть дополнительные накладные расходы по сравнению с «нормальным» массивом 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)

Или в более новых версиях numpy просто используйте x.dot(y)

Лично мне он кажется более читаемым, чем *оператор умножения матриц ...

Для массивов в Python 3.5 используйте x @ y.

Джо Кингтон
источник
10
Его невозможно прочитать, когда у вас есть стек умножений, например x ' A' * A x.
elexhobby
14
@elexhobby - x.T.dot(A.T).dot(A).dot(x)разве не то чтобы нечитабельно, имо Но каждому свое. Если вы в первую очередь выполняете матричное умножение, то обязательно используйте numpy.matrix!
Джо Кингтон
7
Кстати, почему умножение матриц называется «точечным»? В каком смысле это скалярный продукт?
amcnabb
8
@amcnabb - Умножение матриц иногда называют «скалярным произведением» в учебниках (в этих книгах скалярное произведение, о котором вы думаете, называется «скалярным произведением» или «скалярным скалярным произведением»). В конце концов, скалярное скалярное произведение - это просто матричное умножение двух векторов, поэтому использование «точки» для обозначения матричного умножения в целом не представляет большого труда. Это конкретное обозначение кажется (?) Более распространенным в текстах по инженерии и науке, чем в математике, по крайней мере, по моему опыту. Его распространенность в numpy в основном связана с тем, что numpy.matrixmultiplyего сложно набрать.
Джо Кингтон
7
@amcnabb дело в том, что точка обобщается на произвольную размерность без двусмысленности. Это то, что numpy.dotэквивалентно умножению матриц. Если вам действительно не нравятся обозначения, используйте matrixкласс.
Генри Гомерсалл
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]])

но эта операция не выполняется, если эти две матрицы 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) 

хотя использование синтаксиса NP.dot работает с массивами ; эта операция работает как матричное умножение:

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

так вам когда-нибудь понадобится матрица 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

собственные векторы / пары собственных значений :

>>> 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   ]]))

матричная норма :

>>>> LA.norm(m)
22.0227

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.]]))

ранг матрицы :

>>> 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

условие матрицы :

>>> a1 = NP.random.randint(1, 10, 12).reshape(4, 3)
>>> LA.cond(a1)
5.7093446189400954

для инверсии требуется матрица 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'

но псевдообратная модель Мура-Пенроуза работает отлично

>>> 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]])
Дуг
источник
3
mInv = NP.linalg.inv (m) вычисляет инверсию массива
db1234
Здесь важно отметить, что * - это поэлементное умножение, точка - истинное умножение матриц. См. Stackoverflow.com/a/18255635/1780570
Минь Триет
Примечание IMP: следует избегать матриц numpy в пользу массивов. Примечание из документации -> «Больше не рекомендуется использовать этот класс даже для линейной алгебры. Вместо этого используйте обычные массивы. Класс может быть удален в будущем». См. Также stackoverflow.com/a/61156350/6043669
HopeKing
15

Существует ситуация, когда оператор точки даст разные ответы при работе с массивами, как и при работе с матрицами. Например, предположим следующее:

>>> a=numpy.array([1, 2, 3])
>>> b=numpy.array([1, 2, 3])

Преобразуем их в матрицы:

>>> am=numpy.mat(a)
>>> bm=numpy.mat(b)

Теперь мы можем увидеть разные результаты для двух случаев:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Джадьель де Армас
источник
Чтобы быть конкретным, * - это поэлементное умножение, точка - истинное умножение матриц. См. Stackoverflow.com/a/18255635/1780570
Минь Триет,
Это потому, что как массив numpy, aT == a, транспонирование ничего не делает.
patapouf_ai
Если вы напишете at = np.array ([[1], [2], [3]]), то numpy.dot (at, b) должен дать вам то же самое. Разница между matix и array не в точке, а в транспонировании.
patapouf_ai
Или на самом деле, если вы напишете a = numpy.array ([[1,2,3]]), тогда aT действительно транспонирует, и все будет работать так же, как в матрицах.
patapouf_ai
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])

Операции scipy.linalg можно одинаково применять к numpy.matrix или к 2D- объектам numpy.ndarray .

Ён Ян
источник
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
побитовое
источник
5

Уместная цитата из PEP 465 - специальный инфиксный оператор для умножения матриц , как упоминалось @ petr-viktorin, проясняет проблему, к которой имел отношение OP:

[...] numpy предоставляет два разных типа с разными __mul__методами. Для numpy.ndarrayобъектов *выполняется поэлементное умножение, а умножение матриц должно использовать вызов функции ( numpy.dot). Для numpy.matrixобъектов *выполняет матричное умножение, а для поэлементного умножения требуется синтаксис функции. Написание кода с использованием numpy.ndarrayработает нормально. Написание кода с использованием numpy.matrixтоже отлично работает. Но проблемы начинаются, как только мы пытаемся объединить эти два фрагмента кода вместе. Код, который ожидает ndarrayи получает matrix, или наоборот, может дать сбой или вернуть неверные результаты

Введение @инфиксного оператора должно помочь унифицировать и упростить матричный код Python.

cod3monk3y
источник
1

Функция matmul (начиная с numpy 1.10.1) отлично работает для обоих типов и возвращает результат в виде класса матрицы numpy:

import numpy as np

A = np.mat('1 2 3; 4 5 6; 7 8 9; 10 11 12')
B = np.array(np.mat('1 1 1 1; 1 1 1 1; 1 1 1 1'))
print (A, type(A))
print (B, type(B))

C = np.matmul(A, B)
print (C, type(C))

Вывод:

(matrix([[ 1,  2,  3],
        [ 4,  5,  6],
        [ 7,  8,  9],
        [10, 11, 12]]), <class 'numpy.matrixlib.defmatrix.matrix'>)
(array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]]), <type 'numpy.ndarray'>)
(matrix([[ 6,  6,  6,  6],
        [15, 15, 15, 15],
        [24, 24, 24, 24],
        [33, 33, 33, 33]]), <class 'numpy.matrixlib.defmatrix.matrix'>)

Начиная с python 3.5, как упоминалось ранее, вы также можете использовать новый оператор умножения матриц, @например

C = A @ B

и получите тот же результат, что и выше.

безмятежность
источник