web-dev-qa-db-fra.com

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

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

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

130
elexhobby

La principale raison d'éviter d'utiliser la classe matrix est que a) il est intrinsèquement bidimensionnel et b) qu'il existe une surcharge supplémentaire par rapport à un tableau numpy "normal". Si vous ne faites que de l'algèbre linéaire, n'hésitez pas à utiliser la classe matricielle ... Personnellement, je trouve que c'est plus compliqué que cela n'en vaut la peine.

Pour les tableaux (avant Python 3.5), utilisez dot au lieu 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 une multiplication de matrice ...

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

126
Joe Kington

les éléments clés à connaître pour les opérations sur les tableaux NumPy et les opérations sur les matrices NumPy sont:

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

  • Les opérations NumPy array sont élément par élément (une fois la diffusion comptabilisée)

  • 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 les tableaux ; cette opération fonctionne comme une 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? En d'autres termes, un tableau NumPy suffira-t-il pour le calcul d'algèbre linéaire (à condition que vous connaissiez la syntaxe correcte, c'est-à-dire NP.dot)?

la règle semble être que si les arguments (tableaux) ont des formes (m x n) compatibles avec une opération d'algèbre linéaire donnée, alors vous êtes ok, sinon, NumPy jette.

la seule exception que j'ai rencontrée (il y en a probablement d'autres) est en calculant 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, du module Algèbre linéaire de Numpy) et passée 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/valeur propre paires:

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

matrice norme :

>>>> LA.norm(m)
22.0227

qr factorisation :

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

matrice rang :

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

matrice condition :

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

inversion nécessite une matrice NumPy si:

>>> 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 le pseudoinverse de Moore-Penrose semble bien fonctionner

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

Dans la version 3.5, Python _ enfin a obtenu un opérateur de multiplication de matrice . La syntaxe est a @ b.

20
Petr Viktorin

Il existe une situation où l'opérateur de points 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.]]
15
Jadiel de Armas

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 , puisqu’il n’ajoute rien qui ne puisse être accompli avec des objets 2D numpy.ndarray , et peut entraîner une confusion de 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 indifféremment à numpy.matrix ou à Objets 2D numpy.ndarray .

8
Yong Yang

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

Vous pouvez ensuite utiliser quelque chose comme la classe suggérée Infix 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
7
Bitwise

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

[...] numpy fournit deux types différents avec différentes méthodes __mul__. Pour les objets numpy.ndarray, * effectue une multiplication élément par élément et la multiplication de matrice doit utiliser un appel de fonction (numpy.dot). Pour les objets numpy.matrix, * effectue la multiplication de matrice et la multiplication élément par élément requiert la syntaxe de la fonction. Écrire du code en utilisant numpy.ndarray fonctionne bien. Écrire du code en utilisant numpy.matrix fonctionne aussi très bien. Mais les ennuis commencent dès que nous essayons d'intégrer ces deux morceaux de code. Le code qui attend une ndarray et obtient une matrix, ou vice versa, peut planter ou renvoyer des résultats incorrects

L'introduction de l'opérateur @ infix devrait aider à unifier et à simplifier le code de la matrice python.

5
cod3monk3y

La fonction matmul (depuis numpy 1.10.1) fonctionne très bien pour les deux types et renvoie le résultat sous forme de classe numpy matrix:

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

Sortie:

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

Puisque python 3.5 as mentionné plus tôt , vous pouvez également utiliser un nouvel opérateur de multiplication de matrice @ comme

_C = A @ B
_

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

1
Serenity