Home | Lehre | Videos | Texte | Vorträge | Software | Person | Impressum, Datenschutzerklärung | Blog RSS

Stand: 2025-04-02
weitgehend formuliert von ChatGPT 4, verbessert von Claude 3.7 Sonnet, redigiert/korrigiert von Jörn Loviscach

Datenvisualisierung mit Matplotlib

Was ist Matplotlib?

Matplotlib ist eine umfangreiche Bibliothek für die Erstellung von statischen, animierten und interaktiven Visualisierungen in Python. Sie bietet eine Vielzahl von Plot-Typen und -Stilen und ist aufgrund ihrer Effizienz und Flexibilität besonders beliebt in der wissenschaftlichen und technischen Datenvisualisierung. Matplotlib ermöglicht, hochwertige Graphen, Charts und Figuren in verschiedenen Formaten zu erzeugen, und ist kompatibel mit einer breiten Palette von Betriebssystemen und Grafik-Backends.

Die Kernfunktionalität von Matplotlib basiert auf dem Erstellen von Diagrammen (Figure) und Koordinatensystemen (Axis); letzteren werden die Daten zugeordnet. Matplotlib benutzt hauptsächlich eine objektorientierte Programmierschnittstelle, für schnelle und einfache Visualisierungen aber auch eine prozedurale Schnittstelle, bekannt als Pyplot, die an die Funktionsweise von MATLAB® angelehnt ist.

Ein großer Vorteil von Matplotlib ist seine Erweiterbarkeit und Anpassbarkeit. Das Aussehen von Graphen lässt sich weitgehend anpassen, indem man Titel, Achsenbeschriftungen, Legenden und andere Stilelemente modifiziert, siehe die Beispiele auf den Cheatsheets und Handouts. Darüber hinaus gibt es zusätzliche Pakete wie Seaborn, das auf Matplotlib aufbaut, um weiterführende statistische Visualisierungen zu erstellen. Diese Flexibilität macht Matplotlib zu einem unverzichtbaren Werkzeug für die Datenanalyse und -visualisierung in Python.

Natürlich muss Matplotlib vor der Arbeit damit in das virtuelle Environment installiert werden.

Beispiel für ein 2D-Diagramm

Es soll ein xy-Datensatz als Streudiagramm geplottet werden, dazu die Regressionsgerade samt deren Gleichung.

Bibliothek und Daten

Zunächst importieren wir die benötigte Bibliothek. Hier wird das Plot-Modul von matplotlib unter dem kurzen Namen plt eingebunden, so dass seine Funktionen usw. Namen wie plt.tu_was() erhalten (Details später im Semester):

import matplotlib.pyplot as plt

Nun definieren wir eine Liste von x-Werten und eine Liste von y-Werten. Im wahren Leben kämen diese Daten aus einer Datei, aus einer Datenbank, aus dem Netz oder von Sensoren:

x = [3.8, 3.9, 4.0, 4.2, 4.3]
y = [1.9, 3.5, 3.1, 4.6, 6.3]

Scatterplot

Als nächstes erstellen wir einen Scatterplot dieser Daten:

plt.scatter(x, y)

Nun fügen wir dem Diagramm einen Titel und Achsenbeschriftungen hinzu; innerhalb der $...$ stehen mathematische Zeichen und Formeln im LaTeX-Format:

plt.title('Scatterplot mit Regressionsgerade')
plt.xlabel('Was auch immer $x$ ist')
plt.ylabel('Was auch immer $y$ ist')

Zuletzt zeigen wir das Diagramm an:

plt.show()

Das Program bleibt in diesem plt.show() hängen, bis man das Fensterchen mit dem Diagramm schließt. (Es gibt auch einen Weg, Matplotlib sofort weiterarbeiten zu lassen, statt dass es auf das Schließen des Fensters wartet.)

Regressionsgerade

Um nun noch eine Regressionsgerade durch den Scatterplot zu legen, verwenden wir die Funktion linregress der Bibliothek SciPy. Diese eine Funktion liefert fünf Werte zurück, von denen wir nur die ersten beiden speichern:

# an den Anfang des Programms:
import scipy.stats as stats
# vor dem show():
steigung, achsenabschnitt, _, _, _ = stats.linregress(x, y)

Diese Linie können wir dann über den Scatterplot zeichnen. Die Funktion plot erwartet eine Liste an x-Werten (hier sind es nur zwei Stück) und eine Liste dazugehöriger y-Werte. Einer der vielen weiteren Parameter dieser Funktion, die man per Namen angeben kann, ist color:

# vor dem show():
plt.plot([3.8, 4.3], [steigung * 3.8 + achsenabschnitt, steigung * 4.3 + achsenabschnitt], color='red')

Aufgabe: Schreiben Sie die obige Zeile klarer und robuster, indem Sie die x-Werte aus den Daten gewinnen und die y-Werte mit einer selbstdefinierten Funktionen daraus erzeugen!

Und nun setzen wir noch mittels LaTeX in einem f-String die Gleichung der Regressionsgerade in das Diagramm:

# vor dem show():
plt.text(4.05, 3.5, f'$y = {steigung:.2f}x + {achsenabschnitt:.2f}$', fontsize=12, color='red')

Deutsche Schreibweise für Dezimalzahlen

Dies ist eine Ergänzung für Hartgesottene.

Schöner ist das Ganze, wenn die Zahlen in deutscher statt amerikanischer Schreibweise erscheinen (Dezimalkomma statt Dezimalpunkt, Tausender usw. mit Punkt statt mit Komma). Das geht so:

# am Anfang des Programms
import locale
# vor dem ersten Plot-Befehl
plt.rcParams['axes.formatter.use_locale'] = 'True'
# unten im Programm
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
plt.text(4.05, 3.5, f'$y = {steigung:.2n}x {"+" if achsenabschnitt >= 0 else "-"}{abs(achsenabschnitt):.2n}$'.replace(',', '{,}'), fontsize=12, color='red')

Das Einstellen von axes.formatter.use_locale bewirkt, dass für die Zahlen an den Koordinatenachsen die locale, also die regionale Einstellung, berücksichtigt wird. Das n in der Formatangabe im f-String macht Entsprechendes für die Umwandlung von Zahlen in Zeichenketten. Außerdem sorgen wir noch dafür, dass ein negativer achsenabschnitt nicht mit Pluszeichen (wörtlich im f-String angegeben) und Minuszeichen (von der Zahl) erscheint.

Normalerweise ruft man setlocale ganz am Anfang des Programms auf; allerdings setzt Matplotlib diese Einstellung zurück. Und noch ein weiterer Trick: LaTeX interpretiert Zahlen mit Dezimalkomma darin als Listen von Zahlen und lässt deshalb nach dem Komma eine Lücke. Um das zu verhindern, folgt dem f-String noch ein replace, das das Komma durch {,} ersetzt, wie es in LaTeX üblich ist.

Fehlerbalken

Angenommen, wir haben noch Angaben zu den Unsicherheiten der y-Werte:

y_error = [1.2, 0.7, 1.4, 0.9, 0.8]

Dann können wir passende Fehlerbalken zeichnen lassen, indem wir statt plot.scatter dies aufrufen:

plt.errorbar(x, y, yerr=y_error, fmt='o', capsize=5)

Nun müsste man noch die unterschiedlichen Fehlerbalken bei der Berechnung der Regressionsgeraden berücksichtigen und statt einer einzigen Regressionsgeraden ein Konfidenzband zeichnen. Fragen Sie die KI, wie das geht.

Diagramm als Skizze

Ein Easter Egg: Wenn man den Zeichencode in with plt.xkcd(): einbettet, entstehen skizzenhafte Diagramme im Stil der xkcd-Cartoons von Randall Munroe. Im Zweifelsfall muss noch der richtige Font installiert werden und die Fontliste fontlist-v....json von Matplotlib gelöscht werden, damit es die auffrischt.

Diagramm als xkcd-Cartoon

Beispiel für ein 3D-Diagramm

Wir lassen die Umrisse der Kontinente auf einer per Maus drehbaren Kugel zeichnen.

Erdkugel und Kontinente

Daten und Bibliotheken

Laden Sie dazu die Daten von Natural Earth und legen Sie die Dateien ne_110m_land.shp und ne_110m_land.shx aus dem heruntergeladenen Zip-Archiv an eine Stelle, wo Sie sie wiederfinden.

Um solche Shapefile-Dateien zu lesen, ist noch die Bibliothek Geopandas zu installieren. Wir benötigen auch die Bibliothek NumPy (Numerical Python), die effizient mit großen Mengen an Zahlen (Vektoren, Matrizen usw.) umgeht. Numpy ist aber schon automatisch mit Matplotlib installiert worden.

Der Anfang des Programms ist damit:

import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import locale

plt.rcParams["axes.formatter.use_locale"] = "True"  # fur Dezimalkommas usw.

Plotten der Kontinente

Wir erzeugen zum Test ein Diagramm mit einem 3D-Plot eines Dreiecks. Die Eckpunkte sind (0, 0, 0), (1, 0, 0) und (0, 1, 0). Um alle drei Linien zu zeichnen, ist der erste Eckpunkt wiederholt:

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

x = [0, 1, 0, 0]
y = [0, 0, 1, 0]
z = [0, 0, 0, 0]
ax.plot(x, y, z)

ax.set_aspect('equal')
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
plt.show()

Nun können wir stattdessen die Datei mit den Geodaten einlesen und die Umrisse der Kontinente plotten:

# Hier den Pfad zur Ihrer shp-Datei nehmen:
kontinente = gpd.read_file('ne_110m_land.shp')

for poly in kontinente.geometry:
    if poly.geom_type == 'Polygon':
        längengrade, breitengrade = poly.exterior.coords.xy
        phis = np.radians(längengrade)
        thetas = 0.5 * np.pi - np.radians(breitengrade)
        x = np.sin(thetas) * np.cos(phis)
        y = np.sin(thetas) * np.sin(phis)
        z = np.cos(thetas)
        ax.plot(x, y, z, color='k', linewidth=2)

Hier wird der Effizenz halber immer mit allen Eckpunkten eines Polygons auf einmal gerechnet. Die Variablen im if sind dazu allesamt Arrays (wirklich Arrays und nicht die grundlegenden Listen von Python). Die Längengrad-Angaben aller Punkte eines Polygons werden auf einen Schlag von Winkelgrad nach Radiant umgerechnet, von allen Winkeln ϕ wird auf einen Schlag der Cosinus genommen, die jeweiligen Werte von sin (θ) und cos (ϕ) werden für alle Punkte auf einen Schlag multipliziert usw. Sehen Sie sich im Debugger die Werte von längengrade usw. an.

Plotten der Erdkugel

Nun fehlt noch die Kugel. Sie wird nicht mit Linien gezeichnet, sondern mit gefüllten Vierecken. Hier ein Beispiel für ein einziges Viereck:

x = np.array([[1, 2], [3, 4]])
y = np.array([[2, 2], [4, 5]])
z = np.array([[0, 0], [0, 0]])
ax.plot_surface(x, y, z)

Die Funktion plot_surface verlangt die Koordinaten im Raster über die gesamte Fläche. Also müssen x, y und z jeweils zweidimensionale Arrays sein. Das Array für y im Beispiel oben ist dieses:

2 2
4 5

Für die Erdkugel gehen wir den Winkel ϕ ∈ [0, 2π] und den Winkel θ ∈ [0, π] der sphärischen Koordinaten jeweils in (zum Beispiel) 100 Schritten durch. Dazu dient die Funktion linspace. Daraus bildet meshgrid zweidimensionale Arrays, die die Fläche im Raster durchlaufen. Diese Arrays werden dann von den sphärischen Koordinaten auf kartesische Koordinaten x, y und z umgerechnet:

phis = np.linspace(0, 2*np.pi, 100)
thetas = np.linspace(0, np.pi, 100)
phis_all, thetas_all = np.meshgrid(phis, thetas)
x = np.cos(phis_all) * np.sin(thetas_all)
y = np.sin(phis_all) * np.sin(thetas_all)
z = np.cos(thetas_all)
ax.plot_surface(x, y, z, color='b', rstride=1, cstride=1, alpha=0.2, edgecolor='none')