як множення відрізняється для класів NumPy Matrix проти масиву?


130

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

Хтось поділяє мої погляди і знайшов рішення?


8
Ви запитуєте думки, а не питання. Чи є щось більш конкретне, з чим ми могли б вам допомогти або, можливо, допомогти вам зробити його більш зрозумілим?
пшениці

2
Насправді документи рекомендують використовувати матрицю, якщо ви робите лінійну алгебру і не хочете використовувати множення (), і в чому проблема?
Матті Пастель

1
Я детально не переглянув документи. Що цікаво, які переваги пропонують масиви над матричним класом? Я виявив, що масиви не розрізняють рядки та стовпці. Це тому, що масиви повинні вважатися тензорами, а не матрицями? Як вказував Джо, той факт, що матричний клас є 2-тьмяним, досить обмежує. Що думає за таким дизайном, як, наприклад, чому б не мати одного матричного класу, як matlab / octava?
elexhobby

Я думаю, головна проблема полягає в тому, що у python немає .*синтаксису vs '*' для множення елемента мудрого проти матричного. Якби це було, тоді все було б простіше, хоча я дивуюсь, що вони вирішили мати *на увазі стихійне, а не матричне множення.
Чарлі Паркер

Відповіді:


127

Основна причина уникати використання matrix класу полягає в тому, що а) він по суті є двовимірним, і б) є додаткові накладні витрати порівняно з "нормальним" нумеровим масивом. Якщо все, що ви робите, - це лінійна алгебра, то, будь-ласка, не соромтеся використовувати клас матриць ... Хоча я особисто вважаю, що це більше клопоту, ніж варто.

Для масивів (до 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.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
Minh Triet

Примітка IMP: маточні матриці слід уникати на користь масивів. Примітка з документації -> "Цей клас більше не рекомендується використовувати навіть для лінійної алгебри. Натомість використовувати регулярні масиви. Клас може бути видалений у майбутньому." Дивіться також 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
Minh Triet

Це тому, що як нумерований масив, aT == a, транспонтом нічого не робиться.
patapouf_ai

Якщо ви пишете на = np.array ([[1], [2], [3]]), то numpy.dot (at, b) має дати вам те саме. Різниця між matix та масивом полягає не в крапці, а в транспозиції.
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, пояснює проблему, з якою стикалася ОП:

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

Введення @оператора infix має допомогти уніфікувати та спростити матричний код python.


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

і отримати той же результат, що і вище.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.