Como a multiplicação difere das classes NumPy Matrix vs Array?

130

Os documentos numpy recomendam o uso de matriz em vez de matriz para trabalhar com matrizes. No entanto, diferentemente da oitava (que eu estava usando até recentemente), * não realiza multiplicação de matrizes, você precisa usar a função matrixmultipy (). Eu sinto que isso torna o código muito ilegível.

Alguém compartilha minhas opiniões e encontrou uma solução?

elexhobby
fonte
8
Você está pedindo opiniões e não uma pergunta. Existe algo mais específico com o qual poderíamos ajudá-lo ou talvez guiá-lo para torná-lo mais legível?
wheaties
2
Na verdade, os documentos recomendam o uso de matriz se você faz álgebra linear e não deseja usar multiply (), qual é o problema?
quer
1
Não analisei os documentos em detalhes. Apenas curioso, quais vantagens as matrizes oferecem sobre a classe matricial? Eu descobri que matrizes não diferenciam entre linhas e colunas. É porque os arrays devem ser pensados ​​como tensores e não como matrizes? Como Joe apontou, o fato de a classe matriz ser 2-dim é bastante limitante. Qual é o pensamento por trás desse tipo de design, por que não ter uma única classe de matriz como matlab / oitava?
elexhobby
Eu acho que a questão principal é que o python não tem .*sintaxe vs '*' para multiplicação de elementos versus matriz. Se houvesse isso, tudo seria mais simples, embora eu esteja surpreso que eles escolham *significar multiplicação por elementos e não por matriz.
Charlie Parker

Respostas:

127

O principal motivo para evitar o uso da matrixclasse é que: a) é inerentemente bidimensional eb) há sobrecarga adicional em comparação com uma matriz numpy "normal". Se tudo o que você está fazendo é álgebra linear, fique à vontade para usar a classe matrix ... Pessoalmente, acho mais problemas do que vale a pena.

Para matrizes (anteriores ao Python 3.5), use em dotvez de matrixmultiply.

Por exemplo

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

print np.dot(x,y)

Ou nas versões mais recentes do numpy, basta usar x.dot(y)

Pessoalmente, acho muito mais legível do que o *operador implicando multiplicação de matrizes ...

Para matrizes no Python 3.5, use x @ y.

Joe Kington
fonte
10
É ilegível quando você tem uma pilha de multiplicações, por exemplo x ' A' * A x.
Elexhobby 19/10/10
14
@ elexhobby - x.T.dot(A.T).dot(A).dot(x)não é ilegível, imo Para cada um o seu, no entanto. Se você está principalmente fazendo multiplicação de matrizes, use todos os meios numpy.matrix!
quer
7
A propósito, por que a multiplicação de matrizes é chamada de "ponto"? Em que sentido é um produto escalar?
Amcnabb
8
@ amcnabb - A multiplicação de matrizes às vezes é chamada de "produto pontual" nos livros didáticos (nesses livros, o produto pontual em que você pensa é chamado de "produto escalar" ou "produto escalar"). O produto escalar de pontos é apenas a multiplicação matricial de dois vetores, afinal, portanto, usar "ponto" para significar a multiplicação matricial em geral não é muito difícil. Essa notação em particular parece (?) Mais comum em textos de engenharia e ciências do que em matemática, pelo menos na minha experiência. Sua prevalência em numpy é principalmente porque numpy.matrixmultiplyé difícil de digitar.
quer
7
@ amcnabb o ponto é que o ponto generaliza para dimensionalidade arbitrária sem ambiguidade. É isso que numpy.dotequivale à multiplicação de matrizes. Se você realmente não gosta da notação, use a matrixclasse
Henry Gomersall
80

as principais coisas a saber para operações em matrizes NumPy versus operações em matrizes NumPy são:

  • A matriz NumPy é uma subclasse da matriz NumPy

  • As operações de matriz NumPy são elemento-elemento (uma vez que a transmissão é contabilizada)

  • As operações da matriz NumPy seguem as regras comuns da álgebra linear

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

mas essas operações falharão se essas duas matrizes NumPy forem convertidas em matrizes:

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

embora o uso da sintaxe NP.dot funcione com matrizes ; essas operações funcionam como multiplicação de matrizes:

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

então você precisa de uma matriz NumPy? ou seja, uma matriz NumPy será suficiente para o cálculo de álgebra linear (desde que você saiba a sintaxe correta, ou seja, NP.dot)?

a regra parece ser que, se os argumentos (matrizes) tiverem formas (mxn) compatíveis com uma determinada operação de álgebra linear, você estará bem; caso contrário, o NumPy lança.

a única exceção que encontrei (provavelmente existem outras) é calcular a matriz inversa .

abaixo estão trechos nos quais chamei uma operação de álgebra linear pura (de fato, do módulo de álgebra linear da Numpy) e passamos em uma matriz NumPy

determinante de uma 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

pares de autovetores / autovalores :

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

>>>> LA.norm(m)
22.0227

fatoração 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.]]))

classificação da 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

condição da matriz :

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

A inversão requer uma 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'

mas o pseudoinverso de Moore-Penrose parece funcionar muito bem

>>> 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
fonte
3
mInv = NP.linalg.inv (m) calcula o inverso de uma matriz
db1234
Um ponto importante a ser observado aqui é * é a multiplicação por elementos, dot é a verdadeira multiplicação de matrizes. Por favor, veja stackoverflow.com/a/18255635/1780570
Minh Triet
Nota IMP: matrizes numpy devem ser evitadas em favor de matrizes. Nota da documentação -> "Não é mais recomendado usar esta classe, mesmo para álgebra linear. Em vez disso, use matrizes regulares. A classe poderá ser removida no futuro." Veja também stackoverflow.com/a/61156350/6043669
HopeKing
15

Há uma situação em que o operador de ponto fornecerá respostas diferentes ao lidar com matrizes e ao lidar com matrizes. Por exemplo, suponha o seguinte:

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

Vamos convertê-los em matrizes:

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

Agora, podemos ver uma saída diferente para os dois casos:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Jadiel de Armas
fonte
Para ser específico, * é a multiplicação por elementos, dot é a verdadeira multiplicação de matrizes. Por favor, veja stackoverflow.com/a/18255635/1780570
Minh Triet
Isso ocorre porque, como uma matriz numpy, aT == a, a transposição não faz nada.
patapouf_ai
Se você escrever em = np.array ([[1], [2], [3]])), então numpy.dot (at, b) deve fornecer o mesmo. A diferença entre matix e array não está no ponto, mas na transposição.
patapouf_ai
Ou, na verdade, se você escrever a = numpy.array ([[1,2,3]]), o aT realmente transporá e tudo funcionará exatamente como em matrizes.
patapouf_ai
8

Referência de http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

..., o uso da classe numpy.matrix é desencorajado , pois não adiciona nada que não possa ser realizado com objetos numpy.ndarray 2D e pode levar a uma confusão sobre qual classe está sendo usada. Por exemplo,

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

As operações scipy.linalg podem ser aplicadas igualmente a objetos numpy.matrix ou 2D numpy.ndarray .

Yong Yang
fonte
7

Esse truque pode ser o que você está procurando. É uma espécie de sobrecarga simples do operador.

Você pode usar algo como a classe Infix sugerida assim:

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
Bit a bit
fonte
5

Uma citação pertinente do PEP 465 - Um operador de infixo dedicado para multiplicação de matrizes , como mencionado por petr-viktorin, esclarece o problema que o OP estava enfrentando:

[...] numpy fornece dois tipos diferentes com __mul__métodos diferentes . Para numpy.ndarrayobjetos, *executa a multiplicação por elementos e a multiplicação de matrizes deve usar uma chamada de função ( numpy.dot). Para numpy.matrixobjetos, *realiza multiplicação de matrizes e a multiplicação por elementos requer sintaxe de função. Escrever código usando numpy.ndarrayfunciona bem. Escrever código usando numpy.matrixtambém funciona bem. Mas o problema começa assim que tentamos integrar esses dois pedaços de código. O código que espera um ndarraye recebe um matrix, ou vice-versa, pode falhar ou retornar resultados incorretos

A introdução do @operador infix deve ajudar a unificar e simplificar o código da matriz python.

cod3monk3y
fonte
1

A função matmul (desde o numpy 1.10.1) funciona bem para ambos os tipos e retorna o resultado como uma classe 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))

Resultado:

(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 o python 3.5, como mencionado anteriormente, você também pode usar um novo operador de multiplicação de matrizes, @como

C = A @ B

e obtenha o mesmo resultado acima.

Serenidade
fonte