6.11. Reducciones¶
Una reducción colapsa un arreglo a lo largo de uno o varios ejes sumando, promediando, tomando un mínimo, etc. Cada reducción es una sola llamada de biblioteca sobre todo el arreglo, mucho más rápida que el bucle de Python equivalente. numpy cubre las habituales:
sum()– total de todos los elementosmean()– media aritmética (suma dividida por el número de elementos)std()– desviación estándar,ddof=ajusta el divisor (N - ddof)median()– valor central cuando los elementos están ordenados (percentil 50)argmin()/argmax()– el índice del elemento mínimo o máximoall()/any()– reducciones de valor de verdad sobre arreglos booleanos
6.11.1. Sin la palabra clave axis¶
Llamada sin axis=, una reducción devuelve un escalar que abarca todo el arreglo:
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. Con la palabra clave axis¶
axis= contrae un eje concreto y deja los demás intactos. El resultado es un arreglo de un rango inferior al de la entrada:
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
La misma regla de forma se aplica a todas las reducciones: axis=0 colapsa el primer eje, axis=1 colapsa el segundo, y así sucesivamente. La media o la desviación estándar a lo largo de una fila, por ejemplo, se escriben np.mean(m, axis=1) y np.std(m, axis=1). El resultado tiene la longitud del otro eje.
La palabra clave keepdims=True mantiene en su lugar el eje contraído con longitud 1 en vez de eliminarlo. La distinción importa cuando el resultado reducido necesita volver a difundirse (broadcast) contra el original: keepdims preserva el rango, lo que mantiene alineadas las reglas de difusión eje por eje.
Restar la media de cada fila a esa misma fila es el uso canónico:
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
Sin keepdims, np.mean(m, axis=1) devuelve un resultado 1-D de forma (3,). Al difundir (3, 4) - (3,) se alinea (3,) como (1, 3) tras anteponer el rango, lo cual es incompatible con (3, 4): los últimos ejes no concuerdan (4 frente a 3) y ninguno es 1, por lo que numpy lanza ValueError. keepdims=True es lo que mantiene válida la resta.
6.11.3. La disposición importa¶
Combinado con la disposición por filas (row-major) tratada en Forma y pasos, reducir a lo largo del último eje es el caso más barato. La reducción recorre el bloque de datos en la dirección en que está almacenado, sin saltos de fila en fila:
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
Cuando la aplicación puede elegir cómo disponer un búfer, coloca el eje largo en último lugar para que las reducciones a lo largo de él se ejecuten en la dirección rápida.
6.11.4. Iterables como entrada¶
La mayoría de las reducciones aceptan un iterable de Python (una list, un range, una tupla) en lugar de un ndarray. Esa comodidad cuesta unos pocos microsegundos por la conversión implícita, lo que se acumula rápidamente en un bucle. Cuando los mismos datos se reducen varias veces, construye el ndarray una vez y pásalo entre las llamadas.