Zum Inhalt
wannacode

Neue Spalten

Kurz gesagt: Eine neue Spalte in pandas wird aus den anderen Spalten auf einmal berechnet: Arithmetik, np.where oder map; assign liefert eine neue Tabelle, und apply ist der langsamste Weg.

Problem

Was jede Position wert ist

order_items.csv sagt dir für jede Position einer Bestellung, welches Produkt es ist, wie viele Stück und zu welchem Preis. Was die ganze Position wert war, steht nirgends:

PythonDaten: die Tabellen des Shops

Der Chef will den Umsatz, aber ohne stornierte Bestellungen. Er will auch wissen, welche Positionen groß sind, und im Bericht sollen Kategorienamen stehen, keine Zahlen. Für all das brauchst du Spalten, die nicht in der Datei stehen. Du berechnest sie aus denen, die da sind.

Die Idee

Eine Spalte aus Arithmetik

Eine Spalte ist eine Series, und Arithmetik mit Series ist vektorisiert, wie bei NumPy-Arrays. Stück mal Preis liefert den Wert aller 152 Positionen auf einmal. Speicherst du das Ergebnis unter einem neuen Namen, bekommt die Tabelle eine neue Spalte:

PythonDaten: die Tabellen des Shops

total ist rechts erschienen: Die erste Position sind 4 Stück zu 7,90 €, das sind 31,60 €. Gibt es den Namen schon, wird die Spalte durch die neuen Werte ersetzt.

Die Summe aller Positionen ist 1.963,50 €. Das ist noch nicht der Umsatz: In diesen Positionen stecken auch Bestellungen, die Kunden storniert haben.

Sieh es dir an

Umsatz ohne die stornierten

Eine stornierte Bestellung ist nicht bezahlt und zählt deshalb nicht zum Umsatz. Der Status steht in orders.csv, die Positionen in order_items.csv. Mit loc nimmst du die Nummern der stornierten Bestellungen, und dann findet isin aus der letzten Lektion ihre Positionen:

PythonDaten: die Tabellen des Shops

7 Bestellungen wurden storniert. Ohne ihre Positionen bleiben 131 von 152, und der Umsatz ist 1.697,00 €. Die Regel gilt für den ganzen Kurs: Geht es um Umsatz, zählen stornierte Bestellungen nicht.

Probier es aus

Geld in den Regalen

Füge products eine Spalte stock_value hinzu: wie viel Geld für jedes Produkt im Bestand steckt (Preis mal Bestand). Lege dann in top3 die drei Produkte mit dem größten stock_value ab, ohne die Geschenkkarte (id 25).

PythonDaten: die Tabellen des Shops

Die Idee

Eine neue Tabelle mit assign

items['total'] = … ändert die Tabelle selbst. assign macht das Gegenteil: Es liefert eine neue Tabelle mit einer Spalte mehr, und die alte bleibt, wie sie ist. So kannst du gleich mit einem Filter weitermachen:

PythonDaten: die Tabellen des Shops

6 Positionen sind 50 € oder mehr wert. Die letzte Zeile zeigt, dass items kein total hat: assign hat es nicht angefasst. Das ist Copy-on-Write aus der Lektion über das Auswählen: Eine Änderung liefert eine neue Tabelle, und das Original ändert sich nicht.

In assign kannst du statt einer Spalte auch eine Funktion übergeben. lambda d: … bekommt die Tabelle in diesem Moment, deshalb sieht das zweite assign das total des ersten:

PythonDaten: die Tabellen des Shops

Eine solche Folge von Methoden, von denen jede mit dem Ergebnis der vorigen arbeitet, heißt Kette. Mit den Klammern drumherum kannst du sie über mehrere Zeilen schreiben.

Die Idee

Das eine oder das andere: np.where

Eine „große“ Position ist 20 € oder mehr wert. np.where aus der NumPy-Lektion funktioniert auch mit Spalten: Für jede Zeile nimmt es den einen oder den anderen Wert:

PythonDaten: die Tabellen des Shops

Es gibt 35 große und 117 kleine Positionen. Das Ergebnis hat für jede Zeile einen Wert, also wird es zu einer Spalte.

Die Idee

Von der Zahl zum Namen: map

In products ist die Kategorie eine Zahl, und der Chef will einen Namen. map mit einem Dictionary ersetzt jeden Wert durch den passenden aus dem Dictionary:

PythonDaten: die Tabellen des Shops

3 wird zu „Brot und Gebäck“, 8 wird zu „Küche“. Die Geschenkkarte hat keine Kategorie, im Dictionary gibt es keinen Schlüssel dafür, und map lässt NaN stehen.

Das Dictionary musst du nicht von Hand tippen. categories.set_index('id')['name'] ist eine Series mit den Zahlen als Index und den Namen als Werten, und map funktioniert auch mit einer solchen Series: Es schlägt den Wert in ihrem Index nach.

1. Das Dictionary names hat keinen Schlüssel für die Geschenkkarte. Was liefert map dafür?

Die Idee

Deine eigene Funktion: apply

Der Shop gibt auf eine große Position Rabatt: 10 % ab 50 € und 5 % ab 20 €. Die Regel ist eine Python-Funktion mit if. apply führt sie für jeden Wert der Spalte aus:

PythonDaten: die Tabellen des Shops

117 Positionen bekommen keinen Rabatt, 29 bekommen 5 % und 6 bekommen 10 %. Alle Rabatte zusammen machen 84,14 € aus.

apply ist praktisch, wenn die Regel schon eine Funktion ist, die mit einem Wert arbeitet. Diese Regel ließe sich auch vektorisiert schreiben, mit zwei ineinander verschachtelten np.where. Bei 152 Positionen spürst du den Unterschied nicht. Bei vielen Positionen schon.

Sieh es dir an

Was apply kostet

Hier ist dieselbe Arithmetik auf zwei Wegen über alle 26.619 Positionen der Bestellungen des Jahres. apply(…, axis=1) führt die Funktion für jede Zeile aus, und time.perf_counter() misst, wie viel Zeit vergeht:

PythonDaten: die Tabellen des Shops

Das Ergebnis ist dasselbe (True in der vorletzten Zeile). Bei unseren Läufen war apply über hundertmal langsamer als die vektorisierte Arithmetik. Die genauen Zahlen hängen vom Rechner ab, aber der Abstand bleibt.

Die Regel: Zuerst suchst du vektorisierte Arithmetik, np.where oder map. apply bleibt für eine Regel, die sich nicht anders schreiben lässt.

Genauer, für Neugierige

apply mit axis=1 macht aus jeder Zeile eine kleine Series und ruft deine Python-Funktion 26.619-mal auf. Die vektorisierte Arithmetik geht einmal über die Spalte, in einer C-Schleife in NumPy. Deshalb wächst der Abstand mit der Zahl der Zeilen: Bei 152 Zeilen ist er unsichtbar, bei einer Million ist es der Unterschied zwischen einer Sekunde und einer Minute.

Die Idee

Der Bericht: rename und drop

Bevor du die Tabelle dem Chef schickst, entfernst du die Spalten, die er nicht braucht, und gibst Namen, die er versteht. drop(columns=[…]) entfernt Spalten, und rename(columns={alt: neu}) benennt sie um:

PythonDaten: die Tabellen des Shops

Beide liefern eine neue Tabelle, wie assign. Spalten, die nicht im Dictionary von rename stehen, behalten ihren alten Namen.

Übung

Der Umsatz des Jahres

Wie hoch ist der Umsatz des ganzen Jahres? Füge year_items eine Spalte total hinzu (Stück mal Preis), entferne die Positionen stornierter Bestellungen und lege die Summe, auf 2 Nachkommastellen gerundet, in revenue ab.

PythonDaten: die Tabellen des Shops

Wie viel haben die Kaffeebohnen (Produkt 17) im Jahr eingebracht, ohne stornierte Bestellungen? Lege die Summe, auf 2 Nachkommastellen gerundet, in coffee_revenue ab.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Du führst items.assign(total=...) aus und speicherst das Ergebnis nirgends. Was steht danach in items?
2. Was ist besser für den Wert einer Position bei 26.619 Positionen?
3. Wann ist apply mit deiner eigenen Funktion die richtige Wahl?
4. Kaffee kostete bis zum 1. März 13,90 €, jetzt sind es 14,90 €. Welchen Preis nimmst du für den Umsatz mit Kaffee im Jahr?

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