6.7. Views und Kopien¶
Ein View ist ein zweites Fenster auf denselben Datenblock wie die Quelle. Es werden keine Daten kopiert; der View hält einen frischen Deskriptor (seine eigene Form, Strides und dtype), teilt sich aber den Puffer. Views sind im Wesentlichen kostenlos.
Eine Kopie fordert von der Kamera einen neuen Puffer an und durchläuft die Quelle, um ihn zu füllen. Kopien kosten sowohl Zeit als auch RAM.
Die meisten formverändernden Methoden erzeugen Views. Die meisten datentransformierenden erzeugen Kopien. Zu wissen, was was ist, entscheidet darüber, ob eine heiße Schleife der Kamera den RAM ausgehen lässt.
6.7.1. Reshape¶
reshape() gibt ein Array der angeforderten Form zurück. Die Gesamtzahl der Elemente muss unverändert bleiben, sonst wird ValueError ausgelöst:
a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))
Das Ergebnis ist ein View – m und a teilen sich Daten. Das Schreiben über m[0, 0] = 99 ändert auch a[0].
Das Zuweisen eines neuen Tupels an shape ist eine Kurzform für dieselbe Operation:
a = np.arange(9)
a.shape = (3, 3)
6.7.2. Transpose¶
transpose() (oder die Abkürzung .T) kehrt die Achsen um. Implementiert durch Umkehren der Strides – es werden keine Daten bewegt:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T # shape (3, 2), shares m's buffer
Ein transponierter View durchläuft den Datenblock nicht zusammenhängend. Das obige t zeilenweise zu lesen besucht die Speicherpositionen 0, 3, 1, 4, 2, 5, nicht die zugrunde liegende Reihenfolge 0, 1, 2, 3, 4, 5, in der die Bytes angeordnet sind. Gewöhnliche Arithmetik und Reduktionen kommen damit gut zurecht – sie laufen durch die Strides – aber tobytes() kann das nicht, weil es den zugrunde liegenden Puffer direkt ohne Kopieren zurückgibt. Die Bytes, die der Puffer enthält, stimmen nicht mit der Reihenfolge überein, die die Form des Views impliziert, sodass die Methode bei jedem nicht zusammenhängenden View ValueError auslöst. Wenn die Bytes in der transponierten Reihenfolge benötigt werden, erzwingen Sie zuerst eine frische zusammenhängende Kopie:
bytes_out = t.copy().tobytes()
6.7.3. Flatten und flat¶
flatten() gibt eine 1-D-Kopie des Arrays zurück:
f = m.flatten() # new dense 1-D ndarray
Übergeben Sie order='C' (Standard), um zuerst die letzte Achse zu durchlaufen, oder order='F', um zuerst die erste Achse zu durchlaufen:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
# m = [[0, 1, 2],
# [3, 4, 5]]
m.flatten() # array([0, 1, 2, 3, 4, 5], dtype=uint8)
m.flatten(order='F') # array([0, 3, 1, 4, 2, 5], dtype=uint8)
flat ist die Iterator-Form. Sie liefert jedes Element eines ndarray beliebigen Rangs als Skalare, ohne eine flache Kopie zu allozieren:
for x in m.flat:
print(x)
Wenn die Anwendung jedes Element durchlaufen muss, bevorzugen Sie flat; wenn sie einen dichten 1-D-Puffer benötigt, um ihn an eine andere Funktion zu übergeben, verwenden Sie flatten().
6.7.4. Iteration¶
Das Iterieren eines 1-D-Arrays liefert Skalare; das Iterieren eines höherrangigen Arrays liefert (n-1)-D-Views:
m = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.uint8)
for row in m:
print(row) # array([0, 1, 2]), array([3, 4, 5])
Die durch das Iterieren einer Matrix gelieferten Zeilen sind Views, sodass ihre Modifikation die Quelle modifiziert.
6.7.5. Kopien¶
copy() ist die explizite Methode, um ein unabhängiges ndarray zu erhalten, dessen Modifikationen das Original nicht beeinflussen. Es wird ein neuer Puffer alloziert und die Quelle hineingelaufen:
c = a.copy()
tobytes() gibt ein bytearray zurück, das sich Speicher mit dem Datenblock des Arrays teilt. Schreibvorgänge über das bytearray modifizieren das Array an Ort und Stelle. Löst ValueError aus, wenn das Array nicht dicht ist (ein gesliceter View, eine Transposition, …).
tolist() gibt den Inhalt als eine möglicherweise verschachtelte Python-list zurück. Nützlich zum Serialisieren kleiner Ergebnisse; teuer für große, weil jedes Element zu einem separaten Python-Objekt wird.
6.7.6. Welche Operationen welche zurückgeben¶
Die vollständige Regel:
Die folgenden Operationen geben Views zurück:
Slicing –
a[1:5],a[::2],m[:, 0];Indizierung entlang einer einzelnen Achse eines höherrangigen Arrays –
m[0];Iterieren eines n-D-Arrays;
reshape(), wenn das angeforderte Layout kompatibel ist;transpose()/.T;asarray(), wenn der dtype übereinstimmt.
Die folgenden Operationen geben Kopien zurück:
boolesche Indizierung –
a[mask];Arithmetik –
a + b,a * 2,np.sin(a);array()– kopiert immer, auch von einem anderen Array;
Greifen Sie nur dann zu einer expliziten Kopie, wenn wirklich ein unabhängiger Puffer benötigt wird. Auf einer Kamera mit begrenztem RAM ist der Unterschied zwischen einem View und einer Kopie oft der Unterschied zwischen Code, der hineinpasst, und Code, der das nicht tut.