Vektorisierung
Kurz gesagt: Vektorisierung in NumPy heißt, mit dem ganzen Array auf einmal zu rechnen, ohne Schleife: Arithmetik, Vergleiche, Masken mit & und | und Auswahl mit np.where.
Problem
Eine Schleife für jede Frage
Der Chef will die Preise mit Mehrwertsteuer. Gleich danach fragt er, wie viele Produkte über 5 € kosten. Mit einer Liste ist jede Frage eine neue Schleife:
Es funktioniert: 1,20 € wird zu 1,44 €, und 8 Produkte liegen über 5 €. Aber für jede nächste Frage schreibst du noch ein for, noch eine leere Variable und noch einen Zähler. Die Frage selbst geht zwischen den Zeilen verloren.
Die Idee
Rechnen mit dem ganzen Array
Mit einem Array schreibst du die Rechnung einmal, für alle Zahlen:
prices * 1.2 multipliziert jeden der 25 Preise. Die Schleife steckt in NumPy, in C. Das nennt man eine vektorisierte Operation: Sie wirkt auf das ganze Array, nicht auf eine einzelne Zahl. Das Messerset für 39 € wird zu 46,80 €.
+, -, *, / und ** funktionieren so, ebenso Funktionen wie np.round(array, 2), das jede Zahl auf 2 Stellen rundet.
Die Idee
Array mit Array
Zwei Arrays gleicher Länge werden Zahl für Zahl gerechnet: das erste mit dem ersten, das zweite mit dem zweiten. In order_items.csv hat jede Position eine Menge und einen Stückpreis. Der Wert der Position ist das eine mal das andere:
Die erste Position sind 4 Stück zu 7,90 €, das sind 31,60 €. Ohne eine einzige Schleife hast du den Wert aller 152 Positionen. .sum() addiert sie: 1.963,50 €. Zu Zusammenfassungen wie sum kommen wir in Lektion 3.
Probier es aus
Was der Bestand wert ist
Lege in value den Wert des Bestands für jedes Produkt ab: sein Preis mal die Stückzahl im Lager.
Lösung
import pandas as pd
products = pd.read_csv('products.csv')
prices = products['price'].to_numpy()
stock = products['stock'].to_numpy()
value = prices * stock
valuevalue = prices * stock liefert 25 Werte. Die Banitsa und die Gurken sind 0: Sie sind ausverkauft. Die Geschenkkarte macht 19.980 € von insgesamt 22.542,30 € aus. Das ist der Platzhalter 999 aus der ersten Lektion. Deshalb prüfst du jede Rechnung mit einem Blick auf die Daten.
Die Idee
Vergleich und Maske
Auch ein Vergleich ist vektorisiert. prices > 5 liefert nicht ein True oder False, sondern eines für jeden Preis:
Ein solches Array aus True und False heißt Maske. In eckigen Klammern behält sie nur die Stellen mit True. Und .sum() einer Maske zählt die Trues, weil in einer Summe True gleich 1 und False gleich 0 ist:
8 Preise liegen über 5 €: dieselbe Antwort wie mit der Schleife, in einer Zeile. Die Maske aus dem Bestand funktioniert auch bei einem anderen Array gleicher Länge: Die Namen der ausverkauften Produkte sind die Banitsa und die Gurken.
Die Idee
Zwei Bedingungen: &, | und ~
Die Preise von 2 € bis 5 € sind zwei Bedingungen zusammen. In gewöhnlichem Python würdest du and schreiben. Mit Arrays funktioniert and nicht:
and fragt „ist das Ganze wahr?“ und will ein einziges True oder False. Das Array hat 25. NumPy rät nicht, ob du „alle“ oder „mindestens eines“ meinst, und hält mit einem Fehler an.
Für Masken gibt es drei Zeichen, die Zahl für Zahl wirken:
&ist „und“: beide Bedingungen sind wahr;|ist „oder“: mindestens eine ist wahr;~ist „nicht“: Es kehrt die Maske um.
9 Preise liegen von 2 € bis 5 €. Unter 1 € oder über 20 € liegen 0,80 €, 0,90 €, 24,90 € und 39 €. Die Preise der Produkte, die nicht auf Lager sind, sind 2,50 € und 2,80 €.
Sieh es dir an
Die Klammerfalle
Jede Bedingung um & und | steht in Klammern. Lässt du sie weg, rechnet Python & vor >= und <=, das heißt, es beginnt mit 2 & prices:
& zwischen einer Zahl und Dezimalpreisen ergibt keinen Sinn, also bekommst du einen TypeError über bitwise_and. Die Regel lautet: jede Bedingung in Klammern, (prices >= 2) & (prices <= 5).
Genauer, für Neugierige
In Python sind &, | und ~ bitweise Operationen auf ganzen Zahlen. Deshalb binden sie stärker als Vergleiche: 2 & 6 > 1 ist (2 & 6) > 1. NumPy hat sie für Masken genommen, weil and, or und not für Arrays nicht umdefiniert werden können: Sie wollen immer ein einziges True oder False. Dasselbe siehst du bei den Filtern in pandas.
Probier es aus
Billig und auf Lager
Wie viele Produkte kosten unter 3 € und sind auf Lager (Bestand über 0)? Lege die Zahl in count ab.
Lösung
import pandas as pd
products = pd.read_csv('products.csv')
prices = products['price'].to_numpy()
stock = products['stock'].to_numpy()
count = ((prices < 3) & (stock > 0)).sum()
countcount = ((prices < 3) & (stock > 0)).sum() liefert 12. Es gibt 14 Produkte unter 3 €, aber zwei davon sind ausverkauft: die Banitsa für 2,50 € und die Gurken für 2,80 €. Mit and scheitert der Code mit einem ValueError, ohne die Klammern mit einem TypeError.
Die Idee
Das eine oder das andere: np.where
Eine Maske wählt, welche Zahlen bleiben. Manchmal willst du alle, aber je nach Bedingung verschiedene. np.where(maske, wenn_ja, wenn_nein) nimmt für jede Stelle das eine oder das andere:
Wo der Bestand 0 ist, bekommst du „ausverkauft“, sonst „auf Lager“. Anstelle des Texts können auch Arrays stehen: np.where(prices > 10, prices * 0.9, prices) senkt nur die teuren Preise um 10 %.
Übung
Die Preisliste mit Mehrwertsteuer
Zurück zum Chef: die Preise mit Mehrwertsteuer und eine Aktion.
Lege in with_vat die Preise mit Mehrwertsteuer ab (mal 1,2), auf 2 Stellen gerundet. Lege in over_10 ab, wie viele davon über 10 € liegen.
Lösung
import numpy as np
import pandas as pd
prices = pd.read_csv('products.csv')['price'].to_numpy()
with_vat = np.round(prices * 1.2, 2)
over_10 = (with_vat > 10).sum()
over_10with_vat = np.round(prices * 1.2, 2) und over_10 = (with_vat > 10).sum() liefert 7. Ohne Mehrwertsteuer liegen 6 Produkte über 10 €. Das siebte ist der Kaschkawal: 9,50 € werden mit Mehrwertsteuer zu 11,40 €. Vergisst du .sum(), bleibt die Maske selbst übrig, nicht die Zahl.
Aktion: Die Produkte über 10 € sind 10 % billiger, die anderen behalten den alten Preis. Lege die ganze neue Preisliste (25 Preise) in promo ab.
Lösung
import numpy as np
import pandas as pd
prices = pd.read_csv('products.csv')['price'].to_numpy()
promo = np.where(prices > 10, prices * 0.9, prices)
promopromo = np.where(prices > 10, prices * 0.9, prices). Sechs Preise ändern sich: Das Messerset sinkt von 39 € auf 35,10 €, die Kaffeebohnen von 14,90 € auf 13,41 €. Die anderen 19 bleiben gleich. prices[prices > 10] * 0.9 liefert nur die sechs neuen Preise und verliert den Rest, und prices * 0.9 senkt alles.
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Die erste Tabelle · Das NumPy-Array
- Geht weiter in: Zusammenfassungen und fehlende Werte · Broadcasting: die Zahl, die Zeile und die Spalte · Series und DataFrame · Auswählen: [], loc und iloc
- Aufgaben zum Thema: NumPy