Diagramme mit mehreren Graphen#

Oft möchte man nicht nur einen einzelnen Graphen darstellen, sondern mehrere Graphen, Punktwolken oder sogar ganze Matrizen vergleichen. In diesem Kapitel lernen Sie, wie Sie mehrere Vektoren oder Matrizen gleichzeitig als Eingabe für \(\texttt{plt.plot()}\) verwenden können, etwa um verschiedene Funktionen zu vergleichen oder Veränderungen über die Zeit oder Parameter hinweg sichtbar zu machen. Dabei geht es auch darum, wie Sie Ihre Daten sinnvoll strukturieren und optisch klar voneinander trennen.

Wir arbeiten weiterhin mit dem Datensatz zu erneuerbaren Energien, welcher wie folgt aufbereitet wurde:

  • \(\texttt{data_array}\): Numpy-Array, der Größe \( 35 \times 6\), wobei

    • die Spalte 0 die Jahres Zahlen enthält, und

    • die Spalten 1 bis 5 den Anteil erneuerbarer Energien in Prozent für Deutschland, USA, China, Norwegen und Brasilien (in genau dieser Reihenfolge) enthalten.

Hide code cell content
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Datenquelle
url = "https://raw.githubusercontent.com/owid/energy-data/master/owid-energy-data.csv"
df = pd.read_csv(url)

# Auswahlparameter
countries = ['Germany', 'United States', 'China', 'Norway', 'Brazil']
variable = 'renewables_share_energy'

# Daten filtern und auf relevante Spalten reduzieren
df_filtered = df[(df['country'].isin(countries)) & (df['year'] >= 1990)]
df_pivot = df_filtered.pivot(index='year', columns='country', values=variable)

# Nur vollständige Zeilen und relevante Länder
df_selected = df_pivot[countries].dropna()

# Jahr als eigene Spalte
df_selected_with_years = df_selected.copy()
df_selected_with_years.insert(0, 'year', df_selected.index)

# Umwandeln in NumPy-Array
data_array = df_selected_with_years.to_numpy()

Diagramme mit mehreren Vektoren als Eingabe#

Um mehrere Linien in einem Plot darzustellen, stehen Ihnen zwei Optionen zur Verfügung. Zum einen kann \(\texttt{plt.plot()}\) vor dem abschließenden \(\texttt{plt.show()}\) mehrfach aufgerufen werden. Alternativ können Sie auch direkt mehrere Graph-Koordinaten an \(\texttt{plt.plot()}\) übergeben.

import matplotlib.pyplot as plt

plt.plot(x, y1)   # erste Linie
plt.plot(x, y2)   # zweite Linie
plt.show()
import matplotlib.pyplot as plt

plt.plot(x, y1, x, y2)  # beide Linien in einer Zeile
plt.show()

Aufgabe 1.1

Erweitern Sie den Plot, sodass neben den deutschen Daten (Spaltenindex 1) auch die Daten für die USA (Spaltenindex 2) dargestellt werden.

plt.plot(data_array[:, 0], data_array[:, 1])

plt.title('Anteil erneuerbarer Energien in Deutschland und den USA seit 1990')
plt.xlabel('Jahr')
plt.ylabel('Anteil erneuerbarer Energien (%)')
plt.grid(True)
plt.show()

Wenn Sie mehr als zwei Linien zeichnen möchten, fügen Sie einfach weitere \(x,y\)-Paare hinzu – entweder durch zusätzliche \(\texttt{plt.plot()}\) Aufrufe oder als zusätzliche Argumente im selben Aufruf: \(\texttt{plt.plot(x, y1, x, y2, x, y3, ...)}\).

Aufgabe 1.2

Erweitern Sie den Plot, sodass neben den deutschen und amerikanischen Daten, auch die Daten der restlichen drei Länder angezeigt werden (China, Norwegen und Brasilien).

plt.plot(data_array[:, 0], data_array[:, 1])

plt.title('Anteil erneuerbarer Energien in Deutschland und den USA seit 1990')
plt.xlabel('Jahr')
plt.ylabel('Anteil erneuerbarer Energien (%)')
plt.grid(True)
plt.show()

Wenn Sie mehrere Linien gleichzeitig darstellen, kann die Graphik schnell unübersichtlich wirken. Um die Daten besser unterscheidbar zu machen, empfiehlt es sich, für jede Linie passende Eigenschaften wie \(\texttt{label}\), \(\texttt{color}\) und gegebenenfalls \(\texttt{linestyle}\) anzugeben:

plt.plot(x, y1, color='r', label='Wert 1')
plt.plot(x, y2, color='b', label='Wert 2')

plt.legend()

Durch den anschließenden Aufruf von \(\texttt{plt.legend()}\) wird eine Legende eingeblendet, die die Labels den Linien zuordnet.

Aufgabe 1.3

Passen Sie die bestehende Graphik an, sodass jede Linie eine eigene Farbe und ein passendes Label bekommt. Verwenden Sie folgende Farbgebung:

Deutschland

\(\texttt{blue}\)

USA

\(\texttt{red}\)

China

\(\texttt{green}\)

Norwegen

\(\texttt{orange}\)

Brasilien

\(\texttt{purple}\)

# Ihr Code 

Wenn Sie keine Farbe explizit angeben, verwendet die Bibliothek automatisch Farben aus einer vordefinierten Farbpalette, dem sogenannten Color Cycle. Dieser Zyklus besteht standardmäßig aus zehn unterschiedlichen Farben, die in der Reihenfolge verwendet werden, in der Linien gezeichnet werden. Sobald mehr als zehn Linien dargestellt werden, beginnt der Zyklus von vorne, wodurch Farben wiederholt werden können.

Diese automatische Farbvergabe eignet sich gut für schnelle Visualisierungen. Bei komplexeren Diagramme mit mehreren Linien kann es jedoch hilfreich sein, Farben und Beschriftungen explizit festzulegen, um die Lesbarkeit zu verbessern.

Für detailliertere Informationen zur automatischen Farbvergabe und zur Anpassung des Color Cycles in matplotlib können Sie die offizielle Dokumentation konsultieren: Colors in the default property cycle

Plots mit Matrizen als Eingabe#

Als nächstes wollen wir Matrizen direkt als Eingabe für die Funktion \(\texttt{plt.plot()}\) verwenden. Dadurch lassen sich mehrere Datenreihen gleichzeitig und effizient in einem einzigen Diagramm darstellen.

Im Kapitel Graphiken individuell gestalten haben Sie gelernt, wie man einzelne Vektoren gemeinsam in einem Diagramm visualisiert. Nun gehen wir einen Schritt weiter: Statt jede Datenreihe einzeln zu übergeben, können Sie ein zweidimensionales Array direkt an \(\texttt{plt.plot()}\) übergeben.

Wichtig

Die Dimensionen müssen zueinander passen:

  • Der erste Parameter (die \(x\)-Achse) muss ein Vektor mit der gleichen Länge wie die Anzahl der Zeilen des 2D-Arrays sein.

  • Das 2D-Array enthält dann pro Spalte eine eigene Datenreihe, die automatisch als separater Graph geplottet wird.

In diesem Beispiel ist die Eingabe falsch: Der Vektor \(x\) hat die Länge 100, aber \(y\) besitzt nur 2 Zeilen und 100 Spalten.

import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)  # Länge 100
y = np.vstack([np.sin(x), np.cos(x)])  # 100 Spalten, 2 Zeilen

plt.plot(x, y) 
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sinus- und Cosinusfunktionen')
plt.show()

Nun korrigieren wir den Fehler: \(y\) hat 100 Zeilen und 2 Spalten – passend zur Länge von \(x\).

x = np.linspace(0, 2 * np.pi, 100)  # Länge 100
y = np.vstack([np.sin(x), np.cos(x)])  # 2 Spalten, 100 Zeilen

plt.plot(x, y.T) 
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sinus- und Cosinusfunktionen')
plt.show()

Mit dieser Methode lassen sich also mehrere Graphen auf einmal zeichnen, indem Sie nur zwei Argumente übergeben: den gemeinsamen \(x\)-Vektor und die Datenmatrix für die \(y\)-Werte.

Aufgabe 2.1

Erstellen Sie ein Diagramm, das für jedes Land in \(\texttt{data_array}\) den Anteil erneuerbarer Energien ab dem Jahr 1990 zeigt, indem Sie eine Matrix mit den Daten zum Anteil erneuerbarer Energien übergeben.

# Ihr Code 

Wenn Sie eine ganze Matrix an \(\texttt{plt.plot()}\) übergeben, wird für jede Spalte automatisch eine eigene Linie gezeichnet. Die Beschriftung erfolgt anschließend über \(\texttt{plt.legend()}\), indem Sie eine Liste mit genauso vielen Einträgen wie Spalten in der Matrix übergeben. Einzelne \(\texttt{label}\)-Parameter beim Aufruf von \(\texttt{plt.plot()}\) funktionieren in diesem Fall nicht.

x = np.linspace(0, 2 * np.pi, 100)
y = np.vstack([np.sin(x), np.cos(x)])

plt.plot(x, y.T)  
plt.legend(['sin(x)', 'cos(x)'])
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sinus- und Cosinusfunktionen')
plt.show()

Aufgabe 2.2

Fügen Sie dem Plot aus Aufgabe 2.1 eine Legende hinzu, die die einzelnen Länder korrekt beschriftet.

# Ihr Code 

Auch bei der Übergabe einer Matrix an \(\texttt{plt.plot()}\) können Sie Eigenschaften wie \(\texttt{color}\), \(\texttt{linestyle}\) oder \(\texttt{marker}\) direkt im Funktionsaufruf festlegen wie Sie es in Graphiken individuell gestalten kennengelernt haben. Dazu übergeben Sie die gewünschten Eigenschaften einfach als benannte Argumente im Format \(\texttt{Property=Value}\), zum Beispiel:

\[ \texttt{plt.plot(x, y, color='green', linestyle='-')} \]
x = np.linspace(0, 2 * np.pi, 100)
y = np.vstack([np.sin(x), np.cos(x)])

lines = plt.plot(x, y.T, color='green', linestyle='-')

plt.xlabel('x')
plt.ylabel('y')
plt.title('Linien anpassen')
plt.legend(['sin(x)', 'cos(x)'])
plt.grid(True)
plt.show()

Aufgabe 2.3

Passne Sie die Graphik aus Aufgabe 2.2 weiter an, indem Sie Sie runde Marker und eine gestrichelte Linie für alle Graphen verwenden.

# Ihr Code 

Um die erzeugten Graphen gezielt gestalten – also zum Beispiel unterschiedliche \(\texttt{color}\), \(\texttt{linestyle}\) oder \(\texttt{marker}\) verwenden, müssen Sie anders vorgehn als beim Plotten einzelner Vektoren.

Dazu speichern Sie zunächst das Ergebnis des \(\texttt{plt.plot()}\)-Befehls in einer Variable \(\texttt{lines = plt.plot(x, y)}\) ab. Die Variable \(\texttt{lines}\) ist eine Liste von Linienobjekten (\(\texttt{Line2D}\)), wobei jedes Objekt einer Spalte der Datenmatrix \(\texttt{y}\) entspricht. Ein Linienobjekt entspricht also einem Graphen. Die Reihenfolge der Objekte in \(\texttt{lines}\) entspricht genau der Reihenfolge der Spalten in \(\texttt{y}\). Diese Objekte lassen sich anschließend einzeln mit der Methode \(\texttt{.set_<Eigenschaft>()}\) anpassen.

x = np.linspace(0, 2 * np.pi, 100)
y = np.vstack([np.sin(x), np.cos(x)])

lines = plt.plot(x, y.T)

# Erste Linie: rot, gestrichelt, Marker 'o'
lines[0].set_color('red')
lines[0].set_linestyle('--')
lines[0].set_marker('o')
lines[0].set_linewidth(2)

# Zweite Linie: blau, gepunktet, Marker 's'
lines[1].set_color('blue')
lines[1].set_linestyle(':')
lines[1].set_marker('s')
lines[1].set_linewidth(1)

plt.xlabel('x')
plt.ylabel('y')
plt.title('Individuelle Gestaltung von Linien')
plt.legend(['sin(x)', 'cos(x)'])
plt.grid(True)
plt.show()

Aufgabe 2.4

Erstellen Sie erneut den Plot aus Aufgabe 2.2. Nun sollen allerdings für Deutschland und Norwegen runde Marker und gestrichelte Linien verwendet werden.

# Ihr Code 

Aufgabe 2.5

Erstellen Sie erneut den Plot aus Aufgabe 2.2 und plotten Sie den durchschnittlichen Anteil erneuerbarer Energien über alle Länder hinweg als weiteren Graphen. Gestalten Sie diese Durchschnittslinie deutlich sichtbar, zum Beispiel durch eine gestrichelte Linie mit erhöhter Strichstärke.

# Ihr Code 

Sie müssen nicht zwigend die Matrix, welche die Daten zum Anteil erneuerbarer Energien enthält, zwischen speichern. Sie können auch direkt die entsprechenden Daten mit dem Doppelpunkt-Operator \(\texttt{:}\) extrahieren und \(\texttt{plt.plot()}\) übergeben.

Genauso können Sie auch nur bestimmte Teilbereiche eines Datensatzes extrahieren und anschließend plotten. Im folgenden Beispiel plotten wir nur jede 10. Stelle des ursprünglichen Sinus- und Cosinusverlaufs.

x = np.linspace(0, 2 * np.pi, 100)
y = np.vstack([np.sin(x), np.cos(x)])

# Nur jede fünfte Stelle verwenden
x_sliced = x[0:x.shape[0]:10]
y_sliced = y[:, 0:y.shape[1]:10]

plt.plot(x_sliced, y_sliced.T, 'o-')
plt.legend(['sin(x)', 'cos(x)'])
plt.xlabel('x')
plt.ylabel('y')
plt.title('Sinus- und Cosinusfunktionen (jede 5. Stelle)')
plt.grid(True)
plt.show()

Aufgabe 2.6

Erstellen Sie erneut den Plot aus Aufgabe 2.2, jedoch nur mit den Messdaten im Fünf-Jahres-Abstand. Fügen Sie außerdem eine Linie hinzu, die den durchschnittlichen Anteil erneuerbarer Energien aller Länder zusammenfasst, und heben Sie diese hervor.

# Ihr Code 

Anpassung der Achsen#

Im vorherigen Abschnitt haben wir den Anteil erneuerbarer Energien am gesamten Energieverbrauch für ausgewählte Länder betrachtet. Nun erweitern wir unsere Analyse um eine den Stromverbrauch pro Kopf. Dieser Wert beschreibt, wie viel elektrische Energie durchschnittlich von einer Person in einem Land verbraucht wird und gilt als Indikator für den Entwicklungsstand, den Industrialisierungsgrad sowie den Energiebedarf einer Gesellschaft.

Der folgende Code lädt die Daten zum Stromverbrauch pro Kopf seit 1990 für Deutschland, die USA, China, Norwegen und Brasilien. Die Daten werden wie zuvor in ein NumPy-Array mit dem Namen \(\texttt{data_array_demand}\) überführt und hat die folgende Struktur:

  • \(\texttt{data_array_demand}\): Numpy-Array, der Größe \( 33 \times 6\), wobei

    • die Spalte 0 die Jahres Zahlen enthält, und

    • die Spalten 1 bis 5 den Stromverbrauch pro Kopf in Kilowattstunden (kWh) für Deutschland, USA, China, Norwegen und Brasilien (in genau dieser Reihenfolge) enthalten.

Hide code cell content
import pandas as pd
import numpy as np

# Datenquelle
url = "https://raw.githubusercontent.com/owid/energy-data/master/owid-energy-data.csv"
df = pd.read_csv(url)

# Auswahlparameter
countries = ['Germany', 'United States', 'China', 'Norway', 'Brazil']
variable = 'electricity_demand_per_capita'

# Daten filtern und auf relevante Spalten reduzieren
df_filtered = df[(df['country'].isin(countries)) & (df['year'] >= 1990)]
df_pivot = df_filtered.pivot(index='year', columns='country', values=variable)

# Länder in definierter Reihenfolge sicherstellen (für spätere Achsenzuweisung im Plot)
df_selected = df_pivot[countries]

# Jahr als eigene Spalte einfügen
df_selected_with_years = df_selected.copy()
df_selected_with_years.insert(0, 'year', df_selected.index)

# Umwandeln in NumPy-Array
data_array_demand = df_selected_with_years.to_numpy()

Aufgabe 3.1

Erstellen Sie ein Diagramm, das den Stromverbrauch pro Kopf für alle fünf Länder im Zeitverlauf zeigt.

# Ihr Code 

Wie Sie sehen, weist das Diagramm zum Stromverbrauch pro Kopf einige Lücken auf. Diese entstehen dadurch, dass für die USA, China und Brasilien in den 1990er-Jahren keine Daten vorliegen.

Standardmäßig legt Python die Achsengrenzen automatisch so fest, dass alle vorhandenen Daten möglichst gut dargestellt werden. Möchte man sich jedoch gezielt bestimmte Zeiträume anschauen oder zwischen mehreren Diagrammen eine einheitliche Darstellung erreichen, ist es sinnvoll, die Achsen manuell anzupassen. Mit dem Befehl \(\texttt{plt.axis()}\) lässt sich die aktuelle Achsenskalierung abfragen oder gezielt setzen. Wird der Befehl ohne Argumente aufgerufen, gibt er die aktuell verwendeten Grenzen des zuletzt erzeugten Diagramms zurück:

x = [2000, 2005, 2010, 2015, 2020]
y = [3000, 3500, 5000, 7000, 8500]

plt.plot(x, y, label="Beispieldaten")
plt.grid(True)

plt.draw()  # Zeichnung erzwingen
limits = plt.axis()  # Achsengrenzen abfragen
print("Aktuelle Achsengrenzen:", limits)

plt.show() # Plot anzeigen

Damit \(\texttt{plt.axis()}\) die korrekten Achsengrenzen zurückgibt, muss das Diagramm bereits vollständig gezeichnet worden sein. In vielen Umgebungen – insbesondere in Jupyter Notebooks – wird die Zeichnung jedoch erst durch den Befehl \(\texttt{plt.show()}\) ausgelöst. Nach diesem Aufruf ist der Zugriff auf die Achsengrenzen allerdings nicht mehr zuverlässig möglich. Verwenden Sie daher \(\texttt{plt.draw()}\), um das Zeichnen vorab zu erzwingen. Erst danach gibt \(\texttt{plt.axis()}\) die tatsächlich verwendeten Grenzen zurück.

Warning

Achtung Verwenden Sie in Jupyter Notebooks \(\texttt{plt.draw()}\) vor \(\texttt{plt.show()}\) um die Achsen anpassen zu können.

Ein Aufruf von \(\texttt{plt.axis()}\) ohne Argumente gibt die aktuellen Achsengrenzen in folgender Reihenfolge zurück:

\[ \texttt{[x_min, x_max, y_min, y_max]} \]

Das bedeutet: Die ersten beiden Werte stehen für die Grenzen der \(x\)-Achse, die letzten beiden für die \(y\)-Achse. Wenn Sie die Achsengrenzen manuell festlegen möchten, übergeben Sie dem Befehl \(\texttt{plt.axis()}\) genau eine solche Liste:

\[\texttt{plt.axis([x_min, x_max, y_min, y_max])}\]

Aufgabe 3.2

Passen Sie die Achsendarstellung des Diagramms aus Aufgabe 3.1 so an, dass nur Daten ab dem Jahr 2000 auf der \(x\)-Achse sichtbar sind. Die Grenzen der \(y\)-Achse sollen unverändert bleiben.

# Ihr Code 

Aufgabe 3.3

Passen Sie die Achsendarstellung des Diagramms aus Aufgabe 3.2 so an, dass nur Daten ab dem Jahr 2000 angezeigt werden. Die übrigen Achsengrenzen sollen so gewählt werden, dass sie dem minimalen und maximalen Wertebereich der Daten entsprechen.

# Ihr Code 

Möchte man die Achsengrenzen so setzen, dass sie genau dem Datenbereich entsprechen, verwendet man den Befehl \(\texttt{plt.axis('tight')}\).

Aufgabe 3.4

Passen Sie die Achsengrenzen des Plots so an, dass sie genau dem Bereich der vorhandenen Daten entsprechen.

plt.plot(data_array_demand[:, 0], data_array_demand[:, 1], color="blue", label="Deutschland")
plt.plot(data_array_demand[:, 0], data_array_demand[:, 2], color="red", label="USA")
plt.plot(data_array_demand[:, 0], data_array_demand[:, 3], color="green", label="China")
plt.plot(data_array_demand[:, 0], data_array_demand[:, 4], color="orange", label="Norwegen")
plt.plot(data_array_demand[:, 0], data_array_demand[:, 5], color="purple", label="Brasilien")

plt.title('Stromverbrauch pro Kopf')
plt.xlabel('Jahr')
plt.ylabel('Stromverbrauch pro Kopf (kWh)')
plt.grid(True)
plt.legend()
plt.show()