Skip to content
Snippets Groups Projects
Commit d6532c26 authored by Konrad Völkel's avatar Konrad Völkel
Browse files

a list of all numpy commands in here

parent ea809eaa
No related branches found
No related tags found
No related merge requests found
Pipeline #155409 passed
%% 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.49929771 0.59160776 0.77432696 0.80597013 0.18907085 0.11844771
0.70945366 0.87172152 0.8522308 0.42780462]
<class 'numpy.ndarray'>
array([0.49929771, 0.59160776, 0.77432696, 0.80597013, 0.18907085,
0.11844771, 0.70945366, 0.87172152, 0.8522308 , 0.42780462])
%% 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.529
expected= 4.5
std² = 8.260759
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
4.177020760005689
5.418045660000644
%% 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:
In Numpy kann man noch etwas feiner slicen.
Die Allgemeine Syntax ist `[start:stop:step, ..]` wobei man mit dem Komma getrennt über die Achsen geht. Ein zweidimensionaler Array hat zwei Achsen, wobei Achse $0$ von oben nach unten und Achse $1$ von links nach rechts indiziert ist. Während "step" auch mit Python-Listen funktioniert, ist das indizieren mit mehreren Achsen eine Spezialität von Numpy.
%% Cell type:code id: tags:
``` python
matrix = list(range(1,10))
npmatrix = np.array(matrix)
print("not in shape:", npmatrix)
npmatrix.shape = (3,3)
print("in much better shape:\n"+ str(npmatrix))
print("Zeilenvektor Zeile 0:", npmatrix[0])
print("Spaltenvektor Spalte 0:", npmatrix[:,0])
print("Spalten 1-2:\n"+ str(npmatrix[:,1:]))
print("Alle Zeilen, Schrittweite 2\n"+ str(npmatrix[0::2]))
```
%% Output
not in shape: [1 2 3 4 5 6 7 8 9]
in much better shape:
[[1 2 3]
[4 5 6]
[7 8 9]]
Zeilenvektor Zeile 0: [1 2 3]
Spaltenvektor Spalte 0: [1 4 7]
Spalten 1-2:
[[2 3]
[5 6]
[8 9]]
Alle Zeilen, Schrittweite 2
[[1 2 3]
[7 8 9]]
%% Cell type:markdown id: tags:
In klassischem Python-Code würden wir auf einen Eintrag einer Matrix zugreifen mit `matrix[x][y]`, und das funktioniert so auch in Numpy. Allerdings wird dabei zunächst ein weiteres Listenobjekt `matrix[x]` erzeugt (beim Slicing auch zusätzlicher Speicher dafür belegt) und dann darauf `[y]` aufgerufen. Es ist daher grundsätzlich effizienter, direkt Numpy's `[x,y]` zu verwenden.
Numpy erzeugt bewusst keine Kopien beim Slicing, sondern nur eine andere Sichtweise auf den gleichen Speicherbereich (daher auch das oben beobachtete Verhalten bei `[:]`). Ob zwei Arrays auf den gleichen Speicherbereich verweisen, lässt sich mit `np.may_share_memory` prüfen. Dabei bedeutet ein positives Ergebnis keineswegs, dass die Arrays voneinander abhängig sind - so verweisen die erste und die zweite Spalte einer Matrix auch auf den gleichen Speicherbereich, nämlich die ganze Matrix. Wenn man nun einen der beiden Vektoren ändert, bleibt der andere unverändert - die ganze Matrix aber ändert sich mit.
%% Cell type:code id: tags:
``` python
npmatrix = np.array(list(range(1,10))).reshape(3,3)
candidates = (npmatrix[0::2], npmatrix[1])
print("May share memory (but actually don't):",
candidates, np.may_share_memory(*candidates))
print(type([0,0,0])) # vor der Zuweisung
npmatrix[1] = [0,0,0]
print(type(npmatrix[1])) # nach der Zuweisung
print(npmatrix) # die ganze Matrix ist wie verändert
```
%% Output
May share memory (but actually don't): (array([[1, 2, 3],
[7, 8, 9]]), array([4, 5, 6])) True
<class 'list'>
<class 'numpy.ndarray'>
[[1 2 3]
[0 0 0]
[7 8 9]]
%% Cell type:markdown id: tags:
Ausführliche Informationen zum Slicing und Indizieren liefert [die Dokumentation](https://numpy.org/doc/stable/user/basics.indexing.html).
%% Cell type:markdown id: tags:
## Broadcasting
Während für Python-Listen der Additionsoperator die Listenkonkatenation ist, und damit die Multiplikation von Listen mit Skalaren definiert ist, ist die Multiplikation von zwei Listen undefiniert.
Für Numpy-Arrays sind deutlich mehr arithmetische Operationen verfügbar:
%% Cell type:code id: tags:
``` python
E = np.identity(3, int)
print(E, "= E")
A = np.ones((3,3), int)
A[0] = [0,0,0]
print(A, "= A")
print(E + A, "= E + A")
print(E * A, "= EA")
print((E+A)**2, "= (E+A)(E+A)")
```
%% Output
[[1 0 0]
[0 1 0]
[0 0 1]] = E
[[0 0 0]
[1 1 1]
[1 1 1]] = A
[[1 0 0]
[1 2 1]
[1 1 2]] = E + A
[[0 0 0]
[0 1 0]
[0 0 1]] = EA
[[1 0 0]
[1 4 1]
[1 1 4]] = (E+A)(E+A)
%% Cell type:markdown id: tags:
Wenn man das aufmerksam nachverfolgt, stellt man fest, dass diese Rechnungen keine Matrizenmultiplikationen sind,
sondern schlicht elementweise erfolgt sind - so sind die Operationen auf Arrays definiert.
Besonders tückisch ist dies:
%% Cell type:code id: tags:
``` python
v = np.ones(3, int)
print(v, "= v")
print(A*v, "= A*v (aber nicht die Matrixmultiplikation)")
```
%% Output
[1 1 1] = v
[[0 0 0]
[1 1 1]
[1 1 1]] = A*v (aber nicht die Matrixmultiplikation)
%% Cell type:markdown id: tags:
Um explizit mit Matrizenkalkül zu rechnen, hat man früher den Numpy-Datentyp `matrix` verwendet, aber dieser ist als `deprecated` (veraltet) markiert und wird in zukünftigen Numpy-Versionen abgeschafft. Heutzutage nutzt man die Methode `np.matmul` oder den Infix-Operator `@`.
%% Cell type:code id: tags:
``` python
print(np.matmul(A,v))
print(A@v)
```
%% Output
[0 3 3]
[0 3 3]
%% Cell type:markdown id: tags:
Für viele Probleme ist es sehr hilfreich, nicht den Matrizenkalkül zu verwenden, sondern elementweise arithmetische Operationen auszuführen.
*Broadcasting* ist ein Mechanismus, der diesen elementweisen Kalkül etwas praktischer macht.
So ist die Operation ($n \times n$-array) * ($n$-Vektor) automatisch interpretiert, indem der $n$-Vektor $n$-fach kopiert wird, sodass die Multiplikation einer jeden Zeile des linken Arrays mit dem Vektor (elementweise) durchgeführt wird.
Dazu ist es wirklich hilfreich, einmal [die Dokumentation](https://numpy.org/doc/stable/user/basics.broadcasting.html) zu überfliegen.
%% Cell type:markdown id: tags:
## ufuncs
`ufunc` steht für "universal function" und bezeichnet eine Methode, die auf Numpy Arrays vektorisiert laufen kann.
Mit jeder `ufunc` lässt sich z.B. `reduce` durchführen, wo entlang einer Achse des Arrays die `ufunc` auf die resultierenden kleineren Arrays angewandt wird.
Um selbst eine `ufunc` zu schreiben, muss man [C-Code programmieren](https://numpy.org/doc/stable/user/c-info.ufunc-tutorial.html) oder aber [einen Wrapper um eine Python-Methode legen](https://numpy.org/doc/stable/reference/generated/numpy.frompyfunc.html).
Es lohnt sich, einen kurzen Blick auf alle bereits definierten `ufunc`s zu werfen:
[Available ufuncs](https://numpy.org/doc/stable/reference/ufuncs.html#available-ufuncs)
%% Cell type:markdown id: tags:
## Was muss man kennen?
Numpy und andere Bibliotheken des Scientific Computing in Python sind groß, man kann kaum alle Funktionen kennen.
In diesem Skript liefert die händisch bereinigte Ausgabe von `cat *.ipynb | egrep -o "np\.([^(]+)" | sort | uniq` diese vollständige Liste. Diese muss nicht auswendig gelernt werden, gibt aber eine klare Abschätzung nach oben. Mehr muss man definitiv auf keinen Fall können! Außerdem sollte man bedenken, dass diese Liste für das gesamte Skript gilt, und wir vieles davon noch nicht kennen gelernt haben.
%% Cell type:markdown id: tags:
np.abs
np.all
np.allclose
np.arange
np.argmin
np.array
np.atleast_1d
np.byte
np.column_stack
np.concatenate
np.copy
np.corrcoef
np.cos
np.cov
np.diff
np.dot
np.dstack
np.equal
np.exp
np.eye
np.float64
np.frombuffer
np.full
np.genfromtxt
np.heaviside
np.hstack
np.identity
np.isclose
np.isnan
np.linalg.eig
np.linalg.inv
np.linalg.norm
np.linalg.svd
np.linspace
np.log
np.matmul
np.max
np.maximum
np.may_share_memory
np.mean
np.median
np.meshgrid
np.mgrid
np.min
np.minimum
np.nansum
np.ones
np.ones_like
np.outer
np.pi
np.power
np.ptp
np.random.choice
np.random.multivariate_normal
np.random.permutation
np.random.rand
np.random.randint
np.random.randn
np.random.random
np.random.seed
np.random.uniform
np.round
np.sin
np.size
np.sort
np.sqrt
np.square
np.stack
np.std
np.sum
np.uint8
np.unique
np.var
np.vstack
np.zeros
np.zeros_like
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment