Aufteilen, anwenden, zusammenführen
Viele Fragen verlangen eine Zahl pro Gruppe: das Exposure jedes Sektors, die Durchschnittsrendite jeder Strategie, die Zahl der Trades pro Tag. Das Muster ist immer dasselbe, Split-Apply-Combine. df.groupby("sector") teilt die Zeilen nach den Werten einer Spalte in Gruppen, eine Aggregation wie .sum() wird auf jede Gruppe angewandt, und pandas führt die Ergebnisse in einer Tabelle zusammen, die nach den Gruppenschlüsseln indiziert ist. Ohne das schriebe man eine Schleife mit einem Dictionary laufender Summen; damit passt die ganze Frage in eine Zeile: 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}Mehrere Zusammenfassungen auf einmal mit agg
.agg() berechnet mehrere Zusammenfassungen in einem Durchgang und erlaubt es, die Ergebnisspalten zu benennen: df.groupby("sector").agg(count=("value", "size"), mean_return=("ret", "mean"), max_value=("value", "max")). Jedes Schlüsselwort ist der neue Spaltenname, und das Tupel sagt, welche Spalte gelesen und welche Funktion angewandt wird ("sum", "mean", "min", "max", "std", "size", "first"...). Man kann auch nach mehreren Spalten gruppieren, groupby(["sector", "day"]), was ein Ergebnis mit zweistufigem Index liefert. Die Gruppenschlüssel werden standardmäßig sortiert, und Zeilen mit fehlendem Schlüssel (NaN) entfallen.
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: eine Gruppenstatistik in jeder Zeile
Eine Aggregation schrumpft jede Gruppe auf eine Zeile. transform behält die ursprüngliche Form: Es berechnet die Gruppenstatistik und wiederholt sie in jeder Zeile der Gruppe, ausgerichtet am ursprünglichen Index. Damit eignet es sich für „der Wert einer Zeile im Vergleich zu ihrer Gruppe“: Der Anteil einer Position an ihrem Sektor ist df["value"] / df.groupby("sector")["value"].transform("sum"), und das Zentrieren innerhalb einer Gruppe ist x - x.groupby(g).transform("mean"). Um ganze Zeilen pro Gruppe zu wählen, etwa den besten Wert jedes Sektors, liefert idxmax() das Indexlabel des Maximums jeder Gruppe, die Sie dann mit .loc auswählen.
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"): iterieren, was zum Ausgeben oder Zeichnen weniger Gruppen taugt, doch für Zahlen sind die eingebauten Aggregationen und transform kürzer und viel schneller als eine Python-Schleife oder apply mit einer Funktion. Prüfen Sie den Index des Ergebnisses (Gruppenschlüssel, meist sortiert) und die dtypes, und denken Sie daran, dass size Zeilen zählt, count dagegen nicht fehlende Werte.df.groupby(schlüssel)[col].sum()(odermean,max,size...): ein Wert pro Gruppe, nach den Schlüsseln indiziert..agg(name=(spalte, funktion), ...)erzeugt mehrere benannte Zusammenfassungen auf einmal.transformbehält die Zeilenzahl (Anteile, Zentrierung);idxmax+.locwählt ganze Zeilen pro Gruppe.
Als Nächstes: Tabellen zusammenführen und verbinden, Tabellen mit gemeinsamem Schlüssel verknüpfen, etwa Preise und Stammdaten.
Lab: Was bewertet wird
- Mit
total_by_sectordas Exposure jedes Sektors summieren25 Pkt. - Mit
summary_by_sectorjeden Sektor in einer Tabelle zusammenfassen25 Pkt. - Mit
share_of_sectorjede Position an ihrem Sektor messen25 Pkt. - Mit
best_per_sectorden besten Wert jedes Sektors auswählen25 Pkt.