Datenvisualisierung mit

Matplotlib

Matplotlib

Als erstes: IPython interaktiv machen:

In [27]:
%matplotlib inline
# bei euch: %matplotlib (nur in iPython)

Um mit Matplotlib arbeiten zu können, muss die Bibliothek erst einmal importiert werden. Damit wir nicht so viel tippen müssen geben wir ihr einen kürzeren Namen:

In [28]:
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (10, 8)
plt.rcParams['font.size'] = 16

Außerdem brauchen wir ein paar Funktion aus numpy, die euch schon bekannt sind

In [29]:
import numpy as np

Ein einfaches Beispiel: \(f(x)=x^2\)

In [30]:
x = np.linspace(0, 1) # gibt 50 Zahlen in gleichmäßigem Abstand von 0-1
plt.plot(x, x**2)
# Falls nicht interaktiv: 
# plt.show()
Out[30]:
[<matplotlib.lines.Line2D at 0x7fbb4c5e0a90>]

Anderes Beispiel: \(\sin(t)\) mit verschiedenen Stilen. Vorsicht, die Funktionen und \(\pi\) sind Bestandteil von numpy

In [31]:
t = np.linspace(0, 2 * np.pi)
plt.plot(t, np.sin(t))
#plt.plot(t, np.sin(t), 'r--')
#plt.plot(t, np.sin(t), 'go')
Out[31]:
[<matplotlib.lines.Line2D at 0x7fbb4c5aafd0>]

Tabelle mit allen Farben und Styles: matplotlib.axes.Axes.plot

Neue Grenzen mit xlim(a, b) und ylim(a, b)

In [32]:
plt.plot(t, np.sin(t))
plt.xlim(0, 2 * np.pi)
plt.ylim(-1.2, 1.2)
Out[32]:
(-1.2, 1.2)

Es fehlt noch etwas...

XKCD comic on why you should label your axes.

XKCD comic on why you should label your axes.

In [33]:
with plt.xkcd():
    plt.plot(t, np.sin(t))
    plt.xlabel('t')
    plt.ylabel('sin(t)')
    plt.ylim(-1.1, 1.1)
    plt.xlim(0, 2*np.pi)

Achsen-Beschriftungen können mit LaTeX-Code erstellt werden → LaTeX-Kurs in der nächsten Woche.

In [34]:
plt.plot(t, np.sin(t))
plt.xlabel('$t$')
plt.ylabel(r'$\sin(t)$')
Out[34]:
<matplotlib.text.Text at 0x7fbb4c604390>

Legenden für Objekte die ein label tragen

In [35]:
plt.plot(t, np.sin(t), label=r'$\sin(t)$')
plt.legend()
#plt.legend(loc='lower left')
#plt.legend(loc='best')
Out[35]:
<matplotlib.legend.Legend at 0x7fbb4c4b99e8>

Mit grid() wird ein Gitter erstellt:

In [36]:
plt.plot(t, np.sin(t))
plt.grid()

Laden von Daten

In [37]:
x, y = np.genfromtxt('example_data.txt', unpack=True) # unpack=True falls Daten spaltenweise angeordnet sind
plt.plot(x, y, 'k.')

t = np.linspace(0, 10)
plt.plot(t, 5 * t, 'r-')
Out[37]:
[<matplotlib.lines.Line2D at 0x7fbb4c414390>]

Auslagern in ein Skript

Speichert den folgenden Code in eine Textdatei plot.py ab.

Öffnet ein Terminal und startet das Programm:

python plot.py
In [38]:
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 1)
plt.plot(x, x**2, 'b-')
plt.savefig('plot.pdf')

Mit savefig speichert man die Abbildung.

In diesem Fall sollte die Datei plot.pdf erstellt worden sein.

Es gibt viele Ausgabeformate: pdf, png, svg, LaTeX

Komplexere Abbildungen

Natürlich kann man mehrere Linien in einen Plot packen:

In [39]:
x = np.linspace(0, 1)

plt.plot(x, x**2, label='$x^2$')
plt.plot(x, x**4)
plt.plot(x, x**6, 'o', label='$x^6$')

plt.legend(loc='best')
Out[39]:
<matplotlib.legend.Legend at 0x7fbb4c587438>

Es werden nur die Plots in der Legende angezeigt, die ein Label haben.

Man kann auch mehrere Plots in ein Bild packen:

In [40]:
x = np.linspace(0, 2 * np.pi)

# Anzahl Zeile, Anzahl Spalten, Nummer des Plots
plt.subplot(2, 1, 1)
plt.plot(x, x**2)
plt.xlim(0, 2 * np.pi)

plt.subplot(2, 1, 2)
plt.plot(x, np.sin(x))
plt.xlim(0, 2 * np.pi)
Out[40]:
(0, 6.283185307179586)

Dies führt manchmal zu Spacing-Problemen und Teilen die sich überscheneiden, Lösung: plt.tight_layout()

In [41]:
x = np.linspace(0, 2 * np.pi)

# Anzahl Zeile, Anzahl Spalten, Nummer des Plots
plt.subplot(2, 1, 1)
plt.plot(x, x**2)
plt.xlim(0, 2 * np.pi)
plt.title("$f(x)=x^2$")

plt.subplot(2, 1, 2)
plt.plot(x, np.sin(x))
plt.xlim(0, 2 * np.pi)
plt.title("$f(x)=\sin(x)$")

plt.tight_layout()

Plot im Plot:

In [42]:
plt.plot(x, x**2)

# Koordinaten relativ zum Plot (0,0) links unten (1,1) rechts oben
plt.axes([0.2, 0.45, 0.3, 0.3])
plt.plot(x, x**3)
Out[42]:
[<matplotlib.lines.Line2D at 0x7fbb4c143ba8>]

Plots mit Fehlerbalken

Sehr häufig werden im Praktikum Plots mit Fehlerbalken benötigt:

In [43]:
x = np.linspace(0, 2 * np.pi, 10)
errX = 0.4 * np.random.randn(10)
errY = 0.4 * np.random.randn(10)

plt.errorbar(x + errX, x + errY, xerr=0.4, yerr=0.4, fmt='o')
Out[43]:
<Container object of 3 artists>

Achsen-Skalierung

Logarithmische (und auch viele andere) Skalierung der Achsen ist auch möglich:

In [44]:
x = np.linspace(0, 10)

plt.plot(x, np.exp(-x))
plt.yscale('log')
#plt.xscale('log')

Polar-Plot

Manchmal braucht man einen Polarplot:

In [45]:
r = np.linspace(0, 10, 1000)
#r = np.linspace(0, 10, 50)
theta = 2 * np.pi * r

plt.polar(theta, r)
Out[45]:
[<matplotlib.lines.Line2D at 0x7fbb4c09f940>]

Ticks

Man kann sehr viele Sachen mit Ticks machen…

In [46]:
x = np.linspace(0, 2 * np.pi)

plt.plot(x, np.sin(x))
plt.xlim(0, 2 * np.pi)
# erste Liste: Tick-Positionen, zweite Liste: Tick-Beschriftung
plt.xticks([0, np.pi / 2, np.pi, 3 * np.pi / 2, 2 * np.pi],
           [r"$0$", r"$\frac{\tau}{4}$", r"$\frac{\tau}{2}$", r"$\frac{3\tau}{4}$", r"$\tau$"])
plt.title(r"$\tau$ FTW!")
Out[46]:
<matplotlib.text.Text at 0x7fbb4c123b38>
In [47]:
months = ['January',
          'February',
          'March',
          'April',
          'May',
          'June',
          'July',
          'August',
          'September',
          'October',
          'November',
          'December']

plt.plot(np.arange(12), np.random.rand(12))
plt.xticks(np.arange(12), months, rotation=45, rotation_mode='anchor', ha='right', va='top')
plt.xlim(0, 11)
Out[47]:
(0, 11)

Histogramme

Sehr häufig braucht man Histogramme.

In [48]:
# Zufallsdaten generieren:
x = np.random.normal(0, 1, 1000)
plt.hist(x)
Out[48]:
(array([   7.,   33.,   76.,  199.,  235.,  228.,  146.,   48.,   26.,    2.]),
 array([-3.07771392, -2.44088811, -1.8040623 , -1.16723649, -0.53041068,
         0.10641513,  0.74324094,  1.38006675,  2.01689256,  2.65371837,
         3.29054417]),
 <a list of 10 Patch objects>)

Anzahl der Bins und den Bereich festlegen:

In [49]:
plt.hist(x, bins=20, range=[-3,3])
Out[49]:
(array([   2.,    3.,    9.,   24.,   26.,   40.,   92.,   92.,   99.,
         122.,  113.,  105.,   94.,   65.,   52.,   22.,   16.,   15.,
           5.,    1.]),
 array([-3. , -2.7, -2.4, -2.1, -1.8, -1.5, -1.2, -0.9, -0.6, -0.3,  0. ,
         0.3,  0.6,  0.9,  1.2,  1.5,  1.8,  2.1,  2.4,  2.7,  3. ]),
 <a list of 20 Patch objects>)

Möchte man mehre Datensets unterschiedlicher Größe vergleichen, muss man normieren und das gleiche "Binning" wählen.

In [50]:
y = np.random.normal(0, 1, 100)
plt.hist(x, bins=20, range=[-3,3], normed=True)
plt.hist(y, bins=20, range=[-3,3], normed=True)
Out[50]:
(array([ 0.        ,  0.        ,  0.        ,  0.06666667,  0.1       ,
         0.16666667,  0.33333333,  0.2       ,  0.26666667,  0.36666667,
         0.43333333,  0.33333333,  0.3       ,  0.3       ,  0.16666667,
         0.06666667,  0.16666667,  0.        ,  0.03333333,  0.03333333]),
 array([-3. , -2.7, -2.4, -2.1, -1.8, -1.5, -1.2, -0.9, -0.6, -0.3,  0. ,
         0.3,  0.6,  0.9,  1.2,  1.5,  1.8,  2.1,  2.4,  2.7,  3. ]),
 <a list of 20 Patch objects>)

geht noch schöner:

In [51]:
y = np.random.normal(0, 1, 100)
plt.hist(x, bins=20, range=[-3,3], normed=True, histtype='step')
plt.hist(y, bins=20, range=[-3,3], normed=True, histtype='step')
Out[51]:
(array([ 0.        ,  0.        ,  0.        ,  0.1       ,  0.06666667,
         0.13333333,  0.23333333,  0.23333333,  0.53333333,  0.33333333,
         0.53333333,  0.26666667,  0.36666667,  0.06666667,  0.26666667,
         0.13333333,  0.03333333,  0.        ,  0.03333333,  0.        ]),
 array([-3. , -2.7, -2.4, -2.1, -1.8, -1.5, -1.2, -0.9, -0.6, -0.3,  0. ,
         0.3,  0.6,  0.9,  1.2,  1.5,  1.8,  2.1,  2.4,  2.7,  3. ]),
 <a list of 1 Patch objects>)

Objektorientiertes Plotten

Bis jetzt haben wir die schnelle Variante mit der pyplot-Syntax benutzt. Wenn man viele Plots anlegt, ist der objekt-orientierte Ansatz für matplotlib besser geeignet.

In [52]:
import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(0, 2*np.pi, 1000)

fig, (ax1, ax2) = plt.subplots(2, 1)

ax1.plot(t, np.sin(t), 'r-')
ax1.set_title(r"$f(t)=\sin(t)$")
ax1.set_xlabel("$t$")
ax1.set_xlim(0, 2 * np.pi)
ax1.set_ylim(-1.1, 1.1)

ax2.plot(t, np.cos(t), 'b-')
ax2.set_title(r"$f(t)=\cos(t)$")
ax2.set_xlabel("$t$")
ax2.set_xlim(0, 2 * np.pi)
ax2.set_ylim(-1.1, 1.1)

fig.tight_layout()