6.12. Auswahl und Umsortierung#
Reduktionen fassten ein Array zu einem Skalar oder einem Ergebnis niedrigeren Rangs zusammen. Die Auswahl umfasst die Operationen, die festlegen, welche Elemente überleben und wo sie landen: bedingte Auswahl, Begrenzen, Sortieren, Nachschlagen von Indizes, Umsortieren entlang einer Achse.
6.12.1. Bedingte Auswahl#
where() gibt ein Array zurück, das Elemente aus x übernimmt, wo die Bedingung wahr ist, und andernfalls aus y. Die drei Operanden werden gemeinsam gebroadcastet:
a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])
Dies ist das richtige Werkzeug für ein „if/else pro Element“, ohne eine Python-Schleife zu schreiben.
clip() ist eine Kurzform für maximum(lo, minimum(a, hi)) – es sättigt die Werte auf einen Bereich:
np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])
maximum() und minimum() nehmen zwei Operanden und geben elementweise den größeren / kleineren zurück:
np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))
6.12.2. Indizes finden#
nonzero() gibt die Koordinaten jedes Elements ungleich null zurück, aufgeteilt in ein Index-Array pro Dimension. Für eine zweidimensionale Eingabe ist das Ergebnis ein Tupel aus zwei Arrays: Das erste enthält die Zeilenindizes, das zweite die Spaltenindizes. Spaltenweise zusammengefasst ergeben sie die (row, col) jeder Position ungleich null:
m = np.array([[0, 2, 0],
[3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))
Die Einträge ungleich null in m sind m[0, 1] = 2 und m[1, 0] = 3. Das erste zurückgegebene Array [0, 1] liefert deren Zeilenindizes; das zweite [1, 0] liefert deren Spaltenindizes. Liest man die beiden Arrays nebeneinander, erhält man die Positionen (0, 1) und (1, 0) zurück.
Zwei Reduktionen erzeugen ebenfalls Indizes:
argmin()/argmax()– Index des kleinsten / größten Elements.argsort()– ein Integer-Array, das die Eingabe entlang der angegebenen Achse sortieren würde (standardmäßig die letzte):a = np.array([40, 10, 30, 20], dtype=np.uint8) idx = np.argsort(a) # array([1, 3, 2, 0], dtype=uint16) a[idx] # array([10, 20, 30, 40])
argsortgibt immeruint16zurück; das zu sortierende Array darf daher auf der sortierten Achse nicht mehr als 65.535 Elemente haben.
bincount() zählt das Vorkommen jeder nicht-negativen Ganzzahl in einer eindimensionalen uint8 / uint16-Eingabe:
histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)
Nützlich, um Histogramme von Pixelwerten mit kleinen Ganzzahlen zu erstellen, ohne eine Python-Schleife zu schreiben.
6.12.3. Sortieren und Umsortieren#
sort() gibt eine sortierte Kopie des Arrays entlang der angegebenen Achse zurück (standardmäßig die letzte). Verwenden Sie sort() direkt auf dem Array für eine In-Place-Version:
np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])
flip() kehrt die Reihenfolge entlang der angegebenen Achse um (jede Achse, wenn kein axis übergeben wird):
np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])
roll() verschiebt Elemente zyklisch um die angegebene Anzahl. Nützlich zur Implementierung eines Schieberegisters im Ringpuffer-Stil:
np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])
take() ist die explizite Form der Fancy-Indizierung – Elemente an beliebigen Indizes auswählen:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)
6.12.4. Filtern und strukturelle Bearbeitungen#
compress() ist die explizite Form der booleschen Indizierung – es gibt die durch die boolesche Bedingung ausgewählten Ausschnitte von a zurück:
a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)
delete() gibt eine Kopie zurück, in der die Einträge an den angegebenen Indizes entfernt sind:
a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)
diff() gibt die n-te diskrete Vorwärtsdifferenz des Arrays entlang einer Achse zurück. Wird verwendet, um Änderungen erster Ordnung zwischen benachbarten Samples zu berechnen:
samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])
6.12.5. Was jede Operation kostet#
Fast jede Funktion auf dieser Seite gibt ein neu allokiertes Array zurück. Zwei Ausnahmen:
sort()sortiert in-place; die freie Funktionsort()gibt eine sortierte Kopie zurück.take()akzeptiert ein Schlüsselwortout=, um in einen bereits vorhandenen Puffer zu schreiben.
In einer Schleife, die viele Male pro Sekunde läuft, sollte das In-Place-sort() bevorzugt und überall sonst vorab allokierte Puffer wiederverwendet werden. Boolesche Masken selbst werden bei jeder Ausführung des Vergleichs allokiert – erstellen Sie eine Maske einmal und verwenden Sie sie über mehrere Operationen hinweg wieder, anstatt sie in jeder Iteration neu aufzubauen.