Einführung in NumPy

Visualisierung einer Matrix mittels Hinton NumPy und SciPy sind OpenSource Erweiterungs-Module für Python, die schnelle vorkompilierte Funktionen für mathematische und numerische Routinen bereitstellen.
NumPy (Numeric Python) dient zur Bearbeitung von großen Arrays und Matrizen mit numerischen Daten.

SciPy (Scientific Python) erweitert die Funktionalität von NumPy mit nützlichen Funktionen, wie Minimierung, Regression, Fourier-Transformation und vielen anderen.

Sowohl NumPy als auch SciPy sind nicht standardmäßig installiert. Bei der Instaltion sollte man beachten, dass man NumPy vor SciPy installieren muss. SciPy findet man unter

http://www.scipy.org/download

NumPy und SciPy stellen eine kostenlose und freie Alternative zu MATLAB.Auch wenn MATLAB über eine große Anzahl von zusätzlichen Tools verfügt, besteht der große Vorteil von NumPy uund SciPy darin, dass sie auf Python aufbauen und Python eine modernere Programmiersprache als MATLAB ist.

(Anmerkung zum Bild oben rechts: Das Diagramm in dem Bild entspricht der grafischen Veranschaulichung einer Matrix mit 14 Zeilen und 20 Spalten. Es handelt sich dabei um ein sogenanntes "Hinton Diagramm". Die Größe eines Quadrates entspricht der Größe eines Wertes in der Matrix. Die Farbe bestimmt, ob ein Wert positiv oder negativ ist. Also zum Beispiel entspricht die Farbe Rot den negativen Werten und die Farbe Grün in unserem Beispiel den positiven Werten.)

NumPy basiert auf dem Python-Modul Numarray, welches seinerseits eine komplette Neuüberarbeitung eines noch älteren Modules Numeric ist.

NumPy bietet einen großen Zeitvorteil gegenüber Standard-Python.
Betrachten wir die beiden folgenden Funktionen:

import time
import numpy as np

def trad_version():
    t1 = time.time()
    X = range(10000000)
    Y = range(10000000)
    Z = []
    for i in range(len(X)):
        Z.append(X[i] + Y[i])
    return time.time() - t1

def numpy_version():
    t1 = time.time()
    X = np.arange(10000000)
    Y = np.arange(10000000)
    Z = X + Y
    return time.time() - t1

t_trad = trad_version()
t_numpy = numpy_version()
print("Benötigte Zeit für traditonelle Lösung:")
print(t_trad)

print("Benötigte Zeit für Lösung mit NumPy:")
print(t_numpy)

print("Lösung mit NumPy " + str(t_trad * 100 / t_numpy) + " % schneller")
Ein Aufruf mit Python3 liefert folgendes für NumPy beeindruckende Ergebnis:
$ python3 numpy_comparison.py 
Benötigte Zeit für traditonelle Lösung:
6.556201934814453
Benötigte Zeit für Lösung mit NumPy:
0.11806702613830566
Lösung mit NumPy 5552.949158840005 % schneller

Arrays in NumPy

Arrays sind der zentrale Bestandteil von NumPy.
Der wesentliche Unterschied zu Listen in Python besteht darin, dass die Elemente eines Arrays alle vom gleichen Typ sein müssen. Normalerweise float oder int. Arrays sind bedeutend effizienter und schneller als Listen in Python. Prinzipiell können Arrays wie Listen angesehen werden, allerdings mit dem Unterschied: Selbstverständlich sind Arrays in NumPy nicht auf eine Dimension beschränkt. Sie können beliebig dimensional sein.
Erzeugung eines Arrays aus einer Liste:
>>> import numpy as np
>>> x = np.array([42,47,11], int)
>>> x
array([42, 47, 11])
>>> 

Die Methode array wandelt eine Sequenz von Sequenzen - im Beispiel Tupel - in ein zwei-dimensionales Array:
>>> x = np.array( ((11,12,13), (21,22,23), (31,32,33)) )
>>> print x
[[11 12 13]
 [21 22 23]
 [31 32 33]]
>>> 
Entsprechend wandelt sie Folgen von Folgen von Folgen in ein 3-dimensionales Array:
>>> x = np.array( ( ((111,112), (121,122) ), ((211,212),(221,222)) ) )
>>> print(x)
[[[111 112]
  [121 122]]

 [[211 212]
  [221 222]]]
>>> print(x[1][0][1])
212
>>> print(x[1][1][1])
222
>>> 

Das Attribut ndim gibt Auskunft über die Anzahl der Dimensionen eines Arrays:
>>> x = np.array( ((11,12,13), (21,22,23) ))
>>> x.ndim
2
Die Methode shape() liefert ein Tupel mit den Array-Dimensionen zurück:
>>> x = np.array( ((7,3,0), (7,11,9), (15,3,7),(12,4,8)))
>>> x.ndim
2
>>> x.shape
(4, 3)
>>> x
array([[ 7,  3,  0],
       [ 7, 11,  9],
       [15,  3,  7],
       [12,  4,  8]])
>>> 
Ein Besonderheit von NumPy besteht darin, dass man die Verteilung der Elemente auf die Dimensionen auch im Nachhinein wieder verändern kann. Dazu steht der shape-Parameter zur Verfügung. Wie die Achsen eines n-dimensionalen Vektorraumes auf den shape-Parameter verteilt sind, zeigen die beiden Abbildungen.

Die Achsen (axis) eines Arrays beschreiben die Reihenfolge der Indizierungen (axis=0 steht für den ersten Index, axis=1 für den zweiten und so weiter.

Axis eines Arrays

Die "shape" eines Arrays bezeichnet ein Tupel mit der Anzahl der Elemente pro Achse (Dimension) im Fall der obersten Abbildung gilt shape = (6,3), d.h. wir haben 6 Zeilen und 3 Spalten.
Die Bearbeitung von Arrays erfolgt ähnlich wie bei Listen:
>>> x = np.array([42,47,11])
>>> x[:2]
array([42, 47])
>>> x = np.array([42,47,11])
>>> x[0]
42
>>> x[:2]
array([42, 47])
>>> x[1] = 49
>>> x
array([42, 49, 11])
>>>

Axis eines Arrays


Im folgenden zeigen wir ein Beispiel eines zweidimensionalen Arrays:
>>> x = np.array([[0.314, 0.5,17],[1.4142,0.67, 7]], float)
>>> x[0]
array([  0.314,   0.5  ,  17.   ])
>>> x[1]
array([ 1.4142,  0.67  ,  7.    ])
>>> x[0,2]
17.0
>>>

Auch bei 2-dimensionalen Arrays kann man Slices einsetzen:
>>> x[:,0]
array([ 0.314 ,  1.4142])
>>> x[:,1]
array([ 0.5 ,  0.67])
>>> x[:,2]
array([ 17.,   7.])
>>> 

Wir wir bereits erfahren haben, besteht ein NumPy-Array im Gegensatz zu normalen Listen in Python nur aus Daten des gleichen numerischen Types, also Integer oder Float. Mit dem Attribut dtype kann man sich den Typ der Werte eines Arrays ermitteln.
>>> x.dtype
dtype('float64')
float64 ist ein numerischer Typ von NumPy, der die Zahlen in doppelter Präzision abspeichert, ähnlich zu dem Typ float in Python.

Arrays umdimensionieren

Es gibt zwei Methoden, um ein mehrdimensionales Array flach zu machen:
>>> x = np.array([[[ 0,  1],
...         [ 2,  3],
...         [ 4,  5],
...         [ 6,  7]],
... 
...        [[ 8,  9],
...         [10, 11],
...         [12, 13],
...         [14, 15]],
... 
...        [[16, 17],
...         [18, 19],
...         [20, 21],
...         [22, 23]]])
>>> x.flatten()
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
>>> x
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5],
        [ 6,  7]],

       [[ 8,  9],
        [10, 11],
        [12, 13],
        [14, 15]],

       [[16, 17],
        [18, 19],
        [20, 21],
        [22, 23]]])
>>> x.ravel()
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
Die Reihenfolge, in der ravel() die Elemente eines mehrdimensionalen Arrays abarbeitet erfolgt im C-Stil, d.h. der am weitesten rechts stehende Index wird am schnellsten abgearbeitet. In anderen Worten: Die Zeilen werden langsamer als die Spalten abgearbeitet, und es gilt a[0,1] folgt [0,0].

Arrays lassen sich in NumPy beliebig umdimensionieren. Dazu wird die Methode reshape(t) zur Verfügung gestellt. Das Argument von reshape ist ein Tupel, dessen Dimenion die Dimension des veränderten Arrays darstellt, während die Werte dieses Tupels, die Verteilung der Elemente über die Dimensionen wiederspiegelt. Die Methode reshape() liefert ein Array mit geänderten Dimensionen zurück ohne das die Daten des als Parameter übergebenen Arrays verändert werden.
Beispiel:
>>> x = np.array(range(24))
>>> y = x.reshape((3,4,2))
>>> y
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5],
        [ 6,  7]],

       [[ 8,  9],
        [10, 11],
        [12, 13],
        [14, 15]],

       [[16, 17],
        [18, 19],
        [20, 21],
        [22, 23]]])
>>> x
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
>>> 
Im folgenden Beispiel zeigen wir, wie wir mit Slices geschickt den Rand eines Arrays wegschneiden können, also die erste Zeile, die letzte Zeile, die erste Spalte und die letzte Spalte werden entfernt:
>>> x = np.array(range(100))
>>> y = x.reshape(10,10)
>>> y[1:-1,1:-1]

Arrays konkatenieren

Im folgenden Beispiel konkatenieren (verknüpfen) wir drei eindimensionale Arrays. Die Elemente des zweiten Arrays werden an das erste Array angehängt. Anschließend werden die Elemente des dritten Arrays daran angehängt:
>>> x = np.array([11,22])
>>> y = np.array([18,7,6])
>>> z = np.array([1,3,5])
>>> np.concatenate((x,y,z))
array([11, 22, 18,  7,  6,  1,  3,  5])
Wenn wir mehrdimensionale Arrays konkatenieren, wird Default-mäßig nach der ersten Achse (axis = 0) verknüpft. Man kann allerdings auch den optionalen Parameter axis explitzit setzen:
>>> x = np.array(range(24))
>>> x = x.reshape((3,4,2))
>>> y = np.array(range(100,124))
>>> y = y.reshape((3,4,2))
>>> z = np.concatenate((x,y))
>>> z
array([[[  0,   1],
        [  2,   3],
        [  4,   5],
        [  6,   7]],

       [[  8,   9],
        [ 10,  11],
        [ 12,  13],
        [ 14,  15]],

       [[ 16,  17],
        [ 18,  19],
        [ 20,  21],
        [ 22,  23]],

       [[100, 101],
        [102, 103],
        [104, 105],
        [106, 107]],

       [[108, 109],
        [110, 111],
        [112, 113],
        [114, 115]],

       [[116, 117],
        [118, 119],
        [120, 121],
        [122, 123]]])
>>> 
>>> z = np.concatenate((x,y),axis = 1)
>>> z
array([[[  0,   1],
        [  2,   3],
        [  4,   5],
        [  6,   7],
        [100, 101],
        [102, 103],
        [104, 105],
        [106, 107]],

       [[  8,   9],
        [ 10,  11],
        [ 12,  13],
        [ 14,  15],
        [108, 109],
        [110, 111],
        [112, 113],
        [114, 115]],

       [[ 16,  17],
        [ 18,  19],
        [ 20,  21],
        [ 22,  23],
        [116, 117],
        [118, 119],
        [120, 121],
        [122, 123]]])
>>> z = np.concatenate((x,y),axis = 2)
>>> z
array([[[  0,   1, 100, 101],
        [  2,   3, 102, 103],
        [  4,   5, 104, 105],
        [  6,   7, 106, 107]],

       [[  8,   9, 108, 109],
        [ 10,  11, 110, 111],
        [ 12,  13, 112, 113],
        [ 14,  15, 114, 115]],

       [[ 16,  17, 116, 117],
        [ 18,  19, 118, 119],
        [ 20,  21, 120, 121],
        [ 22,  23, 122, 123]]])
>>> 

Neue Dimensionen zu einem Array hinzufügen

Neue Dimensionen können einem Array mittels Slicing und np.newaxis hinzugefügt werden. Wir demonstrieren diese Technik im folgenden Beispiele:
>>> x = np.array([2,5,18,14,4])
>>> x
array([ 2,  5, 18, 14,  4])
>>> y = x[:, np.newaxis]
>>> y
array([[ 2],
       [ 5],
       [18],
       [14],
       [ 4]])

Arrays mit Einsen und Nullen füllen

Es gibt zwei Möglichkeiten Arrays mit Nullen oder Einsen zu initialisieren. The Methode ones(t) benötigt ein Tupel t mit der "shape" des zu schaffenden Arrays und füllt das Array entsprechend mit Einsen. There are two ways of initializing Arrays with Zeros or Ones. The method ones(t) takes a tuple t with the shape of the array and fills the array accordingly with ones. Bei Default wird das Array mit Einsen vom Typ float gefüllt. Wenn man Einsen vom Typ Integer braucht, kann man den optionalen Paramter dtype auf int setzen:
>>> np.ones((2,3))
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
>>> np.ones((3,4),dtype=int)
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])
Was wir über die Methode ones() gesagt haben, gilt analog für die Methode zeros(), wie wir im folgenden Beispiel sehen:
>>> np.zeros((2,4))
array([[ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.]])
Es gibt noch eine weitere interessante Möglichkeit eine Matrix mit Einsen oder Nullen zu füllen, wenn das Array die "shape" eines anderen existierenden Arrays "a" haben soll. Für diesen Zweck stellt Numpy the Methoden ones_like(a) und zeros_like(a) zur Verfügung.
>>> x = np.array([2,5,18,14,4])
>>> np.ones_like(x)
array([1, 1, 1, 1, 1])
>>> np.zeros_like(x)
array([0, 0, 0, 0, 0])