6.11. Reduktioner

En reduktion kollapsar en array längs en eller flera axlar genom att summera, beräkna medelvärde, ta ett minimum och så vidare. Varje reduktion är ett enda biblioteksanrop mot hela arrayen, vilket är mycket snabbare än motsvarande Python-loop. numpy täcker de vanligaste:

  • sum() – summan av alla element

  • mean() – aritmetiskt medelvärde (summan delad med antalet element)

  • std() – standardavvikelse, ddof= justerar nämnaren (N - ddof)

  • min() / max() – det minsta och det största elementet

  • median() – mittvärdet när elementen är sorterade (50:e percentilen)

  • argmin() / argmax()indexet för det minsta eller största elementet

  • all() / any() – sanningsvärdesreduktioner på booleska arrayer

6.11.1. Utan nyckelordet axis

Anropad utan axis= returnerar en reduktion en skalär som täcker hela arrayen:

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. Med nyckelordet axis

axis= kontraherar en namngiven axel och lämnar de övriga orörda. Resultatet är en array av en rang lägre än indata:

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

Samma formregel gäller för alla reduktioner: axis=0 kollapsar den första axeln, axis=1 kollapsar den andra, och så vidare. Medelvärde / standardavvikelse längs en rad skrivs till exempel np.mean(m, axis=1) och np.std(m, axis=1). Resultatet har längden av den andra axeln.

Nyckelordet keepdims=True behåller den kontraherade axeln på plats med längden 1 istället för att ta bort den. Skillnaden spelar roll när det reducerade resultatet behöver broadcastas tillbaka mot originalet: keepdims bevarar rangen, vilket håller broadcasting-reglerna i linje axel för axel.

Att subtrahera varje rads medelvärde från den raden är den kanoniska användningen:

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

Utan keepdims returnerar np.mean(m, axis=1) ett 1-D-resultat med formen (3,). Broadcasting av (3, 4) - (3,) placerar (3,) som (1, 3) efter att rangen lagts till framtill, vilket är inkompatibelt med (3, 4): de sista axlarna stämmer inte överens (4 mot 3) och ingen är 1, så numpy utlöser ValueError. keepdims=True är det som gör subtraktionen giltig.

6.11.3. Layouten spelar roll

Kombinerat med den radvisa layouten som beskrivs i Form och strides är reduktion längs den sista axeln det billigaste fallet. Reduktionen går igenom datablocket i den riktning det är lagrat, utan hopp från rad till rad:

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

När applikationen kan välja hur en buffert ska läggas ut, placera den långa axeln sist så att reduktioner längs den körs i den snabba riktningen.

6.11.4. Itererbara objekt som indata

De flesta reduktioner accepterar ett itererbart Python-objekt (en list, ett range, en tupel) i stället för en ndarray. Bekvämligheten kostar några mikrosekunder för den implicita konverteringen – vilket snabbt adderas i en loop. När samma data reduceras flera gånger, bygg ndarray en gång och skicka runt den.