Breit und lang: pivot_table und melt
Kurz gesagt: pivot_table macht eine breite Tabelle mit einer Spalte für jede Kategorie, und melt bringt die lange mit einer Zeile für jeden Fakt zurück; die Form wählst du nach der Frage.
Problem
„Gib es mir nach Monat und Kategorie“
Der Chef will den Umsatz des Jahres in einer Tabelle: die Monate nach unten, die Kategorien nach rechts, wie in Excel. Du weißt, wie: die Zeilen mit den Bestellungen, den Produkten und den Kategorien verbinden (merge) und nach zwei Schlüsseln gruppieren. Stornierte Bestellungen sind kein Umsatz, also entfernst du sie. Die Geschenkkarte hat keine Kategorie und bekommt einen eigenen Namen, sonst würde sie aus den Gruppen fallen:
Die Zahlen stimmen, aber es sind 84 Zeilen: 12 Monate mal 7 Kategorien, untereinander. Um den Januar mit dem Juli zu vergleichen, durchsuchst du die Liste mit den Augen. Die Tabelle, die der Chef will, hat 12 Zeilen und 7 Spalten mit denselben 84 Zahlen.
Der Monat ist hier seine Nummer: 1 ist der Januar 2026, und 11 und 12 sind November und Dezember 2025.
Die Idee
Lang und breit
Dieselben Zahlen können in zwei Formen stehen:
- Lang: eine Zeile für jeden Fakt. Die Spalten sind
month,categoryundrevenue, und jedes Paar aus Monat und Kategorie ist eine eigene Zeile. - Breit: eine Zeile für jeden Monat und eine Spalte für jede Kategorie. Die Kategorie ist keine Spalte mehr: Ihre Werte sind zu Spaltennamen geworden.
Von lang zu breit geht es mit dem Ergebnis von groupby in einer Zeile: unstack() nimmt die letzte Ebene des Index (die Kategorie) und macht daraus Spalten:
Die Form ist (12, 7): 12 Monate und 7 Kategorien. Januar: 593 € Küche, 2.038 € Milchprodukte, 1.225 € Getränke.
Die Idee
Direkt breit: pivot_table
pivot_table gruppiert und ordnet in einem Schritt an. Du sagst ihm vier Dinge:
index: was in die Zeilen kommt (der Monat);columns: was zu Spalten wird (die Kategorie);values: mit welcher Spalte es arbeitet (der Umsatz);aggfunc: wie es rechnet ('sum').
Jetzt kannst du es auf einen Blick lesen. Der Dezember ist der Gipfel: Die Küche macht 3.241 €, über das Vierfache eines gewöhnlichen Monats, und die Geschenkkarte 2.160 €. Der Sommer bringt Getränke und Obst: Die Getränke sind im Juli 1.756 € gegen 1.225 € im Januar. Der September ist fast überall schwach. Warum, finden wir im Projekt heraus.
Genauer, für Neugierige
Es gibt auch pivot ohne „table“: Es ordnet nur an und addiert nicht. Wenn ein Paar aus Monat und Kategorie in mehr als einer Zeile vorkommt, stoppt pivot mit einem Fehler, während pivot_table sie mit aggfunc zusammenfasst. Für rohe Zeilen brauchst du pivot_table.
Probier es aus
Stück nach Kategorie
Wie viele Stück hat jede Kategorie in jedem Monat verkauft? Leg in units eine Tabelle mit den Kategorien in den Zeilen und den Monaten in den Spalten ab.
Lösung
import pandas as pd
orders = pd.read_csv('year_orders.csv').rename(columns={'id': 'order_id'})
products = pd.read_csv('products.csv').rename(columns={'id': 'product_id'})
categories = pd.read_csv('categories.csv').rename(columns={'id': 'category_id', 'name': 'category'})
orders = orders[orders['status'] != 'cancelled']
orders['month'] = pd.to_datetime(orders['ordered_at']).dt.month
lines = pd.read_csv('year_items.csv').merge(orders[['order_id', 'month']], on='order_id')
lines = lines.merge(products[['product_id', 'category_id']], on='product_id', how='left')
lines = lines.merge(categories[['category_id', 'category']], on='category_id', how='left')
lines['category'] = lines['category'].fillna('Geschenkkarte')
lines['revenue'] = lines['quantity'] * lines['unit_price']
units = lines.pivot_table(index='category', columns='month', values='quantity', aggfunc='sum')
unitslines.pivot_table(index='category', columns='month', values='quantity', aggfunc='sum') liefert 7 Zeilen und 12 Spalten: Die Milchprodukte verkaufen im Dezember 1.159 Stück. Ohne aggfunc kommt die Tabelle in derselben Form heraus, aber in den Zellen stehen die durchschnittlichen Stück pro Zeile, etwa 1 bis 1,6, weil pivot_table standardmäßig mean rechnet. Es sieht aus wie eine Antwort, und genau das macht es gefährlich.
Die Idee
Leere Zellen: fill_value
In der breiten Tabelle hat jedes Paar eine Zelle, auch wenn es das Paar in den Daten nicht gibt. Hier steht, wie viel jeder Kunde in den 60 Bestellungen aus jeder Kategorie gekauft hat:
35 Zellen sind NaN. Kunde 3 hat kein Brot, keine Reinigung und keine Getränke gekauft. Hier heißt leer „null Käufe“, nicht „wir wissen es nicht“. Also ist es richtig, durch null zu ersetzen: fill_value=0.
Das ist der Fall aus der Lektion über Zusammenfassungen, in dem 0 der richtige Ersatz ist: Die Null bedeutet wirklich null. Wenn die Zelle leer wäre, weil ein Bericht fehlte, würde die Null lügen.
Die Idee
Die Summen: margins
margins=True fügt eine Zeile und eine Spalte All mit den Summen hinzu. Hier sind zwei der Kategorien und die Summe zu sehen:
Die Spalte All ist der Umsatz des Monats, und die Zeile All ist der Umsatz der Kategorie für das Jahr: 11.304 € Küche, 17.811 € Getränke. In der Ecke steht die Summe: 111.186,20 €. Der September ist 7.129 €, weniger als der August (8.813 €) und der Oktober (9.920 €).
Die Idee
Zurück zu lang: melt
Der Chef schickt den Plan für die nächsten drei Monate. Die Tabelle kommt aus Excel und ist breit: eine Zeile pro Kategorie und eine Spalte pro Monat.
Eine breite Tabelle ist gut zum Lesen, aber nicht zum Arbeiten. Der Monat ist keine Spalte, er steht in den Spaltennamen, und es gibt nichts zum Gruppieren. melt schmilzt sie zurück zu einer langen: id_vars ist die Spalte, die bleibt, var_name benennt die Spalte mit den alten Spaltennamen und value_name die Spalte mit den Zahlen:
Aus 7 Zeilen und 4 Spalten werden 21 Zeilen und 3 Spalten: Jede Zahl des Plans ist eine eigene Zeile. Jetzt ist der Monat eine Spalte, und groupby funktioniert:
Der Plan für den Dezember ist 16.650 €, für den November 9.700 €.
Probier es aus
Welche Form wofür
Übung
Der Bericht für den Chef
Mach den Bericht von der ersten Karte: report mit dem Umsatz nach Monat (Zeilen) und Kategorie (Spalten), mit den Summen All. Lege dann in dec_share ab, welchen Teil des Jahresumsatzes der Dezember ausmacht.
Lösung
import pandas as pd
orders = pd.read_csv('year_orders.csv').rename(columns={'id': 'order_id'})
products = pd.read_csv('products.csv').rename(columns={'id': 'product_id'})
categories = pd.read_csv('categories.csv').rename(columns={'id': 'category_id', 'name': 'category'})
orders = orders[orders['status'] != 'cancelled']
orders['month'] = pd.to_datetime(orders['ordered_at']).dt.month
lines = pd.read_csv('year_items.csv').merge(orders[['order_id', 'month']], on='order_id')
lines = lines.merge(products[['product_id', 'category_id']], on='product_id', how='left')
lines = lines.merge(categories[['category_id', 'category']], on='category_id', how='left')
lines['category'] = lines['category'].fillna('Geschenkkarte')
lines['revenue'] = lines['quantity'] * lines['unit_price']
report = lines.pivot_table(index='month', columns='category', values='revenue', aggfunc='sum', margins=True)
dec_share = report.loc[12, 'All'] / report.loc['All', 'All']
print(round(dec_share * 100, 1))lines.pivot_table(index='month', columns='category', values='revenue', aggfunc='sum', margins=True) liefert die Tabelle mit den Summen. report.loc[12, 'All'] / report.loc['All', 'All'] ist etwa 0,143: Der Dezember bringt 14,3 % des Jahresumsatzes, obwohl er einer von 12 Monaten ist. Ohne aggfunc stünde in der Ecke die durchschnittliche Zeile, nicht der Umsatz.
Wie hoch ist der Plan für die drei Monate zusammen für jede Kategorie? Mach plan lang (plan_long mit den Spalten category, month und plan) und lege die Summe für jede Kategorie in per_category ab.
Lösung
import pandas as pd
plan = pd.read_csv('plan.csv')
plan_long = plan.melt(id_vars='category', var_name='month', value_name='plan')
per_category = plan_long.groupby('category')['plan'].sum()
print(per_category)plan.melt(id_vars='category', var_name='month', value_name='plan') liefert 21 Zeilen, und plan_long.groupby('category')['plan'].sum() liefert eine Zahl pro Kategorie: Die Küche ist 5.050 €, die Milchprodukte 8.700 €. Ohne id_vars schmilzt auch die Spalte category in die Zeilen, und die Tabelle hat 28 Zeilen, mit Kategorienamen zwischen den Zahlen.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Neue Spalten · Typen und fehlende Werte · Gruppen: groupby · Verbinden: merge
- Geht weiter in: Das erste Diagramm · Welches Diagramm für welche Frage · Ehrliche Diagramme · A/B-Test
- Aufgaben zum Thema: Daten aufbereiten