Motivation:
Die Dokumentation ist hier zu finden.
%%javascript
$.getScript('https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js')
import numpy as np
Hier sind einige erste Beispiele zur Nutzung dieser Arrays.
x_arr = np.array([1, 2, 3, 4, 5])
x_list = [1, 2, 3, 4, 5]
Arrays verhalten sich nicht wie Listen. Mathematischen Operationen werden komponentenweise auf die Elemente des Arrays angewendet.
2 * x_arr
array([ 2, 4, 6, 8, 10])
2 * x_list
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Fast alle mathematischen Operatoren aus Python funktionieren analog mit NumPy Arrays.
x_arr**2
array([ 1, 4, 9, 16, 25])
x_arr**x_arr
array([ 1, 4, 27, 256, 3125])
Achtung: Bei besonderen Funktionen (cos, sin, exp, etc.) werden die NumPy Methoden benötigt, z.B. np.cos()
!
np.cos(x_arr)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362, 0.28366219])
import math
# This doesn't work
#math.cos(x_arr)
Bei großen Datensätzen ist die Laufzeit relevant und NumPy ist einige Größenordnungen schneller:
%%timeit
x_pure = [42] * 10000
x_pure2 = [x**2 for x in x_pure]
2.58 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit
x = np.full(10000, 42)
x2 = x**2
15.7 µs ± 487 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Selbstgeschriebene Funktionen, die nur für eine Zahl geschrieben wurden, funktionieren oft ohne Änderung mit NumPy Arrays.
def poly(y):
return y + 2 * y**2 - y**3
poly(np.pi)
-8.125475224531307
poly(x_arr)
array([ 2, 2, -6, -28, -70])
Das erlaubt es einem unter anderem, sehr leicht physikalische Formeln auf seine Datenpunkte anzuwenden.
Arrays können beliebige Dimension haben:
# two-dimensional array
y = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# element-wise summation, like matrix summation
y + y
array([[ 2, 4, 6], [ 8, 10, 12], [14, 16, 18]])
Das erlaubt es z.B. eine ganze Tabelle von gleichen Datentypen als Array abzuspeichern.
Mit Arrays sind auch Matrixoperationen möglich:
A = np.array([[1,1],
[0,1]])
B = np.array([[2,0],
[3,4]])
# element-wise product
element_wise_product = A * B
print("Elementweise Multiplikation:\n", element_wise_product)
# matrix product
matrix_product = A.T @ B
print("Matrix Multiplikation:\n", matrix_product)
# vector scalar product
one_dim_vectors = np.array([1, 2, 3]).T @ np.array([4, 5, 6])
print("Skalarprodukt von Vektoren:\n", one_dim_vectors)
Elementweise Multiplikation: [[2 0] [0 4]] Matrix Multiplikation: [[2 0] [5 4]] Skalarprodukt von Vektoren: 32
In NumPy wird die Dimension auch Achse (axis) genannt und mit der ndim
-Funktion abgerufen.
Die shape
-Funktion gibt an wie viele Tupel pro Dimension vorhanden sind.
Die Gesamtzahl der Elemente in einem Array können mit der size
-Funktion abgefragt werden.
Der Datentyp eines Arrays muss innerhalb des Arrays der gleiche sein. Um den Datentyp eines Arrays abzufragen gibt es die dtype
-Funktion.
a = np.array([1.5, 3.0, 4.2])
b = np.array([[1, 2], [3, 4]])
print(f'Array a: \n\t a.ndim {a.ndim} \n\t a.shape {a.shape} \n\t a.size {a.size} \n\t a.dtype {a.dtype}')
print(f'Array b: \n\t b.ndim {b.ndim} \n\t b.shape {b.shape} \n\t b.size {b.size} \n\t b.dtype {b.dtype}')
Array a: a.ndim 1 a.shape (3,) a.size 3 a.dtype float64 Array b: b.ndim 2 b.shape (2, 2) b.size 4 b.dtype int64
Es gibt viele nützliche Funktionen, die bei der Erstellung von Arrays helfen. Zum Verständnis der einzugebenden Argumente ist die NumPy Dokumentation zu empfehlen.
np.zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
np.ones((5, 2))
array([[1., 1.], [1., 1.], [1., 1.], [1., 1.], [1., 1.]])
np.linspace(0, 1, 11)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])
# like range() for arrays:
np.arange(0, 10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.logspace(-4, 5, 10)
array([1.e-04, 1.e-03, 1.e-02, 1.e-01, 1.e+00, 1.e+01, 1.e+02, 1.e+03, 1.e+04, 1.e+05])
NumPy erlaubt einem sehr bequem bestimmte Elemente aus einem Array auszuwählen und z.B. nur auf diesen Elementen Operationen auszuführen.
x = np.arange(0, 10)
print(x)
# like lists:
x[4]
[0 1 2 3 4 5 6 7 8 9]
4
# all elements with indices ≥1 and <4:
x[1:4]
array([1, 2, 3])
# negative indices count from the end
x[-1], x[-2]
(9, 8)
# combination:
x[3:-2]
array([3, 4, 5, 6, 7])
# step size
x[::2]
array([0, 2, 4, 6, 8])
# trick for reversal: negative step
x[::-1]
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
y = np.array([x, x + 10, x + 20, x + 30])
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, 24, 25, 26, 27, 28, 29], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]])
# comma between indices
y[3, 2:-1]
array([32, 33, 34, 35, 36, 37, 38])
# only one index ⇒ one-dimensional array
y[2]
array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
# other axis: (`:` alone means the whole axis)
y[:, 3]
array([ 3, 13, 23, 33])
# inspecting the number of elements per axis:
y.shape
(4, 10)
Ausgewählten Elementen kann man auch direkt einen Wert zuweisen.
y[:, 3] = 0
y
array([[ 0, 1, 2, 0, 4, 5, 6, 7, 8, 9], [10, 11, 12, 0, 14, 15, 16, 17, 18, 19], [20, 21, 22, 0, 24, 25, 26, 27, 28, 29], [30, 31, 32, 0, 34, 35, 36, 37, 38, 39]])
Man kann Indexing auch gleichzeitig auf der linken und rechten Seite benutzen.
y[:,0] = x[3:7]
y
array([[ 3, 1, 2, 0, 4, 5, 6, 7, 8, 9], [ 4, 11, 12, 0, 14, 15, 16, 17, 18, 19], [ 5, 21, 22, 0, 24, 25, 26, 27, 28, 29], [ 6, 31, 32, 0, 34, 35, 36, 37, 38, 39]])
Transponieren des Arrays kehrt die Reihenfolge der Indizes um.
y
array([[ 3, 1, 2, 0, 4, 5, 6, 7, 8, 9], [ 4, 11, 12, 0, 14, 15, 16, 17, 18, 19], [ 5, 21, 22, 0, 24, 25, 26, 27, 28, 29], [ 6, 31, 32, 0, 34, 35, 36, 37, 38, 39]])
y.T
array([[ 3, 4, 5, 6], [ 1, 11, 21, 31], [ 2, 12, 22, 32], [ 0, 0, 0, 0], [ 4, 14, 24, 34], [ 5, 15, 25, 35], [ 6, 16, 26, 36], [ 7, 17, 27, 37], [ 8, 18, 28, 38], [ 9, 19, 29, 39]])
print(f"y \tShape: {y.shape} \ny.T \tShape: {y.T.shape}")
y Shape: (4, 10) y.T Shape: (10, 4)
Oft will man Elemente auswählen, die eine oder mehrere Bedingungen erfüllen. Hierzu wird eine Maske (Array aus True/False-Werten) mit der gleichen Dimension erstellt. Die Maske kann in eckigen Klammern übergeben werden.
a = np.linspace(0, 2, 11)
print(a)
# create a mask with the condition: element >= 1
mask = a >= 1
print(mask)
print(a[mask])
# do it in one step:
print(a[a>=1])
[0. 0.2 0.4 0.6 0.8 1. 1.2 1.4 1.6 1.8 2. ] [False False False False False True True True True True True] [1. 1.2 1.4 1.6 1.8 2. ] [1. 1.2 1.4 1.6 1.8 2. ]
Viele Rechenoperationen reduzieren ein Array auf einen einzelnen Wert.
y
array([[ 3, 1, 2, 0, 4, 5, 6, 7, 8, 9], [ 4, 11, 12, 0, 14, 15, 16, 17, 18, 19], [ 5, 21, 22, 0, 24, 25, 26, 27, 28, 29], [ 6, 31, 32, 0, 34, 35, 36, 37, 38, 39]])
So z.B. die Summe aller Elemente oder die Multiplikation.
np.sum(y)
666
np.prod(y)
0
Bei vielen solchen Methoden kann die Dimension mit angegeben werden.
np.sum(y, axis=1) # sum of each row
array([ 45, 126, 207, 288])
np.prod(y, axis=0) # multiplication of each column
array([ 360, 7161, 16896, 0, 45696, 65625, 89856, 118881, 153216, 193401])
Auch Mittelwert und Standardabweichung der Einträge kann einfach bestimmt werden.
np.mean(y)
16.65
np.std(y)
12.590770429167549
Oft wird im Praktikum aber nach der Unsicherheit des Mittelwerts gesucht.
np.std(x, ddof=1) / np.sqrt(len(x))
0.9574271077563381
Dafür braucht man auch den Schätzer der Standardabweichung.
np.std(x, ddof=1)
3.0276503540974917
Um die Differenzen zwischen benachbarten Elementen herauszufinden kann die Funktion np.diff()
genutzt werden.
z = x**2
print('z ', z)
np.diff(z)
z [ 0 1 4 9 16 25 36 49 64 81]
array([ 1, 3, 5, 7, 9, 11, 13, 15, 17])
Um Datenpunkte aus einer Textdatei einzulesen wird die Funktion np.genfromtxt()
genutzt.
Sie gibt den Inhalt einer Textdatei als Array zurück.
Die Funktion die Datenpunkte in eine Datei abspeichert ist np.savetxt()
.
n = np.arange(11)
x = np.linspace(0, 1, 11)
np.savetxt('test.txt', [n, x])
Um den Inhalt der erstellten Datei zu öffnen kann man analog zu Aufgabe 1-python/6-readwrite
die open
-Funktion benutzen.
with open('test.txt', 'r') as f:
print(f.read())
0.000000000000000000e+00 1.000000000000000000e+00 2.000000000000000000e+00 3.000000000000000000e+00 4.000000000000000000e+00 5.000000000000000000e+00 6.000000000000000000e+00 7.000000000000000000e+00 8.000000000000000000e+00 9.000000000000000000e+00 1.000000000000000000e+01 0.000000000000000000e+00 1.000000000000000056e-01 2.000000000000000111e-01 3.000000000000000444e-01 4.000000000000000222e-01 5.000000000000000000e-01 6.000000000000000888e-01 7.000000000000000666e-01 8.000000000000000444e-01 9.000000000000000222e-01 1.000000000000000000e+00
Für schönere Formatierung der Daten kann man auch np.column_stack()
benutzen.
data = np.array([n, x])
np.savetxt('test.txt', np.column_stack([n, x]))
with open('test.txt', 'r') as f:
print(f.read())
0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000056e-01 2.000000000000000000e+00 2.000000000000000111e-01 3.000000000000000000e+00 3.000000000000000444e-01 4.000000000000000000e+00 4.000000000000000222e-01 5.000000000000000000e+00 5.000000000000000000e-01 6.000000000000000000e+00 6.000000000000000888e-01 7.000000000000000000e+00 7.000000000000000666e-01 8.000000000000000000e+00 8.000000000000000444e-01 9.000000000000000000e+00 9.000000000000000222e-01 1.000000000000000000e+01 1.000000000000000000e+00
Am besten sollte aber auch immer erklären werden, was abspeichert wird:
n = np.arange(11)
x = np.linspace(0, 1, 11)
# header schreibt eine Kommentarzeile in die erste Zeile der Datei
np.savetxt('test.txt', np.column_stack([n, x]), header="n x")
with open('test.txt', 'r') as f:
print(f.read())
# n x 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 1.000000000000000056e-01 2.000000000000000000e+00 2.000000000000000111e-01 3.000000000000000000e+00 3.000000000000000444e-01 4.000000000000000000e+00 4.000000000000000222e-01 5.000000000000000000e+00 5.000000000000000000e-01 6.000000000000000000e+00 6.000000000000000888e-01 7.000000000000000000e+00 7.000000000000000666e-01 8.000000000000000000e+00 8.000000000000000444e-01 9.000000000000000000e+00 9.000000000000000222e-01 1.000000000000000000e+01 1.000000000000000000e+00
a, b = np.genfromtxt('test.txt', unpack=True)
a, b
(array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]), array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ]))
Um die Datentypen beim Speichern zu erhalten, muss das Keyword Argument fmt
wie im Folgenden Beispiel angegeben werden.
np.savetxt(
'test.txt',
np.column_stack([n, x]),
fmt=['%d', '%.4f'], # first column integer, second 4 digits float
delimiter=',',
header='n,x',
)
data = np.genfromtxt(
'test.txt',
dtype=None, # guess data types
delimiter=',',
names=True,
)
Das resultierende Array data
ist besonders, da es ein sogenanntes structured array
ist.
Dies ist ein Numpy Array in dem quasi mehrere Arrays in einem abgespeichert sind. Die einzelnen Arrays werden in der Dokumentation fields
genannt und haben jeweils einen zugeordneten Namen und einen Datentyp.
data
array([( 0, 0. ), ( 1, 0.1), ( 2, 0.2), ( 3, 0.3), ( 4, 0.4), ( 5, 0.5), ( 6, 0.6), ( 7, 0.7), ( 8, 0.8), ( 9, 0.9), (10, 1. )], dtype=[('n', '<i8'), ('x', '<f8')])
data['n'], data.shape, data.dtype
(array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), (11,), dtype([('n', '<i8'), ('x', '<f8')]))