¿Cómo difiere la multiplicación para NumPy Matrix vs Array clases?

130

Los documentos numpy recomiendan usar una matriz en lugar de una matriz para trabajar con matrices. Sin embargo, a diferencia de la octava (que estaba usando hasta hace poco), * no realiza la multiplicación de matrices, debe usar la función matrixmultipy (). Siento que esto hace que el código sea muy ilegible.

¿Alguien comparte mis puntos de vista y ha encontrado una solución?

elexhobby
fuente
8
Estás pidiendo opiniones y no una pregunta. ¿Hay algo más específico con lo que podríamos ayudarlo o quizás guiarlo para que sea más legible?
wheaties
2
En realidad, los documentos recomiendan usar una matriz si haces álgebra lineal y no quieres usar multiply (), ¿cuál es el problema?
Matti Pastell
1
No he revisado los documentos en detalle. Por curiosidad, ¿qué ventajas ofrecen las matrices sobre la clase de matriz? Descubrí que las matrices no diferencian entre filas y columnas. ¿Es porque se supone que las matrices se consideran tensores en lugar de matrices? Como señaló Joe, el hecho de que la clase de matriz sea 2-dim es bastante limitante. ¿Cuál es el pensamiento detrás de este tipo de diseño, como en, por qué no tener una sola clase de matriz como matlab / octava?
elexhobby
Supongo que el problema principal es que python no tiene una .*sintaxis vs '*' para la multiplicación de elementos versus matriz. Si tuviera eso, entonces todo sería más simple, aunque me sorprende que elijan *significar en cuanto a elementos y no multiplicación de matrices.
Charlie Parker

Respuestas:

127

La razón principal para evitar usar el matrix clase es que a) es inherentemente bidimensional, yb) hay una sobrecarga adicional en comparación con una matriz numpy "normal". Si todo lo que está haciendo es álgebra lineal, entonces, por supuesto, siéntase libre de usar la clase de matriz ... Sin embargo, personalmente me parece más problemático de lo que vale.

Para matrices (anteriores a Python 3.5), use en dotlugar de matrixmultiply.

P.ej

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

print np.dot(x,y)

O en versiones más recientes de numpy, simplemente use x.dot(y)

Personalmente, me parece mucho más legible que el *operador que implica la multiplicación de matrices ...

Para matrices en Python 3.5, use x @ y.

Joe Kington
fuente
10
Es ilegible cuando tienes una pila de multiplicaciones, por ejemplo x ' A' * A x.
Elexhobby
14
@elexhobby: x.T.dot(A.T).dot(A).dot(x)no es tan ilegible, aunque para cada uno lo suyo. Si principalmente estás haciendo una multiplicación matricial, entonces, ¡por supuesto, úsala numpy.matrix!
Joe Kington
77
Por cierto, ¿por qué la multiplicación de matrices se llama "punto"? ¿En qué sentido es un producto de punto?
amcnabb
8
@amcnabb: la multiplicación de matrices a veces se denomina "producto de punto" en los libros de texto (en esos libros, el producto de punto en el que está pensando se llama "producto escalar" o "producto de punto escalar"). El producto escalar de puntos es simplemente la multiplicación matricial de dos vectores, después de todo, por lo que usar "punto" para significar la multiplicación matricial en general no es demasiado exagerado. Esa notación particular parece (?) Más común en los textos de ingeniería y ciencias que en las matemáticas, al menos en mi experiencia. Su prevalencia en numpy se debe principalmente a que numpy.matrixmultiplyes difícil de escribir.
Joe Kington
77
@amcnabb el punto es que el punto se generaliza a una dimensionalidad arbitraria sin ambigüedad. Esto es lo que hace numpy.dotequivalente a la multiplicación de matrices. Si realmente no te gusta la notación, usa la matrixclase.
Henry Gomersall el
80

Las cosas clave que debe saber para operaciones en matrices NumPy versus operaciones en matrices NumPy son:

  • La matriz NumPy es una subclase de la matriz NumPy

  • Las operaciones de matriz NumPy son por elementos (una vez que se contabiliza la transmisión)

  • Las operaciones de matriz NumPy siguen las reglas ordinarias de álgebra lineal

Algunos fragmentos de código para ilustrar:

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

pero esta operación falla si estas dos matrices NumPy se convierten en matrices:

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

aunque el uso de la sintaxis NP.dot funciona con matrices ; Estas operaciones funcionan como la multiplicación de matrices:

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

Entonces, ¿alguna vez necesitas una matriz NumPy? es decir, ¿será suficiente una matriz NumPy para el cálculo de álgebra lineal (siempre que conozca la sintaxis correcta, es decir, NP.dot)?

la regla parece ser que si los argumentos (matrices) tienen formas (mxn) compatibles con una operación de álgebra lineal dada, entonces está bien, de lo contrario, arroja NumPy.

La única excepción que he encontrado (probablemente haya otras) es calcular la matriz inversa .

a continuación hay fragmentos en los que he llamado una operación de álgebra lineal pura (de hecho, desde el módulo de Álgebra Lineal de Numpy) y pasé en una matriz NumPy

determinante de una matriz:

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

vectores propios / pares de valores propios :

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

norma de la matriz :

>>>> LA.norm(m)
22.0227

factorización 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.]]))

rango de la matriz :

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

condición de la matriz :

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

Sin embargo, la inversión requiere una matriz 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'

pero el pseudoinverso de Moore-Penrose parece funcionar 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
fuente
3
mInv = NP.linalg.inv (m) calcula el inverso de una matriz
db1234
Un punto importante a tener en cuenta aquí es * es la multiplicación por elementos, el punto es la verdadera multiplicación matricial. Consulte stackoverflow.com/a/18255635/1780570
Minh Triet
Nota de IMP: las matrices numpy deben evitarse en favor de las matrices. Nota de la documentación -> "Ya no se recomienda usar esta clase, incluso para álgebra lineal. En su lugar, use matrices regulares. La clase se puede eliminar en el futuro". Ver también stackoverflow.com/a/61156350/6043669
HopeKing
15

Existe una situación en la que el operador de puntos dará diferentes respuestas al tratar con matrices como con las matrices. Por ejemplo, suponga lo siguiente:

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

Vamos a convertirlos en matrices:

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

Ahora, podemos ver una salida diferente para los dos casos:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Jadiel de Armas
fuente
Para ser específicos, * es la multiplicación por elementos, el punto es la verdadera multiplicación matricial. Consulte stackoverflow.com/a/18255635/1780570
Minh Triet
Eso es porque como una matriz numpy, aT == a, la transposición no hace nada.
patapouf_ai
Si escribe en = np.array ([[1], [2], [3]]), entonces numpy.dot (at, b) debería proporcionarle lo mismo. La diferencia entre matix y array no está en el punto sino en la transposición.
patapouf_ai
O, de hecho, si escribe a = numpy.array ([[1,2,3]]), aT realmente se transpondrá y todo funcionará igual que en las matrices.
patapouf_ai
8

Referencia de http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

..., se desaconseja el uso de la clase numpy.matrix , ya que no agrega nada que no se pueda lograr con los objetos 2D numpy.ndarray , y puede dar lugar a una confusión sobre qué clase se está utilizando. Por ejemplo,

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

Las operaciones scipy.linalg se pueden aplicar igualmente a numpy.matrix o a objetos 2D numpy.ndarray .

Yong Yang
fuente
7

Este truco podría ser lo que estás buscando. Es una especie de simple sobrecarga del operador.

Luego puede usar algo como la clase Infix sugerida como esta:

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

Una cita pertinente de PEP 465: un operador infijo dedicado para la multiplicación de matrices , como lo menciona @ petr-viktorin, aclara el problema al que se enfrentaba el OP:

[...] numpy proporciona dos tipos diferentes con __mul__métodos diferentes . Para los numpy.ndarrayobjetos, *realiza la multiplicación por elementos y la multiplicación matricial debe usar una llamada a la función ( numpy.dot). Para los numpy.matrixobjetos, *realiza la multiplicación de matrices y la multiplicación por elementos requiere la sintaxis de la función. Escribir código usando numpy.ndarrayfunciona bien. Escribir código usando numpy.matrixtambién funciona bien. Pero los problemas comienzan tan pronto como intentamos integrar estos dos códigos juntos. El código que espera un ndarrayy obtiene un matrix, o viceversa, puede bloquearse o devolver resultados incorrectos

La introducción del @operador infijo debería ayudar a unificar y simplificar el código de la matriz de Python.

cod3monk3y
fuente
1

La función matmul (desde numpy 1.10.1) funciona bien para ambos tipos y devuelve el resultado como una clase de matriz 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))

Salida:

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

Desde python 3.5 como se mencionó anteriormente , también puede usar un nuevo operador de multiplicación de matrices @como

C = A @ B

y obtenga el mismo resultado que el anterior.

Serenidad
fuente