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.