Pandas GroupBy: Aggregation, Transform, Apply (Guia 2025)
Updated on
O padrão split-apply-combine é o ponto forte do Pandas. A dificuldade: escolher entre agg, transform ou apply, além de lidar com ordenação, grupos ausentes ou SettingWithCopy.
Estrutura PAS:
- Problema: Cálculos agrupados parecem opacos e lentos quando só precisamos de totais ou razões por linha.
- Agitar: Usar
applyem excesso ou esqueceras_index=Falsegera formas inesperadas, colunas duplicadas e pipelines lentos. - Solução: Poucos padrões bastam—
aggpara resumos,transformpara métricas por linha,applyapenas quando necessário—com opções claras de ordenação e tratamento de grupos faltantes.
Guia rápido: quando usar cada método
| Método | Forma retornada | Melhor para | Exemplo |
|---|---|---|---|
agg / aggregate | Uma linha por grupo | Resumos e múltiplas métricas | df.groupby("team").agg(avg_score=("score","mean")) |
transform | Mesma quantidade de linhas | Features por linha baseadas no grupo | df["z"] = df.groupby("team")["score"].transform("zscore") |
apply | Flexível | Casos complexos fora de agg/transform | df.groupby("team").apply(custom_fn) |
Dados de exemplo
import pandas as pd
data = {
"team": ["A", "A", "B", "B", "B", "C"],
"player": ["x1", "x2", "y1", "y2", "y3", "z1"],
"score": [9, 7, 8, 6, 10, 3],
"minutes": [30, 25, 28, 32, 20, 15],
}
df = pd.DataFrame(data)Padrões de agregação (agg)
summary = (
df.groupby("team", as_index=False)
.agg(
avg_score=("score", "mean"),
max_score=("score", "max"),
minutes_played=("minutes", "sum"),
)
)- Use named aggregation para manter nomes limpos.
as_index=Falseconservateamcomo coluna (útil para merges e gráficos).sort=Falsepreserva a ordem original quando isso importa.
Múltiplas agregações em uma coluna
df.groupby("team", as_index=False).agg(
score_mean=("score", "mean"),
score_std=("score", "std"),
score_count=("score", "size"),
)Features por linha (transform)
transform mantém o tamanho original e devolve métricas de grupo para cada linha.
df["score_pct_of_team"] = (
df["score"] / df.groupby("team")["score"].transform("sum")
)Z-score por equipe:
df["score_z"] = (
df.groupby("team")["score"]
.transform(lambda s: (s - s.mean()) / s.std(ddof=0))
)Quando usar transform:
- Razões em relação ao total do grupo
- Scores normalizados
- Flags de grupo (ex.:
rank,cumcount)
Quando apply vale a pena
apply é flexível, mas mais lento; use só quando agg/transform não bastam.
def top_n(group, n=2):
return group.nlargest(n, "score")
top_players = df.groupby("team").apply(top_n, n=1).reset_index(drop=True)Use apply se:
- o número de linhas por grupo varia;
- a lógica precisa de Python não vetorizado.
Grupos faltantes e ordenação
result = (
df.groupby("team", dropna=False, sort=False)
.agg(avg_score=("score", "mean"))
)dropna=Falsemantém rótulosNaN.sort=Falseevita reordenar—útil quando a ordem tem significado.
GroupBy com múltiplas chaves e índices
multi = (
df.groupby(["team", "player"], as_index=False)
.agg(score_mean=("score", "mean"))
)Agrupando por níveis de índice:
df2 = df.set_index(["team", "player"])
df2.groupby(level="team")["score"].mean()Armadilhas comuns
| Problema | Correção |
|---|---|
Forma inesperada (Series vs DataFrame) | Use as_index=False ou depois reset_index(). |
| Colunas duplicadas após merge | Use named aggregation para controlar nomes. |
Lentidão por apply | Troque por agg/transform ou métodos vetorizados. |
| Categorias sumindo na saída | Mantenha observed=False (padrão) ou assegure categorias; dropna=False para NaN. |
Dicas de exportação e visualização
- Depois de agregar, ordene para apresentação:
summary.sort_values("avg_score", ascending=False). - Para gráficos, faça pivot do resumo:
summary.pivot(index="team", values="avg_score").
Guias relacionados
Em resumo
aggpara resumos,transformpara features por linha,applycom parcimônia.- Controle a forma com
as_indexereset_index. - Gerencie ordem e rótulos ausentes com
sortedropna. - Prefira métodos vetorizados para manter pipelines rápidas.