#

- Meist hat man nach in einer Auswertung Datenpunkte, die verarbeitet werden müssen
- Numpy ist eine Python-Bibliothek, die den Umgang mit Datenpunkten enorm vereinfacht

In [1]:

    import numpy as np

- Grunddatentyp von Numpy: das Array
- Kann man sich als effizientere Liste vorstellen
- Idee von Numpy: Man kann ein Array ähnlich wie eine Zahl verwenden. Operationen werden dann auf allen Elementen ausgeführt
- Am besten versteht man das mit einigen Beispielen:

In [2]:

    # Listen können mittels np.array in Arrays konvertiert werden
    x = np.array([1, 2, 3, 4, 5])

In [3]:

    2 * x
    # x + x

Out[3]:

    array([ 2,  4,  6,  8, 10])

In [4]:

    x**2

Out[4]:

    array([ 1,  4,  9, 16, 25])

In [5]:

    x**x

Out[5]:

    array([   1,    4,   27,  256, 3125])

In [6]:

    np.cos(x)

Out[6]:

    array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362,  0.28366219])

Achtung: Man braucht das `cos` aus numpy!

In [7]:

    import math
    #math.cos(x)

Funktionen, die nur für eine Zahl geschrieben wurden, funktionieren oft ohne Änderung mit Arrays!

In [8]:

    def poly(y):
        return y + 2 * y**2 - y**3

    poly(x)

Out[8]:

    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:

In [9]:

    # zweidimensionales Array
    y = np.array([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]])

    y + y

Out[9]:

    array([[ 2,  4,  6],
           [ 8, 10, 12],
           [14, 16, 18]])

Das erlaubt es z.B. eine ganze Tabelle als ein Array abzuspeichern.

Es gibt viele nützliche Funktionen, die bei der Erstellung von Arrays helfen:

In [10]:

    np.zeros(10)

Out[10]:

    array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

In [11]:

    np.ones(5)

Out[11]:

    array([ 1.,  1.,  1.,  1.,  1.])

In [12]:

    np.linspace(0, 1, 11)

Out[12]:

    array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])

In [13]:

    # wie range() für arrays:
    np.arange(0, 10)

Out[13]:

    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

## Numpy Indexing

Numpy erlaubt einem sehr bequem bestimmte Elemente aus einem Array auszuwählen

In [14]:

    x = np.arange(0, 10)

    # kennen wir schon:
    x[4]

Out[14]:

    4

In [15]:

    # alle Elemente mit Index 1 bis 3:
    x[1:4]

Out[15]:

    array([1, 2, 3])

In [16]:

    # negative Indizes zählen vom Ende des Arrays:
    x[-1], x[-2]

Out[16]:

    (9, 8)

In [17]:

    # Kombination aus beiden:
    x[3:-2]

Out[17]:

    array([3, 4, 5, 6, 7])

In [18]:

    # man kann eine Schrittgröße angeben
    x[::2]

Out[18]:

    array([0, 2, 4, 6, 8])

In [19]:

    # Trick zum Umkehren: negative Schrittgröße
    x[::-1]

Out[19]:

    array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])

In [20]:

    y = np.array([x, x + 10, x + 20, x + 30])
    y

Out[20]:

    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]])

In [21]:

    # mehrere Indizes mit Kommas getrennt:
    y[3, 2:-1]

Out[21]:

    array([32, 33, 34, 35, 36, 37, 38])

In [22]:

    # Oder man gibt nur einen Index an ⇒ eindimensionales Array
    y[2]

Out[22]:

    array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29])

# Masken

Oft will man Elemente auswählen, die eine bestimmte Bedingung erfüllen.

Hierzu erstellt man zuerst eine Maske (Arrays aus True/False-Werten).

Diese kann man in eckigen Klammern übergeben.

In [23]:

    a = np.linspace(0, 2, 11)
    b = a**2

    a[a>=1]
    #a[b==a]
    #a[b>a]

Out[23]:

    array([ 1. ,  1.2,  1.4,  1.6,  1.8,  2. ])

## Reduzieren von Arrays

Viele Rechenoperationen reduzieren ein Array auf einen einzelnen Wert

In [24]:

    x

Out[24]:

    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [25]:

    np.sum(x)

Out[25]:

    45

In [26]:

    np.prod(x)

Out[26]:

    0

In [27]:

    np.mean(x)

Out[27]:

    4.5

In [28]:

    # Standardabweichung:
    np.std(x)

Out[28]:

    2.8722813232690143

In [29]:

    # Fehler des Mittelwerts (geht auch einfacher):
    np.std(x) / np.sqrt(len(x))

Out[29]:

    0.90829510622924747

In [30]:

    # Differenzen zwischen benachbarten Elementen
    np.diff(x**2)

Out[30]:

    array([ 1,  3,  5,  7,  9, 11, 13, 15, 17])

## Input / Output

Einlesen aus Textdateien: `genfromtxt`

Sie gibt den Inhalt einer Textdatei als Array zurück.

Das Gegenstück ist `savetxt`.

In [31]:

    n = np.arange(11)
    x = np.linspace(0, 1, 11)

    np.savetxt('test.txt', [n, x])
    print(open('test.txt', 'r').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

Man sollte aber immer erklären, was man da abspeichert:

In [32]:

    n = np.arange(11)
    x = np.linspace(0, 1, 11)

    # header schreibt eine Kommentarzeile in die erste Zeile der Datei
    np.savetxt('test.txt', [n, x], header="n \t x")
    print(open('test.txt', 'r').read())

    # n 	 x
    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

Einlesen der Werte mit `genfromtxt` :

In [33]:

    a, b = np.genfromtxt('test.txt')

    print(a)
    print(b)

    [  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]
    [ 0.   0.1  0.2  0.3  0.4  0.5  0.6  0.7  0.8  0.9  1. ]

Oft schreibt man seine Datenpunkte aber spaltenweise:

    1  1
    2  4
    3  9
    4 16
    5 20

Dann kann man `unpack=True` verwenden, um die Daten zu transponieren. Um die Daten entsprechend transponiert zu speichern, verwenden wir

In [34]:

    np.savetxt('test_T.txt', np.array([n, x]).T)
    print(open('test_T.txt', 'r').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

# SciPy

SciPy

- Baut auf Numpy auf
- Kann numerisch integrieren, DGLs lösen, optimieren, minimieren, …
- Enthält auch physikalische Konstanten und wichtige mathematische Funktionen

In [35]:

    # Fehler des Mittelwerts:
    from scipy.stats import sem # standard error of mean
    sem(x)

Out[35]:

    0.10000000000000002

In [36]:

    # Physikalische Konstanten:
    import scipy.constants as const
    const.epsilon_0

Out[36]:

    8.854187817620389e-12

In [37]:

    # Temperaturen konvertieren:
    const.C2K(100), const.K2C(100)

Out[37]:

    (373.14999999999998, -173.14999999999998)

In [38]:

    # Winkel konvertieren:
    np.degrees(np.pi), np.radians(90)

Out[38]:

    (180.0, 1.5707963267948966)

In [39]:

    # noch mehr Konstanten (mit Einheit und Fehler)!
    list(const.physical_constants.items())[:10]

Out[39]:

    [('electron-triton mass ratio', (0.00018192000653, '', 1.7e-13)),
     ('proton mass in u', (1.007276466812, 'u', 9e-11)),
     ('muon mass in u', (0.1134289267, 'u', 2.9e-09)),
     ('neutron-proton mass difference in u', (0.00138844919, '', 4.5e-10)),
     ('shielded proton magn. moment to Bohr magneton ratio',
      (0.001520993132, '', 1.6e-11)),
     ('nuclear magneton in eV/T', (3.1524512605e-08, 'eV T^-1', 2.2e-17)),
     ('Faraday constant for conventional electric current',
      (96485.3321, 'C_90 mol^-1', 0.0043)),
     ('neutron-proton mass difference energy equivalent',
      (2.0721465e-13, '', 6.8e-20)),
     ('shielded helion magn. moment', (-1.074553024e-26, 'J T^-1', 9.3e-34)),
     ('tau molar mass', (0.00190749, 'kg mol^-1', 1.7e-07))]

### Achtung

Wenn solche Konstanten genutzt werden, muss das korrekt mitgeteilt, also zitiert werden. Darauf gehen wir nächste Woche im LaTeX-Workshop ein :-)

(Quelle hier: python-scipy)

In [40]:

    # physical_constants ist ein dict in dem hinter keys
    # Wert, Einheit und Fehler hinterlegt sind

    const.physical_constants["proton mass"]

Out[40]:

    (1.672621777e-27, 'kg', 7.4e-35)

## Fitten

Oft möchte man eine Funktion, zum Beispiel eine Erwartung aus der Theorie, an die gemessenen Werte anpassen. Dies nennt man Fit.

In [41]:

    # Beliebige Funktionen fitten
    %matplotlib inline
    from scipy.optimize import curve_fit
    import matplotlib.pyplot as plt
    plt.rcParams['figure.figsize'] = (10, 8)
    plt.rcParams['font.size'] = 16

    x, y = np.genfromtxt('example_data.txt', unpack=True)

    def f(x, a, b):
        return a * x + b

    params, covariance = curve_fit(f, x, y)
    # covariance ist die Kovarianzmatrix

    errors = np.sqrt(np.diag(covariance))

    print('a =', params[0], '±', errors[0])
    print('b =', params[1], '±', errors[1])

    x_plot = np.linspace(0, 10)

    plt.plot(x, y, 'rx', label="example data")
    plt.plot(x_plot, f(x_plot, *params), 'b-', label='linearer Fit')
    plt.legend(loc="best")

    a = 4.97742635551 ± 0.0214390034102
    b = 0.070084504726 ± 0.123809150457

Out[41]:

    <matplotlib.legend.Legend at 0x7f84ef425b00>