en quoi la multiplication diffère-t-elle pour les classes NumPy Matrix et Array?

130

Les documents numpy recommandent d'utiliser un tableau au lieu de la matrice pour travailler avec des matrices. Cependant, contrairement à octave (que j'utilisais jusqu'à récemment), * n'effectue pas de multiplication matricielle, vous devez utiliser la fonction matrixmultipy (). Je pense que cela rend le code très illisible.

Quelqu'un partage-t-il mon point de vue et a-t-il trouvé une solution?

elexhobby
la source
8
Vous demandez des opinions et non une question. Y a-t-il quelque chose de plus spécifique sur lequel nous pourrions vous aider ou peut-être vous guider pour le rendre plus lisible?
Wheaties
2
En fait, la documentation recommande d'utiliser la matrice si vous faites de l'algèbre linéaire et que vous ne voulez pas utiliser multiply (), alors quel est le problème?
Matti Pastell
1
Je n'ai pas parcouru les documents en détail. Juste curieux, quels avantages les tableaux offrent-ils par rapport à la classe matricielle? J'ai trouvé que les tableaux ne font pas la différence entre les lignes et les colonnes. Est-ce parce que les tableaux sont censés être considérés comme des tenseurs plutôt que comme des matrices? Comme Joe l'a souligné, le fait que la classe de matrice soit 2-dim est assez limitatif. Quelle est la réflexion derrière ce type de conception, comme dans, pourquoi ne pas avoir une seule classe de matrice comme matlab / octave?
elexhobby
Je suppose que le principal problème est que python n'a pas .*de syntaxe vs '*' pour la multiplication élémentaire vs matrice. Si c'était le cas, tout serait plus simple, même si je suis surpris qu'ils choisissent *de vouloir dire multiplication élémentaire et non matricielle.
Charlie Parker

Réponses:

127

La principale raison pour éviter d'utiliser la matrixclasse est que a) elle est intrinsèquement bidimensionnelle, et b) il y a une surcharge supplémentaire par rapport à un tableau numpy "normal". Si tout ce que vous faites est de l'algèbre linéaire, alors n'hésitez pas à utiliser la classe matricielle ... Personnellement, je trouve que cela pose plus de problèmes que cela ne vaut la peine.

Pour les tableaux (antérieurs à Python 3.5), utilisez à la dotplace de matrixmultiply.

Par exemple

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

print np.dot(x,y)

Ou dans les nouvelles versions de numpy, utilisez simplement x.dot(y)

Personnellement, je le trouve beaucoup plus lisible que l' *opérateur impliquant la multiplication matricielle ...

Pour les tableaux en Python 3.5, utilisez x @ y.

Joe Kington
la source
10
Il est illisible lorsque vous avez une pile de multiplications, par exemple x ' A' * A x.
elexhobby
14
@elexhobby - x.T.dot(A.T).dot(A).dot(x)n'est-ce pas illisible, mais à chacun le sien. Si vous faites principalement de la multiplication matricielle, alors utilisez numpy.matrix!
Joe Kington
7
Au fait, pourquoi la multiplication matricielle est-elle appelée «point»? En quel sens s'agit-il d'un produit scalaire?
amcnabb
8
@amcnabb - La multiplication matricielle est parfois appelée "produit scalaire" dans les manuels (dans ces livres, le produit scalaire auquel vous pensez est appelé "produit scalaire" ou "produit scalaire"). Le produit scalaire scalaire est juste une multiplication matricielle de deux vecteurs, après tout, donc utiliser "point" pour signifier la multiplication matricielle en général n'est pas très compliqué. Cette notation particulière semble (?) Plus courante dans les textes d'ingénierie et de sciences qu'en mathématiques, du moins d'après mon expérience. Sa prévalence dans numpy est principalement due au fait qu'il numpy.matrixmultiplyest difficile à taper.
Joe Kington
7
@amcnabb le point est que le point se généralise à une dimensionnalité arbitraire sans ambiguïté. C'est cela qui rend l' numpy.dotéquivalent de la multiplication matricielle. Si vous n'aimez vraiment pas la notation, utilisez la matrixclasse.
Henry Gomersall
80

les éléments clés à savoir pour les opérations sur les tableaux NumPy par rapport aux opérations sur les matrices NumPy sont:

  • La matrice NumPy est une sous - classe du tableau NumPy

  • Les opérations du tableau NumPy sont élémentaires (une fois la diffusion prise en compte)

  • Les opérations de la matrice NumPy suivent les règles ordinaires de l'algèbre linéaire

quelques extraits de code pour illustrer:

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

mais cette opération échoue si ces deux matrices NumPy sont converties en tableaux:

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

bien que l'utilisation de la syntaxe NP.dot fonctionne avec des tableaux ; cette opération fonctionne comme la multiplication matricielle:

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

alors avez-vous déjà besoin d'une matrice NumPy? c'est-à-dire qu'un tableau NumPy suffira-t-il pour le calcul d'algèbre linéaire (à condition de connaître la syntaxe correcte, c'est-à-dire NP.dot)?

la règle semble être que si les arguments (tableaux) ont des formes (mxn) compatibles avec une opération d'algèbre linéaire donnée, alors tout va bien, sinon, NumPy lance.

la seule exception que j'ai rencontrée (il y en a probablement d'autres) est le calcul de l'inverse de la matrice .

ci-dessous sont des extraits dans lesquels j'ai appelé une opération d'algèbre linéaire pure (en fait, à partir du module d'algèbre linéaire de Numpy) et passé dans un tableau NumPy

déterminant d'un tableau:

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

vecteurs propres / paires de valeurs propres :

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

norme matricielle :

>>>> LA.norm(m)
22.0227

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

rang de la matrice :

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

condition de la matrice :

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

l'inversion nécessite cependant une matrice 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'

mais la pseudoinverse de Moore-Penrose semble fonctionner très bien

>>> 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]])
doug
la source
3
mInv = NP.linalg.inv (m) calcule l'inverse d'un tableau
db1234
Un point important à noter ici est que * est la multiplication par élément, le point est la vraie multiplication de matrice. Veuillez consulter stackoverflow.com/a/18255635/1780570
Minh Triet
Remarque IMP: les matrices numpy sont à éviter au profit des tableaux. Note de la documentation -> "Il n'est plus recommandé d'utiliser cette classe, même pour l'algèbre linéaire. Utilisez plutôt des tableaux réguliers. La classe pourrait être supprimée ultérieurement." Voir aussi stackoverflow.com/a/61156350/6043669
HopeKing
15

Il y a une situation où l'opérateur point donnera des réponses différentes lorsqu'il s'agit de tableaux ou de matrices. Par exemple, supposons ce qui suit:

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

Permet de les convertir en matrices:

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

Maintenant, nous pouvons voir une sortie différente pour les deux cas:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Jadiel de Armas
la source
Pour être précis, * est une multiplication élémentaire, point est la vraie multiplication matricielle. Veuillez consulter stackoverflow.com/a/18255635/1780570
Minh Triet
C'est parce qu'en tant que tableau numpy, aT == a, la transposition ne fait rien.
patapouf_ai
Si vous écrivez à = np.array ([[1], [2], [3]]), alors numpy.dot (at, b) devrait vous donner la même chose. La différence entre matix et array n'est pas dans le point mais dans la transposition.
patapouf_ai
Ou en fait, si vous écrivez a = numpy.array ([[1,2,3]]) alors aT se transposera vraiment et tout fonctionnera comme dans les matrices.
patapouf_ai
8

Référence de http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

..., l'utilisation de la classe numpy.matrix est déconseillée , car elle n'ajoute rien qui ne puisse être accompli avec des objets numpy.ndarray 2D , et peut conduire à une confusion quant à la classe utilisée. Par exemple,

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

Les opérations scipy.linalg peuvent être appliquées aussi bien à numpy.matrix qu'aux objets 2D numpy.ndarray .

Yong Yang
la source
7

Cette astuce pourrait être ce que vous recherchez. C'est une sorte de simple surcharge d'opérateur.

Vous pouvez ensuite utiliser quelque chose comme la classe Infix suggérée comme ceci:

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
Bitwise
la source
5

Une citation pertinente de PEP 465 - Un opérateur infixe dédié pour la multiplication matricielle , comme mentionné par @ petr-viktorin, clarifie le problème auquel l'OP se heurtait:

[...] numpy propose deux types différents avec des __mul__méthodes différentes . Pour les numpy.ndarrayobjets, *effectue une multiplication élémentaire et la multiplication matricielle doit utiliser un appel de fonction ( numpy.dot). Pour les numpy.matrixobjets,* effectue une multiplication matricielle et la multiplication élémentaire nécessite une syntaxe de fonction. Écrire du code en utilisantnumpy.ndarray fonctionne très bien. L'écriture de code en utilisant numpy.matrixfonctionne également très bien. Mais les problèmes commencent dès que nous essayons d'intégrer ces deux morceaux de code ensemble. Le code qui attend un ndarrayet obtient un matrix, ou vice-versa, peut planter ou renvoyer des résultats incorrects

L'introduction du @ opérateur infix devrait aider à unifier et à simplifier le code matriciel python.

cod3monk3y
la source
1

La fonction matmul (depuis numpy 1.10.1) fonctionne bien pour les deux types et renvoie le résultat sous forme de classe matricielle 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))

Production:

(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'>)

Depuis python 3.5 comme mentionné précédemment, vous pouvez également utiliser un nouvel opérateur de multiplication matricielle @comme

C = A @ B

et obtenez le même résultat que ci-dessus.

Sérénité
la source