1
0
Fork 0
mirror of https://gitlab.rlp.net/pgp/pgp1-python-einfuehrung synced 2024-10-12 13:24:22 +00:00
pgp1-python-einfuehrung/Kapitel_1._Einstieg_in_die_Welt_von_Python.ipynb

1173 lines
41 KiB
Text
Raw Normal View History

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 1. Einstieg in die Welt von Python:\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In Ihrer Vorbereitung haben Sie bisher die folgenden Konzepte kennengelernt:\n",
"\n",
"* Aufbau eines Jupyter-Notebooks (Aufgabe 1).\n",
"* Einfache Rechenoperationen (Aufgabe 2 a.)\n",
"* Einfache Zeichenketten (engl. Strings) und formatierte Strings (Aufgabe 2 b.).\n",
"* Das Definieren von Funktionen (Aufgabe 3.)\n",
"* Das Definieren von Messtabellen.\n",
"\n",
"Hierauf wollen wir an unserem heutigen Versuchstag aufbauen."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Arbeiten mit Messreihen:\n",
"\n",
"Bisher hat uns das programmieren eher mehr Arbeit gemacht als uns welche abgenommen. Zeitersparnis bekommen wir, wenn wir viele Rechnungen hintereinander ausführen müssen. Hierfür gibt es die **for**-Schleife. Diese Schleife führt die gleichen Zeilen eins Codes wiederholt für die Elemente in einer Liste aus:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:07:49.905202Z",
"start_time": "2019-11-04T12:07:49.889579Z"
}
},
"outputs": [],
"source": [
"liste = [1, 2, 3, 4]\n",
"\n",
"for wert in liste:\n",
" print('Wert:', wert)\n",
" rechnung = wert + 2\n",
"\n",
"print('Rechnung:', rechnung)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bei einer Schleife ist darauf zu achten, dass der Anweisungsblock, welcher wiederholt ausgeführt werden soll, mit 4x Leerzeichen eingrückt wurde. Dies entspricht einmal die **Tab-Taste**:\n",
"\n",
"<img src=\"images/Tab-Key.png\" alt=\"Tab-Taste\" width=\"80\"/>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:08:53.901374Z",
"start_time": "2019-11-04T12:08:53.885753Z"
}
},
"outputs": [],
"source": [
"liste = [1, 2, 3, 4]\n",
"print('Hier läuft das Hauptprogramm')\n",
"\n",
"for wert in liste:\n",
" print('Schleife')\n",
" print('Wert:', wert)\n",
" rechnung = wert + 2\n",
" \n",
"print('Hier läuft wieder das Hauptprogramm')\n",
"rechnung = rechnung + 5\n",
"print('Letztes Ergebnis + 5: ', rechnung)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Statt das Ergebnis lediglich per `print`-Anweisung darstellen zu lassen, können wir auch unser Wissen um Listen benutzen und die berechneten Werte einer neuen Liste anfügen:"
]
},
2020-08-12 15:02:51 +00:00
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# (Funktion haben wir bereits in der Vorbereitung definiert)\n",
"def Spannung(Strom, Widerstand):\n",
" '''\n",
" Diese Funktion berechnet die Spannung eines Ohmschen \n",
" Widerstands.\n",
" \n",
" Args:\n",
" Strom (float): Der gemessene Strom in mA.\n",
" Widerstand (float): Der Wert des verwendeten Widerstands\n",
" in Ohm.\n",
" \n",
" Returns:\n",
" float: Die berechnete Spannung in V.\n",
2020-08-12 15:02:51 +00:00
" '''\n",
" return Widerstand * Strom/1000"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:10:08.503300Z",
"start_time": "2019-11-04T12:10:08.472059Z"
}
},
"outputs": [],
"source": [
"Stromwerte = [101, 105, 98, 87, 112] # mA\n",
"Spannungswerte = [] # Einheit? <-- Deshalb Docstrings und Help!\n",
"Widerstand = 100 # Ohm\n",
"\n",
"for Strom in Stromwerte:\n",
2020-08-12 15:02:51 +00:00
" res = Spannung(Strom, Widerstand)\n",
" Spannungswerte.append(res)\n",
"\n",
"Spannungswerte"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Python ermöglicht uns auch eine kompaktere Schreibweise, die so genannte \"list comprehension\": "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:11:40.799393Z",
"start_time": "2019-11-04T12:11:40.783772Z"
}
},
"outputs": [],
"source": [
"Spannungswerte = [Spannung(Strom, 100) for Strom in Stromwerte]\n",
"Spannungswerte"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wir können auch über mehrere Daten gleichzeitig \"loopen\". Hierzu kann die `zip` Anweisung genutzt werden. `zip` verbindet hierbei die einzelnen Elemente einer Liste wie bei einem Reißverschluss miteinander:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:12:42.522873Z",
"start_time": "2019-11-04T12:12:42.507254Z"
}
},
"outputs": [],
"source": [
"Werte1 = ['A', 'B', 'C', 'D']\n",
"Werte2 = [0, 1, 2, 3]\n",
"\n",
"for w1, w2 in zip(Werte1, Werte2):\n",
" print(w1, ' und ', w2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dies kann zum Beispiel dann hilfreich sein, wenn sich mehr als eine Variable ändern soll, z.B. bei einer Messreihe für die Schallgeschwindigkeit in Luft:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:13:30.363510Z",
"start_time": "2019-11-04T12:13:30.347888Z"
}
},
"outputs": [],
"source": [
"# Gemessene Werte:\n",
"frequenzen = [30.17, 30.63, 30.01, 29.98, 30.12, 29.87, 29.94] #kHz\n",
"wellenlängen = [11.12, 11.34, 11.45, 11.25, 11.01, 11.45, 11.23] # mm\n",
"\n",
"# Variante 1:\n",
"schallgeschindigkeiten = [] # m/s\n",
"\n",
2020-08-12 15:02:51 +00:00
"for f, l in zip(frequenzen, wellenlängen):\n",
" schallgeschindigkeiten.append(f*l)\n",
"\n",
"print(schallgeschindigkeiten)\n",
"\n",
"# oder Variante 2:\n",
"schallgeschindigkeiten2 = [f*l for f,l in zip(frequenzen, wellenlängen)]\n",
"print(schallgeschindigkeiten2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wir können auch die `zip`-Anweisung mit mehr als nur zwei Listen verwenden:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:13:49.912658Z",
"start_time": "2019-11-04T12:13:49.897039Z"
}
},
"outputs": [],
"source": [
"l1 = ['a', 'b', 'c']\n",
"l2 = [1, 2, 3]\n",
"l3 = ['x', 'y', 'z']\n",
"\n",
"for i,j,k in zip(l1, l2, l3):\n",
" print(i, 'und', j, 'und', k)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=task>\n",
" \n",
"#### Aufgabe 4.b.: Werte berechnen:\n",
"Kopieren Sie Ihre Lösung von Aufgabe 4.a. aus der Vorbereitung in das Notebook und berechnen Sie nun für die Messwerte aus Aufgabe 4 a. die Leistung $P$ und den Widerstand $R$ sowie deren Fehler. Nutzen Sie hierfür die ausführliche schrebweise der **for**-Schleife im Fall des Widerstands $R$ und den list-comprehension Syntax für die Leistung $P$.\n",
"<div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:46:37.221396Z",
"start_time": "2019-11-04T12:46:37.190151Z"
}
},
"outputs": [],
"source": [
2020-08-12 15:02:51 +00:00
"# Hier eine kleine Hilfestellung für den Start:\n",
"# Messwerttabelle aus Aufgabe 4. a.:\n",
"spannung_werte = [12., 11.78, 12.56, 12.34, 12.01, 11.94]\n",
"strom_werte = [110, 98, 102, 124, 105, 95]\n",
"dspannung_werte = [0.32, 0.15, 0.63, 0.12, 0.20, 0.17]\n",
"dstrom_werte = [10] * len(strom_werte)\n",
"widerstand_werte = []\n",
"\n",
2020-08-12 15:02:51 +00:00
"# Beispiel für die Berechnung des Widerstandes:\n",
"def res(i, u):\n",
" r = u / i\n",
" return r\n",
"\n",
"for strom, spannung in zip(strom_werte, spannung_werte):\n",
" widerstand_werte.append(res(strom, spannung))\n",
2020-08-12 15:02:51 +00:00
"\n",
"# Jetzt sind Sie gefragt:\n",
2020-08-12 15:02:51 +00:00
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Darstellung von Messdaten mittels `Matplotlib`:\n",
"Das Plotten von Daten ist eines der wichtigsten Mittel, um eine Fülle von Informationen kompakt und verständlich seinem Gegenüber darzubieten. Gute Plots zu erstellen kann eine regelrechte Kunst sein und ist für ein gutes Paper, bzw. eine gute Bachelor- bzw. Masterarbeit unverzichtbar. \n",
"\n",
"<figure class=\"image\">\n",
"<img src=\"images/MaterialPythonkurs092018/Xenon1tResults1yearx1texposure.png\" alt=\"{{ Xenon1t results 2018 }}\" width=50%>\n",
"<figcaption>Resultate des XENON1T Dunkle Materie Experiments. Die Graphik wurde mittels Matplotlib in Python erstellt. </figcaption>\n",
"</figure>\n",
"\n",
"Jede Programmiersprache verfügt über zusätzliche Pakete (im Englischen \"packages\"), welche die Funktionalität der verwendeten Programmiersprache erweitern. **Matplotlib** ist ein umfangreiches Package, welches das Zeichnen von 2D und 3D Grafiken ermöglicht. Alle Parameter und Einstellungen einer Grafik werden entsprechend des Python-Codes eingestellt. Dadurch wird das Erstellen der Grafik reproduzierbar und man kann schnell dieselbe Grafik mit neuen Daten füttern.\n",
"\n",
"Es ist unmöglich, alle Möglichkeiten und Einstellungen, die **Matplotlib** bietet, auswendig zu kennen. Mit der Zeit werden Sie ein solides Grundwissen der gängisten Befehle haben. Für alles Weitere hilft die [Matplotlib-Dokumentation mit ihren Beispielen](http://matplotlib.org/). Des Weiteren ist insbesondere hier die **IPython-Hilfe** und die **automatische Vervollständigung von Befehlen** besonders hilfreich.\n",
"\n",
"Für das Praktikum wollen wir uns zunächst lediglich drei unterschiedliche Arten von Plots angucken:\n",
"\n",
"* Normale Liniengrafiken\n",
"* Plots mit Fehlerbalken\n",
"* Histogramme "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zunächst müssen wir Python mitteilen, dass wir das **Matplotlib** package nutzen wollen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:52:38.927838Z",
"start_time": "2019-11-04T12:52:36.881444Z"
}
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`import` läd für uns aus dem package matplotlib das Modul `pyplot`. Mit Hilfe des Zusatzes `as plt` wird ein \"alias\" (Abkürzung) erstellt. Dieser Alias erspart uns im Nachfolgenden Arbeit, wie wir im nachfolgenden Beispiel sehen können:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:53:06.331480Z",
"start_time": "2019-11-04T12:53:05.987810Z"
}
},
"outputs": [],
"source": [
"plt.plot([1,2,3,4,5], # <-- x-Daten\n",
" [1,2,3,4,5] # <-- y-Daten\n",
" )\n",
"plt.show() # <-- Zeigen des Plots"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Hätten wir den Alias nicht definiert, hätten wir den folgenden etwas länglichen Code benötigt, den Sie niemals nutzen sollten:\n",
"\n",
"```python\n",
"matplotlib.pyplot.plot([1,2,3,4,5], [1,2,3,4,5])\n",
"matplotlib.pyplot.show()\n",
"```\n",
"\n",
"Innerhalb der Python-Community haben sich ein paar Standards etabliert, an welche man sich halten sollte. So ist für `matplotlib.pyplot` der Alias `plt` zu verwenden.\n",
"\n",
"Im oberen Beispiel haben Sie nun auch bereits gesehen, wie wir einfache Liniengrafiken erstellen können. Dabei sieht der Plot noch etwas blass aus. Dies können wir mit ein paar zusätzlichen Befehlen und Argumenten ändern."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:54:21.547247Z",
"start_time": "2019-11-04T12:54:21.226301Z"
}
},
"outputs": [],
"source": [
"xdaten = [1,2,3,4,5]\n",
"ydaten = [1,2,2,4,5]\n",
"\n",
"plt.plot(xdaten, ydaten, # <-- Wie eben die x und y daten\n",
" color='red', # <-- Farbe der Linie\n",
" linestyle='dashed', # <-- Linientyp\n",
" label='Spannungskurve', # <-- Name der Linie\n",
" )\n",
"plt.xlabel('X-Achse') # <-- Beschriftung der x-Achse\n",
"plt.ylabel('Y-Achse') # <-- Beschiftung der y-Achse\n",
"plt.legend() # <-- Hinzufügen der Legend mit den \n",
" # in plot definierten labels\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Viele der eben verwendeten Optionen bieten euch unterschiedliche Auswahlmöglichkeiten:\n",
"\n",
"**Linestyle:**\n",
"* `''`: keine Linie\n",
"* `'-'`: durchgehende Linie\n",
"* `'--'`: gestrichelte Linie\n",
"* `'-.'`: Strich-Punktlinie\n",
"* `':'`: Punktlinie\n",
"\n",
"**Color**:\n",
"* red, blue, yellow, ...\n",
"* RGB Werte von 0 bis 1 (statt von 0 bis 255): (1, 1, 1), (1, 0.2, 0.4)\n",
"\n",
"Darüber hinaus gibt es auch noch andere nützliche Styleoptionen wie `alpha`, was die Transparenz der Linie ändert (Werte zwischen 0-1), oder die `linewidth`-Option, mit dessen Hilfe Sie die Linienbreite ändern können.\n",
"\n",
"Auch die anderen Befehle, welche wir verwendetet haben, verfügen über zusätzliche Optionen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:55:35.863633Z",
"start_time": "2019-11-04T12:55:35.535586Z"
}
},
"outputs": [],
"source": [
"xdaten = [1,2,3,4,5]\n",
"ydaten = [1,2,2,4,5]\n",
"\n",
"plt.plot(xdaten, ydaten, \n",
" color='red', \n",
" linestyle='dashed', \n",
" label='Graph 1' \n",
" )\n",
"plt.xlabel('X-Achse',\n",
" color=(0,1,0) # <-- Beschriftungsfrabe\n",
" ) \n",
"\n",
"plt.ylabel('Y-Achse', \n",
" fontsize=14) # <-- Beschiftungsgröße\n",
"\n",
"plt.legend(title='Messwerte', # <-- Legendentitel\n",
" loc=3) # <-- Legendenposition: \n",
" # 0: Best, \n",
" # 1: Oben Rechts \n",
" # 2: Oben Links\n",
" # 3: Unten Links \n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sofern ihr mehrere Graphen in einen Plot zeichnen möchtet geht dies auch ganz einfach."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:56:56.976082Z",
"start_time": "2019-11-04T12:56:56.644588Z"
}
},
"outputs": [],
"source": [
"xdaten = [-3, -2, -1, 0, 1, 2, 3]\n",
"ydaten1 = xdaten\n",
"ydaten2 = [x**2 for x in xdaten]\n",
"ydaten3 = [x**3 for x in xdaten]\n",
"\n",
"plt.plot(xdaten, ydaten1, label='Linear')\n",
"plt.plot(xdaten, ydaten2, label='Quadratisch')\n",
"plt.plot(xdaten, ydaten3, label='Cubisch')\n",
"\n",
"plt.legend(title='Exponent')\n",
"plt.xlabel('X-Werte')\n",
"plt.ylabel('Y-Werte')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ihr seht, das `plot` zwischen den angegebene Werte interpoliert. Möchtet ihr eine glatte Kurve zeichnen so müsst ihr die Anzahl an Punkten für die Interpolation erhöhen."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T12:58:19.439740Z",
"start_time": "2019-11-04T12:58:19.116107Z"
}
},
"outputs": [],
"source": [
"def cubic(x):\n",
" '''\n",
" Eine Funktion, die den cubischen Wert einer Zahl zurück gibt.\n",
" '''\n",
" return x**3\n",
"\n",
"\n",
"x1 = range(-3, 4, 1) # <- Werte zwischen -3 und 3\n",
"x2 = [i / 10 for i in range(-30, 31, 1)] # <- 10 mal mehr Werte\n",
"\n",
"y1 = [cubic(j) for j in x1]\n",
"y2 = [cubic(value) for value in x2]\n",
"\n",
"\n",
"plt.plot(x1, y1, label='Werte 1', linestyle='dashed')\n",
"plt.plot(x2, y2, label='Werte 2')\n",
"\n",
"plt.xlabel('x-Werte')\n",
"plt.ylabel('y-Werte')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Errorbarplot\n",
"\n",
"In der Physik gehören zu jedem gemessen Wert eine Messunsicherheit / ein Messfehler. Diese Fehler sollten natürlich auch in unseren Grafiken korrekt dargestellt werden. Hierfür können wir den `errorbar`-Plot verwenden."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:11:34.208204Z",
"start_time": "2019-11-04T13:11:33.895770Z"
}
},
"outputs": [],
"source": [
"spannung = [0.9, 2.0, 3.0, 4.1, 4.9, 6.2] # [V]\n",
"strom = [105, 204, 298, 391, 506, 601] # [mA]\n",
"spannung_error = [0.3]*len(spannung) # Konstanter Ablesefehler [V]\n",
"strom_error = [14, 9, 12, 8, 7, 11] # gemessener schwankender Fehler[mA]\n",
"\n",
2020-08-12 15:02:51 +00:00
"# plt.errorbar() # <--- Wie verwende ich den errorbar plot?\n",
"\n",
"plt.ylabel('Spannung [V]')\n",
"plt.xlabel('Strom [mA]')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=task>\n",
" \n",
"#### Aufgabe 5.: Erstellen einer `errorbar`-Plot:\n",
"\n",
"Editieren Sie die obere Zelle so, dass Sie mithilfe des Befehls \n",
"\n",
"```python\n",
"plt.errorbar()\n",
"```\n",
"\n",
"einen Errorbarplot erstellen. Verwenden Sie hierfür die IPython-Hilfe-Funktion, um die exakte Syntax zu erfahren. \n",
"\n",
"**Erinnerung:**\n",
"Sie können die IPython-Hilfe aufrufen, indem Sie den Cursor innerhalb des Worts errorbar von plt.errorbar bewegen und die Tastenkombination **Shift + Tab** verwenden. Lesen Sie nun nach, wie Sie die x- und y-Werte und deren Fehler an die Funktion übergeben müssen."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Leider ist diese Standardvariante des Errorbar-Plots noch nicht das, was wir möchten. Die Messwerte sind linear interpoliert und die errorbars sehen noch etwas eigenartig aus. Dies können wir jedoch im Handumdrehen ändern. Kümmern wir uns zunächst um die Plotmarker:"
]
},
{
"cell_type": "code",
2019-10-31 13:05:56 +00:00
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-31T12:32:08.949153Z",
"start_time": "2019-10-31T12:32:08.543000Z"
}
},
2019-10-31 13:05:56 +00:00
"outputs": [],
"source": [
"#plt.errorbar(, \n",
"# ,\n",
"# ,\n",
"# , \n",
"# # Änderungen für plotmarker: | Kurzform:\n",
"# linestyle='', # <-- Schaltet den Linienstyle aus | ls=''\n",
"# marker='d', # <-- Ändert den Markertyp in Diamanten | -----\n",
"# markerfacecolor='orange', # <-- Ändert die Markerfarbe zu Orange | mfc='orange'\n",
"# markeredgecolor='k', # <-- Setzt die Kantenfarbe auf schwarz | mec='k'\n",
"# markersize=7 # <-- Ändert die Markergröße | ms='7'\n",
"# ) \n",
"\n",
"plt.ylabel('Spannung [V]')\n",
"plt.xlabel('Strom [mA]')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All die Optionen, welche wir hier für die Plotmarker verwendet haben, können wir auch in der normalen `plt.plot`-Anweisung verwenden. Dabei gibt es eine ganze Fülle an unterschiedlichen [Marker-Symbole](http://matplotlib.org/api/markers_api.html):\n",
" \n",
"* `+`: Plus\n",
"* `o`: Kreis\n",
"* `*`: Stern\n",
"* `,`,`.`: kleiner und sehr kleiner Punkt\n",
"* `s`: Quadrat\n",
"* `p`: Pentagon\n",
"* `h`: Hexagon\n",
"* `1`, `2`, `3`, `4`: nach unten, oben, links, rechts zeigendes Dreieck\n",
" \n",
"Nach dem wir uns um unsere Marker gekümmert haben, müssen wir nun auch noch unsere Fehlerbalken enstprechend anpassen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:11:52.919783Z",
"start_time": "2019-11-04T13:11:52.638600Z"
}
},
"outputs": [],
"source": [
"plt.errorbar(strom, \n",
" spannung,\n",
" xerr=strom_error,\n",
" yerr=spannung_error, \n",
" ls='', \n",
" marker='d', \n",
" mfc='orange', \n",
" mec='k', \n",
" ms=7,\n",
" # Fehlerbalken optionen:\n",
" ecolor='k', # <-- Ändert die Linienfarbe der errorbars\n",
" elinewidth=2, # <-- Ändert die Fehlerbalkenbreite\n",
" capsize=5, # <-- Ändert die Breite der Endkappen der Fehlerbalken\n",
" capthick=2, # <-- Ändert die Dicke der Endkappen\n",
" ) \n",
"\n",
"plt.ylabel('Spannung [V]')\n",
"plt.xlabel('Strom [mA]')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Histogramme:\n",
"\n",
"Ein weiterer Plottyp, welcher häufig Verwendung findet, ist das Histogramm. Um unser Histogramm mit Pseudozufallszahlen zu bestücken, müssen wir diese erst erzeugen. Hierfür können wir das `numpy`-Modul verwenden. `numpy` ist ein weiteres Standardmodul, welches viele nützliche Funktionen mit sich bringt. Hier wollen wir uns jedoch nur auf die Erstellung von Zufallszahlen beschränken. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:13:40.357937Z",
"start_time": "2019-11-04T13:13:40.342316Z"
}
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`np` ist eine konvetionelle Abkürkung."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:13:40.844488Z",
"start_time": "2019-11-04T13:13:40.828850Z"
}
},
"outputs": [],
"source": [
"rnd_numbers = np.random.normal(0, 1, 1000) # <-- Hier werden 1000 gausförmig verteile Zufallszahlen\n",
" # mit einem Mittelwert von 0 und einer Standardabweichung \n",
" # von 1 erzeugt."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Das Histgromm lässt sich ganz einfach mit der `plt.hist`-Anweisung erstellt:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:13:52.473958Z",
"start_time": "2019-11-04T13:13:52.177152Z"
}
},
"outputs": [],
"source": [
"plt.hist(rnd_numbers)\n",
"\n",
"plt.xlabel('Zufallswert')\n",
"plt.ylabel('Anzahl der Einträge')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Auch für Histogramme gibt es viele unterschiedliche Optionen, welche Sie entweder mithilfe der Help-Funktion oder anhand der Beispiele in der [Matplolib-Dokumentation](http://matplotlib.org/) herrausfinden können."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:15:09.390753Z",
"start_time": "2019-11-04T13:15:09.031464Z"
}
},
"outputs": [],
"source": [
2020-08-12 15:02:51 +00:00
"rnd_numbers2 = np.random.normal(1, 2, 1000)\n",
"\n",
"\n",
"plt.hist(rnd_numbers, \n",
" bins=13, \n",
" range=(-3,5), # <-- Achtung: Im Gegensatz zur range-Anweisung ist \n",
" # das Intervall hier geschlossen [-3, 5]\n",
" histtype='step', # Ändert den Balkentyp in Stufen\n",
" linestyle='dashed',\n",
" label='Verteilung 1'\n",
" )\n",
"\n",
"plt.hist(rnd_numbers2, \n",
" bins=13,\n",
" range=(-3,5),\n",
" alpha=0.5, # Ändert die Transparenz der Balken \n",
" label='Verteilung 2'\n",
" )\n",
"\n",
"plt.legend()\n",
"plt.xlabel('Zufallswert')\n",
"plt.ylabel('Anzahl der Einträge')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bei Histogrammen sollten Sie immer darauf achten, dass das \"binning\" sinnvoll gewählt ist. Weder zu viele noch zu wenige Bins führen zu einer sinnvollen Darstellung Ihrer Daten."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T13:15:48.283946Z",
"start_time": "2019-11-04T13:15:47.327389Z"
}
},
"outputs": [],
"source": [
"plt.hist(rnd_numbers, \n",
" bins=100, \n",
" range=(-3,3),\n",
" label='Zu viele bins'\n",
" )\n",
"\n",
"plt.legend()\n",
"plt.xlabel('Zufallswert')\n",
"plt.ylabel('Anzahl der Einträge')\n",
"plt.show()\n",
"\n",
"plt.hist(rnd_numbers, \n",
" bins=3, \n",
" range=(-3,3),\n",
" label='Zu wenige bins'\n",
" )\n",
"\n",
"plt.legend()\n",
"plt.xlabel('Zufallswert')\n",
"plt.ylabel('Anzahl der Einträge')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nach dem wir jetzt die verschiedenen Plottypen mit ihren unterschiedlichen Optionen kennengelernt haben, möchten wir diese natürlich auch speichern können. Dies können wir auf zwei unterschiedliche Arten machen.\n",
"\n",
"Entweder Sie machen mit Ihrer Maus einen Rechtsklick auf die Grafik und wählen \"Grafik speichern als\" aus, oder Sie verwenden statt der `plt.show`- die `plt.savefig`-Anweisung dafür, wobei Letzteres empfohlen ist."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=task>\n",
" \n",
"#### Aufgabe 6.: Erstellen einer gauss'schen Wahrscheinlichkeitsdichte:\n",
"\n",
"Im folgenden wollen wir ein Plot mit einer gauss'schen Wahrscheinlichkeitsdichte erstellen. Gehen Sie hierfür wie folgt vor:\n",
"\n",
"1. Erstellen Sie 500000 pseudo-Zufallszahlen, welche einer Gaußverteilung mit $µ=5$ und $sigma=2$ folgen.\n",
"2. Tragen Sie die Zufallszahlen in ein Histogramm ein und normieren Sie dieses, sodass die Gesamtfläche 1 beträgt. **Tipp: `plt.hist` hat hierfür einen optionalen Parameter. Benutzen Sie die Help oder das Internet, um herrauszufinden, welcher es ist.**\n",
"3. Wählen Sie eine geeignete `range` und ein `binning` von 100 für das Histogram.\n",
"4. Plotten Sie anschließend die dazugehörige Gaußverteilung als Funktion. Gehen Sie dabei wie folgt vor:\n",
" 1. Erstellen Sie eine Gaußfunktion. *Erinnerung:* eine Gaußverteilung ist gegeben durch:\n",
" $$g(x, \\mu, \\sigma) = \\frac{1}{\\sqrt{2 \\pi} \\, \\sigma} \\exp\\bigg( \\frac{ -(x - \\mu)^2}{2 \\sigma^2}\\bigg) $$\n",
" **Tipp:** Das Numpy-Paket beinhaltet die Zahlen $\\pi$ und die Exponentialfunktion. Sie können diese über `np.pi` und `np.exp()` verwenden. \n",
" 2. Erstellen Sie eine Liste von x-Werten in der von Ihnen gewählten range in 0.1er Schritten. Verwenden Sie hierfür die `range`-Funktion zusammen mit der list-comprehension.\n",
" 3. Erstellen Sie den plot.\n",
"Das Ergebnis sollte wie folgt aussehen:\n",
"\n",
"<figure class=\"image\">\n",
"<img src=\"images/MaterialPythonkurs092018/Gaußverteilung.png\" alt=\"{{ Gaussverteilung }}\" width=70%>\n",
"</figure>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fitten von Messdaten:\n",
"\n",
"### Methode der kleinsten Quadrate\n",
"\n",
"Die Herleitung dieser Methode befindet sich im separaten Notebook `Herleitung_Methode_der_kleinsten_Quadarate.ipynb`.\n",
"\n",
"Diese Methode ist in der Funktion `curve_fit` implementiert."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:03:03.767521Z",
"start_time": "2019-11-04T14:03:02.583918Z"
}
},
"outputs": [],
"source": [
"from scipy.optimize import curve_fit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Gucken wir uns einen Fit ohne Messfehler an um die Funktion etwas näher kennenzulernen."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:04:02.759738Z",
"start_time": "2019-11-04T14:04:02.714523Z"
}
},
"outputs": [],
"source": [
"# Und jetzt fitten wir:\n",
"para, pcov = curve_fit(Spannung, # <-- Funktion, die an die Messdaten gefittet werden soll\n",
" strom, # <-- gemessenen \"X\"-Werte\n",
" spannung # <-- gemessenen \"Y\"-Werte \n",
" )\n",
"\n",
2020-08-12 15:02:51 +00:00
"print(para[0])\n",
"print(pcov[0,0]**0.5)\n",
"\n",
"print(f'Widerstand R {para[0]:.2f} +/- {pcov[0,0]**0.5:.2f} Ohm')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sie sehen `curve_fit` gibt uns zwei unterschiedliche Listen zurück. Die erste Liste `para` beinhaltet die berechneten Fitparameter. `pcov` hingegen ist eine [Kovarianzmatrix](https://de.wikipedia.org/wiki/Kovarianzmatrix) auf deren Diagonalen Sie die Varianzen ($\\sigma^2$) der einzelnen Parameter finden (auf der Nebendiagonalen befinden sich die Kovarianzen). D.h. bei einer Funktion mit drei Parametern `def f(x, p1, p2, p3):` würde `para` und `pcov` allgemein so aussehen:\n",
"\n",
"```\n",
"para = [p1, p2, p3]\n",
"pcov = [[cov_1,1, cov_1,2, cov_1,3], \n",
" [cov_2,1, cov_2,2, cov_2,3],\n",
" [cov_3,1, cov_3,2, cov_3,3]]\n",
"```\n",
"wobei `cov_i,i` wie bereits erwähnt die einzelnen Kovarianzen bzw. Varianzen sind. Aber was genau macht jetzt curve_fit eigentlich, um auf diese Werte zu kommen? Wie bereits erklärt, basiert `curve_fit` auf der Methode der kleinsten Quadrate. D.h. die Funktion probiert etliche verschiedene Varianten Ihrer Parameter durch, bis es die Kombination gefunden hat, bei der das $\\chi^2$ klein wird. Gucken wir uns mal ein paar Zwischenschritte für unser Beispiel des ohm'schen Widerstandes an: \n",
"\n",
"<figure class=\"image\">\n",
"<img src=\"images/MaterialPythonkurs092018/Fitting_gif.gif\" alt=\"{{ Least Square Beispiel }}\" width=100%>\n",
"</figure>\n",
"\n",
"Nach dem wir nun wissen, was genau `curve_fit` macht, wollen wir unser Resultat etwas schöner darstellen:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:06:00.969312Z",
"start_time": "2019-11-04T14:06:00.676047Z"
}
},
"outputs": [],
"source": [
"plt.plot(strom, \n",
" spannung, \n",
" ls='', \n",
" marker='d', \n",
" mfc='orange', \n",
" mec='k', \n",
" ms=7,\n",
" label='Messwerte aus A. 5 (ohne Fehler)'\n",
" ) \n",
"plt.plot(strom, \n",
" [Spannung(value, para[0]) for value in strom], \n",
" ls ='dashed',\n",
" color='orange',\n",
" label = f'Fitgerade mit R = {para[0]:0.2f} +/- {pcov[0,0]**(1/2):0.2f} Ohm'\n",
" )\n",
"\n",
"plt.legend()\n",
"plt.ylabel('Spannung [V]')\n",
"plt.xlabel('Strom [mA]')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Das Ergebnis sieht bereits ganz gut aus, allerdings kennt hier unsere Funktion `curve_fit` die Fehler unserer Messwerte noch gar nicht. Da dies sehr unphysikalisch ist, wiederholen wir das Ganze nochmal mit Unsicherheiten:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:08:11.387120Z",
"start_time": "2019-11-04T14:08:11.137181Z"
}
},
"outputs": [],
"source": [
"para2, pcov2 = curve_fit(Spannung, \n",
" strom, \n",
" spannung,\n",
" sigma=spannung_error, # <-- Diesesmal mit Fehler\n",
" absolute_sigma=True # <-- Diese Option müssen wir auf True (wahr) setzen, da \n",
" # wir in der Regel absolute und keine relativen \n",
" # Unsicherheiten messen.\n",
" )\n",
2020-08-12 15:02:51 +00:00
"\n",
"plt.plot(strom,\n",
2020-08-12 15:02:51 +00:00
" [Spannung(value, para2[0]) for value in strom], \n",
" ls ='dashed',\n",
" color='orange',\n",
" label = f'Fitgerade mit R = {para2[0]:0.2f} +/- {pcov2[0,0]**(1/2):0.2f} ohm'\n",
" )\n",
"\n",
"plt.errorbar(strom, \n",
" spannung,\n",
" xerr=strom_error,\n",
" yerr=spannung_error, \n",
" ls='', \n",
" marker='d', \n",
" mfc='orange', \n",
" mec='k', \n",
" ms=7,\n",
" ecolor='k', \n",
" elinewidth=2, \n",
" capsize=5, \n",
" capthick=2, \n",
" label='Messwerte aus A. 5'\n",
" ) \n",
2020-08-12 15:02:51 +00:00
"\n",
"\n",
"plt.legend()\n",
"plt.ylabel('Spannung [V]')\n",
"plt.xlabel('Strom [mA]')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wie Sie sehen können, ist der Wert für den Widerstand zwar gleich geblieben, jedoch die Unsicherheit des Wertes hat sich erhöht.\n",
"\n",
"Wie gut fittet unsere obige Funktion unsere Messdaten? Sehr gut? Gut? Befriedigend? Oder doch eher schlecht? Wäre es nicht gut, ein Maß für die Güte des Fits zu haben? Wie könnte ein solches Maß aussehen?\n",
"\n",
"Sie haben das entscheidende Kriterium bereits kennengelernt, bei der Methode der kleinsten Quadrate geht es darum, das $\\chi^2$ zu minimieren. Gucken wir uns hierzu erst noch einmal an, wie sich das $\\chi^2$ berechnet:\n",
"\n",
"$$ \\chi(\\phi_1 ... \\phi_N)^2 = \\sum_{i = 1}^{N} \\frac{ (y_i - \\lambda(x_i; \\phi))^2}{\\Delta y_i^2}$$\n",
"\n",
"Dies bedeute in unserem Fall:\n",
"\n",
"$$ \\chi(R)^2 = \\sum_{i = 1}^{N} \\frac{ (U_i - u(I_i; R))^2}{\\Delta U_i^2}$$\n",
"\n",
"wobei hier groß $U$ unsere gemessene Spannung und klein $u$ unsere Funktion entspricht."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:09:37.708408Z",
"start_time": "2019-11-04T14:09:37.683983Z"
}
},
"outputs": [],
"source": [
"chi_2 = [(u - Spannung(i, para2[0]))**2 / du**2 for i, u, du in zip(strom, spannung, spannung_error)]\n",
"chi_2 = sum(chi_2)\n",
"print(f'Das chi-qudrat ist {chi_2:.2f}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wie vergleicht sich dieses $\\chi^2$ nun mit einer Funktion, welche unsere Daten schlechter beschreibt? Zum Beispiel sofern wir die Spannung über die Funktion\n",
"\n",
2020-08-12 15:02:51 +00:00
"$$ U(R,I) = R \\cdot I $$\n",
"\n",
"$$ U(R,I) = R \\cdot I^2 $$\n",
"\n",
"beschreiben würden."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"ExecuteTime": {
"end_time": "2019-11-04T14:10:02.649772Z",
"start_time": "2019-11-04T14:10:02.619695Z"
}
},
"outputs": [],
"source": [
"def Spannung2(I, R):\n",
" return R * I**2\n",
"\n",
"para3, pcov3 = curve_fit(Spannung2, \n",
" strom, \n",
" spannung,\n",
" sigma=spannung_error,\n",
" absolute_sigma=True,\n",
" )\n",
"\n",
"chi_2_new = [(u - Spannung2(I, *para3))**2 / du**2 for I, u, du in zip(strom, spannung, spannung_error)]\n",
"chi_2_new = sum(chi_2_new)\n",
"print(f'Chi-qudrat nach URI: {chi_2:.2f}\\nChi-qudrat nach URI-Parabel: {chi_2_new:.2f}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wie Sie sehen können, ist das $\\chi^2$ für unsere zweite Funktion etwas größer als für das klassische ohm'sche Gesetzt. Somit würden wir unseren zweiten Ansatz verwerfen.\n",
"\n",
"Damit man für einen gegebenen Datensatz nicht hunderte von verschiedenen Funktionen durchprobieren muss, gibt es für das $\\chi^2$ eine allgemeine Faustregel, welche den berechneten $\\chi^2$-Wert mit der Anzahl unserer Freiheitsgrade vergleicht. Die Anzahl an Freiheitsgrade ist allgemeinhin gegeben als *Anzahl der Messwerte - Anzahl der Funktionsparameter* ($m - n$).\n",
"\n",
"1. Sofern $\\chi^2/\\text{ndof} >> 1$: sollte die Hypothese bzw. die Fitfunktion angezweifelt werden. Sie beschreibt in diesem Fall die Messdaten nur unzureichend. (Bzw. sollte $\\chi^2/\\text{ndof} > 1$ kann dies auch bedeuten, dass die Unsicherheiten unterschätzt sind)\n",
"2. Sofern $\\chi^2/\\text{ndof} \\approx 1$: beschreibt die Hypothese bzw. die Fitfunktion die Daten wie erwartet und wird nicht abgelehnt. \n",
"3. Falls $\\chi^2/\\text{ndof} << 1$ beschreibt die Hypothese bzw. die Fitfunktion die Daten wesentlich besser als erwartet. In diesem Fall heißt es nicht, dass unsere Hypothese falsch ist, aber man sollte überprüfen, ob die gemessenen Fehler nicht überschätzt worden sind (oder eine Korrelation zwischen den Messfehlern vorliegt). \n",
"\n",
"Sofern Sie eine Arbeit schreiben und Ihre **Goodness-of-the-Fit** ($\\chi^2/\\text{ndof}$) angeben wollen, so geben Sie immer beides an, das $\\chi^2$ und die Anzahl an Freiheitsgraden ndof. Beide Werte getrennt haben einen größeren Informationsgehalt als der resultierende Quotient (Genaueres lernen Sie z.B. in der Vorlesung *Statistik, Datenanalyse und Simulationen* im Master)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=task>\n",
" \n",
"#### Aufgabe 7.: Fitten:\n",
"\n",
"Jetzt sind Sie ein letztes mal gefordert. In dieser Aufgabe wollen wir alles, was wir heute gelernt haben, nochmal reflektieren und anwenden. Erstellen Sie hierfür **ein neues Jupyter-Notebook** und bearbeiten Sie die folgende Aufgabe:\n",
"\n",
"Bestimmen Sie mithilfe Ihrer Vorbereitungsaufgabe 1 im anderen Notebook und der entsprechenden Funktion die Fallbeschleunigung $g_X$ mittels eines **$\\chi^2$-Fits**. Diskutieren Sie anschließend mittels der **Fit-Güte** Ihres Fits, ob Ihre Fitfunktion die gemessenen Daten gut widerspiegelt. Auf welchen Planeten in unserem Sonnensystem befinden Sie sich?\n",
"\n",
"Testen Sie anschließend, ob nicht ein **linearer Fit** mit der linearen Funktion `h(t, g, h0) = t * g + h0` besser geeignet wäre. Begründen Sie Ihre Antwort.\n",
"\n",
"Ist die lineare Funktion physikalisch sinnvoll?\n",
"\n",
2022-10-05 22:37:28 +00:00
"Vergessen Sie nicht, dass zu jedem Fit ein **Plot** gehört!\n",
"\n",
"---\n",
"\n",
"Es ist für das PGP (und später das fortgeschrittene Praktikum) **sehr wichtig**, sich mit der Vorgehensweise dieser Aufgabe vertraut zu machen! Wenn Sie Fragen bzw. Probleme haben, vergessen Sie nicht auf die folgenden Hilfsmöglichkeiten zurückzugreifen:\n",
"\n",
"1. Verwendung der IPython-Hilfe unter Verwendung der **Shift + Tab** Tasten.\n",
"2. Die ausführliche Dokumentation von Python und das Angebot etlicher nützlicher Hilfsbeiträge in verschiedenen Foren (z.B. stackoverflow) im Internet.\n",
2022-10-05 22:37:28 +00:00
"3. **Fragen Sie** den Assistenten per E-Mail :)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Neues Notebook nutzen!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
2022-10-05 22:37:28 +00:00
"version": "3.10.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}