4.2    Matplotlib

Mit den Methoden des Moduls Matplotlib können Sie Funktionsgraphen, Linien, geometrische Figuren und Histogramme darstellen. Auch interaktive Programme mit Textfeldern, Befehlsschaltflächen (Buttons), Radiobuttons und Schiebereglern (Slidern) können Sie mit diesem Modul schreiben. Hinweise zur Installation, ein einführendes Tutorial, eine ausführliche Dokumentation und eine umfangreiche Sammlung von Beispielen finden Sie im Internet unter https://matplotlib.org/. Fast alle Abbildungen dieses Buches wurden mit Matplotlib-Programmen erzeugt. Die hier vorgestellten Beispiele bilden die Grundlage für die Veranschaulichung mathematischer Zusammenhänge in den nachfolgenden Kapiteln.

Die Programmoberfläche von Matplotlib

Abbildung 4.2     Die Programmoberfläche von Matplotlib

Importiert wird das Modul in der Regel zusammen mit dem Modul NumPy:

import numpy as np
import matplotlib.pyplot as plt

pyplot ist ein Untermodul von matplotlib. Die Abkürzungen np und plt haben sich als Konvention durchgesetzt.

Der Bereich Figure umfasst die gesamte Programmoberfläche. Ein Figure-Objekt können Sie mit fig=plt.figure() erzeugen.

Der Bereich Axes verwaltet die Darstellungsoptionen des Funktionsplots wie Skalierung und Beschriftung der Koordinatenachsen. Mit der Anweisung fig,ax=plt.subplots() können Sie ein Tupel (fig,ax) eines Figure- und eines Axes-Objekts erzeugen. In einem Figure-Objekt können mehrere Axes-Objekte (Koordinatensysteme) eingebettet werden.

4.2.1    Die objektorientierte API

Im Kern ist das Modul matplotlib objektorientiert programmiert. Mit Figure-Methoden können Sie das Design der Programmoberfläche eines Matplotlib-Programms beeinflussen. Tabelle 4.4 enthält eine Zusammenstellung ausgewählter Figure-Methoden.

Methode

Beschreibung

add_axes([l,u,b,h])

Erzeugt ein Koordinatensystem: linker Rand, Rand links unten, Breite, Höhe. Die Werte müssen zwischen 0 und 1 liegen.

add_subplot(z,s,n)

Erzeugt Unterdiagramme mit z Zeilen und s Spalten. Die Zahl n gibt die Anzahl der Unterdiagramme an. Wenn keine Parameter eingetragen werden, wird nur ein Diagramm erzeugt.

savefig('name.png')

Speichert den Funktionsplot im png-Format auf die Festplatte. Andere mögliche Formate sind .svg, .jpg, .pdf und .eps. Das Speicherformat wird automatisch durch die Dateiendung erkannt.

show()

Stellt den Funktionsplot auf dem Bildschirm dar.

subplots_adjust(par)

Legt die Position der Zeichenfläche fest. Als Parameter par sind möglich: left, right, bottom und top.

tight_layout()

Erzeugt Abstände zwischen Unterdiagrammen.

Tabelle 4.4     Wichtige Figure-Methoden

Mit Axes-Methoden können Sie das gesamte Erscheinungsbild eines Funktionsplots gestalten. Tabelle 4.5 zeigt ausgewählte Axes-Methoden.

Methode

Beschreibung

axis([x1,x2,y1,y2])

Legt den Darstellungsbereich für die x- und y-Werte fest.

grid(True)

Zeichnet ein Gitternetz.

legend(loc='best')

Kennzeichnungen von Funktionsgraphen, Darstellung von Funktionsgleichungen (auch in LaTeX-Notation).

Die Beschriftungen werden an der bestmöglichen Stelle auf der Zeichenfläche positioniert.

plot(x,y)

Berechnet die Daten der x,y-Koordinaten einer mathematischen Funktion.

set_title('....')

Legt eine Überschrift für den Funktionsplot fest.

set_xlabel('...')

Beschriftet die x-Achse.

set_ylabel('...')

Beschriftet die y-Achse.

set_xlim(...)

Legt den Darstellungsbereich auf der x-Achse fest.

set_ylim(...)

Legt den Darstellungsbereich auf der y-Achse fest.

text(x,y,"text")

Gibt einen Text auf dem Bildschirm aus.

Tabelle 4.5     Wichtige Axes-Methoden

Ein Matplotlib-Programm hat folgende Grobstruktur:

4.2.2    Funktionsplot mit Beschriftungen

Für Facharbeiten und wissenschaftliche Dokumente müssen mehrere Funktionsplots in einem Diagramm dargestellt werden. Um die Funktionsgraphen voneinander unterscheiden zu können, sollte man sie aussagekräftig mit Legenden markieren.

Die LaTeX-Notation ermöglicht ein professionelles Design der Beschriftungen.

LaTeX-Notation

Für Beschriftungen kann auch die LaTeX-Notation verwendet werden. Der LaTeX-Quelltext muss in Dollarzeichen eingebettet werden. Für die Normalparabel y1 = x2 gilt beispielsweise: $y_{1}=x^{2}$.

Kurvenverläufe sollten durch unterschiedliche Farben, Formen und Breiten der Linien optisch ansprechender visualisiert werden.

Es sollen die zwei Funktionsgraphen der Funktionen y1 = x2 und y2 = –10x2 + 100 in einem Diagramm dargestellt werden. Außerdem soll eine Legende die Funktionsgraphen kennzeichnen. Der Schnittpunkt der Funktionsgraphen soll durch einen roten Punkt markiert werden. Abbildung 4.3 zeigt eine mögliche Lösung.

Listing 4.3 zeigt, wie Axes-Objekte für die Beschriftung von Funktionsplots verwendet werden können:

01  #03_ plot_funktion.py
02 import numpy as np
03 import matplotlib.pyplot as plt
04 #x-Werte
05 x = np.linspace(0, 10, 500)
06 #mathematische Funktionen definieren
07 y1 = x**2
08 y2=-10*x+100
09 #Grafikbereich
10 fig, ax = plt.subplots(figsize=(6,6))
11 ax.set_title("Funktionsplot")
12 ax.set_xlabel('x')
13 ax.set_ylabel('y',rotation=True)
14 ax.plot(x,y1,'r--',lw=2,label='$y_1=x^{2}$')
15 ax.plot(x,y2,'b-',lw=2,label='$y_2=-10\cdot x+100$')
16 ax.plot(6.18,38.2,'ro')
17 ax.legend(loc='best')
18 ax.text(6.5,37,"Schnittpunkt")
19 ax.set_xticks(np.arange(0, 11, 1))
20 ax.set_yticks(np.arange(0, 110, 10))
21 ax.grid(True)
22 fig.savefig('parabel.png')
23 plt.show()

Listing 4.3     Funktionsplot mit Beschriftungen

Funktionsplot mit Beschriftungen

Abbildung 4.3     Funktionsplot mit Beschriftungen

Analyse

In den Zeilen 02 und 03 werden das Modul numpy und das Untermodul pyplot importiert. Mit dem Alias np kann auf die NumPy-Funktion linspace() zugegriffen werden (Zeile 05). In Zeile 05 erzeugt die NumPy-Funktion linspace() ein eindimensionales Array vom Typ <class 'numpy.ndarray'> mit 500 Gleitpunktzahlen für den Wertebereich der unabhängigen Variablen x.

In den Zeilen 07 und 08 können Sie jede zulässige mathematische Formel eintragen. Der Python-Interpreter skaliert die y-Achse automatisch.

In Zeile 10 erzeugt die Methode subplots() die Objekte fig und ax als Tupel. Die Zeichenfläche hat eine Abmessung von 6 mal 6 Inch. Der erste Wert legt die Breite und der zweite Wert legt die Höhe der Zeichenfläche fest.

In den Zeilen 12 und 13 beschriften die Methoden set_xlabel() und set_ylabel() die Koordinatenachsen. Den Titel des Funktionsplots und die Achsenbeschriftung können Sie auch mit der Anweisung

ax.set(xlabel='x',ylabel='y',title='Funktionsplot')

auf eine Programmzeile reduzieren. Allerdings schränkt diese Option die Gestaltungsmöglichkeiten der Achsenbeschriftung ein.

Mit dem Objekt ax greifen Sie auf die plot-Methode zu (Zeilen 14 bis 16). Diese Methode speichert die x,y-Koordinatenwerte in eine Liste. Erst der implizite Aufruf der Methode show() in Zeile 23 bewirkt, dass der Funktionsgraph auf dem Monitor angezeigt wird. Wenn Sie die Methode show() auskommentieren, dann wird der Funktionsgraph auch nicht auf dem Bildschirm dargestellt.

Die Argumente der plot-Methoden bestimmen die Eigenschaften der Kurvenformen (Zeilen 14 und 15). In Zeile 14 bewirkt die Eigenschaft 'r--', dass die Parabel als roter gestrichelter Linienzug gezeichnet wird. Die lineare Funktion wird als durchgezogene blaue Linie dargestellt (wegen 'b-'). Die Eigenschaft lw=2 legt die Linienbreite fest. In Zeile 16 bewirkt die Eigenschaft 'ro', dass der Schnittpunkt der Funktionsgraphen als roter Punkt markiert wird. Die Eigenschaft label='$...$' ermöglicht auch die Darstellung mathematischer Funktionen in LaTeX-Notation.

In Zeile 17 legt die Methode legend(loc='best') die optimale Position für die Beschriftungen auf der Zeichenfläche fest.

Mit der Methode text(x,y,"Text") können Sie einen beliebigen Text auf der Zeichenfläche an der Stelle x, y platzieren.

Interessant sind die Skalierungsmöglichkeiten in den Zeilen 19 und 20. Durch die NumPy-Funktion arange() lassen sich die x- und die y-Achse benutzerdefiniert skalieren.

In Zeile 21 bewirkt die Methode grid(True), dass ein Gitternetz für die vorgegebene Skalierung angezeigt wird. Der Parameter True ist optional.

Testen Sie das Programm auch mit der Anweisung ax.axis([0,10,0,100]), und beobachten Sie den Unterschied zur Abbildung 4.3.

Mit dem Objekt fig können Sie auf die Methode savefig('parabel.png') zugreifen (Zeile 22). Diese Methode unterstützt alle gängigen Grafikformate. Diese werden anhand der Dateiendung automatisch erkannt.

4.2.3    Unterdiagramme

Wenn man mehrere Funktionen miteinander vergleichen oder die Ableitungen von Funktionen darstellen möchte, kann es mitunter übersichtlicher sein, diese in separaten, voneinander abgegrenzten Zeichenflächen darzustellen. Diese einzelnen Zeichenflächen werden als Unterdiagramme bezeichnet. Abbildung 4.4 zeigt vier Unterdiagramme mit vier Funktionsplots.

Unterdiagramme

Abbildung 4.4     Unterdiagramme

In der linken Spalte sehen Sie eine Parabel und darunter ihre Ableitung und in der rechten Spalte die Sinus-Funktion mit ihrer Ableitung.

Mit Listing 4.4 können diese Unterdiagramme erzeugt werden.

01  #04_unterdiagramme.py
02 import numpy as np
03 import matplotlib.pyplot as plt
04 #Funktionsdefinition
05 def f(x):
06 return x**2,np.sin(x),x,np.cos(x)
07 #Grafikbereich
08 fig, ax = plt.subplots(2,2,label='Funktionen')
09 x=np.linspace(0,10,100)
10 for i,j,k in [[0,0,0],[0,1,1],[1,0,2],[1,1,3]]:
11 ax[i,j].plot(x,f(x)[k])
12 fig.tight_layout()
13 plt.show()

Listing 4.4     Funktionsplot mit vier Unterdiagrammen

Analyse

In der Zeile 06 stehen die vier mathematischen Funktionen, die in den vier Unterdiagrammen dargestellt werden sollen. Diese Funktionen werden als Tupel zurückgegeben. Die runden Klammern müssen nicht gesetzt werden.

In Zeile 08 erzeugt die Methode subplots(2,2, ...) ein fig-Objekt und vier ax-Objekte. Der erste Eintrag steht für die Anzahl der Zeilen und der zweite für die Anzahl der Spalten.

Mit ax[i,j].plot(x,f(x)[k]) werden die Unterdiagramme in Zeile 11 erzeugt. Man könnte die Unterdiagramme auch ohne Schleifenkonstrukt erstellen:

ax[0,0].plot(x,f(x)[0]) 
ax[0,1].plot(x,f(x)[1])
ax[1,0].plot(x,f(x)[2])
ax[1,1].plot(x,f(x)[3])

Die Methode fig.tight_layout() in Zeile 12 sorgt dafür, dass genügend Abstand zwischen den Unterdiagrammen eingehalten wird.

4.2.4    Das Slider-Steuerelement

Das Slider-Steuerelement spielt eine wichtige Rolle in fast allen nachfolgenden Kapiteln. Es dient dazu, mathematische Zusammenhänge dynamisch zu veranschaulichen: Durch die Verstellung des Slider-Knopfes kann beispielweise gezeigt werden, wie sich die Steigung einer Tangente, die längs einer Kurve verschoben wird, kontinuierlich ändert.

Abbildung 4.5 zeigt die Matplotlib-Programmoberfläche mit einem Slider-Steuerelement. Das Programm berechnet die Quadratzahlen für 1 x 10.

Quadratzahlen mit dem Slider-Steuerelement berechnen

Abbildung 4.5     Quadratzahlen mit dem Slider-Steuerelement berechnen

Wenn Sie den Slider-Knopf verschieben, erscheint simultan das Ergebnis auf der Programmoberfläche.

Listing 4.5 zeigt den grundlegenden Aufbau eines Matplotlib-Programms mit einem Slider-Steuerelement. Es kann als Vorlage für dynamische Funktionsplots benutzt werden.

01  #05_slider.py
02 import numpy as np
03 import matplotlib.pyplot as plt
04 from matplotlib.widgets import Slider
05 #Slider-Einstellung abfragen
06 def update(val):
07 x=sldX.val
08 txtY.set_text("y=%.2f"%x**2)
09 #Grafikbereich
10 fig,ax=plt.subplots(figsize=(6,1.5),label='Slider')
11 fig.subplots_adjust(left=0.12,bottom=0.5)
12 txtY=ax.text(0.45,0.5,'y=25') #Text
13 dicB={'size':'12','facecolor':'red'}
14 #x1, y1, x2, y2
15 xySlider = fig.add_axes([0.1, 0.18, 0.8, 0.12])
16 sldX=Slider(xySlider,'x',valmin=1,valmax=10,valinit=5,handle_style=dicB)
17 sldX.on_changed(update)
18 ax.set_xticks([])
19 ax.set_yticks([])
20 ax.set_frame_on(False)
21 plt.show()

Listing 4.5     Grundprogramm für das Slider-Steuerelement

Analyse

In Zeile 04 wird die Klasse Slider importiert. Mit print(type(Slider)) können Sie sich den Typ vom Slider ausgeben lassen.

Die selbst definierte Python-Funktion update(val) in Zeile 06 fragt die aktuelle Slider-Einstellung ab. In Zeile 07 wird der aktuelle Wert der Slider-Einstellung der Variablen x zugewiesen. In Zeile 08 veranlasst die Methode set_text(), dass das Quadrat von x auf der Programmoberfläche angezeigt werden kann. Angezeigt wird der aktualisierte Wert des Quadrates aber erst dann, wenn in Zeile 17 die Methode on_changed(update) die Funktion update aufruft und in Zeile 21 die Funktion plt.show() aufgerufen wird.

Zeile 11 legt den Bereich der Zeichenfläche fest. In Zeile 12 erzeugt die Methode text(x,y, ...) das Objekt txtY. Es wird in Zeile 08 genutzt. Die beiden Einträge 0.45 und 0.5 legen die x- und y-Koordinate der Platzierung der Textausgabe fest.

In Zeile 13 können Sie das Aussehen des Slider-Knopfes gestalten. Die Daten werden in ein Dictionary gespeichert.

In Zeile 15 wird das Objekt xySlider erzeugt. Es enthält die Daten der x,y-Position sowie der Breite und der Höhe des Sliders.

In Zeile 16 erzeugt der Konstruktor Slider(xySlider,'x',valmin=1,valmax=10,valinit=5,...) der Klasse Slider das Slider-Objekt sldX. Der zweite Eintrag legt die Beschriftung fest. Die folgenden drei Einträge bestimmen den Anfangs- und Endwert sowie den Initialisierungswert.

Die Anweisungen in den Zeilen 18 und 19 verhindern die Anzeige der Achsenbeschriftung. Die Axes-Methode set_frame_on(False) in Zeile 20 unterdrückt die Anzeige des äußeren Rahmens der Koordinatenachsen.

Dieses Programm sollten Sie mit verschiedenen Daten ausführlich testen, damit Sie die dynamischen Programme in den nachfolgenden Kapiteln besser verstehen können.

4.2.5    Funktionsplotter

Ein Funktionsplotter ist ein Programm, das die Funktionsgleichung einer mathematischen Funktion grafisch darstellt. Abbildung 4.6 zeigt die Matplotlib-Programmoberfläche eines einfachen Funktionsplotters. Dieses Programm ist z. B. nützlich, wenn Sie die ungefähre Lage von Nullstellen und lokalen Extremwerten einer Funktion ermitteln wollen.

Ein einfacher Funktionsplotter mit einer TextBox (Textfeld)

Abbildung 4.6     Ein einfacher Funktionsplotter mit einer TextBox (Textfeld)

Die Programmoberfläche besteht aus zwei Bereichen. Im oberen Bereich befindet sich das Ausgabefenster für den Funktionsplot. Darunter ist ein Textfeld (engl. textbox) für die Eingabe des Funktionsterms angeordnet. Das Textfeld wird mit dem Matplotlib-Steuerelement TextBox implementiert. In das Textfeld können Sie gültige mathematische Terme eingeben. Wenn Sie die Funktionsgraphen von e-Funktionen oder trigonometrischen Funktionen darstellen wollen, müssen Sie den Alias np des NumPy-Moduls mit angeben (z. B.: np.exp(x), np.cos(x)). Die Eingabe müssen Sie mit der (¢)-Taste abschließen. Das Programm ist nicht gegen eine fehlerhafte Eingabe abgesichert.

Der Funktionsterm wird mit der eingebauten Python-Funktion eval(expression, globals=None,locals=None) ausgewertet. Der erste Parameter muss ein String sein. Die Bedeutungen der beiden folgenden Parameter werden in der Programmanalyse erklärt.

Listing 4.6 dokumentiert die Implementierung des Funktionsplotters. Als Vorlage diente das Programmbeispiel aus der Matplotlib-Dokumentation. In Zeile 05 können Sie den Wertebereich für die unabhängige Variable x ändern.

01  #06_textbox_funktionsplotter.py
02 import numpy as np
03 import matplotlib.pyplot as plt
04 from matplotlib.widgets import TextBox
05 x1,x2=0,5
06 #Funktionsterm auswerten
07 def auswerten(funktionsterm):
08 y = eval(funktionsterm, {'np': np}, {'x': x})
09 kurve.set_ydata(y)
10 ax.relim()
11 ax.autoscale_view()
12 fig.canvas.draw()
13 #Grafikbereich
14 fig, ax = plt.subplots(label='Funktionsplotter')
15 x = np.linspace(x1,x2,200)
16 kurve, = ax.plot(x,np.zeros_like(x),lw=1.5)
17 fig.subplots_adjust(bottom=0.23)
18 #x-, y-Position, Breite, Höhe
19 xyEingabe = fig.add_axes([0.1, 0.05, 0.8, 0.075])
20 txtEingabe = TextBox(xyEingabe,'y',textalignment='center')
21 txtEingabe.on_submit(auswerten)
22 txtEingabe.set_val('-x**2+5*x-3')
23 ax.set(xlabel='x',ylabel='y')
24 ax.grid(True)
25 plt.show()

Listing 4.6     Funktionsplotter mit dem Steuerelement »TextBox«

Analyse

In Zeile 04 wird die Klasse TextBox aus dem Matplotlib-Untermodul widgets importiert. Wenn Sie mit der Methode TextBox(), dem Konstruktor der Klasse TextBox, ein Objekt erzeugen, können Sie mit diesem Objekt auf Methoden dieser Klasse zugreifen.

In den Zeilen 07 bis 12 wird die Python-Funktion auswerten(funktionsterm) definiert. In Zeile 08 wertet die eingebaute Python-Funktion eval(funktionsterm, {'np': np}, {'x': x}) den String des Funktionsterms aus. Das Dictionary {'np': np} ermöglicht den Zugriff auf mathematische NumPy-Funktionen mit dem Alias np, und das Dictionary {'x': x} legt den Bezeichner der unabhängigen Variablen fest. Im Objekt y sind alle Werte der y-Koordinatendaten als numpy.ndarray gespeichert.

In Zeile 09 erzeugt die Methode set_ydata(y) ein eindimensionales Datenarray aus dem Objekt y. Mithilfe des in Zeile 16 erzeugten Objekts kurve kann die mathematische Funktion dargestellt werden.

Die Methoden relim() und autoscale_view() in den Zeilen 10 und 11 bewirken, dass sich der Wertebereich der y-Achse bei der Eingabe neuer Funktionsgleichungen automatisch anpasst.

In Zeile 12 aktualisiert die Methode draw() den Funktionsplot, wenn Sie eine neue Funktionsgleichung in das Textfeld eintragen und die Eingabe mit (¢) abschließen. Kommentieren Sie diese Zeile aus, und tragen Sie eine neue Funktionsgleichung in das Textfeld ein. Sie werden feststellen, dass das Programm nach der Betätigung der (¢)-Taste nicht mehr korrekt funktioniert.

In Zeile 19 legt die Methode fig.add_axes() die Position der TextBox auf der Programmoberfläche fest. Für die Listeneinträge sind nur Werte zwischen 0 und 1 zulässig.

In Zeile 20 erzeugt die Methode TextBox() das Objekt txtEingabe. Mit diesem Objekt können Sie auf die Methoden on_submit() und set_val() der Klasse TextBox zugreifen. Als erstes Argument wird die Position des Textfeldes xyEingabe übergeben. Der zweite Parameter legt die Beschriftung y fest, und mit dem dritten Parameter textalignment können Sie die Ausrichtung der Formel bestimmen. Standardmäßig ist der Texteintrag auf linksbündig eingestellt.

Zeile 21 ist für die Ereignisverarbeitung zuständig. Dort ruft die Methode on_submit(auswerten) die selbst definierte Python-Funktion aus Zeile 07 auf. Der Parameter funktionsterm darf nicht mit angegeben werden.

In Zeile 22 fügt die Methode set_val() den String '-x**2+5*x-3' in das Textfeld ein. Hier können Sie auch andere mathematische Funktionsterme als Voreinstellung eintragen.