Wie unterscheidet sich die Multiplikation für NumPy Matrix- und Array-Klassen?

130

In den Numpy-Dokumenten wird empfohlen, für die Arbeit mit Matrizen ein Array anstelle einer Matrix zu verwenden. Im Gegensatz zur Oktave (die ich bis vor kurzem verwendet habe) führt * jedoch keine Matrixmultiplikation durch, sondern Sie müssen die Funktion matrixmultipy () verwenden. Ich denke, das macht den Code sehr unlesbar.

Teilt jemand meine Ansichten und hat eine Lösung gefunden?

Elexhobby
quelle
8
Sie fragen nach Meinungen und nicht nach einer Frage. Gibt es etwas Spezifischeres, bei dem wir Ihnen helfen oder das Sie vielleicht dabei unterstützen könnten, es lesbarer zu machen?
Wheaties
2
Tatsächlich empfehlen die Dokumente die Verwendung von Matrix, wenn Sie lineare Algebra verwenden und nicht multiply () verwenden möchten. Was ist also das Problem?
Matti Pastell
1
Ich habe die Dokumente nicht im Detail durchgesehen. Nur neugierig, welche Vorteile bieten Arrays gegenüber der Matrixklasse? Ich fand heraus, dass Arrays nicht zwischen Zeilen und Spalten unterscheiden. Liegt es daran, dass Arrays eher als Tensoren als als Matrizen betrachtet werden sollen? Wie Joe betonte, ist die Tatsache, dass die Matrixklasse 2-dim ist, ziemlich einschränkend. Was ist der Gedanke hinter dieser Art von Design, wie in, warum nicht eine einzelne Matrixklasse wie Matlab / Oktave haben?
elexhobby
Ich denke, das Hauptproblem ist, dass Python keine .*vs '*' - Syntax für die elementweise vs Matrix-Multiplikation hat. Wenn es das hätte, wäre alles einfacher, obwohl ich überrascht bin, dass sie sich *für elementweise und nicht für Matrixmultiplikation entscheiden.
Charlie Parker

Antworten:

127

Der Hauptgrund, die Verwendung der matrixKlasse zu vermeiden, besteht darin, dass a) sie von Natur aus zweidimensional ist und b) im Vergleich zu einem "normalen" Numpy-Array zusätzlicher Overhead entsteht. Wenn Sie nur lineare Algebra tun, können Sie auf jeden Fall die Matrixklasse verwenden ... Ich persönlich finde es jedoch schwieriger als es wert ist.

Verwenden Sie für Arrays (vor Python 3.5) dot anstelle von matrixmultiply.

Z.B

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

print np.dot(x,y)

Oder in neueren Versionen von numpy einfach verwenden x.dot(y)

Persönlich finde ich es viel lesbarer als das * Operator, der eine Matrixmultiplikation impliziert ...

Verwenden Sie für Arrays in Python 3.5 x @ y .

Joe Kington
quelle
10
Es ist nicht lesbar, wenn Sie einen Stapel von Multiplikationen haben, zum Beispiel x ' A' * A x.
elexhobby
14
@elexhobby - x.T.dot(A.T).dot(A).dot(x)ist das nicht unlesbar, imo Jedem sein eigenes. Wenn Sie hauptsächlich Matrixmultiplikation durchführen, verwenden Sie auf jeden Fall numpy.matrix!
Joe Kington
7
Warum heißt Matrixmultiplikation übrigens "Punkt"? Inwiefern ist es ein Punktprodukt?
Amcnabb
8
@amcnabb - Die Matrixmultiplikation wird in Lehrbüchern manchmal als "Punktprodukt" bezeichnet (in diesen Büchern wird das Punktprodukt, an das Sie denken, als "Skalarprodukt" oder "Skalarpunktprodukt" bezeichnet). Das skalare Punktprodukt ist schließlich nur eine Matrixmultiplikation von zwei Vektoren, so dass die Verwendung von "Punkt" als Matrixmultiplikation im Allgemeinen keine große Dehnung darstellt. Diese spezielle Notation scheint (?) In technischen und naturwissenschaftlichen Texten häufiger zu sein als in der Mathematik, zumindest nach meiner Erfahrung. Seine Prävalenz bei Numpy ist hauptsächlich darauf zurückzuführen, dass numpy.matrixmultiplyes schwer zu tippen ist.
Joe Kington
7
@amcnabb Der Punkt ist, dass der Punkt ohne Mehrdeutigkeit auf eine beliebige Dimensionalität verallgemeinert wird . Dies numpy.dotentspricht der Matrixmultiplikation. Wenn Sie die Notation wirklich nicht mögen, verwenden Sie die matrixKlasse.
Henry Gomersall
80

Die wichtigsten Informationen für Operationen mit NumPy- Arrays im Vergleich zu Operationen mit NumPy- Matrizen sind:

  • Die NumPy-Matrix ist eine Unterklasse des NumPy-Arrays

  • NumPy- Array- Operationen sind elementweise (sobald die Übertragung berücksichtigt wurde)

  • NumPy- Matrixoperationen folgen den üblichen Regeln der linearen Algebra

Einige Code-Schnipsel zur Veranschaulichung:

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

Diese Operation schlägt jedoch fehl, wenn diese beiden NumPy-Matrizen in Arrays konvertiert werden:

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

Die Verwendung der NP.dot- Syntax funktioniert jedoch mit Arrays . Diese Operation funktioniert wie eine Matrixmultiplikation:

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

Benötigen Sie jemals eine NumPy-Matrix? Wird ein NumPy-Array für die Berechnung der linearen Algebra ausreichen (vorausgesetzt, Sie kennen die richtige Syntax, dh NP.dot)?

Die Regel scheint zu sein, dass wenn die Argumente (Arrays) Formen (mxn) haben, die mit einer bestimmten linearen Algebra-Operation kompatibel sind, Sie in Ordnung sind, andernfalls wirft NumPy.

Die einzige Ausnahme, auf die ich gestoßen bin (es gibt wahrscheinlich andere), ist die Berechnung der inversen Matrix .

Unten sind Ausschnitte, in denen ich eine reine lineare Algebra-Operation (tatsächlich aus Numpys linearer Algebra-Modul) aufgerufen und in einem NumPy-Array übergeben habe

Determinante eines Arrays:

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

Eigenvektoren / Eigenwertpaare :

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

Matrix norm :

>>>> LA.norm(m)
22.0227

qr Faktorisierung :

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

Matrix 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

Matrix - Zustand :

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

Für die Inversion ist jedoch eine NumPy- Matrix erforderlich:

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

Aber die Moore-Penrose-Pseudoinverse scheint gut zu funktionieren

>>> 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
quelle
3
mInv = NP.linalg.inv (m) berechnet die Umkehrung eines Arrays
db1234
Ein wichtiger Punkt, der hier zu beachten ist, ist * die elementweise Multiplikation, Punkt ist die wahre Matrixmultiplikation. Bitte sehen Sie stackoverflow.com/a/18255635/1780570
Minh Triet
IMP-Hinweis: Numpy-Matrizen sind zugunsten von Arrays zu vermeiden. Hinweis aus der Dokumentation -> "Es wird nicht mehr empfohlen, diese Klasse auch für lineare Algebra zu verwenden. Verwenden Sie stattdessen reguläre Arrays. Die Klasse wird möglicherweise in Zukunft entfernt." Siehe auch stackoverflow.com/a/61156350/6043669
HopeKing
20

In 3.5 bekam Python endlich einen Matrixmultiplikationsoperator . Die Syntax lautet a @ b.

Petr Viktorin
quelle
2
Vielen Dank! Ja, ich bin froh zu sehen, dass ich nicht der einzige bin, der das Gefühl hat, dass die aktuelle Notation nicht lesbar ist.
elexhobby
15

Es gibt eine Situation, in der der Punktoperator beim Umgang mit Arrays andere Antworten gibt als beim Umgang mit Matrizen. Angenommen, Folgendes:

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

Konvertieren wir sie in Matrizen:

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

Jetzt können wir für beide Fälle eine unterschiedliche Ausgabe sehen:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]
Jadiel de Armas
quelle
Um genau zu sein, * ist elementweise Multiplikation, Punkt ist die wahre Matrixmultiplikation. Bitte sehen Sie stackoverflow.com/a/18255635/1780570
Minh Triet
Das liegt daran, dass als Numpy-Array aT == a die Transponierte nichts tut.
Patapouf_ai
Wenn Sie bei = np.array ([[1], [2], [3]]) schreiben, sollte numpy.dot (at, b) Ihnen dasselbe geben. Der Unterschied zwischen Matix und Array liegt nicht im Punkt, sondern in der Transponierung.
Patapouf_ai
Oder wenn Sie a = numpy.array ([[1,2,3]]) schreiben, wird aT wirklich transponieren und alles funktioniert genau wie in Matrizen.
Patapouf_ai
8

Referenz von http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

... wird von der Verwendung der Klasse numpy.matrix abgeraten , da sie nichts hinzufügt, was mit 2D- Objekten numpy.ndarray nicht erreicht werden kann , und zu einer Verwirrung darüber führen kann, welche Klasse verwendet wird. Beispielsweise,

>>> 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- Operationen können gleichermaßen auf numpy.matrix- oder 2D- numpy.ndarray- Objekte angewendet werden.

Yong Yang
quelle
7

Dieser Trick könnte das sein, wonach Sie suchen. Es ist eine Art einfache Bedienerüberlastung.

Sie können dann so etwas wie die vorgeschlagene Infix-Klasse wie folgt verwenden:

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
Bitweise
quelle
5

Ein einschlägiges Zitat aus PEP 465 - Ein dedizierter Infix-Operator für die Matrixmultiplikation , wie von @ petr-viktorin erwähnt, verdeutlicht das Problem, mit dem das OP konfrontiert war:

[...] numpy bietet zwei verschiedene Typen mit unterschiedlichen __mul__Methoden. Führt für numpy.ndarrayObjekte *eine elementweise Multiplikation durch, und die Matrixmultiplikation muss einen Funktionsaufruf ( numpy.dot) verwenden. Führt für numpy.matrixObjekte *eine Matrixmultiplikation durch, und die elementweise Multiplikation erfordert eine Funktionssyntax. Das Schreiben von Code mit numpy.ndarrayfunktioniert einwandfrei. Das Schreiben von Code mit numpy.matrixfunktioniert auch gut. Der Ärger beginnt jedoch , sobald wir versuchen, diese beiden Codeteile miteinander zu integrieren. Code, der ein erwartet ndarrayund ein erhält matrixoder umgekehrt, kann abstürzen oder falsche Ergebnisse zurückgeben

Die Einführung des @Infix-Operators sollte dazu beitragen, den Python-Matrix-Code zu vereinheitlichen und zu vereinfachen.

cod3monk3y
quelle
1

Die Funktion matmul (seit numpy 1.10.1) funktioniert für beide Typen einwandfrei und gibt das Ergebnis als numpy-Matrixklasse zurück:

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

Ausgabe:

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

Seit Python 3.5 können Sie, wie bereits erwähnt , auch einen neuen Matrixmultiplikationsoperator @wie verwenden

C = A @ B

und erhalten Sie das gleiche Ergebnis wie oben.

Gelassenheit
quelle