Das erste Diagramm
Kurz gesagt: Ein Diagramm in matplotlib ist eine Figure mit Axes: plot zeichnet eine Linie, bar zeichnet Balken, und Titel, Achsenbeschriftungen und Legende sagen, was gezeigt wird.
Problem
„Zeig mir das Jahr“
Der Chef will sehen, wie das Jahr läuft. Du weißt schon, wie du den Umsatz nach Monat berechnest: die Bestellzeilen ohne die stornierten, nach Monat summiert. Der Monat sind die ersten 7 Zeichen von ordered_at, zum Beispiel „2026-03“:
Die Zahlen stimmen, aber in 12 Zeilen ist schwer zu sehen, was ein Gipfel und was ein Einbruch ist. Der Dezember ist 15.915,90 €, und der Januar ist 7.228,20 €. Wie groß ist der Einbruch im Vergleich zu den anderen Monaten? In einer Tabelle sieht das Auge es nicht. In einer Linie sofort.
Die Idee
Figure und Axes
In der Lektion „Willkommen“ hast du ein Diagramm mit einer einzigen Zeile Code gesehen. Jetzt siehst du, was dahintersteht.
Ein Diagramm in matplotlib hat zwei Objekte:
- die Figure (
Figure) ist das ganze Blatt: die Größe und das Speichern in eine Datei; - die Axes (
Axes) ist der Kasten mit der waagerechten und der senkrechten Achse, in den du zeichnest.
plt.subplots() liefert dir beide: fig, ax = plt.subplots(). Dann zeichnest du in die Axes. ax.plot(x, y) verbindet die Punkte mit einer Linie:
[Diagramm] im Text der Ausgabe heißt, dass das Diagramm keinen Titel hat. Die Linie zeigt schon den Gipfel im Dezember und den Einbruch danach. Es fehlen noch drei Dinge: ein Titel, Beschriftungen an den Achsen und Monate, die du lesen kannst.
Genauer, für Neugierige
Hier und in Jupyter erscheint das Diagramm von allein am Ende der Zelle, deshalb gibt es kein plt.show(). In einem Skript (python chart.py) zeigst du es mit plt.show() oder speicherst es mit fig.savefig('monthly.png'). Sonst endet das Skript, und die Figure geht mit.
plt.plot(...) ohne fig, ax zeichnet auch: matplotlib nimmt dann selbst die „aktuellen“ Axes. Das ist praktisch für ein schnelles Diagramm, aber bei mehreren Axes weißt du nicht, welche gerade aktuell ist. Deshalb zeichnest du hier immer in benannte Axes.
Die Idee
Titel, Beschriftungen und Ticks
Ein Diagramm ohne Beschriftungen lässt den Leser raten. Die Axes haben für jede Sache eine Methode:
ax.set_titlesagt, was du ansiehst;ax.set_xlabelundax.set_ylabelsagen, was unten und was an der Seite steht;ax.tick_params(axis='x', labelrotation=45)dreht die Tick-Beschriftungen, die Zahlen und Namen unter der Achse, damit sie sich nicht überlappen;fig.tight_layout()ordnet das Blatt so an, dass die gedrehten Beschriftungen nicht abgeschnitten werden.
marker='o' setzt einen Punkt auf jeden Monat:
Jetzt siehst du, dass 11 Monate zwischen 7.000 € und 10.000 € liegen und der Dezember über 15.000 €. Januar, Februar und September sind die niedrigsten. Warum der September einbricht, findest du im Projekt heraus.
Probier es aus
Bestellungen nach Monat
Zeichne die Anzahl der Bestellungen in jedem Monat (ohne die stornierten) als Linie. Leg die Anzahlen in counts ab und gib dem Diagramm einen Titel und Beschriftungen an beiden Achsen.
Lösung
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
sold = orders[orders['status'] != 'cancelled']
counts = sold.groupby(sold['ordered_at'].str[:7]).size()
fig, ax = plt.subplots()
ax.plot(counts.index, counts.values)
ax.set_title('Bestellungen nach Monat')
ax.set_xlabel('Monat')
ax.set_ylabel('Bestellungen')
ax.tick_params(axis='x', labelrotation=45)
fig.tight_layout()sold.groupby(sold['ordered_at'].str[:7]).size() liefert 12 Zahlen: 886 Bestellungen im November, 1.211 im Dezember, 728 im Januar und 701 im September. Die Linie hat dieselbe Form wie der Umsatz: ein Gipfel im Dezember und ein Einbruch danach. Mit orders statt sold würdest du auch die stornierten Bestellungen zählen. Ohne set_xlabel und set_ylabel weiß der Leser nicht, ob die senkrechte Achse in Stück oder in Euro ist.
Die Idee
Balken: ax.bar
Eine Linie verbindet Punkte und sagt „von hier bis hier hat sich etwas verändert“. Das ergibt Sinn für Monate, die aufeinander folgen. Kategorien folgen nicht aufeinander: Für sie zeichnest du Balken (ein Balkendiagramm) mit ax.bar(names, values).
Der Umsatz nach Kategorie braucht den Kategorienamen in jeder Zeile: Produkt → category_id → Name. Zwei map-Aufrufe liefern ihn:
Es gibt 6 Balken, und sie ergeben zusammen 107.466,20 €. Der Umsatz des Jahres ist 111.186,20 €. Es fehlen 3.720 €: Die Geschenkkarte hat keine Kategorie, map gibt ihr NaN, und groupby überspringt NaN stillschweigend. Das Diagramm sieht gut aus. Also zähle die Balken und vergleiche ihre Summe mit dem Umsatz.
Probier es aus
Alle Kategorien
Zeichne den Umsatz nach Kategorie so, dass die Geschenkkarte ein eigener Balken ist. Leg den Umsatz nach Kategorie in by_cat ab und füge einen Titel und eine Beschriftung an der senkrechten Achse hinzu.
Lösung
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
items = pd.read_csv('year_items.csv')
# Der Umsatz enthält die stornierten Bestellungen nicht.
sold = orders[orders['status'] != 'cancelled']
lines = items.merge(sold, left_on='order_id', right_on='id')
lines['revenue'] = lines['quantity'] * lines['unit_price']
lines['month'] = lines['ordered_at'].str[:7]
products = pd.read_csv('products.csv')
categories = pd.read_csv('categories.csv')
cat_of = products.set_index('id')['category_id']
cat_name = categories.set_index('id')['name']
lines['category'] = lines['product_id'].map(cat_of).map(cat_name).fillna('Geschenkkarte')
by_cat = lines.groupby('category')['revenue'].sum()
fig, ax = plt.subplots()
ax.bar(by_cat.index, by_cat.values)
ax.set_title('Umsatz nach Kategorie, €')
ax.set_ylabel('Umsatz, €')
ax.tick_params(axis='x', labelrotation=30)
fig.tight_layout().fillna('Geschenkkarte') nach den zwei map-Aufrufen gibt der fehlenden Kategorie einen Namen, und groupby behält sie. Jetzt sind es 7 Balken, und sie ergeben genau 111.186,20 €. Die Milchprodukte sind der höchste Balken mit 31.427,90 €, und die Geschenkkarte der niedrigste mit 3.720 €. Ohne fillna hat das Diagramm 6 Balken, und die Geschenkkarte fehlt.
Die Idee
Die Abkürzung: df.plot
Series und DataFrame haben eine Methode plot. Sie ruft matplotlib für dich auf: kind='line' (der Standard), kind='bar' und die anderen Arten, die in den nächsten Lektionen kommen. Mit ax=ax zeichnet sie in deine Axes. Dann beendest du sie mit den Methoden der Axes, wie vorher:
pandas hat die Kategorienamen unter die Balken gesetzt und gedreht. Es hat auch eine Beschriftung unten gesetzt: den Namen des Index, category. Der Titel und die senkrechte Beschriftung kommen aus deinem Code.
Beide Wege liefern dasselbe Diagramm. ax.bar gibt dir volle Kontrolle. df.plot spart Zeilen, wenn die Daten schon in einer Series oder Tabelle liegen.
Die Idee
Mehrere Linien und eine Legende
Der Dezember ist der Gipfel des Jahres. Wachsen alle Kategorien gleich? Zwei Linien in einer Axes beantworten das. Jedes ax.plot fügt eine Linie hinzu, label gibt ihr einen Namen, und ax.legend() fügt die Legende hinzu: einen Kasten, der sagt, welche Linie welche ist:
Die Küche springt von 964,80 € im November auf 3.240,60 € im Dezember, das 3,4-Fache. Dann fällt sie im Januar auf 592,80 €. Die Bratpfanne und die Messer werden wohl als Geschenke gekauft. Die Milchprodukte wachsen weniger: von 2.792,10 € auf 3.561,20 €.
Eine Tabelle mit einer Spalte für jede Kategorie zeichnet df.plot in einem Zug, eine Linie pro Spalte, mit einer Legende aus den Spaltennamen. Deshalb ist die Tabelle in der Lektion über pivot_table breit:
Sieben Linien in einer Axes verheddern sich schon. Welches Diagramm wann hilft und wie du viele Reihen zeigst, ohne dass sie sich verheddern, ist die nächste Lektion.
Übung
Wer trägt den Dezember?
Die zwei großen Gruppen von Kategorien sind Lebensmittel und Haushalt (parent_id in categories.csv). Zeichne den Umsatz jeder Gruppe nach Monat als zwei Linien in einer Axes, mit Legende, Titel und Beschriftungen an den Achsen. Leg die Tabelle nach Monat und Gruppe in pv ab.
Lösung
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
items = pd.read_csv('year_items.csv')
# Der Umsatz enthält die stornierten Bestellungen nicht.
sold = orders[orders['status'] != 'cancelled']
lines = items.merge(sold, left_on='order_id', right_on='id')
lines['revenue'] = lines['quantity'] * lines['unit_price']
lines['month'] = lines['ordered_at'].str[:7]
products = pd.read_csv('products.csv')
categories = pd.read_csv('categories.csv')
cat_of = products.set_index('id')['category_id']
cat_name = categories.set_index('id')['name']
parent_of = categories.set_index('id')['parent_id']
lines['group'] = lines['product_id'].map(cat_of).map(parent_of).map(cat_name)
pv = lines.pivot_table(index='month', columns='group', values='revenue', aggfunc='sum')
fig, ax = plt.subplots()
ax.plot(pv.index, pv['Lebensmittel'], label='Lebensmittel')
ax.plot(pv.index, pv['Haushalt'], label='Haushalt')
ax.legend()
ax.set_title('Lebensmittel und Haushalt nach Monat, €')
ax.set_xlabel('Monat')
ax.set_ylabel('Umsatz, €')
ax.tick_params(axis='x', labelrotation=45)
fig.tight_layout()Die Gruppe einer Zeile braucht drei map-Aufrufe: Produkt → Kategorie → parent_id → Name. pivot_table nach Monat und Gruppe liefert eine Spalte Lebensmittel und eine Spalte Haushalt. Im Dezember wachsen die Lebensmittel um 30 % (von 7.097,40 € auf 9.249,30 €), und der Haushalt mehr als verdoppelt sich (von 2.077,10 € auf 4.506,60 €). Die Geschenkkarte hat keine Gruppe und liegt in keiner der Linien. Ohne label ist die Legende leer, und ohne ax.legend() gibt es gar keine Legende.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Typen und fehlende Werte · Gruppen: groupby · Verbinden: merge · Breit und lang: pivot_table und melt
- Geht weiter in: Welches Diagramm für welche Frage · Verteilungen · Ehrliche Diagramme · Die Daten beschreiben
- Aufgaben zum Thema: Diagramme