5. Übung

Beispiel 5.1

Numerical Python (NumPy)

In den bisherigen Übungen haben wir zwar schon ein wenig mit Python gerechnet und haben auch bereits Listen und Tupel als Datenstrukturen kennengelernt. Aber so richtig gut ist Python von Haus aus nicht für numerische Aufgaben geeignet. Denn im (ingenieur-)wissenschaftlichen Bereich wenden wir meist Vektor- und Matrizenrechnung an und benutzen dabei jede Menge spezialisierte mathematische Funktionen jenseits von Sinus und Cosinus. (Einer der Gründe für den Erfolg von MATLAB(=MATrix LABoratory) ist dessen Fähigkeit originär mit Matrizen rechnen zu können und eine Unmenge an Spezialfunktionen (Toolboxen) für viele technische Bereiche anzubieten.)

Für Anwendungen in diesem Bereich gibt es in Python die Bibliothek NumPy, deren Entwicklung 1995 begonnen hat und die - ebenso wie die Matplotlib - einen immensen Funktionsumfang enthält. Auf der NunPy-Bibliothek bauen wiederum viele weitere Bibliotheken auf. NumPy zählt aber ebenfalls nicht zur Standard-Distribution, so dass wir auch diese Bibliothek nachzuinstallieren haben:

Unter Windows geben wir folgende Kommandozeile ein:

> pip install numpy

Unter Linux und MacOS hingegen:

$ pip3 install numpy

Beim Installieren werden wir wahrscheinlich sinngemäß die Meldung erhalten, dass "nichts installiert wurde, weil das Modul bereits vorhanden ist". Die Erklärung dafür ist einfach, denn da die Matplotlib auf Numpy aufbaut, wurde es bei der Installation der Matplotlib durch pip gleich mit installiert.

Ein kleiner Auszug der besonderen Merkmale von NumPy:

  1. Es stellt eine Reihe elementarer Datentypen bereit, so dass man anwendungsbezogen den am besten passenden Datentyp wählen kann:

    • Integer-Werte mit 8 - 256-Bit Auflösung mit/ohne Vorzeichen

    • Float-Werte mit 16 - 256-Bit Auflösung

  2. Es lassen sich aus den elementaren Datentypen

    • Vektoren und (mehrdimensionale) Matrizen in der Form von Arrays bilden, wobei alle Elemente immer den gleichen Datentyp haben

    • Strukturierte Arrays bilden, die mehrere benamte Werte des gleichen oder unterschiedlichen Datentyps zusammenfassen (ähnlich den structs in C)

  3. Es stellt eine Vielzahl an Funktionen bereit, die auf den Datentypen und auf den daraus gebildeten Arrays arbeiten (z.B. zur Matrizenmultiplikation)

Der wesentlichste Vorteil von NumPy ist tatsächlich die Möglichkeit mehrdimensionale Arrays eines definierten Datentyps zu bilden. Nur durch diese Eigenschaft ist es möglich hocheffiziente Funktionen in NumPy bereitzustellen, die mit ganzen Vektoren und Matrizen auf einmal umgehen können.

Python-Interpreter und -Bibliotheken

Hinter der Effizienz von NumPy verbirgt sich ganz einfach, dass die NumPy-Aufrufe sämtlich in C geschriebene Funktionen sind, die hochoptimiert sind und von der Laufzeit her viel schneller ausgeführt werden als originärer Python-Code.

Und, um das Ganze noch zu komplettieren: Auch der Python-Interpreter selbst ist wie auch alle anderen Bibliotheken in der Programmiersprache C/C++ programmiert.

Wir werden uns der NumPy-Bibliothek - wie üblich - Schritt-für-Schritt nähern. Was wäre für einen ersten Versuch besser geeignet als unsere "altbekannte" und "vertraute" Sinus-Berechnung:

 1import numpy as np
 2import matplotlib.pyplot as plt
 3
 4# ---- Berechnung --------
 5# 2 np-arrays mit Nullen vom Typ np.float32 anlegen
 6x = np.zeros(100, dtype=np.float32)
 7sin_x = np.zeros(100, dtype=np.float32)
 8
 9# Schrittweite für 100 Schritte im Intervall 0..2PI
10dx = 2*np.pi / 100
11
12# x-Startwert für die for-Schleife
13curr_x = 0.0
14
15# und jetzt über alle 100 Schritte iterieren
16for i_n in range(0,100):
17  x[i_n] = curr_x
18  sin_x[i_n] = np.sin(x[i_n])
19  curr_x += dx
20
21# ---- Anzeige --------
22# figure anlegen
23(fig, ax) = plt.subplots()
24
25# dann plotten
26ax.plot(x, sin_x)
27
28# und anzeigen
29plt.show()

Was im Code grundsätzlich passiert, sollte nach den vorherigen Übungen keiner besonderen Erklärung mehr bedürfen - bis auf die NumPy-Spezialitäten.

Zeilen 6 und 7:

Mit np.zeros() erzeugen wir ein Array mit 100 Datenelementen, von denen jedes einen 32-Bit-float-Wert aufnehmen kann. Wir können den Elementen des Arrays keine unterschiedlichen Datentypen zuweisen und den Datentyp auch nicht einfach so im Programm komplett ändern.

Zeilen 10 und 18:

Hier sehen wir, dass \(\pi\) in NumPy ebenfalls definiert ist und es auch eine sin()-Funktion in NumPy gibt, die zunächst mal so aussieht, wie die sin()-Funktion im math-Modul.

Zeile 26:

Auch die Matplotlib kann mit NumPy-Arrays umgehen, wenn wir diese als Argument übergeben.

Unser erster Eindruck ist also: läuft, sieht aber nach nichts Besonderem aus. Dann schauen wir mal auf eine zweite Realisierung der gleichen Aufgabe:

 1import numpy as np
 2import matplotlib.pyplot as plt
 3
 4# ---- Berechnung --------
 5# x als np-array mit 100 Werten im Intervall 0..2 PI anlegen ...
 6x = np.linspace(0, 2*np.pi, 100)
 7
 8# ... und den Sinus für alle Werte von x berechnen
 9sin_x = np.sin(x)
10
11# ---- Anzeige --------
12# figure anlegen
13(fig, ax) = plt.subplots()
14
15# dann plotten
16ax.plot(x, sin_x)
17
18# und anzeigen
19plt.show()

Das sieht jetzt schon etwas anders aus, denn:

Zeile 6:

Hier erzeugen wir x mit der Funktion np.linspace(), dass uns in diesem Fall 100 Werte im Intervall zwischen \(0 \dots \pi\) liefert.

Zeile 9:

Die Funktion np.sin() ist doch schlauer als wir dachten, die kann nämlich den Sinus für alle Werte in x auf einmal berechnen. (Dahinter steckt aber auch eine Programmschleife, allerdings eine schnelle Schleife in C und keine "langsame" wie in Python.)

So wie die Sinus-Funktion auf einem ganzen Vektor (oder auch ganzen Matrizen) arbeitet, können wir auch andere Rechenoperation der Matrixrechnung anwenden. Wenn wir die Zeile 9 folgendermaßen modifizieren:

sin_x = 0.5*np.sin(2*(x+np.pi/4))

erhalten wir eine Sinusschwingung mit der halben Amplitude, der doppelten Frequenz und einer Phasenverschiebung von \(\pi/4\). Auch hier wird die Multiplikation bzw. die Addition mit einem Skalar von NumPy auf alle Elemente des Arrays angewendet.

Aufgabe 5.1

Als erste Aufgabe zum Warmwerden mit NumPy bleiben wir in unserer vertrauten Welt der trigonometrischen Funktionen.

Aufgabe 5.1.1

To Do

Es ist ein Programm zu erstellen, dass die drei folgenden trigonometrischen Funktionen im Intervall \(0 \dots 2\pi\) wie in der Tabelle angegeben darstellt:

Funktion

Farbe

Linienart

Marker

\(\sin(x)\)

rot

durchgezogen

(ohne)

\(\sin(3x)\)

blau

gestrichelt

(ohne)

\(\sin^2(x)\)

schwarz

(ohne)

+

  1. Die drei Funktionen sollen alle drei in einem gemeinsamen Plot dargestellt werden.

  2. Der Plot soll mit einem Raster, einer Achsenbeschriftung und einer Legende versehen werden.

  3. Zur Berechnung der Funktionen sind NumPy-Datentypen und -Funktionen zu verwenden.

  4. Das Programm soll aus einem Hauptprogramm und zwei Funktionen bestehen:

    1. Funktion zur Berechnung der drei Sinus-Funktionen

    2. Funktion zur Plot-Ausgabe

  5. Das Programm soll eine Meldung, was es macht, beginnen und an den entscheidenden Stellen Kommentare zum Verständnis enthalten.

Lösungshinweise:

  1. Die Berechnung der drei Sinus-Funktionen kann analog zu Beispiel 5.1, also auf das ganze Array angewendet, erfolgen.

  2. Das Plotten mehrerer Kurven in dieselben Achsen kann einfach durch aufeinanderfolgende Aufrufe der plot()-Funktion mit jeweils anderen Parametern erfolgen. Alternativ können auch mehrere Datensätze in einem plot()-Aufruf übergeben werden - einfach herausfinden, wie beides geht!

Aufgabe 5.1.2

To Do

In Abänderung der Aufgabe 5.1.1 soll die grafische Darstellung der drei Funktionen nun wie folgt gestaltet werden:

  1. Die drei Funktionen sollen jeweils in einem Subplot eines gemeinsamen Figures untereinander dargestellt werden.

  2. Die Plots sollen jeweils mit einem Raster, einer Achsenbeschriftung und einer Legende versehen werden.

  3. (Die sonstigen Parameter zur grafischen Darstellung entsprechen weiter den Vorgaben der Tabelle in Aufgabe 5.1.1.)

Lösungshinweise:

  1. Um mehrere (Sub-)Plots in einem Fenster darzustellen, kann man die gewünschte Anzahl und Anordnung der Fenster beim Anlegen des Plot-Fenster durch subplots() als Parameter übergeben. Wenn wir keinen Wert übergeben, wird der Defaultwert(=1) zur Erzeugung eines Plots im Figure angenommen.

  2. Beim Anlegen mehrerer Subplots liefert die Funktion subplots() ein Array zurück, bei dem jeder Eintrag den Achsen eines der Subplots entspricht.

Aufgabe 5.1.3

To Do

Als letztes soll das Programm um drei weitere Funktionen ergänzt werden:

Funktion

Farbe

Linienart

Marker

\(\sin(10x) \cdot \exp^{-0.5x}\)

blau

durchgezogen

(ohne)

\(+\exp^{-0.5x}\)

blau

gestrichelt

(ohne)

\(-\exp^{-0.5x}\)

blau

gestrichelt

(ohne)

Anmerkung: Die beiden unteren Funktionen stellen die sog. "Einhüllende" der abklingenden Sinus-Schwingung dar.

  1. Die drei Funktionen sollen alle drei in einem gemeinsamen Subplot dargestellt werden.

  2. Das Plotfenster ist auf vier Plots in [2x2]-Matrix-Anordnung zu erweitern.

  3. Jeder der Plots soll mit einem Raster, einer Achsenbeschriftung und einer Legende versehen werden.

  4. Zur Berechnung der Funktionen sind NumPy-Datentypen und -Funktionen zu verwenden.

Lösungshinweise:

  1. Wenn man es geschickt anstellt muss man die Exponentialfunktion nur einmal berechnen und kann sie dann an drei Stellen mit entsprechenden Vorzeichen einsetzen.

  2. Um mehrere Plots in einem Figure sowohl neben- wie untereinander anzuordnen übergibt man die Anordnung in Matrixnotation als Tupel (Zeilen, Spalten) und erhält auch die Achsen als zweidimensionales Array zurück.

Beispiel 5.2

Vektor- und Matrizenrechnung mit NumPy

NumPy ist DIE Bibliothek für numerische Berechnungen mit Python. Es ist in seiner Funktionalität mehr oder weniger stark an das kommerzielle Tool MATLAB angelehnt. Aufgrund der "sauberen" Integration in die Programmiersprache Python ist die Integration der Matrizenrechnung nicht ganz so umfassend und transparent wie es in MATLAB der Fall ist.

Anlegen von NumPy-Datenstrukturen

Im Folgenden schauen wir uns beispielhaft an, wie man mit NumPy Datenstrukturen anlegt und wie man diese in Rechnungen nutzt. Zunächst geht es also um das Anlegen von leeren oder vorbesetzten Vektoren und Matrizen:

 1import numpy as np
 2
 3print('---------------------------------')
 4v1 = np.zeros(3, dtype=np.float32)
 5print(v1)
 6print(type(v1))
 7print(v1.dtype)
 8print('---------------------------------')
 9v2 = np.zeros(3, dtype=np.uint8)
10print(v2)
11print(type(v2))
12print(v2.dtype)
13print('---------------------------------')
14v3 = np.zeros_like(v1)
15print(v3)
16print(type(v3))
17print(v3.dtype)
18print('---------------------------------')
19v4 = np.ones(6, dtype=np.float32)
20print(v4)
21print(v4.dtype)
22print('---------------------------------')
23m1 = np.zeros((3,6), dtype=np.float16)
24print(m1)
25print(m1.dtype)
26print('---------------------------------')
27m2 = np.ones((3,6), dtype=np.float16)
28print(m2)
29print(m2.dtype)
30print('---------------------------------')
31m3 = np.eye(3, dtype=np.float16)
32print(m3)
33print(m3.dtype)
34print('---------------------------------')
35v5 = np.array([0.1, 1.2, 2.3])
36print(v5)
37print(v5.dtype)
38print('---------------------------------')
39v6 = np.array([0, 1, 2])
40print(v6)
41print(v6.dtype)

Wie wir anhand des Programmbeispiels und dessen Ausführung sehen, können wir Arrays sowohl mit Nullen oder Einsen gefüllt anlegen oder aber auch eine Liste mit Initialisierungswerten übergeben. Beim Anlegen können wir die Dimensionen und auch den Datentyp (dtype) vorgeben. Geben wir keinen Datentyp vor, so wählt NumPy bei Übergabe einer Liste einen passenden Datentyp aus, ansonsten wird als Standard-Datentyp np.float64 gewählt. Wir können aber auch eine Einheitsmatrix (np.eye()) erzeugen oder uns ein neues Array "nach Vorlage" (np.zeros_like() oder np.ones_like()) erzeugen lassen.

Das Hauptaugenmerk bei den NumPy-Datenstrukturen ist es, diese möglichst kompatibel zu den in der Programmiersprache C verwendeten Datentypen zu halten, da die NumPy-Bibliothek selber in C geschrieben ist. Über die gezeigten Datenstrukturen mit Arrays des gleichen Datentyps gibt es auch noch sogenannte strukturierte Arrays, die den structs aus C entsprechen, die für uns im Moment aber noch nicht von Interesse sind.

Vektor- und Matrizenrechnung mit NumPy

Als nächstes Werfen wir einen exemplarischen Blick auf einige elementare Rechenoperationen mit Vektoren und Matrizen:

 1import numpy as np
 2
 3print('---------------------------------')
 4print('Anlegen von 2 Vektoren')
 5v1 = np.array([1,2,3], dtype=np.float32)
 6print('v1= ', v1)
 7v2 = np.array([4,5,6], dtype=np.float32)
 8print('v2= ', v2)
 9print('---------------------------------')
10print('Anlegen einer Matrix')
11m1 = np.array([[1, 2, 3],
12               [4, 5, 6],
13               [7, 8, 9]], dtype=np.float32)
14print('m1= ', m1)
15print('---------------------------------')
16print('Matrixmultiplikation')
17v3a = np.matmul(v1, m1)
18print('v1*m1= ', v3a)
19print('---------------------------------')
20print('Elementweise Multiplikation')
21v3b = np.multiply(v1, m1)
22print('v1.*m1= ', v3b)
23print('---------------------------------')
24print('Matrix transponieren')
25m2 = m1.transpose()
26print('m1: ', m1)
27print('m2: ', m2)
28print('---------------------------------')
29print('Matrixmultiplikation mit der Transponierten')
30v4 = np.matmul(v1, m1.transpose())
31print('v1*m1\'= ', v4)
32print('---------------------------------')
33print('Vektor-/Matrix-Addition')
34v5 = v1 + v2
35print('v5= ', v5)
36print('---------------------------------')
37print('Vektor-/Matrix-Addition verschachtelt mit Matrixmultiplikation')
38v6 = np.matmul((v1+v2), m2)
39print('v6= ', v6)
40print('---------------------------------')
41print('Multiplikation von 2 Matrizen')
42m3 = np.matmul(m1, m2)
43print('m1*m2= ', m3)
44print('---------------------------------')

Anhand der dargestellten Operationen sehen wir, dass die Matrizenoperationen i.d.R. durch Funktionsaufrufe (teilweise in objektorientierter Notation) realisiert werden. Die Addition/Subtraktion von Vektoren und Matrizen kann auch mit den bekannten Opertoren '+' und '-' dargestellt werden. Der Multiplikatiosoperator '*' ist zwar auch definiert, da es aber sowohl den Fall der "echten" Matrixmultiplikation (Funktion np.matmul()) als auch der elementweisen Multiplikation (Funktion np.multiply()) gibt, kann es hier zu Mißverständnissen und Verwirrungen kommen. Es ist nämlich für Ungeübte keineswegs trivial zu erkennen, wie NumPy die Operation interpretiert. Bei Unklarheiten empfiehlt es sich unbedingt, dies in der Python-Shell oder mit einem kleinen Testprogramm auszuprobieren!

Aufgabe 5.2

In dieser Aufgabe soll der Beschleunigungsvorgang eines in einer Dimension beweglichen Objekts simuliert werden. Die Beschleunigungswerte sollen aus einer Datei eingelesen werden, die nach folgendem Muster aufgebaut ist:

\(t\ [s]\)

\(\Delta a\ [m/s^2]\)

Kommentar

0.0

+1.0

\(a = a+\Delta a = +1.0m/s^2\)

15.5

-1.0

\(a = a+\Delta a = +0.0m/s^2\)

30.5

+0.5

\(a = a+\Delta a = +0.5m/s^2\)

41.0

-2.5

\(a = a+\Delta a = -2.0m/s^2\)

50.0

+2.0

\(a = a+\Delta a = +0.0m/s^2\)

60.0

+0.0

Endzeitpunkt

Anhand der Tabelle für die Beschleunigungsänderungen lässt sich die Bewegung anhand der kinematischen Beziehungen berechnen. In kontinuierlicher Form lauten diese:

\[\begin{split}\begin{eqnarray} s(t) &=& \int v(t) dt \\ v(t) &=& \int a(t) dt \\ a(t) &=& u(t) \ \ \ \text{(abschnittsweise konstant)} \end{eqnarray}\end{split}\]

Da wir \(a(t)\) mittels der sog. Steuergröße \(u(t)\) vorgeben und dies damit die höchste Ableitung darstellt, haben wir es mit einem Modell 2. Ordnung zu tun. Die Größen \(s(t), v(t), a(t)\) beschreiben jeweils den momentanen Bewegungszustand des Objekts und werden deshalb auch als dessen Zustandsgrößen bezeichnet.

Da die Beschleunigung \(a(t)\) die zweite Ableitung des Weges \(s(t)\) darstellt, haben wir es hier mit einem Modell 2. Ordnung zu tun. Die Größen \(s(t), v(t), a(t)\) beschreiben jeweils den momentanen Bewegungszustand des Objekts und werden deshalb auch als dessen Zustandsgrößen bezeichnet. Die Größe \(u(t)\), die wir vorgeben, wird als Steuergröße bezeichnet.

Anmerkung

Die Steuergröße \(u(t)\), die wir mittels unserer Tabelle als \(\Delta a(t)\) vorgeben ist physikalisch nicht ganz korrekt dargestellt. Die zeitliche Änderung der Beschleunigung \(a(t)\) bezeichnet man korrekt als Ruck \(j(t)\), der sich eigentlich nicht sprunghaft ändern kann. Um aus diesem Programmierkurs keine Vorlesung über physikalische Modellierung zu machen, nutzen wir pragmatisch einfach die sprunghafte Änderung \(\Delta a(t)\) zu definierten Zeitpunkten.

Für die Anwendung im Rechner müssen wir das kontinuierliche Bewegungsmodell in ein sog. diskretes Bewegungsmodell überführen, da wir mit einem Digitalrechner nur in diskreten Zeitschritten rechnen können und nicht kontinuierlich.

  1. Sei \(t_k\) ein beliebiger Zeitschritt und seien \(s(t_k)\), \(v(t_k)\) und \(a(t_k)\) die entsprechenden Zustandsgrößen zu diesem Zeitpunkt.

  2. Unter der Annahme, das \(\Delta T\) ein hinreichend kleines Zeitintervall sei und die Zustandsgrößen für die Dauer eines Zeitschritts als konstant angenommen werden können, ergeben sich die Zustandsgrößen für den Zeitpunkt \(t_{k+1}\) zu:

\[\begin{split}\begin{eqnarray} s(t_{k+1}) &=& s(t_{k}) + v(t_{k}) \cdot \Delta T + a(t_{k}) \cdot \frac{\Delta T^2}{2} \tag{5.1} \\ v(t_{k+1}) &=& v(t_{k}) + a(t_{k}) \cdot \Delta T \tag{5.2} \\ a(t_{k+1}) &=& a(t_{k}) + u(t_k) \tag{5.3} \end{eqnarray}\end{split}\]
Anmerkung:

Bei der von uns angenommenen abschnittsweise konstanten Beschleunigung laut o.a. Tabelle, gilt dieses Modell jeweils innerhalb eines Intervalls. An jeder Intervallgrenze folgt ein Beschleunigungssprung und wir setzen die Berechnung prinzipiell in gleicher Weise fort, nur mit einem anderen "konstanten" Wert für \(a(t_k)\).

Intuitive Erklärung des Modells

Das diskrete Modell lässt sich auf Basis des Grundwissens aus der Schulphysik plausibilisieren. Betrachten wir Gleichung (5.1), so stellt:

  • \(s(t_k)\) den zu Beginn des Intervalls erreichten Weg dar, mithin den Ausgangspunkt.

  • \(v(t_k) \cdot \Delta T\) ist der Weganteil, der aufgrund von \(v(t_k)\) im Zeitraum \(\Delta T\) zurückgelegt wird.

  • \(a(t_k) \cdot \frac{\Delta T^2}{2}\) ist der Weganteil, der aufgrund von \(a(t_k)\) im Zeitraum \(\Delta T\) zurückgelegt wird.

Die Erklärungen für die Geschwindigkeit (Gleichung 5.2) und die Beschleunigung (Gleichung 5.3) erfolgen analog.

Es wird also einfach das bekannte Weg-Zeit-Gesetz für ein kleines Zeitintervall angewendet, wobei die Anfangsbedingungen mit einbezogen werden.

Wenn wir nun weiter die Zustandsgrößen in einem Vektor zusammenfassen

\[\begin{split}\underline{x}(t_k) = \underline{x}_k = \left( \begin{array}{c} s(t_k) \\ v(t_k) \\ a(t_k) \end{array} \right)\end{split}\]

dann können wir die drei o.a. Gleichungen auch wie folgt darstellen:

\[\begin{split}\underline{x}_{k+1} = \left( \begin{array}{ccc} 1 & \Delta T & \Delta T^2 / 2 \\ 0 & 1 & \Delta T \\ 0 & 0 & 1 \end{array} \right) \cdot \underline{x}_{k} + \left( \begin{array}{c} 0 \\ 0 \\ 1 \end{array} \right) \cdot u_k = \underline{A} \cdot \underline{x}_{k} + \underline{b} \cdot u_k\end{split}\]

Die Matrix \(\underline{A}\) wird als Systemmatrix und der Vektor \(\underline{b}\) als Steuervektor bezeichnet. Die Übereinstimmung mit den o.a. Gleichungen sollte offensichtlich sein (überprüfen!).

To Do

Es ist ein Programm zu erstellen, dass:

  1. die Beschleunigungswerte für mindestens 5 Zeitintervalle aus einer Datei einliest. (Die Datei ist ebenfalls selbst zu erstellen.)

  2. unter Nutzung der NumPy-Bibliothek die o.a. Berechnung des Zustandsvektors \(\underline{x}\) über alle Zeitintervalle unter Annahme von \(\Delta T = 0.1s\) implementiert.

  3. die Verläufe für die Elemente von \(x = (s, v, a)\) sowie \(u\) mittels der Matplotlib untereinander in 4 einzelnen Subplots darstellt. (Grid, Achsenbezeichnungen, Legenden)

  4. die verschiedenen Aufgaben sinnvoll in Funktionen gliedert, Meldungen an die Nutzer ausgibt und Kommentare enthält.

Lösungshinweise:

  1. Da die Lösung für jeden Zeitschritt vom vorherigen Zeitschritt abhängig ist, können wir hier nicht einfach alle Werte für die \(\underline{x}_k\) gleichzeitig berechnen, sondern müssen in einer Schleife darüber iterieren.

  2. Da wir die Zeit \(t\) für die graphische Darstellung für alle Zeitschritte \(k\) benötigen, bietet es sich an, diese ebenfalls als NumPy-Array anzulegen und darüber zu iterieren.

  3. Zum Lesen/Schreiben von tabellenartigen Dateien gibt es in NumPy die beiden Funktionen loadtxt()/saveTxt(), die direkt ein Array verarbeiten können.

  4. Um den Vektor für \(t\) zu erzeugen bietet sich die NumPy-Funktion np.arange() besser an als die uns schon bekannte Funktion np.linspace().

  5. Auf NumPy-Arrays lässt sich ebenfalls durch Slicing auf Teilbereiche zugreifen - sowohl in eindimensionalen wie in mehrdimensionalen Arrays.

  6. Da das Standardlayout sowie die Standardgröße des durch Matplotlib erzeugten Figure-Fensters meist eher ungünstig ist, empfiehlt es sich, diese selber vorzugeben. In der folgenden Beispielfunktion sehen wir außerdem, wie man auch auf dunklem Hintergrund plotten kann:

 1def plot_dark():
 2  # Werte und Funktionen erzeugen
 3  x = np.linspace(0, 2*np.pi, 100)
 4  sin_x = np.sin(x)
 5  cos_x = 1.5*np.cos(2*x)
 6
 7  # mit der folgenden Anweisung ändern wir die Voreinstellungen zum Plotten
 8  # für den eingerückten Block auf 'dark_background'
 9  with plt.style.context('dark_background'):
10    # figure anlegen
11    (fig, ax) = plt.subplots()
12
13    # Größe des figure einstellen
14    fig.set_size_inches(8, 4)   # (figure_width, figure_height)
15    # Layout auf 'tight' (=schmaler Rand) setzen
16    fig.set_tight_layout(True)
17
18    # dann plotten
19    ax.plot(x, sin_x,'b')
20    ax.plot(x, cos_x,'y')
21    ax.grid()
22    ax.set_xlabel('x')
23    ax.legend(['sin(x)','1.5*cos(2*x)'])
24
25    plt.show()

Hier noch ein Beispiel, wie das Ergebnis aussehen sollte, wenn wir die o.a. Beispieltabelle verwenden:

../_images/exer_05_matplotlib_02.PNG