Zum Inhalt
wannacode

Gruppen: groupby

Kurz gesagt: groupby in pandas teilt die Zeilen nach einem Schlüssel, rechnet in jeder Gruppe (sum, mean, agg) und fügt das Ergebnis zu einer Tabelle zusammen.

Problem

„Wie groß ist die durchschnittliche Bestellung?“

Der Chef fragt, wie viel ein Kunde im Durchschnitt für eine Bestellung ausgibt. Du hast die Bestellungen des Jahres und ihre Zeilen: Jede Zeile ist ein Produkt in einer Bestellung, mit Menge und Preis. Stornierte Bestellungen sind kein Umsatz, also entfernst du sie mit einer Maske, wie in der Lektion über Filter:

PythonDaten: die Tabellen des Shops

Es sind 25.069 Zeilen und 10.459 Bestellungen. Der Mittelwert der Spalte revenue ist 4,44 €, aber das ist die durchschnittliche Zeile, nicht die durchschnittliche Bestellung. Eine Bestellung hat mehrere Zeilen, und für eine Bestellung musst du zuerst ihre Zeilen addieren.

Du brauchst eine Summe für jede Bestellung: 10.459 Zahlen aus 25.069 Zeilen. Mit einer for-Schleife und einem Dictionary geht das, aber es ist langsam und lang. pandas hat dafür eine einzige Operation.

Die Idee

Teilen, anwenden, zusammenfügen

groupby('order_id') teilt die Zeilen in Gruppen: eine Gruppe für jeden verschiedenen Wert von order_id. Dann wählst du eine Spalte und eine Rechnung: ['revenue'].sum() addiert den Umsatz in jeder Gruppe. Am Ende legt pandas die Ergebnisse in eine Series mit order_id als Index.

Das heißt Split-Apply-Combine. Hier an den ersten 7 Zeilen:

PythonDaten: die Tabellen des Shops

Bestellung 1 hat eine Zeile (31,60 €), und die Bestellungen 2, 3 und 4 haben je zwei. Bestellung 3 ist 37,5 + 8,4 = 45,90 €. Aus 7 Zeilen werden 4 Zahlen, eine pro Gruppe. Der Schlüssel order_id ist der Index des Ergebnisses, deshalb steht er unter dem Namen der Spalte.

Statt sum kannst du jede Zusammenfassung aus NumPy nehmen: mean, min, max, count.

Sieh es dir an

Wie groupby durch die Zeilen geht

Dieselben 7 Zeilen, langsam: Jede Zeile kommt in die Gruppe ihrer order_id, die Gruppe wird zu einer Zahl addiert, und die Zahlen reihen sich im Ergebnis auf.

import pandas as pd
items = pd.read_csv('order_items.csv').head(7)
items['revenue'] = items['quantity'] * items['unit_price']
print(items[['order_id', 'revenue']])
items.groupby('order_id')['revenue'].sum()
Schritt für Schritt, in Worten
  1. 7 Zeilen aus order_items mit dem Umsatz jeder Zeile: Bestellungen 1, 2, 3 und 4.
  2. Teilen (split): groupby('order_id') legt jede Zeile in die Gruppe ihrer order_id.
  3. 4 verschiedene order_id, 4 Gruppen. Bestellung 1 hat eine Zeile, die anderen je 2.
  4. Anwenden (apply): ['revenue'].sum() in jeder Gruppe. Bestellung 3: 37,5 + 8,4 = 45,9.
  5. Zusammenfügen (combine): Die 4 Summen werden eine Series mit order_id als Index.
  6. 7 Zeilen gingen hinein, 4 Zahlen kamen heraus: eine für jede Gruppe.

Probier es aus

Welches Produkt am meisten bestellt wird

In den 60 Bestellungen aus order_items.csv: Wie viele Stück von jedem Produkt wurden bestellt? Leg sie in units ab und die drei Produkte mit den meisten Stück in top3.

PythonDaten: die Tabellen des Shops

Die Idee

Mehrere Rechnungen auf einmal: agg

Oft brauchst du für jede Gruppe mehrere Zahlen. agg nimmt eine Liste von Rechnungen und liefert für jede eine Spalte:

PythonDaten: die Tabellen des Shops

Die Spalten heißen wie die Rechnungen. Für einen Bericht benennst du sie besser selbst. Das sind benannte Aggregationen: neuer_name=('Spalte', 'Rechnung'), und jede kann eine andere Spalte nutzen:

PythonDaten: die Tabellen des Shops

Bestellung 3 hat 2 Zeilen, 7 Stück und 45,90 € Umsatz. Die Tabelle hat eine Zeile pro Bestellung und eine Spalte für jede Frage, die du gestellt hast.

Die Idee

Das Zählen: size oder count

Die zwei Zähler sehen ähnlich aus, zählen aber Verschiedenes. size() zählt die Zeilen in jeder Gruppe. count() zählt die ausgefüllten Werte in jeder Spalte: NaN wird nicht gezählt. Hier die Kunden nach dem Jahr ihrer Anmeldung:

PythonDaten: die Tabellen des Shops

16 Kunden haben sich 2025 angemeldet, aber die Spalte city hat 15 Werte: Bei einem Kunden fehlt die Stadt. Wenn du wissen willst „wie viele Kunden“, ist size() die richtige Antwort. count() auf einer Spalte beantwortet „bei wie vielen kennen wir die Stadt“.

1. Es gibt 16 Kunden aus 2025, und bei einem fehlt die Stadt. Was zeigt customers.groupby('year')['city'].count() für 2025?

Die Idee

Mehrere Schlüssel

Der Schlüssel kann eine Liste von Spalten sein. Dann ist jedes Paar von Werten eine Gruppe. Hier die Stück des Jahres nach Produkt und nach Preis:

PythonDaten: die Tabellen des Shops

Es gibt 25 Produkte und 26 Gruppen: Ein Produkt wurde zu zwei Preisen verkauft. Das ist der Kaffee (Produkt 17): 245 Packungen zu 13,90 € und 444 Packungen zu 14,90 €, nachdem der Preis am 1. März gestiegen war. Das Ergebnis hat einen Index mit zwei Ebenen, und units.loc[17] holt die Gruppen des Kaffees.

Genauer, für Neugierige

Gruppen mit NaN im Schlüssel fallen heraus: Die Geschenkkarte hat keine Kategorie, und products.groupby('category_id') lässt sie stillschweigend weg. groupby('category_id', dropna=False) behält sie als NaN-Gruppe. Prüfe mit len, wie viele Zeilen in die Gruppen hineingehen und wie viele herauskommen.

Die Idee

Die Zahl der Gruppe auf jeder Zeile: transform

Manchmal willst du die Gruppe nicht schrumpfen, sondern ihre Zahl neben jede ihrer Zeilen legen. Zum Beispiel: wie teuer ein Produkt im Vergleich zum Durchschnittspreis seiner Kategorie ist. transform('mean') rechnet den Mittelwert der Gruppe aus und wiederholt ihn auf jeder Zeile, wobei Reihenfolge und Index der Tabelle erhalten bleiben:

PythonDaten: die Tabellen des Shops

transform liefert 25 Zahlen, eine pro Produkt, und mean() liefert 6, eine pro Kategorie. Deshalb passt die Spalte cat_avg direkt in die Tabelle. Der Kaffee kostet 14,90 €, und der Durchschnitt bei den Getränken ist 5,35 €: das 2,79-Fache. Das Waschpulver ist das 2,19-Fache des Durchschnitts bei der Reinigung.

Genauer, für Neugierige

Das Ergebnis von mean() hat category_id als Index, und die Tabelle products hat den Index 0, 1, 2… Wenn du es als Spalte zuweist, richtet pandas es nach dem Index aus, nicht nach der Kategorie, und fast alles wird NaN. transform behält den Index der Tabelle. Die Geschenkkarte hat keine Kategorie, also ist ihr cat_avg NaN.

Probier es aus

Der Anteil der Zeile an der Bestellung

Für jede Zeile von order_items.csv: Welchen Teil des Umsatzes ihrer Bestellung bringt sie ein? Füge items eine Spalte order_total mit dem Umsatz der ganzen Bestellung und eine Spalte share mit dem Anteil der Zeile hinzu.

PythonDaten: die Tabellen des Shops

Die Idee

Der Schlüssel als Spalte: as_index=False

Das Ergebnis von groupby hat den Schlüssel im Index. Um ihn zu filtern, mit einer anderen Tabelle zu verbinden oder in ein Diagramm zu legen, brauchst du eine gewöhnliche Spalte. Es gibt zwei Wege mit demselben Ergebnis:

PythonDaten: die Tabellen des Shops

as_index=False sagt es beim Gruppieren, und reset_index() macht es danach: Der Index wird zur Spalte, und die Zeilen werden 0, 1, 2… nummeriert. Die Spalten sind order_id und revenue.

Übung

Die durchschnittliche Bestellung

Beantworte die Frage des Chefs von der ersten Karte. Leg den Umsatz jeder Bestellung des Jahres ohne die stornierten in order_totals ab und die durchschnittliche Bestellung, auf 2 Stellen gerundet, in avg_order.

PythonDaten: die Tabellen des Shops

Wie viele Kunden haben im Lauf des Jahres mindestens einmal bestellt, und wie viele davon nur einmal? Leg die Anzahl der Bestellungen jedes Kunden in per_customer ab, die Anzahl der Kunden in n_customers und wie viele genau eine Bestellung haben in once.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. order_items.csv hat 152 Zeilen aus 60 Bestellungen. Wie viele Zeilen hat items.groupby('order_id')['revenue'].sum()?
2. Die durchschnittliche Bestellzeile bringt 4,44 € ein, die durchschnittliche Bestellung 10,63 €. Warum sind sie verschieden?
3. Neben jedem Produkt willst du in einer neuen Spalte den Durchschnittspreis seiner Kategorie haben. Was schreibst du?
4. Was macht as_index=False in groupby?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·