Zum Inhalt
wannacode

Das NumPy-Array

Kurz gesagt: Ein NumPy-Array (ndarray) enthält Zahlen eines einzigen Typs (dtype) mit einer Form und rechnet damit viel schneller als eine Python-Liste.

Problem

„Schlag auf jeden Preis die Mehrwertsteuer auf“

Der Shop hat 25 Produkte. Der Chef will die Preise mit Mehrwertsteuer: jeder Preis mal 1,2. Danach will er ein Jahr Verkäufe pro Tag: 25 Produkte an 365 Tagen, das sind 9.125 Zahlen.

Bisher lebten Zahlen in Python in einer Liste. Eine Liste rechnet aber nicht:

PythonDaten: die Tabellen des Shops

* 2 verdoppelt die Preise nicht, es wiederholt die Liste zweimal. * 1.2 ist ein Fehler. Bleibt eine for-Schleife für jede Rechnung, und bei 9.125 Zahlen, dann bei einer Million, wird der Code lang und langsam.

Die Idee

Ein Array: ein Block von Zahlen

Eine Liste ist eine Sammlung einzelner Objekte. Jeder ihrer Plätze enthält eine Adresse, und die Zahl selbst liegt woanders im Speicher, mit ihrer eigenen Python-„Hülle“. Deshalb kann eine Liste alles halten: eine Zahl, Text, eine andere Liste.

Die Datenanalyse braucht das nicht. Sie braucht viele Zahlen desselben Typs. NumPy ist die Bibliothek dafür, und ihr Kern ist das Array: Die Zahlen liegen nebeneinander in einem Block des Speichers, alle vom selben Typ. Hier sind eine Million Preise als Liste und als Array:

PythonDaten: die Tabellen des Shops

Die Liste braucht 20,2 MB und das Array 8,0 MB: 8 Byte pro Preis und nichts weiter. Die Sandbox ist 32-Bit. Auf einem gewöhnlichen 64-Bit-Computer sind Adressen und Hüllen größer, und die Liste wächst auf etwa 32 MB, während das Array bei 8 MB bleibt.

Ein Block gleichartiger Zahlen lässt sich auch schneller abarbeiten: NumPy läuft mit fertigem C-Code darüber, ohne bei jeder Zahl nach dem Typ zu fragen. Hier ist die Mehrwertsteuer auf eine Million Preise:

PythonDaten: die Tabellen des Shops

array * 1.2 multipliziert alle Preise auf einmal. In der Sandbox ist das Array mehrmals schneller als die Schleife. Auf einem gewöhnlichen Computer ist der Abstand das Zehnfache und mehr. Die genaue Zeit hängt vom Rechner ab, deshalb sagt der Code nur, was schneller ist.

Genauer, für Neugierige

import numpy as np ist die Gewohnheit aller, wie pd bei pandas. pandas selbst baut auf NumPy auf: Eine Spalte der Tabelle hält ihre Zahlen in einem Array. Was du hier lernst, gilt also auch für Tabellen.

Die Idee

Ein Array aus der Tabelle: dtype

Die Preise stehen schon in products.csv. to_numpy() nimmt eine Spalte der Tabelle als Array. Jedes Array kennt den Typ seiner Zahlen: Das ist der dtype.

PythonDaten: die Tabellen des Shops

prices[:5] liefert die ersten fünf, wie bei einer Liste. Ein Array wird ohne Kommas ausgegeben: [1.2 1.9 0.8 2.5 1.6].

  • float64: Dezimalzahlen, je 8 Byte (64 Bit). So sind die Preise.
  • int64: ganze Zahlen, ebenfalls je 8 Byte. So ist der Bestand.

Sieh es dir an

Wenn sich Typen mischen

Für das ganze Array gibt es einen Typ. Was passiert, wenn du Verschiedenes hineinlegst?

PythonDaten: die Tabellen des Shops
  • Ganze und Dezimalzahlen: Alle werden zu Dezimalzahlen. 40 wird ohne Verlust zu 40.0, umgekehrt würde 1.5 abgeschnitten.
  • Ein einziges Stück Text: Alles wird zu Text. <U32 bedeutet einen String von bis zu 32 Zeichen. Die Zahlen sind keine Zahlen mehr.
1. Was liefert np.array([1.2, 1.9, '0.8']) * 2?
Genauer, für Neugierige

Schreibst du np.array([40, 25, 60]) in der Sandbox, ist der Typ int32, nicht int64. Die Standardart der ganzen Zahl richtet sich nach der Maschine, und die Sandbox ist 32-Bit. Auf deinem Computer ist es int64. Spalten, die mit pandas gelesen werden, sind auch hier int64. Für Mengen in einem Shop genügen beide Typen: int32 fasst Zahlen bis etwa 2,1 Milliarden.

Die Idee

Die Form: shape und ndim

Die Preise sind eine Reihe von Zahlen. Die Verkäufe pro Woche sind eine Tabelle: eine Zeile für jedes Produkt, eine Spalte für jede Woche. Ein Array kann beides sein.

  • shape ist die Form: wie viele Zahlen es in jeder Richtung gibt.
  • ndim ist die Zahl der Richtungen (Dimensionen).
PythonDaten: die Tabellen des Shops

Die Preise haben die Form (25,): eine Dimension mit 25 Zahlen. Das Komma steht da, weil eine Form immer ein Paar, ein Tripel und so weiter ist (ein Tupel), auch mit nur einer Zahl darin. Die Tabelle ist (3, 4): 3 Zeilen (die Produkte) und 4 Spalten (die Wochen). Zuerst die Zeilen, dann die Spalten, wie bei shape in pandas.

Probier es aus

Produkte und Wochen

qty enthält Stück pro Produkt (Zeilen) und pro Woche (Spalten). Lege die Zahl der Produkte in n_products und die Zahl der Wochen in n_weeks ab, aus der Form des Arrays genommen.

PythonDaten: die Tabellen des Shops

Die Idee

Fertige Arrays: arange und zeros

Oft kommt ein Array nicht aus einer Datei, du machst es selbst:

  • np.arange(start, ende) liefert aufeinanderfolgende Zahlen, wie range. Das Ende gehört nicht dazu.
  • np.zeros(shape) liefert ein Array aus Nullen. Das ist praktisch für eine Tabelle, die du füllen wirst. Die Form einer Tabelle ist ein Paar: np.zeros((Zeilen, Spalten)), mit doppelten Klammern.
PythonDaten: die Tabellen des Shops

Die Tage laufen von 1 bis 365, also ist das Ende 366. Die Tabelle für ein Jahr Verkäufe ist (25, 365): 9.125 Nullen vom Typ float64, bereit für die Zahlen.

Übung

Die Preisliste und das Jahr

Lege days mit den Tagen von 1 bis 365 an und sales: eine Tabelle aus Nullen mit einer Zeile für jedes der 25 Produkte und einer Spalte für jeden Tag.

PythonDaten: die Tabellen des Shops

Lege die Preise und den Bestand in eine Array-Tabelle mit zwei Zeilen: in der ersten die Preise, in der zweiten den Bestand. Lege den Typ der Tabelle in kind ab.

PythonDaten: die Tabellen des Shops

Test

Teste dich

1. Warum braucht ein Array mit einer Million Preisen weniger Speicher als eine Liste mit denselben Preisen?
2. Was ist der dtype von np.array([40, 25, 1.5])?
3. Die Verkäufe von 25 Produkten an 7 Tagen stehen in einem Array mit einer Zeile pro Produkt. Was ist sales.shape?
4. Welche Tage liefert np.arange(1, 8)?

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