[PYTHON] Mischgefahr! Ndarray und Matrix

TL;DR

numpy.matrix ist veraltet. Verwenden Sie die Operatoren "numpy.ndarray" und "@".

Einführung

Als ich vor einiger Zeit einen Artikel schrieb "[https://qiita.com/kaityo256/items/78b16c58228e131f8144)", sagte ein ehemaliger Kollege, "numpy.matrix" sei veraltet. Ich war überrascht, das zu hören [^ 1].

[^ 1]: Derzeit wurde es so geändert, dass "Matrix" nicht verwendet wird, aber es wurde verwendet, als es zum ersten Mal erschien.

Als ich es nachgeschlagen habe, gab es in StackOverflow eine sehr detaillierte Erklärung, daher werde ich es basierend darauf erklären.

Singularitätszerlegung und Matrixklasse

In der linearen Algebra gibt es einen Prozess namens Singular Value Decomposition (SVD). Die m-mal-n-Matrix X ist in die Produkte der m-mal-m-Einheitsmatrix U, der m-mal-n-Diagonalmatrix S und der n-mal-n-Einheitsmatrix V unterteilt. Lassen Sie uns eine geeignete Matrix erstellen.

import numpy as np
from scipy import linalg

X = (np.arange(6)+1).reshape(2,3)

Jetzt ist X eine 2x3-Matrix wie folgt:

[[1 2 3]
 [4 5 6]]

Lassen Sie uns dies SVD.

U, s, V = linalg.svd(X)

linalg.svd gibt dreindarrays für die Singularitätszerlegung zurück. U ist m Zeilen und n Spalten, V ist n Zeilen und n Spalten. Die Matrix in der Mitte ist eine diagonale Matrix mit m Zeilen und n Spalten, aber es ist ein eindimensionales Array, da es nur diagonale Elemente gibt.

print(s) # => [9.508032   0.77286964]

Die Singularwertzerlegung kehrt nun zur ursprünglichen Matrix zurück, indem das Matrixprodukt in der Reihenfolge U, S, V genommen wird. Da es sich bei der Diagonalmatrix in der Mitte jedoch um eine eindimensionale Anordnung handelt, muss sie einmal in eine Matrix umgeformt werden. Dafür gibt es eine Funktion namens linalg.diagsvd.

S = linalg.diagsvd(s, X.shape[0], X.shape[1])

Jetzt ist S eine 2x3-Diagonalmatrix.

print(S)
# => 
# [[9.508032   0.         0.        ]
#  [0.         0.77286964 0.        ]]

Wenn Sie danach das Produkt aus drei Matrizen nehmen, kehrt es zur ursprünglichen Matrix zurück, aber das Produkt "*" von "numpy.ndarray" ist das Produkt jedes Elements. Sie müssen numpy.ndarray.dot für das Matrixprodukt verwenden, bei dem es sich um eine Zerkleinerungsberechnung handelt.

print(U.dot(S.dot(V)))
# => 
# [[1. 2. 3.]
# [4. 5. 6.]]

Es ist wieder normal, aber ich möchte Infix-Operatoren wie "*" anstelle von Methodenaufrufen wie "U.dot (S.dot (V))" verwenden. Zu diesem Zweck wurde numpy.matrix vorbereitet. Lassen Sie uns die Diagonalmatrix "S" von "ndarray" in "Matrix" ändern.

Z = np.matrix(S)

S ist numpy.ndarray, aber Z ist numpy.matrix.

type(S) # => numpy.ndarray
type(Z) # => numpy.matrix

numpy.matrix wird zu einem Matrixprodukt, wenn * als Infix-Operator verwendet wird. Es macht auch das Produkt von "numpy.ndarray" und "numpy.matrix" zu einem Matrixprodukt. Von hier aus können Sie so etwas wie "U * Z * V" schreiben.

print(U * Z * V)
# =>
# [[1. 2. 3.]
# [4. 5. 6.]]

Es ist bequem.

Probleme mit der Matrix

Wie Sie sehen können, wurde "Matrix" eher verwendet, weil es praktisch war, aber es gab einige Probleme. Insbesondere kann es sich unintuitiv verhalten, wenn es mit "ndarray" gemischt wird. Lassen Sie mich Ihnen ein Beispiel aus dem vorherigen SO geben.

Bereiten wir eine geeignete "ndarray" -Matrix "arr" vor und machen eine "Matte", die sie zu einer "Matrix" macht.

arr = np.zeros((3,4))
mat = np.matrix(arr)

Sowohl "arr" als auch "mat" repräsentieren eine 3-mal-4-Matrix.

Zuerst fügen wir die beiden hinzu.

(arr+mat).shape # => (3, 4)

Es gibt kein Problem, da wir Matrizen derselben Form hinzufügen. Als nächstes schneiden wir es in Scheiben. Fügen wir jeweils nur die erste Zeile hinzu. Da ich nur die erste Zeile mit 3 Zeilen und 4 Spalten hinzugefügt habe, möchte ich, dass es sich um eine Matrix mit 1 Zeile und 4 Spalten handelt.

(arr[0,:] + mat[0,:]).shape # => (1, 4)

Dies ist auch kein Problem.

Fügen wir nun die Spalten anstelle der Zeilen hinzu. Fügen wir jeweils die erste Zeile hinzu. Voraussichtlich 3 Zeilen und 1 Spalte.

(arr[:,0] + mat[:,0]).shape # => (3, 3)

Es sind 3 Zeilen und 3 Spalten geworden. Was ist passiert?

Dies liegt daran, dass die Form von "arr [: .0]" (3,) "ist, während die Form von" mat [:, 0] "(3,1)" ist, also Broadcasting Regeln wurde angewendet.

Lassen Sie uns tatsächlich eine Matrix von (3,1) und eine Matrix von (3,) erstellen und diese hinzufügen.

x = np.ones(3)
y = np.arange(3).reshape(3,1)
x.shape # => (3,)
y.shape # => (3, 1)

Die Situation ist die gleiche wie zuvor. Fügen wir es hinzu.

(x+y).shape # => (3, 3)

Es ist (3, 3). Der Wert sieht übrigens so aus.

print(x)
# => 
# [1. 1. 1.]
print(y)
# => 
#[[0]
# [1]
# [2]]
print(x+y)
# =>
#[[1. 1. 1.]
# [2. 2. 2.]
# [3. 3. 3.]]

Auch "*" von "Matrix" ist das Matrixprodukt, aber "/" ist die Division für jedes Element.

a = np.matrix([[2,4],[6,8]])
b = np.matrix(([2,2],[2,2]])
print(a)
# => 
# [[2 4]
#  [6 8]]
print(b)
# =>
# [[2 2]
#  [2 2]]
print(a*b)
# => 
# [[12 12]
# [28 28]]← Ich verstehe das
print(a/b)
# =>
# [[1. 2.]
# [3. 4.]] ← !!!

Dies ist auch nicht intuitiv.

Einführung des Operators "@"

Nun, der Grund, warum wir "Matrix" verwenden wollten, war, dass das Matrixprodukt von "ndarray" in der Methode schwer zu verwenden war und wir das Matrixprodukt in einleitender Notation wie "A * B" schreiben wollten. Seit Python 3.5 wurde jedoch der Infix-Operator "@" eingeführt, der das Matrixprodukt von "ndarray" darstellt (PEP 465. )). Nun können die Singularwertzerlegung und der Rückgängig-Prozess wie folgt geschrieben werden.

import numpy as np
from scipy import linalg

X = (np.arange(6)+1).reshape(2,3)
print(X)
# =>
# [[1 2 3]
# [4 5 6]]
U, s, V = linalg.svd(X)
S = linalg.diagsvd(s, X.shape[0], X.shape[1])
print(U @ S @ V)
# =>
# [[1. 2. 3.]
# [4. 5. 6.]]

Es ist einfach. Wenn $ m <n $ in m Zeilen und n Spalten ist, werden nicht alle Elemente von $ V $ verwendet, sodass Sie mit np.diag anstelle von linalg.diagsvd wie folgt schreiben können.

U, s, V = linalg.svd(X)
S = np.diag(s)
print(U @ S @ V[:2,:])

Selbst wenn eine Singularwertzerlegung durchgeführt wird und eine Annäherung an Rang r mit niedrigem Rang durchgeführt wird, kann dies wie folgt als "np.diag" geschrieben werden. Das Folgende ist ein Beispiel einer 200 mal 50-Matrix, die einzeln zerlegt und niedrig eingestuft ist. Erstellen wir zunächst eine Matrix X mit 200 Zeilen und 50 Spalten.

m = 200
n = 50
X = np.arange(m*n).reshape(m,n)
print(X)

X ist eine solche Matrix.

[[   0    1    2 ...   47   48   49]
 [  50   51   52 ...   97   98   99]
 [ 100  101  102 ...  147  148  149]
 ...
 [9850 9851 9852 ... 9897 9898 9899]
 [9900 9901 9902 ... 9947 9948 9949]
 [9950 9951 9952 ... 9997 9998 9999]]

Gehen Sie wie folgt vor, um dies mit Rang r = 10 zu approximieren:

U, s, V = linalg.svd(X)
S = np.diag(s)
r = 10
Ur = U[:, :r]
Sr = S[:r, :r]
Vr = V[:r, :]
Y = Ur @ Sr @ Vr
print(np.array(Y, np.int))

Y ist eine reelle Zahl, aber es sieht so aus, wenn es in eine ganze Zahl konvertiert wird.

[[   0    0    1 ...   46   47   48]
 [  50   50   51 ...   97   98   98]
 [ 100  101  102 ...  146  147  149]
 ...
 [9850 9851 9851 ... 9897 9897 9899]
 [9900 9901 9901 ... 9947 9947 9949]
 [9950 9951 9952 ... 9996 9998 9999]]

Es fühlt sich ziemlich gut an.

Zusammenfassung

Mit der Einführung des Operators "@", der das Matrixprodukt in der Einschubnotation von "numpy.ndarray" darstellt, ist der größte Vorteil der Verwendung von "numpy.matrix" verschwunden. Verwenden wir von nun an numpy.ndarray und @.

Recommended Posts

Mischgefahr! Ndarray und Matrix
Verteilung der Eigenwerte der Laplace-Matrix
Das Problem der Lügner und der Ehrlichkeit
Mechanismus von Pyenv und Virtualenv
Vor- und Nachbearbeitung von Pytest
Kombination von rekursiv und Generator
Kombination von anyenv und direnv
Erklärung und Implementierung von SocialFoceModel
Differenzierung der Sortierung und Verallgemeinerung der Sortierung
Koexistenz von Pyenv und Autojump
Verwendung und Integration von "Shodan"
Das Problem der Lügner und der Ehrlichkeit
Auftreten und Auflösung von tensorflow.python.framework.errors_impl.FailedPreconditionError
Vergleich von Apex und Lamvery
Quellinstallation und Installation von Python
Einführung und Tipps von mlflow.Tracking
Finden Sie den Schnittpunkt eines Kreises und einer geraden Linie (Sympymatrix)