Ein zusammenhängendes Array ist nur ein Array, das in einem ununterbrochenen Speicherblock gespeichert ist: Um auf den nächsten Wert im Array zuzugreifen, wechseln wir einfach zur nächsten Speicheradresse.
Betrachten Sie das 2D-Array arr = np.arange(12).reshape(3,4)
. Es sieht aus wie das:
Im Speicher des Computers werden die Werte von arr
wie folgt gespeichert:
Dies bedeutet, dass arr
es sich um ein C-zusammenhängendes Array handelt, da die Zeilen als zusammenhängende Speicherblöcke gespeichert sind. Die nächste Speicheradresse enthält den nächsten Zeilenwert in dieser Zeile. Wenn wir eine Spalte nach unten verschieben möchten, müssen wir nur über drei Blöcke springen (z. B. von 0 auf 4 zu springen bedeutet, dass wir 1,2 und 3 überspringen).
Das Transponieren des Arrays mit arr.T
bedeutet, dass die C-Kontiguität verloren geht, weil sich benachbarte Zeileneinträge nicht mehr in benachbarten Speicheradressen befinden. Allerdings arr.T
ist Fortran zusammenhängende , da die Spalten in zusammenhängende Speicherblöcke sind:
In Bezug auf die Leistung ist der Zugriff auf Speicheradressen, die nebeneinander liegen, sehr oft schneller als der Zugriff auf Adressen, die "verteilter" sind (das Abrufen eines Werts aus dem RAM kann dazu führen, dass mehrere benachbarte Adressen für die CPU abgerufen und zwischengespeichert werden.) bedeutet, dass Operationen über zusammenhängende Arrays oft schneller sind.
Infolge des zusammenhängenden C-Speicherlayouts sind zeilenweise Operationen normalerweise schneller als spaltenweise Operationen. Zum Beispiel werden Sie das normalerweise finden
np.sum(arr, axis=1) # sum the rows
ist etwas schneller als:
np.sum(arr, axis=0) # sum the columns
In ähnlicher Weise sind Operationen an Spalten für zusammenhängende Fortran-Arrays etwas schneller.
Warum können wir das zusammenhängende Fortran-Array nicht durch Zuweisen einer neuen Form reduzieren?
>>> arr2 = arr.T
>>> arr2.shape = 12
AttributeError: incompatible shape for a non-contiguous array
Damit dies möglich ist, müsste NumPy die Zeilen folgendermaßen arr.T
zusammensetzen:
(Das Setzen des shape
Attributs setzt direkt die Reihenfolge C voraus - dh NumPy versucht, die Operation zeilenweise auszuführen.)
Das ist unmöglich zu machen. Für jede Achse muss NumPy eine konstante Schrittlänge (die Anzahl der zu verschiebenden Bytes) haben, um zum nächsten Element des Arrays zu gelangen. Das Reduzieren arr.T
auf diese Weise würde das Vor- und Zurückspringen im Speicher erfordern, um aufeinanderfolgende Werte des Arrays abzurufen.
Wenn wir arr2.reshape(12)
stattdessen schreiben würden, würde NumPy die Werte von arr2 in einen neuen Speicherblock kopieren (da es keine Ansicht zu den Originaldaten für diese Form zurückgeben kann).
arr2
in die 1D-Form(12,)
verwendet die C-Reihenfolge, was bedeutet, dass die Achse 1 vor der Achse 0 abgewickelt wird (dh jede der vier Zeilen muss nebeneinander platziert werden, um das gewünschte 1D-Array zu erstellen). Es ist unmöglich, diese Folge von ganzen Zahlen (0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11) mit einer konstanten Schrittlänge (die zu besuchenden Bytes) aus dem Puffer zu lesen Diese Elemente wären nacheinander 4, 4, -7, 4, 4, -7, 4, 4, 7, 4, 4). NumPy erfordert eine konstante Schrittlänge pro Achse.arr[:, ::-1]
also eine Ansicht des gleichen Speicherpuffers wiearr
angezeigt wird, betrachtet NumPy diese nicht als C- oder F-Reihenfolge, da die Werte im Puffer in einer "nicht standardmäßigen" Reihenfolge durchlaufen wurden ...Vielleicht hilft dieses Beispiel mit 12 verschiedenen Array-Werten:
Die
C order
Werte sind in der Reihenfolge, in der sie generiert wurden. Die transponierten sind es nichtSie können 1d Ansichten von beiden erhalten
Die Form von
x
kann auch geändert werden.Die Form der Transponierten kann jedoch nicht geändert werden. Das
data
ist immer noch in der0,1,2,3,4...
Reihenfolge, auf die nicht wie0,4,8...
in einem 1d-Array zugegriffen werden kann .Eine Kopie von
x1
kann jedoch geändert werden:Ein Blick
strides
könnte auch helfen. Ein Schritt ist, wie weit (in Bytes) es gehen muss, um zum nächsten Wert zu gelangen. Für ein 2d-Array gibt es 2 Schrittwerte:Um zur nächsten Zeile zu gelangen, Schritt 16 Bytes, nächste Spalte nur 4.
Transponieren ändert nur die Reihenfolge der Schritte. Die nächste Zeile besteht nur aus 4 Bytes, dh der nächsten Nummer.
Durch Ändern der Form werden auch die Schritte geändert. Gehen Sie jeweils 4 Byte durch den Puffer.
Obwohl es so
x2
aussiehtx1
, hat es einen eigenen Datenpuffer mit den Werten in einer anderen Reihenfolge. Die nächste Spalte ist jetzt 4 Byte länger, während die nächste Zeile 12 (3 * 4) ist.Und wie bei
x
reduziert das Ändern der Form auf 1d die Schritte auf(4,)
.Denn
x1
mit Daten in der0,1,2,...
Reihenfolge gibt es keinen 1d-Schritt, der geben würde0,4,8...
.__array_interface__
ist eine weitere nützliche Methode zum Anzeigen von Array-Informationen:Die
x1
Datenpufferadresse ist dieselbe wie fürx
, mit der die Daten geteilt werden.x2
hat eine andere Pufferadresse.Sie können auch mit dem Hinzufügen eines
order='F'
Parameters zu den Befehlencopy
und experimentierenreshape
.quelle