Welches Diagramm für welche Frage
Kurz gesagt: Die Art des Diagramms richtet sich nach der Frage: sortierte Balken für einen Vergleich, eine Linie für die Veränderung über die Zeit und wieder Balken, kein Kreisdiagramm, für einen Anteil am Ganzen.
Problem
Vier Fragen, ein Datensatz
Der Chef hat vier Fragen zum Jahr:
- Welche Produkte bringen das meiste Geld?
- Wie verläuft der Umsatz Tag für Tag?
- Welchen Teil des Umsatzes macht jede Kategorie aus?
- Was ist eine übliche Bestellung?
Die Daten sind dieselben: die Bestellungen und ihre Zeilen. Das Diagramm ist aber für jede Frage ein anderes. Es gibt kein Diagramm, das alle beantwortet. Es gibt ein Diagramm, das eine Frage gut beantwortet, und viele, die sie schlecht beantworten.
Die Idee
Erst die Frage, dann das Diagramm
Fang nicht bei „Ich will ein Kreisdiagramm“ oder „Ich will eine Linie“ an. Fang bei der Frage an und sieh, von welcher Art sie ist:
- Vergleich („was ist mehr“): nach Wert sortierte Balken; bei langen Namen waagerecht (
ax.barh); - Veränderung über die Zeit („wie läuft es“): eine Linie;
- Anteil an einem Ganzen („welcher Teil“): wieder sortierte Balken, in Prozent; ein Kreisdiagramm ist schwerer zu lesen;
- Verteilung („was ist üblich, wie stark streut es“): ein Histogramm, in der nächsten Lektion;
- Zusammenhang („wachsen sie zusammen“): ein Streudiagramm, auch in der nächsten Lektion.
Sehen wir uns die ersten drei mit denselben Daten an.
Die Idee
Vergleich: sortierte waagerechte Balken
„Welche Produkte bringen am meisten?“ ist ein Vergleich von 25 Produkten. Ihre Namen sind lang, deshalb sind die Balken waagerecht: ax.barh(names, values). Die Namen stehen links und lassen sich lesen, ohne sie zu drehen.
Das Sortieren mit sort_values() macht das Diagramm zu einer Rangliste. barh zeichnet die erste Zeile unten, also setzt die aufsteigende Reihenfolge den größten nach oben. figsize=(7, 7) gibt der Figure eine Größe in Zoll: 25 Balken brauchen Höhe.
Der Weißkäse bringt am meisten: 10.325,30 €. Danach kommen die Kaffeebohnen (9.585,10 €) und der Kaschkawal (8.616,50 €). Sortiert beantworten die Balken die Frage, bevor du die Zahlen liest.
Genauer, für Neugierige
Ohne sort_values() kommen die Produkte von groupby in alphabetischer Reihenfolge. Die alphabetische Reihenfolge ist praktisch, wenn du ein Produkt nach Namen suchst. Für die Frage „welches ist das meiste“ brauchst du die Reihenfolge nach Wert.
Probier es aus
Die Bestseller nach Stück
Welche 10 Produkte haben im Jahr die meisten Stück verkauft? Leg sie in top ab (das Produkt ist der Index, die Stück sind der Wert) und zeichne sie als waagerechte Balken mit dem größten oben.
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')
products = pd.read_csv('products.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]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
top = lines.groupby('product')['quantity'].sum().nlargest(10).sort_values()
fig, ax = plt.subplots()
ax.barh(top.index, top.values)
ax.set_title('Die meistverkauften Produkte')
ax.set_xlabel('Stück im Jahr')
fig.tight_layout()lines.groupby('product')['quantity'].sum().nlargest(10).sort_values() liefert die zehn. Oben steht das Weißbrot mit 4.223 Stück, dann die Frischmilch (3.882) und das Mineralwasser (3.707). Der Weißkäse, der das meiste Geld bringt, ist nach Stück nur Zehnter (1.307). „Bestseller“ hängt davon ab, ob du Stück oder Euro zählst. nlargest setzt den größten zuerst, und ohne sort_values() landet er unten.
Die Idee
Veränderung über die Zeit: eine Linie
„Wie verläuft der Umsatz Tag für Tag?“ ist eine Veränderung über die Zeit. Eine Linie verbindet jeden Tag mit dem nächsten, und das Auge folgt der Richtung. 365 Balken wären ein Zaun, in dem man nichts sieht.
Der Tagesumsatz springt wegen des Wochenrhythmus. Eine zweite Linie mit dem 7-Tage-Durchschnitt (rolling(7) aus der Lektion über Datumswerte) zeigt die Richtung unter dem Rauschen:
Es sind 365 Tage. Der stärkste ist der 26. Dezember 2025 mit 852,90 €. Die dünne Linie zickzackt jede Woche, und die dicke zeigt den großen Gipfel im Dezember und die Einbrüche im Januar und September. Unten stehen Datumswerte: ordered_at wurde mit pd.to_datetime zu Datumswerten gemacht, und matplotlib setzt die Monate selbst ein.
Die Idee
Kleine Diagramme nebeneinander
In der vorigen Lektion haben sich die sieben Kategorien in einer Axes verheddert. Ein anderer Weg ist, jede Kategorie in ein eigenes kleines Diagramm zu legen und die Diagramme nebeneinander zu stellen. Das heißt Small Multiples.
plt.subplots(2, 3) liefert eine Figure mit 2 Zeilen und 3 Spalten von Axes. axes ist ein NumPy-Array der Form (2, 3), und axes.flat gibt sie nacheinander her. sharey=True gibt allen dieselbe senkrechte Skala. fig.suptitle ist ein Titel für die ganze Figure. Unten steht nur die Nummer des Monats (pv.index.str[5:]), damit es passt:
Mit einer gemeinsamen Skala lassen sich die Höhen vergleichen: Die Milchprodukte sind das ganze Jahr am höchsten, und die Küche ist niedrig mit einem Gipfel im Dezember. Jede Linie liest sich für sich, ohne Legende und ohne Farben zur Unterscheidung.
Die Idee
Anteil an einem Ganzen: Balken gegen Kreis
„Welchen Teil des Umsatzes macht jede Kategorie aus?“ ist eine Frage nach Anteilen. Oft wird sie als Kreisdiagramm gezeichnet. Hier sind der Kreis und die sortierten Balken mit denselben Zahlen nebeneinander. plt.subplots(1, 2) liefert zwei Axes: Hier heißen sie left und right:
Der Kreis zeigt, dass die Milchprodukte das größte Stück sind, etwas über ein Viertel. Reinigung (9,8 %) und Küche (10,2 %) sehen aber gleich aus. Auch Brot (11,9 %) liegt nah dran. Die Balken rechts bringen sie in Ordnung, und die Unterschiede zeigen sich.
Die Idee
Warum ein Kreis in die Irre führt
Balken vergleicht man nach der Länge von derselben Linie aus. Das Auge kann das gut. Ein Kreis lässt das Auge Winkel und Flächen von Stücken vergleichen, die in verschiedene Richtungen gedreht sind. Das Auge kann das schlecht, besonders wenn die Anteile nah beieinander liegen.
Ein Kreis hat noch andere Probleme:
- bei mehr als 4–5 Stücken türmen sich die Beschriftungen;
- zwei Kreise nebeneinander (Dezember gegen das Jahr) lassen sich kaum vergleichen;
- allein die Farbe verbindet ein Stück mit seinem Namen.
Ein Kreis erfüllt seinen Zweck in einem Fall: zwei oder drei Stücke und die Frage „mehr oder weniger als die Hälfte?“. Für alles andere sagen sortierte Balken dasselbe klarer.
Die Idee
Verteilung und Zusammenhang
Zwei Arten von Fragen bleiben übrig. Sie brauchen Diagramme, die in der nächsten Lektion kommen:
- Verteilung: „Was ist eine übliche Bestellung?“ Die Antwort ist nicht eine Zahl, sondern wie sich 10.459 Bestellungen verteilen: wie viele klein und wie viele groß sind. Ein Histogramm zeigt das;
- Zusammenhang: „Sind Bestellungen mit mehr Zeilen teurer?“ Jede Bestellung wird zu einem Punkt mit zwei Zahlen. Die Punktwolke ist ein Streudiagramm.
Übung
Was der Dezember bringt
Der Dezember ist der stärkste Monat. Hat er mehr Bestellungen oder größere? Leg in orders_n die Anzahl der Bestellungen in jedem Monat (ohne die stornierten) und in avg die durchschnittliche Bestellung in Euro ab und zeichne sie als zwei Linien in zwei Axes nebeneinander, jede mit einem Titel.
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')
products = pd.read_csv('products.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]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
orders_n = sold.groupby(sold['ordered_at'].str[:7]).size()
revenue = lines.groupby('month')['revenue'].sum()
avg = revenue / orders_n
fig, (left, right) = plt.subplots(1, 2, figsize=(10, 4))
left.plot(orders_n.index, orders_n.values)
left.set_title('Bestellungen pro Monat')
right.plot(avg.index, avg.values)
right.set_title('Durchschnittliche Bestellung, €')
left.tick_params(axis='x', labelrotation=90)
right.tick_params(axis='x', labelrotation=90)
fig.tight_layout()orders_n liefert 1.211 Bestellungen im Dezember gegen 701 bis 961 in den anderen Monaten. avg = revenue / orders_n liefert im Dezember eine durchschnittliche Bestellung von 13,14 € und in den anderen Monaten zwischen 9,93 € und 10,49 €. Der Dezember hat sowohl mehr Bestellungen als auch größere. In einer Axes würde die durchschnittliche Bestellung (etwa 10) unter den Bestellungen (Hunderte) auf der Null liegen. lines.groupby('month')['revenue'].mean() ist die durchschnittliche Zeile, nicht die durchschnittliche Bestellung.
Welchen Teil des Umsatzes im Dezember 2025 macht jede Kategorie aus, mit der Geschenkkarte? Leg den Anteil in Prozent sortiert in share ab und zeichne ihn als waagerechte Balken mit dem größten oben.
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')
products = pd.read_csv('products.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]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
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')
dec = lines[lines['month'] == '2025-12']
share = (dec.groupby('category')['revenue'].sum() / dec['revenue'].sum() * 100).sort_values()
fig, ax = plt.subplots()
ax.barh(share.index, share.values)
ax.set_title('Anteil jeder Kategorie im Dezember')
ax.set_xlabel('Umsatzanteil, %')
fig.tight_layout()Der Filter lines['month'] == '2025-12' behält den Dezember. Die Summe nach Kategorie, geteilt durch die Summe des Monats und mal 100, liefert die Anteile. Die Milchprodukte stehen oben mit 22,4 %, aber die Küche liegt mit 20,4 % nah dran (im ganzen Jahr sind es 10,2 %). Die Geschenkkarte macht 13,6 % des Dezembers aus und 3,3 % des Jahres. Bei einem Kreis wären diese Unterschiede Raten. Ohne den Filter bekämst du die Anteile für das ganze Jahr.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Verbinden: merge · Breit und lang: pivot_table und melt · Datumswerte und Zeitreihen · Das erste Diagramm
- Geht weiter in: Verteilungen · Ehrliche Diagramme · Warum ist der Umsatz gefallen?
- Aufgaben zum Thema: Diagramme