Matplotlib Subplots — Vollständige Anleitung
Erstelle ein- und mehrpanelige Abbildungen mit Matplotlib Subplots: plt.subplots(), GridSpec, gemeinsame Achsen, Größen und Speicherbeispiele.
Subplots ermöglichen es dir, mehrere Diagramme in einer einzigen Matplotlib-Abbildung zu platzieren. Anstatt für jedes Diagramm separate Fenster zu erstellen, ordnest du sie in einem Raster an — nebeneinander, übereinander oder in einem individuellen Layout — damit der Leser Daten auf einen Blick vergleichen kann. Dieses Kapitel behandelt alles von einer einfachen zweipaneligen Abbildung bis hin zu flexiblen Rasterlayouts mit gemeinsamen Achsen und zellenübergreifenden Spans.
Bevor du beginnst, installiere Matplotlib, falls noch nicht geschehen:
pip install matplotlib numpyWenn du neu in der Bibliothek bist, lies zuerst die Kapitel Matplotlib Introduction und Getting Started.
Der schnellste Weg: plt.subplots()
plt.subplots(nrows, ncols) ist der standardmäßige Einstiegspunkt. Er gibt ein Figure- und ein array von Axes-Objekten zurück.
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))
x = np.linspace(0, 2 * np.pi, 200)
axes[0].plot(x, np.sin(x), color='steelblue')
axes[0].set_title('Sine')
axes[0].set_xlabel('x')
axes[0].set_ylabel('sin(x)')
axes[1].plot(x, np.cos(x), color='darkorange')
axes[1].set_title('Cosine')
axes[1].set_xlabel('x')
axes[1].set_ylabel('cos(x)')
plt.tight_layout()
plt.show()Wichtige Punkte:
figsize=(width, height)legt die Abbildungsgröße in Zoll fest. Der Standardwert ist(6.4, 4.8), der für mehrere Panels oft zu klein ist.plt.tight_layout()passt den Abstand automatisch an, sodass sich Titel und Beschriftungen nicht überlappen. Rufe es direkt vorshow()odersavefig()auf.- Wenn
ncols=1undnrows=1(der Standard), istaxesein einzelnesAxes-Objekt und kein array.
Erstellen eines 2×2-Rasters
Übergib sowohl nrows als auch ncols, um ein zweidimensionales NumPy-array von Axes zu erhalten. Indiziere es mit [row, col], wobei beide Indizes nullbasiert sind.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 200)
fig, ax = plt.subplots(nrows=2, ncols=2, figsize=(10, 8))
# Top-left
ax[0, 0].plot(x, np.sin(x), color='steelblue')
ax[0, 0].set_title('sin(x)')
# Top-right
ax[0, 1].plot(x, np.cos(x), color='darkorange')
ax[0, 1].set_title('cos(x)')
# Bottom-left — clamp tan to avoid ±∞ spikes
y_tan = np.clip(np.tan(x), -10, 10)
ax[1, 0].plot(x, y_tan, color='seagreen')
ax[1, 0].set_title('tan(x) [clipped]')
# Bottom-right
ax[1, 1].plot(x, np.exp(x / np.pi), color='tomato')
ax[1, 1].set_title('exp(x/π)')
plt.suptitle('Trigonometric & Exponential Functions', fontsize=14, y=1.02)
plt.tight_layout()
plt.show()plt.suptitle() fügt einen übergeordneten Abbildungstitel über allen Subplots hinzu. Der Versatz y=1.02 schiebt ihn von den Paneltiteln der obersten Zeile weg.
Über Achsen iterieren
Bei einem großen Raster ist es einfacher, über das geglättete Achsen-array zu iterieren, als jede Zelle manuell zu indizieren:
import matplotlib.pyplot as plt
import numpy as np
functions = [np.sin, np.cos, np.tan, np.arctan]
names = ['sin', 'cos', 'tan', 'arctan']
colors = ['steelblue', 'darkorange', 'seagreen', 'tomato']
x = np.linspace(-np.pi, np.pi, 300)
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
for ax, fn, name, color in zip(axes.flat, functions, names, colors):
y = fn(x)
y = np.clip(y, -5, 5) # keep tan from dominating the scale
ax.plot(x, y, color=color)
ax.set_title(name)
ax.axhline(0, color='black', linewidth=0.6, linestyle='--')
ax.axvline(0, color='black', linewidth=0.6, linestyle='--')
plt.tight_layout()
plt.show()axes.flat gibt einen eindimensionalen Iterator zurück, unabhängig von der Rasterform — dieselbe Schleife funktioniert für 2×2, 3×3 oder jedes andere Layout.
Achsen gemeinsam nutzen
Wenn Panels dieselbe Größe darstellen (gleicher x-Bereich oder gleiche y-Skala), verknüpfe ihre Achsen, sodass Schwenken oder Zoomen in einem Panel alle anderen aktualisiert:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 4 * np.pi, 400)
# sharex=True links horizontal axes; sharey=True links vertical axes
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6), sharex=True)
ax1.plot(x, np.sin(x), color='steelblue')
ax1.set_ylabel('sin(x)')
ax1.set_title('Shared x-axis example')
ax2.plot(x, np.sin(2 * x), color='darkorange')
ax2.set_ylabel('sin(2x)')
ax2.set_xlabel('x (radians)')
# Hide redundant x-tick labels on the top panel
ax1.tick_params(labelbottom=False)
plt.tight_layout()
plt.show()sharex=True entfernt die doppelten x-Achsenbeschriftungen aus den oberen Panels und hält alle Panels ausgerichtet. Verwende sharey=True entsprechend, wenn die y-Skala spaltenübergreifend übereinstimmen muss.
Achsen direkt entpacken
Wenn das Layout klein und im Voraus bekannt ist, kannst du das zurückgegebene array direkt in benannte Variablen entpacken:
import matplotlib.pyplot as plt
import numpy as np
# 1 row, 3 columns → unpack into three named axes
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(13, 4))
x = np.linspace(0, 10, 300)
ax1.plot(x, x ** 0.5, color='steelblue', label='√x')
ax1.set_title('Square Root')
ax1.legend()
ax2.plot(x, np.log1p(x), color='darkorange', label='ln(1+x)')
ax2.set_title('Natural Log')
ax2.legend()
ax3.plot(x, x, color='seagreen', label='x')
ax3.set_title('Linear')
ax3.legend()
plt.tight_layout()
plt.show()Dies ist sauberer als axes[0], axes[1], axes[2] für kurze Layouts. Für ein 2-D-Raster mit zwei Zeilen verschachtle das Entpacken: (ax1, ax2), (ax3, ax4) = axes.
Individuelle Layouts mit GridSpec
plt.subplots() erstellt nur Raster, bei denen jede Zelle gleich groß ist. Für ungleiche Layouts — ein breites Übersichtspanel oben mit Detailpanels darunter — verwende matplotlib.gridspec.GridSpec:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
x = np.linspace(0, 2 * np.pi, 300)
fig = plt.figure(figsize=(10, 8))
gs = gridspec.GridSpec(nrows=2, ncols=3, figure=fig)
# Top row: one wide panel spanning all three columns
ax_top = fig.add_subplot(gs[0, :])
ax_top.plot(x, np.sin(x), color='steelblue', linewidth=2)
ax_top.set_title('Overview — sin(x)')
# Bottom row: three equal panels
ax_bl = fig.add_subplot(gs[1, 0])
ax_bl.plot(x, np.sin(x), color='steelblue')
ax_bl.set_title('sin(x)')
ax_bm = fig.add_subplot(gs[1, 1])
ax_bm.plot(x, np.cos(x), color='darkorange')
ax_bm.set_title('cos(x)')
ax_br = fig.add_subplot(gs[1, 2])
ax_br.plot(x, np.sin(x) * np.cos(x), color='seagreen')
ax_br.set_title('sin(x)·cos(x)')
plt.tight_layout()
plt.show()Die gs[row, col]-Slice-Syntax spiegelt NumPy-Slicing wider. gs[0, :] erstreckt sich über alle drei Spalten; gs[1, 0] nimmt nur die erste Zelle in Zeile 1.
Zeilen- und Spaltenhöhen steuern
GridSpec akzeptiert height_ratios und width_ratios, um einige Zeilen oder Spalten größer als andere zu machen:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
fig = plt.figure(figsize=(9, 7))
gs = gridspec.GridSpec(
2, 2,
height_ratios=[3, 1], # top row is 3× taller than bottom row
width_ratios=[2, 1], # left column is 2× wider than right column
hspace=0.4,
wspace=0.3,
)
x = np.linspace(0, 4 * np.pi, 400)
ax1 = fig.add_subplot(gs[0, 0])
ax1.plot(x, np.sin(x), color='steelblue')
ax1.set_title('Main (tall + wide)')
ax2 = fig.add_subplot(gs[0, 1])
ax2.plot(x, np.cos(x), color='darkorange')
ax2.set_title('Side (tall + narrow)')
ax3 = fig.add_subplot(gs[1, 0])
ax3.plot(x, np.sin(x) ** 2, color='seagreen')
ax3.set_title('Bottom-left (short + wide)')
ax4 = fig.add_subplot(gs[1, 1])
ax4.plot(x, np.cos(x) ** 2, color='tomato')
ax4.set_title('Bottom-right (short + narrow)')
plt.suptitle('Proportional Grid Layout', fontsize=13)
plt.show()hspace und wspace steuern den vertikalen und horizontalen Abstand zwischen Subplots (als Bruchteil der durchschnittlichen Subplot-Höhe/-Breite).
Eine gemeinsame Abbildungsgröße festlegen
Der Parameter figsize bezieht sich immer auf die gesamte Abbildung, nicht auf einzelne Subplots. Eine gute Faustregel:
| Layout | Empfohlene figsize |
|---|---|
| 1 Zeile × 2 Spalten | (10, 4) |
| 1 Zeile × 3 Spalten | (13, 4) |
| 2 Zeilen × 2 Spalten | (10, 8) |
| 3 Zeilen × 3 Spalten | (13, 11) |
Du kannst die Werte je nach deinen Daten anpassen; dies sind Ausgangspunkte, die Platz für Titel und Beschriftungen lassen.
Titel und Beschriftungen hinzufügen
Jedes Axes-Objekt hat seinen eigenen Titel und eigene Achsenbeschriftungen. Die gesamte Abbildung kann einen übergeordneten Titel haben:
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
x = np.linspace(0, 5, 100)
for ax, power, color in zip(axes, [2, 3], ['steelblue', 'darkorange']):
ax.plot(x, x ** power, color=color)
ax.set_title(f'$x^{power}$') # LaTeX in title
ax.set_xlabel('x')
ax.set_ylabel(f'$x^{power}$')
ax.grid(True, linestyle='--', alpha=0.5)
plt.suptitle('Power Functions', fontsize=14)
plt.tight_layout()
plt.show()Eine mehrteilige Abbildung speichern
Rufe plt.savefig() vor plt.show() auf (wenn show() zuerst aufgerufen wird, wird die Abbildung gelöscht):
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 3, figsize=(13, 4))
x = np.linspace(0, 2 * np.pi, 300)
axes[0].plot(x, np.sin(x), color='steelblue')
axes[0].set_title('sin(x)')
axes[1].plot(x, np.cos(x), color='darkorange')
axes[1].set_title('cos(x)')
axes[2].plot(x, np.sin(x) + np.cos(x), color='seagreen')
axes[2].set_title('sin(x) + cos(x)')
plt.tight_layout()
plt.savefig('trig_panels.png', dpi=150, bbox_inches='tight')
plt.show()bbox_inches='tight' schneidet überschüssigen Leerraum um die Abbildung herum ab — nützlich beim Einbetten des Bildes in ein Dokument oder eine Webseite.
Häufige Fallstricke
tight_layout() vergessen
Ohne tight_layout() überlappen sich Subplot-Titel und Achsenbeschriftungen benachbarter Panels oft. Rufe es immer vor show() oder savefig() auf.
Falsche Indexdimensionen
plt.subplots(2, 3) gibt ein 2-D-array zurück; plt.subplots(1, 3) gibt ein 1-D-array zurück. Der Versuch, axes[0, 0] auf einem 1-D-array zu verwenden, löst einen IndexError aus. Verwende entweder axes[0] oder übergib squeeze=False, um immer ein 2-D-array zu erhalten:
fig, axes = plt.subplots(1, 3, squeeze=False)
# axes is now shape (1, 3) — always index with [row, col]
axes[0, 0].plot(...)
axes[0, 1].plot(...)
axes[0, 2].plot(...)Suptitle wird abgeschnitten
plt.suptitle() kann durch tight_layout() abgeschnitten werden. Behebe dies, indem du ein rect an tight_layout übergibst:
plt.tight_layout(rect=[0, 0, 1, 0.95]) # leave 5% at top for suptitleOder verwende stattdessen fig.subplots_adjust(top=0.90).
Kurzübersicht
| Aufgabe | Code |
|---|---|
| 1×2-Raster | fig, (ax1, ax2) = plt.subplots(1, 2) |
| 2×2-Raster | fig, ax = plt.subplots(2, 2) |
| Auf Zelle zugreifen | ax[row, col] |
| Alle Zellen durchlaufen | for ax in axes.flat: |
| x-Achse teilen | plt.subplots(2, 1, sharex=True) |
| y-Achse teilen | plt.subplots(1, 2, sharey=True) |
| Immer 2-D-array | plt.subplots(..., squeeze=False) |
| Individuelles Layout | gs = GridSpec(rows, cols); fig.add_subplot(gs[r, c]) |
| Spalten überspannen | fig.add_subplot(gs[0, :]) |
| Höhenverhältnisse | GridSpec(2, 1, height_ratios=[3, 1]) |
| Abbildungstitel | plt.suptitle('Title') |
| Abbildung speichern | plt.savefig('out.png', dpi=150, bbox_inches='tight') |
Verwandte Kapitel
- Matplotlib Introduction — Bibliotheksübersicht und Installation
- Matplotlib Getting Started — dein erstes Diagramm
- Matplotlib Line Plots — kontinuierliche Daten darstellen
- Matplotlib Labels — Achsenbeschriftungen, Titel und Anmerkungen
- Matplotlib Grid — Rasterlinien hinzufügen und gestalten
- Matplotlib Bar Charts — Kategorien vergleichen
- Matplotlib Scatter Plots — Beziehungen zwischen Variablen
- Matplotlib Histograms — Datenverteilungen