NumPy

  • stellt mehrdimensionale Arrays (Vektoren, Matrizen,...) zur Verfügung
  • Auf Geschwindigkeit optimiert - schneller als Python-Listen
  • Arrays sind homogen (alle Elemente sind vom gleichen Typ).
  • Arithmetische Operationen und Funktionen arbeiten mit ganzen Arrays und werden elementweise interpretiert.
  • Arrays haben eine feste Größe. Man läßt sie nicht dynamisch wachsen wie Listen mit z.B. list.append(), sondern man legt sie mit der gewünschten Größe an, z.B. voller Nullen, und modifizert dann die Einträge.
  • Das folgende ist nur eine kleine Auswahl der Funktionalität, die NumPy bereitstellt. Noch umfangreicher ist die Bibliothek SciPy, welche auf NumPy aufbaut.
In [1]:
import numpy as np

NumPy-Arrays

Die array()-Funktion wandelt Listen, Tupeln, ... in numpy-arrays um:

In [2]:
np.array([2,3,4,5])
Out[2]:
array([2, 3, 4, 5])
In [3]:
a = np.array([[1,2,3], [4,5,6]], dtype=np.float64)
a
Out[3]:
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

Wichtige Eigenschaften eines Array-Objektes können abgefragt werden:

In [4]:
print(a.ndim)         # Zahl der Indizes
print(a.shape)        # Gestalt, z.B. (n,m) für nxm-Matrix
print(a.size)         # Gesamtzahl der Elemente
print(a.dtype)        # Typ der Elemente
print(a.itemsize)     # Größe eines Elements in Bytes 
print(type(a))        # Typ von a selbst
2
(2, 3)
6
float64
8
<class 'numpy.ndarray'>

Erzeugung von Arrays

numpy hat seine eigene range-Funktion namens arange(), die gleich einen numpy-Array erzeugt:

In [5]:
a = np.arange(1, 16)

print(a)
[ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]

Die .reshape()-Funktion kann einen array in einen mit anderer Dimension aber der gleichen Gesamtzahl von Elementen umwandeln. Dabei wird eine Matrix zeilenweise gefüllt.

In [6]:
b = a.reshape(3,5)

b
Out[6]:
array([[ 1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10],
       [11, 12, 13, 14, 15]])
In [7]:
# Hier mal ein Objekt mit 3 Indizes
c = np.arange(12).reshape(2, 3, 2)
c
Out[7]:
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]]])
In [8]:
print( 'c_000=', c[0,0,0],  '      c_010=', c[0,1,0],  '        c_101=', c[1,0,1]) 
c_000= 0       c_010= 2         c_101= 7
In [9]:
# array voller Nullen: Das Argument kann eine Zahl, eine Liste [4,3] oder ein Tupel (4,3) sein
np.zeros([3, 5])
Out[9]:
array([[ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
In [10]:
np.zeros(5)
Out[10]:
array([ 0.,  0.,  0.,  0.,  0.])
In [11]:
# array  mit gegebenem Füllwert
np.full([3, 5], 2.01)
Out[11]:
array([[ 2.01,  2.01,  2.01,  2.01,  2.01],
       [ 2.01,  2.01,  2.01,  2.01,  2.01],
       [ 2.01,  2.01,  2.01,  2.01,  2.01]])
In [12]:
# array aus einer mit list comprehension gebildeten Liste
x = np.array([[i-j for i in range(5)] for j in range(6)])
x
Out[12]:
array([[ 0,  1,  2,  3,  4],
       [-1,  0,  1,  2,  3],
       [-2, -1,  0,  1,  2],
       [-3, -2, -1,  0,  1],
       [-4, -3, -2, -1,  0],
       [-5, -4, -3, -2, -1]])
In [13]:
# diese Funktion erzeugt eine Nullmatrix derselben Gestalt wie das Argument
x0 = np.zeros_like(x)
x0
Out[13]:
array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]])
In [14]:
# Man kann aus einem numpy-array auch wieder eine Python-Liste machen:
x.tolist()
Out[14]:
[[0, 1, 2, 3, 4],
 [-1, 0, 1, 2, 3],
 [-2, -1, 0, 1, 2],
 [-3, -2, -1, 0, 1],
 [-4, -3, -2, -1, 0],
 [-5, -4, -3, -2, -1]]
In [15]:
# 3x3 Einheitsmatrix 
np.eye(3)
Out[15]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
In [16]:
# Diagonalmatrix aus 1-dim array 
np.diag([3,4,5])
Out[16]:
array([[3, 0, 0],
       [0, 4, 0],
       [0, 0, 5]])

Zum Plotten von Funktionen recht nützlich ist linspace. Im Unterschied zu arange gibt man nach Start- und Endwert nicht die Schrittweite sondern die gewünschte Anzahl von Werten im Intervall an. Der Endwert ist hier in der Folge enthalten.

In [17]:
np.linspace(1,2,10)
Out[17]:
array([ 1.        ,  1.11111111,  1.22222222,  1.33333333,  1.44444444,
        1.55555556,  1.66666667,  1.77777778,  1.88888889,  2.        ])

Indizes und Slicing

  • Indices: Elemente eines arrays kann man über Indizes ansprechen.
  • Slicing: Mit der n:m:k-Syntax kann man Teilfelder ansprechen. Dabei ist der Startindex n enthalten, der Endindex m ist nicht mehr enthalten und k ist eine mögliche Schrittweite.
In [18]:
x = np.arange(10)
x
Out[18]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [19]:
# Ein Element des Feldes
x[5]
Out[19]:
5
In [20]:
# Man kann den Elementen Werte zuweisen
x[5] = 77
x
Out[20]:
array([ 0,  1,  2,  3,  4, 77,  6,  7,  8,  9])
In [21]:
# Ein Teilfeld
x[2:7]
Out[21]:
array([ 2,  3,  4, 77,  6])
In [22]:
# Zuweisung eines Teilfeldes 
x[2:4] = [-5, -6]
x
Out[22]:
array([ 0,  1, -5, -6,  4, 77,  6,  7,  8,  9])
In [23]:
# Teilfeld mit Schrittweite
x[0:8:2]
Out[23]:
array([ 0, -5,  4,  6])
In [24]:
# fehlender Startindex
x[:5]
Out[24]:
array([ 0,  1, -5, -6,  4])
In [25]:
# fehlender Endindex
x[3:]
Out[25]:
array([-6,  4, 77,  6,  7,  8,  9])
In [26]:
# fehlender Start- und Endindex
x[:]
Out[26]:
array([ 0,  1, -5, -6,  4, 77,  6,  7,  8,  9])
In [27]:
# nochmal, mit Schrittweite
x[::-1]
Out[27]:
array([ 9,  8,  7,  6, 77,  4, -6, -5,  1,  0])
In [28]:
# Es gibt einen Unterschied zwischen x[7] und x[7:8]. Beides addressiert nur 
# ein Element, aber im 2. Fall erhält man trotzdem einen eindimensionalen Vektor
# 
print(x[7])
print(x[7:8])
7
[7]
In [29]:
# 2-dimensionale Beispiele
A = np.arange(16).reshape(4,4)
A
Out[29]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
In [30]:
B = A[0:2, 1:3]
B
Out[30]:
array([[1, 2],
       [5, 6]])
In [31]:
C = A[0:2, :]
C
Out[31]:
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])
In [32]:
D = A[::-1, ::2]
D
Out[32]:
array([[12, 14],
       [ 8, 10],
       [ 4,  6],
       [ 0,  2]])

Kopien und Views

Numpy ist für den Umgang mit sehr großen Matrizen etc. gedacht. Da ist das Erstellen einer Kopie bereits eine zeit- und vor allem speicherplatzintensive Operation. Numpy vermeidet daher das Erstellen von Kopien, solange es nicht unumgänglich oder explizit angewiesen ist.

Insbesondere erstellen einfache Zuweisungen

B = A

oder Slices

B = A[2:3, 4:6]

keine Kopie, sondern nur einen neuen view auf die existierenden Daten.

Modifizierungen von B modifizieren daher auch A:

In [33]:
B = A
C = A[2:4, 1:3]
print(B)
print('\n')
print(C)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]


[[ 9 10]
 [13 14]]
In [34]:
B[1,1] = -199
C[1,1] = 999
A
Out[34]:
array([[   0,    1,    2,    3],
       [   4, -199,    6,    7],
       [   8,    9,   10,   11],
       [  12,   13,  999,   15]])
In [35]:
# Explizites erstellen einer Kopie
B = A.copy()
C = A[2:4, 1:3].copy()
B[1,1] = 99999999
A
Out[35]:
array([[   0,    1,    2,    3],
       [   4, -199,    6,    7],
       [   8,    9,   10,   11],
       [  12,   13,  999,   15]])

Advanced Indexing

Ein Index kann auch eine Bedingung sein. Dann werden alle Elemente geliefert, die diese Bedingung erfüllen:

In [36]:
A[A>3]
Out[36]:
array([  4,   6,   7,   8,   9,  10,  11,  12,  13, 999,  15])
In [37]:
# Das ist nützlich, weil man so auf genau diese Elemente  einwirken kann:
A[A>5] = 5
A
Out[37]:
array([[   0,    1,    2,    3],
       [   4, -199,    5,    5],
       [   5,    5,    5,    5],
       [   5,    5,    5,    5]])
In [38]:
# Noch allgemeiner: der Vergleich A>5 liefert einen Array aus Wahrheitswerten:
A>0
Out[38]:
array([[False,  True,  True,  True],
       [ True, False,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]], dtype=bool)
In [39]:
# und einen solchen Array aus Wahrheitswertne kann man als Index verwenden:
B = np.array([1,2,3,4,5,6])
C = np.array([False, False, True, False, False, True])
B[C]
Out[39]:
array([3, 6])

Iterationen

Eine Matriz ist eine Sequenz von Zeilen.

Allgemeiner: for r in A iteriert über den am weitesten links stehenden Index.

In [40]:
for z in A:
    print ('Zeile= ', z)
Zeile=  [0 1 2 3]
Zeile=  [   4 -199    5    5]
Zeile=  [5 5 5 5]
Zeile=  [5 5 5 5]
In [41]:
for z in A:
    for x in z:
        print(x)
0
1
2
3
4
-199
5
5
5
5
5
5
5
5
5
5
In [42]:
# diese Funktion liefert einen Iterator mit Indizes:
for (i,j), x in np.ndenumerate(A):
    print(i, j, x)
0 0 0
0 1 1
0 2 2
0 3 3
1 0 4
1 1 -199
1 2 5
1 3 5
2 0 5
2 1 5
2 2 5
2 3 5
3 0 5
3 1 5
3 2 5
3 3 5

Vergleichsoperationen

Vergleichsoperationen liefern einen array aus boolschen Werten:

In [43]:
x = np.arange(4)
x == x[:]
Out[43]:
array([ True,  True,  True,  True], dtype=bool)

Mit den Operatoren all und any kann man einen array of bools reduzieren:

In [44]:
x == np.array([0,2,2,3])
Out[44]:
array([ True, False,  True,  True], dtype=bool)
In [45]:
# Alles wahr?
np.all(x==np.array([0,2,2,3]))
Out[45]:
False
In [46]:
# Irgendwas wahr?
np.any(x==np.array([0,2,2,3]))
Out[46]:
True

Arithmetische Operationen

  • Arithmetische Operationen werden elementweise ausgeführt.
  • Multiplikation ist also nicht die Matrixmultiplikation, Inversion 1/A nicht die Matrixinversion!
  • Ein Operand kann auch ein Skalar sein. ZEr wird dann behandelt wie ein Feld der erforderlichen Größe, das mit diesem Skalar gefüllt ist.
In [47]:
a=np.array([[ 1, 2],
            [ 3, 4] ])

b=np.array([[ 2, 2],
            [ 5, 5]])

a + b
Out[47]:
array([[3, 4],
       [8, 9]])
In [48]:
1/a
Out[48]:
array([[ 1.        ,  0.5       ],
       [ 0.33333333,  0.25      ]])
In [49]:
a * b
Out[49]:
array([[ 2,  4],
       [15, 20]])
In [50]:
a + 1
Out[50]:
array([[2, 3],
       [4, 5]])
In [51]:
a += 100
a
Out[51]:
array([[101, 102],
       [103, 104]])
In [52]:
# Auch alle üblichen mathematischen Funktionen werden von numpy so 
# bereitgestellt, dass sie elementweise auf ein Feld wirken 
np.log10(a)
Out[52]:
array([[ 2.00432137,  2.00860017],
       [ 2.01283722,  2.01703334]])

Produkte

Das innere Produkt von Vektoren und das übliche Matrixprodukt sowie die Wirkung von Matrizen auf Vektoren liefert die Funktion np.matmul().

Ab Python-Version 3.5 kann man für np.matmul(x,y) auch x @ y schreiben.

In [53]:
v = np.array([1,2,3])
w = np.array([3,4,5])
v @ w
Out[53]:
26
In [54]:
A = np.arange(6).reshape(2,3)
A
Out[54]:
array([[0, 1, 2],
       [3, 4, 5]])
In [55]:
#  3x2-Matrix mal 2x3-Matrix -> 3x3-Matrix 
#      ( .T  trasnponiert )

A.T @ A
Out[55]:
array([[ 9, 12, 15],
       [12, 17, 22],
       [15, 22, 29]])
In [56]:
# 2x3-Matrix mal 3x2-Matrix -> 2x2-Matrix

A @ A.T
Out[56]:
array([[ 5, 14],
       [14, 50]])
In [57]:
# 2x3-Matrix mal 3-vektor -> 2-Vektor

A @ v
Out[57]:
array([ 8, 26])
In [58]:
# 2-Vektor mal 2x3-Matrix -> 3-Vektor

[4,5] @ A
Out[58]:
array([15, 24, 33])
In [59]:
# Es gibt auch ein äußeres (Tensor-) Produkt:

np.outer(v, w)
Out[59]:
array([[ 3,  4,  5],
       [ 6,  8, 10],
       [ 9, 12, 15]])

Reduktionsfunktionen

  • Reduktionsfunktionen reduzieren die Dimension eines Arrays.
  • Beispiele: sum, prod, amin, amax
In [60]:
A = np.array([[1, 18, 7],
              [2, 13, 5]])
In [61]:
print( np.sum(A) )
print( np.amax(A) )
46
18
In [62]:
# Spaltensumme, größtes Element jeder Spalte
print( np.sum( A, axis=0) )
print('')
print( np.amax(A, axis=0) )
[ 3 31 12]

[ 2 18  7]
In [63]:
# Zeilensumme, größtes Element jeder Zeile
print( np.sum( A, axis=1) )
print('')
print( np.amax(A, axis=1) )
[26 20]

[18 13]

Lineare Algebra

Eigenwerte, Matrixinverses, Lösen linearer Gleichungssyteme und vieles mehr

In [64]:
from numpy.linalg import eigvals, inv, solve

A = 20*np.eye(5) + np.arange(10,35).reshape(5,5)
A           
Out[64]:
array([[ 30.,  11.,  12.,  13.,  14.],
       [ 15.,  36.,  17.,  18.,  19.],
       [ 20.,  21.,  42.,  23.,  24.],
       [ 25.,  26.,  27.,  48.,  29.],
       [ 30.,  31.,  32.,  33.,  54.]])
In [65]:
inv(A)
Out[65]:
array([[ 0.04893617, -0.00255319, -0.00404255, -0.00553191, -0.00702128],
       [-0.00425532,  0.04478723, -0.00617021, -0.00712766, -0.00808511],
       [-0.00744681, -0.00787234,  0.04170213, -0.0087234 , -0.00914894],
       [-0.0106383 , -0.01053191, -0.01042553,  0.03968085, -0.01021277],
       [-0.01382979, -0.01319149, -0.01255319, -0.01191489,  0.0387234 ]])
In [66]:
inv(A) @ A
Out[66]:
array([[  1.00000000e+00,   2.22044605e-16,   1.66533454e-16,
          2.49800181e-16,   1.66533454e-16],
       [  1.11022302e-16,   1.00000000e+00,   2.22044605e-16,
          1.66533454e-16,   5.55111512e-17],
       [  1.11022302e-16,  -5.55111512e-17,   1.00000000e+00,
          0.00000000e+00,  -1.11022302e-16],
       [ -1.66533454e-16,  -5.55111512e-17,  -1.66533454e-16,
          1.00000000e+00,  -1.11022302e-16],
       [  0.00000000e+00,  -2.22044605e-16,  -2.22044605e-16,
         -2.22044605e-16,   1.00000000e+00]])
In [67]:
eigvals(A)
Out[67]:
array([ 132.22761571,   17.77238429,   20.        ,   20.        ,   20.        ])
In [68]:
x = [-2, 3 , 4, 8, 10]
solve(A,x)
Out[68]:
array([-0.23617021, -0.01968085, -0.00319149,  0.16329787,  0.22978723])