6.11. Redukce¶
Redukce sbalí pole podél jedné nebo více os pomocí sčítání, průměrování, hledání minima a podobně. Každá redukce je jedním voláním knihovny nad celým polem, což je mnohem rychlejší než ekvivalentní smyčka v Pythonu. numpy pokrývá ty každodenní:
sum()– součet všech prvkůmean()– aritmetický průměr (součet dělený počtem prvků)std()– směrodatná odchylka,ddof=upravuje dělitele (N - ddof)median()– prostřední hodnota po seřazení prvků (50. percentil)argmin()/argmax()– index minimálního nebo maximálního prvkuall()/any()– redukce pravdivostní hodnoty nad logickými poli
6.11.1. Bez klíčového slova axis¶
Při volání bez axis= vrací redukce skalár pokrývající celé pole:
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 klíčovým slovem axis¶
axis= smrští jednu pojmenovanou osu a ostatní ponechá beze změny. Výsledkem je pole o jeden řád nižší než vstup:
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
Stejné pravidlo pro tvar platí pro každou redukci: axis=0 sbalí první osu, axis=1 sbalí druhou a tak dále. Průměr / směrodatná odchylka podél řádku se například zapisují jako np.mean(m, axis=1) a np.std(m, axis=1). Výsledek má délku druhé osy.
Klíčové slovo keepdims=True ponechá smrštěnou osu na místě s délkou 1 místo jejího odstranění. Tento rozdíl je důležitý, když je potřeba redukovaný výsledek vysílat (broadcast) zpět proti původnímu poli: keepdims zachovává řád, což udržuje pravidla broadcastingu zarovnaná osu po ose.
Kanonickým použitím je odečtení průměru každého řádku od tohoto řádku:
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 vrací np.mean(m, axis=1) jednorozměrný výsledek tvaru (3,). Broadcasting (3, 4) - (3,) zarovná (3,) jako (1, 3) po doplnění řádu zleva, což je nekompatibilní s (3, 4): poslední osy si neodpovídají (4 proti 3) a ani jedna není 1, takže numpy vyvolá ValueError. Právě keepdims=True udržuje odečítání platným.
6.11.3. Na uspořádání záleží¶
Ve spojení s uspořádáním po řádcích (row-major), které popisuje Tvar a kroky, je redukce podél poslední osy nejlevnějším případem. Redukce prochází datový blok ve směru, ve kterém je uložen, bez přeskoků z řádku na řádek:
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
Když má aplikace na výběr, jak rozvrhnout buffer, umístěte dlouhou osu jako poslední, aby redukce podél ní běžely v rychlém směru.
6.11.4. Iterovatelné objekty jako vstup¶
Většina redukcí přijímá místo objektu ndarray také iterovatelný objekt Pythonu (list, range, n-tici). Toto pohodlí stojí několik mikrosekund za implicitní konverzi – což se ve smyčce rychle nasčítá. Když se stejná data redukují vícekrát, vytvořte ndarray jednou a předávejte jej dál.