6.11. Redukcje¶
Redukcja zwija tablicę wzdłuż jednej lub kilku osi, sumując ją, uśredniając, wyznaczając minimum i tak dalej. Każda redukcja to pojedyncze wywołanie biblioteki na całej tablicy, znacznie szybsze niż równoważna pętla w Pythonie. numpy obejmuje te codzienne operacje:
sum()– suma wszystkich elementówmean()– średnia arytmetyczna (suma podzielona przez liczbę elementów)std()– odchylenie standardowe,ddof=koryguje dzielnik (N - ddof)median()– wartość środkowa po posortowaniu elementów (50. percentyl)argmin()/argmax()– indeks elementu minimalnego lub maksymalnegoall()/any()– redukcje wartości logicznych na tablicach typu boolean
6.11.1. Bez słowa kluczowego axis¶
Wywołana bez axis=, redukcja zwraca skalar obejmujący całą tablicę:
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. Ze słowem kluczowym axis¶
axis= redukuje jedną wskazaną oś, pozostawiając pozostałe nienaruszone. Wynikiem jest tablica o rangę niższej niż dane wejściowe:
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
Ta sama reguła kształtu obowiązuje przy każdej redukcji: axis=0 zwija pierwszą oś, axis=1 zwija drugą i tak dalej. Na przykład średnią / odchylenie standardowe wzdłuż wiersza zapisuje się jako np.mean(m, axis=1) oraz np.std(m, axis=1). Wynik ma długość drugiej osi.
Słowo kluczowe keepdims=True zachowuje zredukowaną oś w miejscu z długością 1, zamiast ją usuwać. Rozróżnienie ma znaczenie, gdy zredukowany wynik musi z powrotem rozpropagować się względem oryginału: keepdims zachowuje rangę, co utrzymuje zgodność reguł rozpropagowania oś po osi.
Odejmowanie od każdego wiersza jego średniej to kanoniczne zastosowanie:
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
Bez keepdims np.mean(m, axis=1) zwraca jednowymiarowy wynik o kształcie (3,). Rozpropagowanie (3, 4) - (3,) ustawia (3,) jako (1, 3) po dodaniu rangi z przodu, co jest niezgodne z (3, 4): ostatnie osie się różnią (4 wobec 3) i żadna nie jest równa 1, więc numpy zgłasza ValueError. To właśnie keepdims=True utrzymuje poprawność odejmowania.
6.11.3. Układ danych ma znaczenie¶
W połączeniu z układem wierszowym (row-major) omówionym w Kształt i kroki, redukcja wzdłuż ostatniej osi jest przypadkiem najtańszym. Redukcja przechodzi przez blok danych w kierunku, w jakim są one zapisane, bez przeskoków z wiersza do wiersza:
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
Gdy aplikacja ma swobodę w doborze rozkładu bufora, umieść długą oś jako ostatnią, aby redukcje wzdłuż niej działały w szybkim kierunku.
6.11.4. Iterowalne jako dane wejściowe¶
Większość redukcji przyjmuje iterowalny obiekt Pythona (list, range, krotkę) zamiast ndarray. Ta wygoda kosztuje kilka mikrosekund na niejawną konwersję – co szybko narasta w pętli. Gdy te same dane są redukowane wielokrotnie, zbuduj ndarray raz i przekazuj go dalej.