GroupBy splits a table into buckets by some key, runs a function on each bucket, and stitches the answers back together. One line — df.groupby('country').sum() — replaces a manual loop over every distinct value. The pattern has a name: split-apply-combine.
Where you meet it
- Summary stats per category — total sales per country, average score per class.
- Feature engineering — subtract each row's group mean, rank within group, fill gaps with a group value.
- Pivot tables and reports — group by two keys for a multi-level breakdown.
- Anywhere you'd write SQL's
GROUP BY; this is its in-memory twin.
What it does
It turns "do this per group" into one expression. df.groupby(key) alone computes nothing — it returns a lazy GroupBy object that just remembers which rows belong to which bucket. The real work happens when you attach an operation: aggregate, transform, or filter.
Aggregate collapses each group to one row; transform hands the answer back to every row. Pick by the shape you need.
How it works
Three steps. Split: rows are bucketed by their key value. Apply: a function runs once on each bucket, independently. Combine: the per-bucket results are assembled into a new Series or DataFrame, with the group keys becoming the index.
df.groupby('country')['sales'].sum()
# split: DE -> [120, 140, 80] US -> [90, 110] UK -> [60]
# apply: sum each bucket
# combine: country
# DE 340
# UK 60
# US 200
There are three flavours of "apply", and the difference is the shape of what comes back:
- Aggregate —
.sum(),.mean(),.agg(['sum','mean']): collapses each group to one row. N groups in, N rows out. - Transform —
.transform('mean'): returns a result the same length and index as the input, so it broadcasts the group answer back onto every row. Perfect forx - group_mean. - Filter —
.filter(lambda g: len(g) > 2): keeps or drops whole groups based on a per-group test, returning a subset of the original rows.
Watch out
- Aggregate reduces, transform preserves. Mixing them up is the classic error — use
transformwhen you need the result aligned back to the original rows. - NaN keys vanish. Like SQL
GROUP BY, but rows whose grouping key isNaNare dropped by default. Passdropna=Falseto keep them as their own group. - Sorting costs time. Groups come back sorted by key (
sort=True). On large frames,sort=Falseis faster and keeps first-seen order — order within each group is preserved either way. - The key becomes the index. The grouped column moves out of the columns into the index; use
as_index=Falseor.reset_index()if you want it back as a plain column.
Go deeper
GroupBy teilt eine Tabelle nach einem Schlüssel in Eimer, lässt auf jedem Eimer eine Funktion laufen und fügt die Ergebnisse wieder zusammen. Eine Zeile — df.groupby('country').sum() — ersetzt eine manuelle Schleife über jeden Wert. Das Muster hat einen Namen: split-apply-combine.
Wo es vorkommt
- Kennzahlen pro Kategorie — Gesamtumsatz pro Land, Durchschnittsnote pro Klasse.
- Feature Engineering — Gruppenmittelwert von jeder Zeile abziehen, Rang innerhalb der Gruppe, Lücken mit einem Gruppenwert füllen.
- Pivot-Tabellen und Reports — nach zwei Schlüsseln gruppieren für eine mehrstufige Aufschlüsselung.
- Überall, wo du in SQL
GROUP BYschreiben würdest; das ist sein In-Memory-Zwilling.
Was es tut
Es verwandelt "tu das pro Gruppe" in einen Ausdruck. df.groupby(key) allein rechnet nichts — es liefert ein faules GroupBy-Objekt, das sich nur merkt, welche Zeilen in welchen Eimer gehören. Gerechnet wird erst, wenn du eine Operation anhängst: aggregieren, transformieren oder filtern.
Aggregieren kollabiert jede Gruppe zu einer Zeile, Transformieren gibt die Antwort jeder Zeile zurück. Wähl nach der Form, die du brauchst.
Wie es funktioniert
Drei Schritte. Split: Zeilen werden nach ihrem Schlüsselwert in Eimer einsortiert. Apply: eine Funktion läuft einmal je Eimer, unabhängig voneinander. Combine: die Ergebnisse pro Eimer werden zu einer neuen Series oder einem DataFrame zusammengesetzt, die Gruppenschlüssel werden zum Index.
df.groupby('country')['sales'].sum()
# split: DE -> [120, 140, 80] US -> [90, 110] UK -> [60]
# apply: jeden Eimer summieren
# combine: country
# DE 340
# UK 60
# US 200
Es gibt drei Spielarten von "apply", und der Unterschied ist die Form dessen, was zurückkommt:
- Aggregieren —
.sum(),.mean(),.agg(['sum','mean']): kollabiert jede Gruppe zu einer Zeile. N Gruppen rein, N Zeilen raus. - Transformieren —
.transform('mean'): liefert ein Ergebnis mit gleicher Länge und gleichem Index wie der Input, broadcastet die Gruppen-Antwort also auf jede Zeile zurück. Ideal fürx - group_mean. - Filtern —
.filter(lambda g: len(g) > 2): behält oder verwirft ganze Gruppen anhand eines Tests pro Gruppe und gibt eine Teilmenge der Originalzeilen zurück.
Worauf achten
- Aggregieren reduziert, Transformieren behält die Form. Sie zu verwechseln ist der klassische Fehler — nimm
transform, wenn das Ergebnis wieder zu den Originalzeilen passen soll. - NaN-Schlüssel verschwinden. Wie bei SQL
GROUP BY, aber Zeilen, deren GruppierungsschlüsselNaNist, werden standardmäßig ausgelassen. Mitdropna=Falsebleiben sie als eigene Gruppe erhalten. - Sortieren kostet Zeit. Gruppen kommen nach Schlüssel sortiert zurück (
sort=True). Bei großen Frames istsort=Falseschneller und behält die Erst-Reihenfolge — die Reihenfolge innerhalb jeder Gruppe bleibt so oder so erhalten. - Der Schlüssel wird zum Index. Die gruppierte Spalte wandert aus den Spalten in den Index; mit
as_index=Falseoder.reset_index()holst du sie als normale Spalte zurück.