Resúmenes y valores faltantes
En resumen: Las agregaciones en NumPy (sum, mean, max, argmax) hacen un número a partir de muchos, axis dice qué dimensión desaparece, y NaN marca un valor faltante.
Problema
«¿Qué es lo que más vendemos?»
La caja manda un informe: cuántas unidades de cuatro productos vendimos cada semana, en las últimas seis semanas. Cada fila es una semana, cada columna un producto:
El gerente pregunta tres cosas: cuánto vendimos en total, qué producto va mejor y qué semana fue la más fuerte. Luego manda también los ingresos por día, con un día que falta.
Cada respuesta es un número (o uno por producto), calculado a partir de muchos números. Eso se llama una agregación.
La idea
Un número a partir de muchos: sum, mean, min, max
Un arreglo tiene métodos que resumen todos sus valores: sum() para el total, mean() para la media, min() y max() para el menor y el mayor. Aquí están con los precios de los 25 productos. to_numpy() toma la columna como arreglo de NumPy:
La suma de los precios sale 175.89999999999998, no 175,9. La computadora guarda los decimales en binario y 0,1 no se puede escribir exactamente, así que se acumulan errores diminutos. No son errores de los datos.
Para un informe redondeas: round(x, 2) da 2 decimales. El precio medio pasa a ser 7,04 €, como en describe() del primer vistazo.
Más precisamente, para curiosos
round es la función de Python y trabaja con un solo número. Un arreglo tiene el método .round(2) (o np.round(arr, 2)), que redondea cada número. Redondea al final, para mostrar: si redondeas a mitad de camino, el error de redondeo pasa a los pasos siguientes.
La idea
Cuál, no cuánto: argmax y argmin
max() te dice cuánto cuesta el producto más caro. Pero el gerente pregunta cuál es. argmax() da la posición del mayor valor, y argmin() la del menor. Con la posición tomas el nombre del arreglo de nombres:
El más caro es el juego de cuchillos en la posición 22 (39 €), y el más barato es el bollo de mantequilla en la posición 2 (0,80 €). Las posiciones empiezan en 0, como en las listas.
Más precisamente, para curiosos
Si el mayor valor aparece varias veces, argmax da su primera posición. En el informe de caja, las semanas 1 y 4 tienen 103 unidades cada una, y argmin sobre las semanas da solo la semana 1. Si los empates importan, compruébalos con una máscara: week_totals == week_totals.min().
Míralo
A lo largo de qué dimensión: axis
sales.sum() suma las 24 celdas: 654 unidades en seis semanas. Para «qué producto» necesitas un total por cada columna, y para «qué semana» un total por cada fila. El parámetro axis dice cuál:
axis=0es la dimensión de las filas (las semanas). Las semanas se funden y queda un número por producto: el pan son 263 unidades, el café 39.axis=1es la dimensión de las columnas (los productos). Los productos se funden y queda un número por semana: de 103 a 120.
Aquí se confunde todo el mundo, porque «axis=0» suena a «por filas» y da un resultado por columna. La regla que no miente: axis es la dimensión que desaparece. La forma (6, 4) con axis=0 pierde el 6 y pasa a (4,), y con axis=1 pierde el 4 y pasa a (6,).
Lo mismo vale para mean, min, max, argmax y el resto. La media por semana de cada producto, redondeada a un decimal:
Pruébalo
La semana más fuerte
¿Qué semana vendió más unidades en total? Pon el total de cada semana en week_totals y su número (de 1 a 6) en best_week.
Solución
import numpy as np
names = np.array(['Pan blanco', 'Leche fresca', 'Manzanas', 'Café en grano'])
sales = np.array([
[42, 30, 25, 6],
[38, 34, 28, 4],
[45, 29, 31, 7],
[40, 36, 22, 5],
[51, 33, 27, 9],
[47, 31, 26, 8],
])
week_totals = sales.sum(axis=1)
best_week = week_totals.argmax() + 1
print(week_totals)
print(best_week)week_totals = sales.sum(axis=1) da [103 104 112 103 120 112]: los productos desaparecen, las semanas quedan. week_totals.argmax() es 4, y la posición 4 es la quinta semana, así que best_week = 5 (120 unidades). Con axis=0 tendrías 4 números, los totales por producto, y la «semana más fuerte» sería el pan blanco.
La idea
Cuánto hasta ahora: cumsum
A veces el gerente pregunta no «cuánto esta semana» sino «cuánto desde el principio hasta esta semana». Es la suma acumulada: cumsum() suma los números uno a uno y guarda cada suma parcial:
Después de la tercera semana hemos vendido 319 unidades, y el último número, 654, es el total de sales.sum(). La suma acumulada es la línea que crece en los gráficos de «ventas en lo que va del año».
La idea
Un día que falta: NaN
Ahora los ingresos por día de la última semana. El miércoles la caja no mandó informe, así que en el lugar del miércoles hay un np.nan: el valor para «faltante» que viste en el primer vistazo. Esto le pasa a la media:
La media normal es nan. Cualquier cuenta con NaN da NaN: ¿cuánto es 412,5 más «no sé»? No sé. NumPy no lanza ningún error; devuelve nan en silencio, y en un informe largo pasa desapercibido.
np.isnan(revenue) es una máscara que dice dónde faltan valores, y .sum() sobre ella los cuenta: 1 día. np.nanmean y np.nansum se saltan los NaN: los ingresos medios de los seis días conocidos son 494,70 €, y su suma es 2968,20 €. Esa suma es «lo que sabemos hasta ahora», no los ingresos de toda la semana: el miércoles no está en ella.
Míralo
Por qué no 0
La tentación es sustituir el valor que falta por 0 y seguir. np.nan_to_num hace justo eso:
La media baja de 494,70 € a 424,03 €. El cero dice «el miércoles no vendimos nada», y eso no es verdad: la tienda estaba abierta, solo falta el informe. 0 no es «faltante». El cero es un valor, y NaN es la ausencia de un valor.
0 es la sustitución correcta solo cuando de verdad significa cero. Si la caja no manda ninguna línea para un producto que nadie compró, el hueco son cero ventas. Eso lo decide el significado de los datos, no el código.
Pruébalo
Una semana sin cuenta
La caja no mandó cuántos paquetes de café vendimos en la tercera semana. Pon en avg_coffee cuántos paquetes vendemos por semana, en promedio.
Solución
import numpy as np
# Paquetes de café por semana. La caja no envió la cuenta de la semana 3.
coffee = np.array([6, 4, np.nan, 5, 9, 8])
avg_coffee = np.nanmean(coffee)
print(avg_coffee)avg_coffee = np.nanmean(coffee) da 6,4: la media de las cinco semanas conocidas (32 paquetes en 5 semanas). coffee.mean() da nan, y con 0 en lugar del hueco sale alrededor de 5,33: seis semanas, una de las cuales cuenta como semana sin ventas, y no lo fue.
Ejercicio
El informe para el gerente
Responde al gerente a partir del informe de caja. Pon en product_totals las unidades de cada producto en las seis semanas, en best_product el nombre del producto más vendido, y en avg_per_week cuántas unidades vende cada producto por semana en promedio, redondeado a un decimal.
Solución
import numpy as np
names = np.array(['Pan blanco', 'Leche fresca', 'Manzanas', 'Café en grano'])
sales = np.array([
[42, 30, 25, 6],
[38, 34, 28, 4],
[45, 29, 31, 7],
[40, 36, 22, 5],
[51, 33, 27, 9],
[47, 31, 26, 8],
])
product_totals = sales.sum(axis=0)
best_product = names[product_totals.argmax()]
avg_per_week = sales.mean(axis=0).round(1)
print(best_product)
print(avg_per_week)product_totals = sales.sum(axis=0) da [263 193 159 39]: las semanas desaparecen. names[product_totals.argmax()] es «Pan blanco» con 263 unidades. sales.mean(axis=0).round(1) da [43.8 32.2 26.5 6.5]: el pan se vende unas 44 veces por semana, y el café 6 o 7 paquetes. product_totals.max() habría respondido «263» a una pregunta de «cuál».
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: La primera tabla · El arreglo de NumPy · Vectorización
- Continúa en: Broadcasting: el número, la fila y la columna · Series y DataFrame · Filtrar y ordenar · Columnas nuevas
- Ejercicios sobre el tema: NumPy