Dividir, aplicar, combinar
Muitas perguntas pedem um número por grupo: a exposição de cada setor, o retorno médio de cada estratégia, o número de operações por dia. O padrão é sempre o mesmo, dividir-aplicar-combinar (split-apply-combine). df.groupby("sector") divide as linhas em grupos pelos valores de uma coluna, uma agregação como .sum() é aplicada a cada grupo e o pandas combina os resultados em uma tabela indexada pelas chaves dos grupos. Sem isso você escreveria um laço com um dicionário de totais; com isso a pergunta toda cabe numa linha: 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}Vários resumos de uma vez com agg
.agg() calcula vários resumos numa só passada e permite nomear as colunas do resultado: df.groupby("sector").agg(count=("value", "size"), mean_return=("ret", "mean"), max_value=("value", "max")). Cada argumento nomeado é o nome da nova coluna, e a tupla diz qual coluna ler e qual função aplicar ("sum", "mean", "min", "max", "std", "size", "first"...). Também é possível agrupar por várias colunas de uma vez, groupby(["sector", "day"]), o que dá um resultado com índice de dois níveis. As chaves dos grupos são ordenadas por padrão, e as linhas cuja chave falta (NaN) são descartadas.
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: uma estatística de grupo em cada linha
Uma agregação reduz cada grupo a uma linha. transform mantém o formato original: calcula a estatística do grupo e a repete em cada linha do grupo, alinhada ao índice original. É a ferramenta para «o valor de uma linha comparado ao seu grupo»: a fatia de uma posição no seu setor é df["value"] / df.groupby("sector")["value"].transform("sum"), e centralizar dentro de um grupo é x - x.groupby(g).transform("mean"). Para pegar linhas inteiras por grupo, como o melhor de cada setor, use idxmax() para obter o rótulo de índice do máximo de cada grupo e selecione essas linhas com .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"):, o que serve para imprimir ou plotar poucos grupos, mas para números as agregações embutidas e transform são mais curtas e muito mais rápidas que um laço Python ou um apply com uma função. Confira o índice do resultado (chaves dos grupos, em geral ordenadas) e seus dtypes, e lembre que size conta linhas enquanto count conta valores não ausentes.df.groupby(chave)[col].sum()(oumean,max,size...): um valor por grupo, indexado pelas chaves..agg(nome=(coluna, função), ...)produz vários resumos nomeados de uma vez.transformmantém o número de linhas (fatias, centralização);idxmax+.locpega linhas inteiras por grupo.
A seguir: mesclar e juntar tabelas, combinando tabelas que compartilham uma chave, como preços e dados de referência.
Lab: o que é avaliado
- Totalizar a exposição de cada setor com
total_by_sector25 pts - Resumir cada setor em uma tabela com
summary_by_sector25 pts - Medir cada posição contra o seu setor com
share_of_sector25 pts - Escolher o melhor de cada setor com
best_per_sector25 pts