6.11. Reduktionen¶
Eine Reduktion fasst ein Array entlang einer oder mehrerer Achsen zusammen, indem sie summiert, mittelt, ein Minimum bildet und so weiter. Jede Reduktion ist ein einziger Bibliotheksaufruf über das gesamte Array, deutlich schneller als die entsprechende Python-Schleife. numpy deckt die gebräuchlichen ab:
sum()– Summe aller Elementemean()– arithmetisches Mittel (Summe geteilt durch die Anzahl der Elemente)std()– Standardabweichung,ddof=passt den Divisor an (N - ddof)median()– mittlerer Wert, wenn die Elemente sortiert sind (50. Perzentil)argmin()/argmax()– der Index des minimalen oder maximalen Elementsall()/any()– Wahrheitswert-Reduktionen auf booleschen Arrays
6.11.1. Ohne das Schlüsselwort axis¶
Ohne axis= aufgerufen, liefert eine Reduktion einen Skalar zurück, der das gesamte Array abdeckt:
a = np.array([1, 2, 3, 4], dtype=np.float)
np.sum(a) # 10.0
np.mean(a) # 2.5
np.std(a) # 1.118...
np.median(a) # 2.5
b = np.array([40, 10, 30, 20], dtype=np.float)
np.max(b) # 40.0
np.argmax(b) # 0 (index of the maximum)
6.11.2. Mit dem Schlüsselwort axis¶
axis= zieht eine benannte Achse zusammen und lässt die übrigen unverändert. Das Ergebnis ist ein Array, dessen Rang um eins niedriger ist als der der Eingabe:
m = np.arange(12, dtype=np.float).reshape((3, 4))
np.sum(m) # 66.0 - scalar
np.sum(m, axis=0) # length-4 - column sums
np.sum(m, axis=1) # length-3 - row sums
Dieselbe Formregel gilt für jede Reduktion: axis=0 fasst die erste Achse zusammen, axis=1 fasst die zweite zusammen und so weiter. Mittelwert / Standardabweichung entlang einer Zeile werden beispielsweise als np.mean(m, axis=1) und np.std(m, axis=1) geschrieben. Das Ergebnis hat die Länge der anderen Achse.
Das Schlüsselwort keepdims=True behält die zusammengezogene Achse mit Länge 1 bei, anstatt sie wegzulassen. Diese Unterscheidung ist wichtig, wenn das reduzierte Ergebnis gegen das Original zurückgebroadcastet werden muss: keepdims erhält den Rang, was die Broadcasting-Regeln Achse für Achse ausgerichtet hält.
Das kanonische Beispiel ist das Subtrahieren des Mittelwerts jeder Zeile von dieser Zeile:
m = np.arange(12, dtype=np.float).reshape((3, 4))
row_means = np.mean(m, axis=1, keepdims=True)
# row_means has shape (3, 1)
centred = m - row_means
# (3, 4) - (3, 1) -> (3, 4), each row centred on its own mean
Ohne keepdims liefert np.mean(m, axis=1) ein eindimensionales Ergebnis der Form (3,). Beim Broadcasting von (3, 4) - (3,) wird (3,) nach dem Voranstellen des Rangs als (1, 3) ausgerichtet, was mit (3, 4) inkompatibel ist: Die letzten Achsen stimmen nicht überein (4 gegen 3) und keine ist 1, sodass numpy einen ValueError auslöst. keepdims=True ist das, was die Subtraktion gültig hält.
6.11.3. Das Layout ist wichtig¶
In Kombination mit dem zeilenweisen Layout, das in Form und Strides behandelt wird, ist die Reduktion entlang der letzten Achse der günstigste Fall. Die Reduktion durchläuft den Datenblock in der Richtung, in der er gespeichert ist, ohne Sprünge von Zeile zu Zeile:
m = np.arange(2000, dtype=np.float).reshape((2, 1000))
np.sum(m, axis=1) # cheap - long axis is the inner one
np.sum(m, axis=0) # has to jump rows on every step
Wenn die Anwendung die Wahl hat, wie ein Puffer angeordnet wird, sollte die lange Achse zuletzt platziert werden, damit Reduktionen entlang dieser Achse in der schnellen Richtung laufen.
6.11.4. Iterables als Eingabe¶
Die meisten Reduktionen akzeptieren ein Python-Iterable (eine list, ein range, ein Tupel) anstelle eines ndarray. Diese Bequemlichkeit kostet ein paar Mikrosekunden für die implizite Umwandlung – was sich in einer Schleife schnell summiert. Wenn dieselben Daten mehrmals reduziert werden, sollte das ndarray einmal erstellt und weitergereicht werden.