Zusammenfassungen und fehlende Werte
Kurz gesagt: Aggregationen in NumPy (sum, mean, max, argmax) machen aus vielen Zahlen eine, axis sagt, welche Dimension verschwindet, und NaN markiert einen fehlenden Wert.
Problem
„Was verkaufen wir am meisten?“
Die Kasse schickt einen Bericht: wie viele Stück von vier Produkten wir in jeder der letzten sechs Wochen verkauft haben. Jede Zeile ist eine Woche, jede Spalte ein Produkt:
Der Chef fragt drei Dinge: wie viel wir insgesamt verkauft haben, welches Produkt am besten läuft und welche Woche die stärkste war. Danach schickt er auch noch die Einnahmen pro Tag, wobei ein Tag fehlt.
Jede Antwort ist eine Zahl (oder eine pro Produkt), berechnet aus vielen Zahlen. Das nennt man eine Aggregation.
Die Idee
Eine Zahl aus vielen: sum, mean, min, max
Ein Array hat Methoden, die alle seine Werte zusammenfassen: sum() für die Summe, mean() für den Mittelwert, min() und max() für den kleinsten und den größten Wert. Hier sind sie an den Preisen der 25 Produkte. to_numpy() nimmt die Spalte als NumPy-Array:
Die Summe der Preise kommt als 175.89999999999998 heraus, nicht als 175,9. Der Computer speichert Dezimalzahlen binär, und 0,1 lässt sich nicht genau schreiben, deshalb summieren sich winzige Fehler. Das sind keine Fehler in den Daten.
Für einen Bericht rundest du: round(x, 2) liefert 2 Nachkommastellen. Der mittlere Preis wird 7,04 €, wie bei describe() im ersten Blick.
Genauer, für Neugierige
round ist die Funktion von Python und arbeitet mit einer einzelnen Zahl. Ein Array hat die Methode .round(2) (oder np.round(arr, 2)), die jede Zahl darin rundet. Runde am Ende, zur Anzeige: Wenn du mittendrin rundest, geht der Rundungsfehler in die nächsten Schritte ein.
Die Idee
Welches, nicht wie viel: argmax und argmin
max() sagt dir, wie viel das teuerste Produkt kostet. Der Chef fragt aber, welches es ist. argmax() liefert die Position des größten Werts und argmin() die des kleinsten. Mit der Position nimmst du den Namen aus dem Array der Namen:
Das teuerste ist das Messerset an Position 22 (39 €), das billigste das Butterbrötchen an Position 2 (0,80 €). Positionen beginnen bei 0, wie bei Listen.
Genauer, für Neugierige
Kommt der größte Wert mehrmals vor, liefert argmax seine erste Position. Im Kassenbericht haben die Wochen 1 und 4 je 103 Stück, und argmin über die Wochen liefert nur Woche 1. Wenn Gleichstände wichtig sind, prüfe sie mit einer Maske: week_totals == week_totals.min().
Sieh es dir an
Entlang welcher Dimension: axis
sales.sum() addiert alle 24 Zellen: 654 Stück in sechs Wochen. Für „welches Produkt“ brauchst du eine Summe für jede Spalte, für „welche Woche“ eine Summe für jede Zeile. Der Parameter axis sagt, welche:
axis=0ist die Dimension der Zeilen (die Wochen). Die Wochen verschmelzen, und eine Zahl pro Produkt bleibt: Brot sind 263 Stück, Kaffee 39.axis=1ist die Dimension der Spalten (die Produkte). Die Produkte verschmelzen, und eine Zahl pro Woche bleibt: von 103 bis 120.
Hier verwirrt sich jeder, denn „axis=0“ klingt nach „zeilenweise“ und liefert doch ein Ergebnis pro Spalte. Die Regel, die nicht lügt: axis ist die Dimension, die verschwindet. Die Form (6, 4) verliert mit axis=0 die 6 und wird zu (4,), und mit axis=1 verliert sie die 4 und wird zu (6,).
Dasselbe gilt für mean, min, max, argmax und den Rest. Der Mittelwert pro Woche für jedes Produkt, auf eine Nachkommastelle gerundet:
Probier es aus
Die stärkste Woche
Welche Woche hat insgesamt die meisten Stück verkauft? Lege die Summe jeder Woche in week_totals ab und ihre Nummer (1 bis 6) in best_week.
Lösung
import numpy as np
names = np.array(['Weißbrot', 'Frischmilch', 'Äpfel', 'Kaffeebohnen'])
sales = np.array([
[42, 30, 25, 6],
[38, 34, 28, 4],
[45, 29, 31, 7],
[40, 36, 22, 5],
[51, 33, 27, 9],
[47, 31, 26, 8],
])
week_totals = sales.sum(axis=1)
best_week = week_totals.argmax() + 1
print(week_totals)
print(best_week)week_totals = sales.sum(axis=1) liefert [103 104 112 103 120 112]: Die Produkte verschwinden, die Wochen bleiben. week_totals.argmax() ist 4, und Position 4 ist die fünfte Woche, also best_week = 5 (120 Stück). Mit axis=0 bekämst du 4 Zahlen, die Summen pro Produkt, und die „stärkste Woche“ wäre das Weißbrot.
Die Idee
Wie viel bisher: cumsum
Manchmal fragt der Chef nicht „wie viel in dieser Woche“, sondern „wie viel vom Anfang bis zu dieser Woche“. Das ist die laufende Summe: cumsum() addiert die Zahlen nacheinander und behält jede Teilsumme:
Nach der dritten Woche haben wir 319 Stück verkauft, und die letzte Zahl, 654, ist die Gesamtsumme aus sales.sum(). Die laufende Summe ist die Linie, die in Diagrammen der „Verkäufe seit Jahresbeginn“ wächst.
Die Idee
Ein fehlender Tag: NaN
Jetzt die Einnahmen pro Tag der letzten Woche. Am Mittwoch hat die Kasse keinen Bericht geschickt, deshalb steht an der Stelle des Mittwochs np.nan: der Wert für „fehlend“, den du im ersten Blick gesehen hast. Das passiert mit dem Mittelwert:
Der gewöhnliche Mittelwert ist nan. Jede Rechnung mit NaN ergibt NaN: Was ist 412,5 plus „ich weiß nicht“? Ich weiß nicht. NumPy löst keinen Fehler aus; es liefert still nan, und in einem langen Bericht fällt das nicht auf.
np.isnan(revenue) ist eine Maske, die sagt, wo Werte fehlen, und .sum() darauf zählt sie: 1 Tag. np.nanmean und np.nansum überspringen NaN: Die mittleren Einnahmen der sechs bekannten Tage sind 494,70 €, ihre Summe ist 2.968,20 €. Diese Summe ist „was wir bisher wissen“, nicht die Einnahmen der ganzen Woche: Der Mittwoch ist nicht darin.
Sieh es dir an
Warum nicht 0
Die Versuchung ist, den fehlenden Wert durch 0 zu ersetzen und weiterzumachen. np.nan_to_num tut genau das:
Der Mittelwert fällt von 494,70 € auf 424,03 €. Die Null sagt „am Mittwoch haben wir nichts verkauft“, und das stimmt nicht: Der Shop war geöffnet, nur der Bericht fehlt. 0 ist nicht „fehlend“. Null ist ein Wert, und NaN ist das Fehlen eines Werts.
0 ist nur dann der richtige Ersatz, wenn es wirklich null bedeutet. Schickt die Kasse für ein Produkt, das niemand gekauft hat, keine Zeile, dann ist die Lücke null Verkäufe. Das entscheidet die Bedeutung der Daten, nicht der Code.
Probier es aus
Eine Woche ohne Zählung
Die Kasse hat nicht geschickt, wie viele Packungen Kaffee wir in der dritten Woche verkauft haben. Lege in avg_coffee ab, wie viele Packungen wir pro Woche im Durchschnitt verkaufen.
Lösung
import numpy as np
# Packungen Kaffee pro Woche. Die Kasse hat die Zahl für Woche 3 nicht geschickt.
coffee = np.array([6, 4, np.nan, 5, 9, 8])
avg_coffee = np.nanmean(coffee)
print(avg_coffee)avg_coffee = np.nanmean(coffee) liefert 6,4: den Mittelwert der fünf bekannten Wochen (32 Packungen in 5 Wochen). coffee.mean() liefert nan, und mit 0 an der Stelle der Lücke kommen etwa 5,33 heraus: sechs Wochen, von denen eine als Woche ohne Verkäufe zählt, was sie nicht war.
Übung
Der Bericht für den Chef
Beantworte die Fragen des Chefs aus dem Kassenbericht. Lege in product_totals die Stück jedes Produkts über die sechs Wochen ab, in best_product den Namen des meistverkauften Produkts und in avg_per_week, wie viele Stück jedes Produkt pro Woche im Durchschnitt verkauft, auf eine Nachkommastelle gerundet.
Lösung
import numpy as np
names = np.array(['Weißbrot', 'Frischmilch', 'Äpfel', 'Kaffeebohnen'])
sales = np.array([
[42, 30, 25, 6],
[38, 34, 28, 4],
[45, 29, 31, 7],
[40, 36, 22, 5],
[51, 33, 27, 9],
[47, 31, 26, 8],
])
product_totals = sales.sum(axis=0)
best_product = names[product_totals.argmax()]
avg_per_week = sales.mean(axis=0).round(1)
print(best_product)
print(avg_per_week)product_totals = sales.sum(axis=0) liefert [263 193 159 39]: Die Wochen verschwinden. names[product_totals.argmax()] ist „Weißbrot“ mit 263 Stück. sales.mean(axis=0).round(1) liefert [43.8 32.2 26.5 6.5]: Brot verkauft sich etwa 44-mal pro Woche, Kaffee 6 oder 7 Packungen. product_totals.max() hätte auf eine „welches“-Frage „263“ geantwortet.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Die erste Tabelle · Das NumPy-Array · Vektorisierung
- Geht weiter in: Broadcasting: die Zahl, die Zeile und die Spalte · Series und DataFrame · Filtern und Sortieren · Neue Spalten
- Aufgaben zum Thema: NumPy