Dividir, aplicar, combinar
Muchas preguntas piden un número por grupo: la exposición de cada sector, el rendimiento medio de cada estrategia, el número de operaciones por día. El patrón es siempre el mismo, dividir-aplicar-combinar (split-apply-combine). df.groupby("sector") divide las filas en grupos según los valores de una columna, una agregación como .sum() se aplica a cada grupo y pandas combina los resultados en una tabla indexada por las claves de grupo. Sin esto escribirías un bucle con un diccionario de totales; con esto toda la pregunta cabe en una línea: df.groupby("sector")["value"].sum().
import pandas as pd
df = pd.DataFrame({
"symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
"sector": ["tech", "tech", "tech", "auto", "auto"],
"value": [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
"ret": [0.012, -0.004, 0.021, 0.03, -0.01],
})
print(df.groupby("sector")["value"].sum())
# sector
# auto 39120.0
# tech 108660.0
# Name: value, dtype: float64
print(df.groupby("sector").size().to_dict()) # {'auto': 2, 'tech': 3} rows per group
print(df.groupby("sector")["ret"].mean().round(4).to_dict()) # {'auto': 0.01, 'tech': 0.0097}Varios resúmenes a la vez con agg
.agg() calcula varios resúmenes en una pasada y permite nombrar las columnas del resultado: df.groupby("sector").agg(count=("value", "size"), mean_return=("ret", "mean"), max_value=("value", "max")). Cada argumento con nombre es el nombre de la columna nueva, y la tupla dice qué columna leer y qué función aplicar ("sum", "mean", "min", "max", "std", "size", "first"...). También puedes agrupar por varias columnas a la vez, groupby(["sector", "day"]), lo que da un resultado con índice de dos niveles. Las claves de grupo se ordenan por defecto, y las filas cuya clave falta (NaN) se descartan.
import pandas as pd
df = pd.DataFrame({
"symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
"sector": ["tech", "tech", "tech", "auto", "auto"],
"value": [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
"ret": [0.012, -0.004, 0.021, 0.03, -0.01],
})
summary = df.groupby("sector").agg(
count=("value", "size"),
mean_return=("ret", "mean"),
max_value=("value", "max"),
)
print(summary)
# count mean_return max_value
# sector
# auto 2 0.010000 30120.0
# tech 3 0.009667 51300.0
print(df.groupby("sector")["ret"].agg(["min", "max"])) # a list of function names
# min max
# sector
# auto -0.010 0.030
# tech -0.004 0.021transform: una estadística de grupo en cada fila
Una agregación reduce cada grupo a una fila. transform conserva la forma original: calcula la estadística del grupo y la repite en cada fila del grupo, alineada con el índice original. Es la herramienta para «el valor de una fila comparado con su grupo»: la proporción de una posición en su sector es df["value"] / df.groupby("sector")["value"].transform("sum"), y centrar dentro de un grupo es x - x.groupby(g).transform("mean"). Para elegir filas enteras por grupo, como el mejor de cada sector, usa idxmax() para obtener la etiqueta de índice del máximo de cada grupo y selecciona esas filas con .loc.
import pandas as pd
df = pd.DataFrame({
"symbol": ["AAPL", "MSFT", "GOOG", "TSLA", "F"],
"sector": ["tech", "tech", "tech", "auto", "auto"],
"value": [36850.0, 20510.0, 51300.0, 30120.0, 9000.0],
"ret": [0.012, -0.004, 0.021, 0.03, -0.01],
})
totals = df.groupby("sector")["value"].transform("sum") # one value per original row
print(totals.tolist()) # [108660.0, 108660.0, 108660.0, 39120.0, 39120.0]
print((df["value"] / totals).round(3).tolist()) # [0.339, 0.189, 0.472, 0.77, 0.23]
best = df.loc[df.groupby("sector")["ret"].idxmax()].set_index("sector")[["symbol", "ret"]]
print(best)
# symbol ret
# sector
# auto TSLA 0.030
# tech GOOG 0.021import pandas as pd
t = pd.DataFrame({
"day": ["mon", "mon", "tue", "tue"],
"sector": ["tech", "auto", "tech", "tech"],
"value": [1.0, 2.0, 3.0, 4.0],
})
# Two keys give a two-level index
print(t.groupby(["day", "sector"])["value"].sum())
# day sector
# mon auto 2.0
# tech 1.0
# tue tech 7.0
# Name: value, dtype: float64
# reset_index() turns the keys back into ordinary columns
print(t.groupby(["day", "sector"])["value"].sum().reset_index())
# day sector value
# 0 mon auto 2.0
# 1 mon tech 1.0
# 2 tue tech 7.0for name, group in df.groupby("sector"):, lo cual está bien para imprimir o graficar unos pocos grupos, pero para números las agregaciones integradas y transform son más cortas y mucho más rápidas que un bucle de Python o un apply con una función. Comprueba el índice del resultado (claves de grupo, normalmente ordenadas) y sus dtypes, y recuerda que size cuenta filas mientras que count cuenta valores no ausentes.df.groupby(clave)[col].sum()(omean,max,size...): un valor por grupo, indexado por las claves..agg(nombre=(columna, función), ...)produce varios resúmenes con nombre a la vez.transformconserva el número de filas (proporciones, centrado);idxmax+.locelige filas enteras por grupo.
A continuación: combinar y unir tablas, para juntar tablas que comparten una clave, como precios y datos de referencia.
Lab: qué se califica
- Totalizar la exposición de cada sector con
total_by_sector25 pts - Resumir cada sector en una tabla con
summary_by_sector25 pts - Medir cada posición frente a su sector con
share_of_sector25 pts - Elegir el mejor de cada sector con
best_per_sector25 pts