Zum Inhalt
wannacode

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:

PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

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.

PythonDaten: die Tabellen des Shops

Die Idee

Vergleich und Maske

Auch ein Vergleich ist vektorisiert. prices > 5 liefert nicht ein True oder False, sondern eines für jeden Preis:

PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

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.
PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

& 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.

PythonDaten: die Tabellen des Shops

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:

PythonDaten: die Tabellen des Shops

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.

PythonDaten: die Tabellen des Shops

Aktion: Die Produkte über 10 € sind 10 % billiger, die anderen behalten den alten Preis. Lege die ganze neue Preisliste (25 Preise) in promo ab.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Was liefert np.array([2, 3]) * np.array([1.5, 4.0])?
2. Was liefert (prices > 5).sum()?
3. Welche Zeile nimmt die Preise von 2 € bis 5 €?
4. Warum ist der Rabatt für die teuren Produkte np.where(prices > 10, prices * 0.9, prices) und nicht prices[prices > 10] * 0.9?

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