Commit 47d38b2c authored by Konrad Völkel's avatar Konrad Völkel
Browse files

Upload New File

parent d8efdedd
Pipeline #90530 canceled with stages
in 1 minute and 53 seconds
%% Cell type:markdown id: tags:
# Numpy
%% Cell type:markdown id: tags:
In diesem kurzen Abschnitt lernen wir die wesentlichen Ideen bei Numpy kennen.
Eine längere und bessere (und englischere) Fassung davon sind die [Numpy fundamentals](https://numpy.org/devdocs/user/basics.html) im Numpy User Guide, die Sie am besten im Anschluss überfliegen sollten.
Ein richtiges (und sehr gutes) Lehrbuch für alle, die mit Python vertraut sind (und als das betrachten wir uns zu diesem Punkt der Vorlesung) ist [Nicolas Rougier's From Python to Numpy](https://www.labri.fr/perso/nrougier/from-python-to-numpy/), wo für uns zunächst Kapitel 3 relevant ist.
Kurz lässt sich sagen, dass mit Numpy [Array-orientierte Programmierung](https://en.wikipedia.org/wiki/Array_programming) (auch: *Vektorisierung*) in Python möglich wird.
## Arrays
<!--
Zur Probability mit Numpy ist das hier (auf deutsch) gut geeignet:
https://www.python-kurs.eu/python_numpy_wahrscheinlichkeit.php
-->
Numeric Python (Numpy) wird meist als `np` abgekürzt importiert:
%% Cell type:code id: tags:
``` python
import numpy as np
sample = np.random.random(10)
print(sample)
print(type(sample))
sample
```
%% Output
[0.71853665 0.11280385 0.89256017 0.04671813 0.01798126 0.67993356
0.10616332 0.45492242 0.38529958 0.5457223 ]
<class 'numpy.ndarray'>
array([0.71853665, 0.11280385, 0.89256017, 0.04671813, 0.01798126,
0.67993356, 0.10616332, 0.45492242, 0.38529958, 0.5457223 ])
%% Cell type:markdown id: tags:
Viele nützliche Hilfsfunktionen sind in Numpy enthalten, die wiederum Numpy-eigene Datenstrukturen (den Array) verarbeiten.
%% Cell type:code id: tags:
``` python
sample = np.random.randint(low=0, high=10, size=5000)
print("mean =", np.mean(sample), "\nexpected=", 9/2)
print("std² =", np.std(sample)**2, "\nvariance=", 99/12)
```
%% Output
mean = 4.465
expected= 4.5
std² = 8.012775
variance= 8.25
%% Cell type:markdown id: tags:
Es gibt auch eine `arange`-Methode, sie erzeugt aber keine Range-Objekte in Numpy:
%% Cell type:code id: tags:
``` python
myRange = np.arange(1, 7)
print(myRange, type(myRange), myRange.dtype)
myRange
```
%% Output
[1 2 3 4 5 6] <class 'numpy.ndarray'> int64
array([1, 2, 3, 4, 5, 6])
%% Cell type:markdown id: tags:
Der Numpy-Array `ndarray` trägt im Gegensatz zur Python-Liste einen festen Datentyp, den alle Elemente gemeinsam haben. Das können alle Numpy-datatypes (dtypes) sein, z.B. `double` (kompatibel mit dem Python-`float`) oder `float32` (auf den meisten Plattformen kompatibel mit dem C-`float`) oder `long` (kompatibel mit dem Python-`int`).
%% Cell type:code id: tags:
``` python
import timeit
ordinary_list = [1,2,3,4,5,6,5,4,3,2,1]*10
def sort_array(dtype):
a = np.array(ordinary_list, dtype)
a.sort()
print(timeit.timeit(lambda : sort_array(np.byte)))
print(timeit.timeit(lambda : sort_array(np.float64)))
```
%% Output
6.738261688995408
8.536722582997754
%% Cell type:markdown id: tags:
Dadurch, dass der Datentyp präzise bekannt ist, kann Numpy darauf optimierte Algorithmen, direkt in C implementiert, verwenden.
Was es noch für `dtype`s gibt und wie sie eingesetzt werden, können wir der Dokumentation entnehmen:
* ["Structured Arrays"](https://numpy.org/doc/stable/user/basics.rec.html)
* ["Data types" in den "Numpy fundamentals"](https://numpy.org/doc/stable/user/basics.types.html)
* ["Scalars"](https://numpy.org/doc/stable/reference/arrays.scalars.html)
* ["Data type objects (dtype)"](https://numpy.org/doc/stable/reference/arrays.dtypes.html)
* ["numpy.dtype"](https://numpy.org/doc/stable/reference/generated/numpy.dtype.html)
Es gibt noch mehr Wege, Arrays zu erzeugen, außer mit `arange` oder durch konvertieren einer Python-Sequenz. Z.B. lässt sich mit `ones` ein Array gefüllt mit $1$ und mit `zeros` ein Array gefüllt mit $0$ erzeugen.
%% Cell type:markdown id: tags:
Der Grund dafür, dass das Array den Namen `ndarray` trägt, ist, dass es für \`\`$n$-dimensional array'' steht.
Wenn man in Python eine Matrix speichern möchte, würde man das als Liste der Zeilenvektoren (oder der Spaltenvektoren) tun, etwa
%% Cell type:code id: tags:
``` python
matrix = [[1,0,0], [0,1,0], [0,0,1]] # Einheitsmatrix
print(matrix, type(matrix))
quarkix = matrix # eine Kopie
print(quarkix[0][0])
quarkix[0][0] = 0 # wir ändern den oberen linken Eintrag
print(matrix[0][0])
```
%% Output
[[1, 0, 0], [0, 1, 0], [0, 0, 1]] <class 'list'>
1
0
%% Cell type:markdown id: tags:
Daran sehen wir ein Problem: Python behandelt unsere Matrix wie eine Liste (so haben wir es ja auch hingeschrieben), also wird beim kopieren der Liste der Inhalt (die Zeilenvektoren) nicht mitkopiert (sondern nur die Pointer darauf).
%% Cell type:code id: tags:
``` python
npmatrix = np.identity(3, int) # Einheitsmatrix
print(npmatrix, type(npmatrix))
npquarkix = npmatrix[:]
print(npquarkix[0][0])
npquarkix[0][0] = 2 # wir ändern den oberen linken Eintrag
print(npmatrix[0][0])
# Mit einer echten Kopie wäre das nicht passiert:
real_copy = npmatrix.copy()
real_copy[0][0] = 1
assert npmatrix[0][0] != 1
```
%% Output
[[1 0 0]
[0 1 0]
[0 0 1]] <class 'numpy.ndarray'>
1
2
%% Cell type:markdown id: tags:
Es ist wichtig, festzustellen, dass der Numpy-Array das gleiche Verhalten an den Tag legt wie unsere Python-Liste-von-Listen. Wir können gleich damit indizieren und slicen, und es gibt das gleiche Problem beim Kopieren über die Slicing-Syntax.
Der `shape`-Parameter sagt uns, welche Form unser Numpy-Array hat. Dabei handelt es sich um ein $d$-Tupel, wobei $d$ die Dimension ist. Eine Matrix ist $2$-dimensional, ein Vektor $1$-dimensional und ein Skalar $0$-dimensional.
%% Cell type:code id: tags:
``` python
print(npmatrix.shape)
print(npmatrix[0], npmatrix[0].shape)
print(npmatrix[0][0], npmatrix[0][0].shape)
```
%% Output
(3, 3)
[2 0 0] (3,)
2 ()
%% Cell type:markdown id: tags: