Ir al contenido
wannacode

Grupos: groupby

En resumen: groupby en pandas divide las filas por una clave, calcula en cada grupo (sum, mean, agg) y junta el resultado en una tabla.

Problema

«¿Qué tamaño tiene el pedido medio?»

El gerente pregunta cuánto gasta un cliente en un pedido, en promedio. Tienes los pedidos del año y sus líneas: cada línea es un producto en un pedido, con la cantidad y el precio. Los pedidos cancelados no son ingresos, así que los quitas con una máscara, como en la lección sobre filtros:

Pythondatos: las tablas de la tienda

Hay 25 069 líneas y 10 459 pedidos. La media de la columna revenue es 4,44 €, pero esa es la línea media, no el pedido medio. Un pedido tiene varias líneas, y para un pedido primero tienes que sumar sus líneas.

Necesitas un total para cada pedido: 10 459 números a partir de 25 069 líneas. Con un for y un diccionario se puede, pero es lento y largo. pandas tiene para eso una sola operación.

La idea

Dividir, aplicar, combinar

groupby('order_id') divide las filas en grupos: un grupo por cada valor distinto de order_id. Después eliges una columna y un cálculo: ['revenue'].sum() suma los ingresos de cada grupo. Al final pandas pone los resultados en una Serie con order_id como índice.

Eso se llama split-apply-combine (dividir, aplicar, combinar). Aquí con las primeras 7 líneas:

Pythondatos: las tablas de la tienda

El pedido 1 tiene una línea (31,60 €), y los pedidos 2, 3 y 4 tienen dos cada uno. El pedido 3 es 37,5 + 8,4 = 45,90 €. De 7 líneas salen 4 números, uno por grupo. La clave order_id es el índice del resultado, por eso aparece debajo del nombre de la columna.

En lugar de sum puedes usar cualquier resumen de NumPy: mean, min, max, count.

Míralo

Cómo recorre groupby las filas

Las mismas 7 líneas, despacio: cada línea entra en el grupo de su order_id, el grupo se suma en un número, y los números se alinean en el resultado.

import pandas as pd
items = pd.read_csv('order_items.csv').head(7)
items['revenue'] = items['quantity'] * items['unit_price']
print(items[['order_id', 'revenue']])
items.groupby('order_id')['revenue'].sum()
Paso a paso, en palabras
  1. 7 filas de order_items con los ingresos de cada línea: pedidos 1, 2, 3 y 4.
  2. Dividir (split): groupby('order_id') pone cada fila en el grupo de su order_id.
  3. 4 order_id distintos, 4 grupos. El pedido 1 tiene una fila, los demás 2 cada uno.
  4. Aplicar (apply): ['revenue'].sum() en cada grupo. Pedido 3: 37,5 + 8,4 = 45,9.
  5. Combinar (combine): las 4 sumas pasan a ser una Serie con order_id como índice.
  6. Entraron 7 filas, salieron 4 números: uno por cada grupo.

Pruébalo

Qué producto se pide más

En los 60 pedidos de order_items.csv: ¿cuántas unidades de cada producto se pidieron? Guárdalas en units, y los tres productos con más unidades en top3.

Pythondatos: las tablas de la tienda

La idea

Varios cálculos a la vez: agg

A menudo necesitas varios números para cada grupo. agg toma una lista de cálculos y da una columna para cada uno:

Pythondatos: las tablas de la tienda

Las columnas se llaman como los cálculos. Para un informe es mejor ponerles nombre tú mismo. Son las agregaciones con nombre: nombre_nuevo=('columna', 'cálculo'), y cada una puede usar otra columna:

Pythondatos: las tablas de la tienda

El pedido 3 tiene 2 líneas, 7 unidades y 45,90 € de ingresos. La tabla tiene una fila por pedido y una columna por cada pregunta que hiciste.

La idea

El conteo: size o count

Los dos contadores se parecen, pero cuentan cosas distintas. size() cuenta las filas de cada grupo. count() cuenta los valores rellenados de cada columna: NaN no se cuenta. Aquí están los clientes por el año en que se registraron:

Pythondatos: las tablas de la tienda

16 clientes se registraron en 2025, pero la columna city tiene 15 valores: falta la ciudad de un cliente. Si quieres «cuántos clientes», size() es la respuesta correcta. count() sobre una columna responde «de cuántos conocemos la ciudad».

1. Hay 16 clientes de 2025, y de uno falta la ciudad. ¿Qué muestra customers.groupby('year')['city'].count() para 2025?

La idea

Varias claves

La clave puede ser una lista de columnas. Entonces un grupo es cada par de valores. Aquí están las unidades del año por producto y por precio:

Pythondatos: las tablas de la tienda

Hay 25 productos y 26 grupos: un producto se vendió a dos precios. Es el café (producto 17): 245 paquetes a 13,90 € y 444 paquetes a 14,90 €, después de que el precio subió el 1 de marzo. El resultado tiene un índice con dos niveles, y units.loc[17] toma los grupos del café.

Más precisamente, para curiosos

Los grupos con NaN en la clave se caen: la tarjeta de regalo no tiene categoría, y products.groupby('category_id') la deja fuera en silencio. groupby('category_id', dropna=False) la conserva como un grupo NaN. Comprueba con len cuántas filas entran en los grupos y cuántas salen.

La idea

El número del grupo en cada fila: transform

A veces no quieres reducir el grupo, sino poner su número junto a cada una de sus filas. Por ejemplo: qué tan caro es un producto comparado con el precio medio de su categoría. transform('mean') calcula la media del grupo y la repite en cada fila, conservando el orden y el índice de la tabla:

Pythondatos: las tablas de la tienda

transform da 25 números, uno por producto, y mean() da 6, uno por categoría. Por eso la columna cat_avg encaja directamente en la tabla. El café cuesta 14,90 €, y la media de bebidas es 5,35 €: 2,79 veces más. El detergente en polvo es 2,19 veces la media de limpieza.

Más precisamente, para curiosos

El resultado de mean() tiene category_id como índice, y la tabla products tiene el índice 0, 1, 2… Si lo asignas como columna, pandas lo alinea por el índice, no por la categoría, y casi todo se vuelve NaN. transform conserva el índice de la tabla. La tarjeta de regalo no tiene categoría, así que su cat_avg es NaN.

Pruébalo

La parte de la línea en el pedido

Para cada línea de order_items.csv: ¿qué parte de los ingresos de su pedido aporta? Añade a items una columna order_total con los ingresos de todo el pedido y una columna share con la parte de la línea.

Pythondatos: las tablas de la tienda

La idea

La clave como columna: as_index=False

El resultado de groupby tiene la clave en el índice. Para filtrarla, unirla con otra tabla o ponerla en un gráfico necesitas una columna normal. Hay dos maneras, con el mismo resultado:

Pythondatos: las tablas de la tienda

as_index=False lo dice al agrupar, y reset_index() lo hace después: el índice pasa a ser una columna, y las filas se numeran 0, 1, 2… Las columnas son order_id y revenue.

Ejercicio

El pedido medio

Responde al gerente de la primera tarjeta. Guarda en order_totals los ingresos de cada pedido del año sin los cancelados, y el pedido medio, redondeado a 2 decimales, en avg_order.

Pythondatos: las tablas de la tienda

¿Cuántos clientes hicieron al menos un pedido durante el año, y cuántos de ellos solo uno? Guarda el número de pedidos de cada cliente en per_customer, el número de clientes en n_customers, y cuántos tienen exactamente un pedido en once.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. order_items.csv tiene 152 líneas de 60 pedidos. ¿Cuántas filas tiene items.groupby('order_id')['revenue'].sum()?
2. La línea de pedido media aporta 4,44 €, y el pedido medio 10,63 €. ¿Por qué son distintos?
3. Junto a cada producto quieres el precio medio de su categoría, en una columna nueva. ¿Qué escribes?
4. ¿Qué hace as_index=False en groupby?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·