6.11. Redukcije

Redukcija sažima polje duž jedne ili više osi zbrajanjem, usrednjavanjem, uzimanjem minimuma i tako dalje. Svaka redukcija je jedan poziv biblioteke nad cijelim poljem, mnogo brži od ekvivalentne Python petlje. numpy pokriva uobičajene:

  • sum() – zbroj svih elemenata

  • mean() – aritmetička sredina (zbroj podijeljen brojem elemenata)

  • std() – standardna devijacija, ddof= prilagođava djelitelj (N - ddof)

  • min() / max() – najmanji i najveći element

  • median() – srednja vrijednost kada su elementi sortirani (50. percentil)

  • argmin() / argmax()indeks minimalnog ili maksimalnog elementa

  • all() / any() – redukcije istinitosne vrijednosti nad booleovim poljima

6.11.1. Bez ključne riječi axis

Pozvana bez axis=, redukcija vraća skalar koji pokriva cijelo polje:

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. S ključnom riječi axis

axis= sažima jednu imenovanu os i ostavlja ostale netaknute. Rezultat je polje za jedan rang nižeg ranga od ulaza:

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

Isto pravilo oblika vrijedi za svaku redukciju: axis=0 sažima prvu os, axis=1 sažima drugu, i tako dalje. Sredina / standardna devijacija duž retka, na primjer, pišu se kao np.mean(m, axis=1) i np.std(m, axis=1). Rezultat ima duljinu druge osi.

Ključna riječ keepdims=True zadržava sažetu os na mjestu s duljinom 1 umjesto da je izbaci. Razlika je važna kada se reducirani rezultat treba ponovno proširiti (broadcast) prema izvornom: keepdims čuva rang, što održava pravila proširivanja usklađenima os po os.

Oduzimanje sredine svakog retka od tog retka kanonska je upotreba:

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) vraća 1-D rezultat oblika (3,). Proširivanje (3, 4) - (3,) poravnava (3,) kao (1, 3) nakon dodavanja ranga sprijeda, što je nekompatibilno s (3, 4): posljednje osi se ne podudaraju (4 naspram 3) i nijedna nije 1, pa numpy baca ValueError. keepdims=True je ono što oduzimanje održava valjanim.

6.11.3. Raspored je važan

U kombinaciji s rasporedom po recima (row-major) opisanim u Oblik i koraci, redukcija duž posljednje osi najjeftiniji je slučaj. Redukcija prolazi kroz blok podataka u smjeru u kojem je pohranjen, bez skokova iz retka u redak:

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

Kada aplikacija ima izbor kako rasporediti međuspremnik, stavite dugu os zadnju kako bi redukcije duž nje radile u brzom smjeru.

6.11.4. Iterabilni objekti kao ulaz

Većina redukcija prihvaća Python iterabilni objekt (list, range, n-torku) umjesto ndarray. Ta pogodnost košta nekoliko mikrosekundi za implicitnu konverziju – što se brzo nakuplja u petlji. Kada se isti podaci reduciraju više puta, izgradite ndarray jednom i proslijedite ga uokolo.