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

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

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


person elexhobby    schedule 08.10.2010    source источник
comment
Вы спрашиваете мнение, а не вопрос. Есть ли что-то более конкретное, с чем мы могли бы вам помочь или, возможно, помочь вам сделать его более читабельным?   -  person wheaties    schedule 08.10.2010
comment
На самом деле документы рекомендуют использовать матрицу, если вы занимаетесь линейной алгеброй и не хотите использовать multiply (), так в чем проблема?   -  person Matti Pastell    schedule 08.10.2010
comment
Я не просматривал документацию подробно. Просто любопытно, какие преимущества у массивов перед матричным классом? Я обнаружил, что массивы не различают строки и столбцы. Это потому, что массивы должны рассматриваться как тензоры, а не как матрицы? Как заметил Джо, тот факт, что матричный класс является двумерным, весьма ограничивает. Что подразумевается за подобным дизайном, например, почему бы не создать единственный матричный класс, такой как matlab / octave?   -  person elexhobby    schedule 10.10.2010
comment
Я предполагаю, что основная проблема в том, что у python нет синтаксиса .* vs '*' для поэлементного умножения и матричного умножения. Если бы это было так, все было бы проще, хотя я удивлен, что они выбрали * для обозначения поэлементного, а не матричного умножения.   -  person Charlie Parker    schedule 15.08.2017


Ответы (8)


Основная причина, по которой следует избегать использования класса matrix, заключается в том, что а) он по своей природе двумерен и б) есть дополнительные накладные расходы по сравнению с "нормальным" массивом numpy. Если все, что вы делаете, - это линейная алгебра, то во что бы то ни стало, не стесняйтесь использовать матричный класс ... Лично я нахожу это больше проблем, чем оно того стоит.

Для массивов (до Python 3.5) используйте dot вместо matrixmultiply.

E.g.

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.

person Joe Kington    schedule 08.10.2010
comment
Его невозможно прочитать, когда у вас есть стек умножений, например x 'A' * A x. - person elexhobby; 19.10.2010
comment
@elexhobby - x.T.dot(A.T).dot(A).dot(x) не так уж и нечитабельно, я. К каждому его собственное, хотя. Если вы в первую очередь выполняете матричное умножение, то обязательно используйте numpy.matrix! - person Joe Kington; 19.10.2010
comment
Кстати, а почему умножение матриц называется точкой? В каком смысле это скалярный продукт? - person amcnabb; 14.03.2013
comment
@amcnabb - умножение матриц иногда называют скалярным произведением в учебниках (в этих книгах скалярное произведение, о котором вы думаете, называется скалярным произведением или скалярным скалярным произведением). В конце концов, скалярное скалярное произведение - это просто матричное умножение двух векторов, поэтому использование точки для обозначения матричного умножения в целом не представляет большого труда. Это конкретное обозначение кажется (?) Более распространенным в текстах по инженерным наукам и наукам, чем в математике, по крайней мере, по моему опыту. Его распространенность в numpy в основном связана с тем, что numpy.matrixmultiply сложно набирать. - person Joe Kington; 15.03.2013
comment
@amcnabb - Кроме того, matrixmultiply был обесценен много лет назад и был удален в любой недавней (>v1.3, может быть?) версии numpy. - person Joe Kington; 15.03.2013
comment
@JoeKington - Действительно ли скалярное скалярное произведение просто матричное умножение двух векторов? Если оператор * представляет собой матричное умножение, то u.T * v будет скалярным скалярным произведением, но u * v будет неопределенным (поскольку размеры не совпадают). Если умножение матриц определяется как сумма по последней оси левого массива со второй предпоследней осью правого массива, то я бы предположил, что умножение матриц 1-D массивов numpy не удастся, потому что правый массив не иметь предпоследнюю ось. Во всяком случае, вот почему я запутался. - person amcnabb; 15.03.2013
comment
@amcnabb - numpy.dot имеет специальный корпус, чтобы вести себя как inner для 1d векторов. Внутреннее произведение двух одномерных векторов строго идентично скалярному скалярному произведению. (Между прочим, u.T и u идентичны, если u является одномерным. Векторы строк и векторы столбцов являются двухмерными, а не векторами, строго говоря. В numpy векторы являются векторами и не имеют второго измерения, поэтому вы необходимо преобразовать их в 2D, чтобы получить вектор-строку или вектор-столбец.) - person Joe Kington; 16.03.2013
comment
@JoeKington - Я понимаю, что numpy.dot ведет себя как внутренний продукт для одномерных векторов, но из-за этого специального корпуса функция кажется самосогласованной. Возможно, я был бы менее смущен, если бы в книгах было более распространено использовать точку для умножения матриц, как вы описываете. В любом случае, я понимаю это достаточно, чтобы использовать его, но я ненавижу обязательство засорять код комментариями, объясняющими, что numpy.dot выполняет матричное умножение. - person amcnabb; 18.03.2013
comment
@amcnabb дело в том, что точка обобщается на произвольные размерность без двусмысленности. Именно это делает numpy.dot эквивалентным умножению матриц. Если вам действительно не нравится нотация, используйте класс matrix. - person Henry Gomersall; 21.04.2013
comment
Надеюсь, вы не против, я обновил ваш ответ для Python 3.5 - person Neil G; 25.01.2015
comment
Мне точка тоже кажется неестественным названием, и поначалу это сбивало с толку. - person Scientist1642; 07.10.2015
comment
Здесь важно отметить, что * - это поэлементное умножение, точка - истинное умножение матриц. См. stackoverflow.com/a/18255635/1780570 - person Minh Triet; 24.12.2015
comment
@amcnabb Верно, что умножение матриц и скалярное произведение между двумя векторами выполняются очень похоже; однако скалярное произведение между двумя векторами коммутирует, а матричное умножение - нет. - person HelloGoodbye; 21.06.2017
comment
Я предполагаю, что основная проблема в том, что у python нет синтаксиса .* vs '*' для поэлементного умножения и матричного умножения. Если бы это было так, все было бы проще, хотя я удивлен, что они выбрали * для обозначения поэлементного, а не матричного умножения. - person Charlie Parker; 15.08.2017
comment
использование dot для умножения матриц - УЖАСНАЯ нотация, поскольку скалярное произведение векторов и множественное матричное произведение - очень разные операции. Например, скалярное произведение двух векторов коммутативно, то есть a.dot(b) == b.dot(a) (или, по крайней мере, должно соответствовать определению скалярного произведения). В матричном умножении np.matmul(a,b) != np.matmul(b,a) - person schrödinbug; 10.07.2019

Ключевые вещи, которые необходимо знать для операций с 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)?

кажется, правило состоит в том, что если аргументы (массивы) имеют формы (m x n), совместимые с данной операцией линейной алгебры, тогда все в порядке, в противном случае 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]])
person doug    schedule 19.08.2013
comment
mInv = NP.linalg.inv (m) вычисляет инверсию массива - person db1234; 19.01.2015
comment
Здесь важно отметить, что * - это поэлементное умножение, точка - истинное умножение матриц. См. stackoverflow.com/a/18255635/1780570 - person Minh Triet; 24.12.2015
comment
Примечание IMP: следует избегать матриц numpy в пользу массивов. Примечание из документации - ›Больше не рекомендуется использовать этот класс даже для линейной алгебры. Вместо этого используйте обычные массивы. В будущем этот класс может быть удален. См. Также stackoverflow.com/a/61156350/6043669 - person HopeKing; 28.06.2020

В версии 3.5 Python наконец получил оператор умножения матриц. Синтаксис a @ b.

person Petr Viktorin    schedule 24.08.2014
comment
Спасибо! Ура, рад видеть, что я не единственный, кто считает, что текущая нотация нечитаема. - person elexhobby; 25.08.2014

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

>>> 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.]]
person Jadiel de Armas    schedule 02.10.2013
comment
Чтобы быть конкретным, * - это поэлементное умножение, точка - истинное умножение матриц. См. stackoverflow.com/a/18255635/1780570 - person Minh Triet; 24.12.2015
comment
Это потому, что как массив numpy, a.T == a, транспонирование ничего не делает. - person patapouf_ai; 14.07.2016
comment
Если вы напишете at = np.array ([[1], [2], [3]]), то numpy.dot (at, b) должен дать вам то же самое. Разница между matix и array не в точке, а в транспонировании. - person patapouf_ai; 14.07.2016
comment
Или на самом деле, если вы напишете a = numpy.array ([[1,2,3]]), тогда a.T действительно транспонирует, и все будет работать так же, как в матрицах. - person patapouf_ai; 14.07.2016

Ссылка с сайта 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 объектам.

person Yong Yang    schedule 07.01.2014

Этот трюк может быть тем, что вы ищете. Это своего рода простая перегрузка оператора.

Затем вы можете использовать что-то вроде предлагаемого класса 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
person Bitwise    schedule 19.08.2013

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

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

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

person cod3monk3y    schedule 11.01.2015

Функция 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 как упомянутого ранее, вы также можете использовать новый оператор умножения матриц _ 3_ как

C = A @ B

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

person Serenity    schedule 11.04.2019