{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kapitel 1. Einstieg in die Welt von Python:\n",
"\n",
"In unserer heutigen digitalen Welt sind Computer nicht mehr aus unserem Alltag wegzudenken. Ob in der Finanzwelt, Industrie aber auch in der Wissenschaft erledigen Computer in Sekundenschnelle komplizierte Rechnungen und helfen dem Anwender komplizierte Sachverhalte vereinfacht wieder zu geben. Daher empfiehlt es sich insbesondere als Physiker zumindest die Grundlagen einer beliebigen Programmiersprache zu beherrschen. \n",
"\n",
"Im folgenden werden wir uns gemeinsam die Grundzüge der Programmiersprache **Python** erarbeiten. Ein besonderes Augenmerk liegt hierbei auf den verschiedenen Herausforderungen die das analysieren von Experimentdaten mit sich bringt. Um euch bestens auf die Anforderungen im **physikalische Grundpraktikum (PGP)** vorzubereiten lernen wir im Folgenden wie man:\n",
"\n",
"* einfache Rechnungen mit Python durchführt\n",
"* \"Mathematische\" Funktionen definiert\n",
"* Funktionen auf größere Zahlenmengen anwendet\n",
"* Daten in Form von Graphen richtig darstellt\n",
"* eine Ausgleichsgerade von Datenpunkten berechnen kann.\n",
"\n",
"Damit ihr das neu gelernte Wissen direkt vertiefen könnt, wird dieses Notebook an verschiedenen Stellen kleinere Aufgaben für euch bereit halten."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Grundlagen zu Python bzw. Jupyter Notebooks:\n",
"\n",
"Bevor wir mit dem eigentlichen programmieren beginnen wollen müssen wir uns jedoch erst einmal mit unserem so genannten Interpreter (**Jupyter Notebook**) vertraut machen. Bei der Programmiersprache **Python** handelt es sich um eine so genannte **Interpretersprache**. Dies bedeutet, dass eingegebene Befehle, ähnlich wie bei einem Taschenrechner, direkt ausgeführt werden.\n",
"\n",
"Zum Beispiel beim berechnen von: "
]
},
{
"cell_type": "code",
"execution_count": 115,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.511195Z",
"start_time": "2019-10-27T12:26:11.504240Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 115,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3 + 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unser Interpreter, das **Jupyter Notebook**, stellt hierbei einen komfortable Interpreterumgebung dar. Diese erlaubt es uns neben **Code**-Zellen auch Texte und Formeln in so genannten **Markdown**-Zellen darzustellen. Hierbei existiert eine ganze Bandbreite an Formatierungsmöglichkeiten. Zum Beispiel:\n",
"\n",
"**Überschriften:**\n",
"\n",
"# Level 1.\n",
"## Level 2.\n",
"### Level 3.\n",
"\n",
"**Aufzählungen: **\n",
"\n",
"* Mit normalen\n",
"* Aufzählungspunkten\n",
" 1. oder \n",
" 2. Numerierungen\n",
" * Auf unterschiedlichen\n",
" 1. Ebenen\n",
"\n",
"**Schriftarten:**\n",
"\n",
"**Fett**\n",
"\n",
"*Italic (Kursive)*\n",
"\n",
"`True type`\n",
"\n",
"bzw. Syntax highlighting\n",
"\n",
"```python \n",
"def EasyFunc(x):\n",
" return 2 * x\n",
"```\n",
"\n",
"**Formeln mit Hilfe des Latex-Syntax:**\n",
"\n",
"$ f(x) = \\int\\limits_0^\\infty e^{-x} \\, dx $\n",
"\n",
"(Latex werdet ihr beim F-Praktikum kennen lernen)\n",
"\n",
"\n",
"**Bilder:**\n",
"\n",
"![The Python logo](https://www.python.org/static/community_logos/python-powered-w-200x80.png \"Das Python Logo\")\n",
"\n",
"\n",
"Darüber hinaus bietet uns das Jupyter Notebook noch diverse weitere Optionen an welche unseren harten Alltag vereinfachen. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Neben diesen nützlichen Befehlemm gibt es noch weitere tolle Kürzel wie zum Beispiel:\n",
"* **D + D** um eine Zelle zu **löschen** \n",
"* **Y** verwandelt eine aktuelle **Markdown**-Zelle in eine **Code**-Zelle\n",
"* **Strg** + **Shift** + **Minus** Splittet eine Zelle an der Position eures Cursors \n",
"* **F** für \"Find and Replace\" (nützlich wenn ihr zum Beispiel ein Variablennamen austauschen wollt)\n",
"* **I** + **I** Um den *\"Kernel\"* zu stoppen (wichtig falls ihr mal eine unendliche LOOP gebaut habt)\n",
"\n",
"Des weiteren könnt ihr [hier](https://www.cheatography.com/weidadeyue/cheat-sheets/jupyter-notebook/) eine Auflistung weiterer Jupyter-Befehle finden."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python als Taschenrechner:\n",
"\n",
"Neben dem einfachen summieren zweier Zahlen ermöglicht uns Python natürlich auch das verwendet weiterer Operatoren. Hierbei haben die Operatoren ähnlich wie in der Mathematik gewisse Prioritäten (*Punkt vor Strich*). Die Operation mit dem niedrigeren Prioritätswert wird zu erst ausgeführt. \n",
"\n",
"
\n",
"
\n",
"
\n",
"
\n",
"
\n",
"
\n",
"
\n",
"\n",
"
Operator
\n",
"
Ergebnis
\n",
"
Priorität
\n",
"
\n",
"\n",
"\n",
"
x+y
\n",
"
Die Summe von x und y
\n",
"
6
\n",
"
\n",
"
x-y
\n",
"
Differenz von x und y
\n",
"
5
\n",
"
\n",
"
x*y
\n",
"
Produkt von x und y
\n",
"
4
\n",
"
\n",
"
x/y
\n",
"
Quotient von x und y
\n",
"
3
\n",
"
\n",
"
x%y
\n",
"
Rest von x/y
\n",
"
2
\n",
"
\n",
"
x**y
\n",
"
x bei der Potenz von y
\n",
"
1
\n",
"
\n",
"\n",
"
\n",
"\n",
"Hier ein paar Beispiele:"
]
},
{
"cell_type": "code",
"execution_count": 116,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.527053Z",
"start_time": "2019-10-27T12:26:11.518434Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"-1.3333333333333335"
]
},
"execution_count": 116,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 / 3 - 2"
]
},
{
"cell_type": "code",
"execution_count": 117,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.542542Z",
"start_time": "2019-10-27T12:26:11.529137Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 117,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3**2 * 2 - 8 "
]
},
{
"cell_type": "code",
"execution_count": 118,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.560548Z",
"start_time": "2019-10-27T12:26:11.545269Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"81"
]
},
"execution_count": 118,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3**2**2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wie in der Mathematik können wir auch bei Python Klammern verwenden um die Rechenreihenfolge zu ändern:"
]
},
{
"cell_type": "code",
"execution_count": 119,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.577576Z",
"start_time": "2019-10-27T12:26:11.563352Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 119,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3**2 * 2 - 8 "
]
},
{
"cell_type": "code",
"execution_count": 120,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.592854Z",
"start_time": "2019-10-27T12:26:11.580500Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"-54"
]
},
"execution_count": 120,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3**2 * (2 - 8 ) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Um unsere Rechnungen besser zu Strukturieren können wir Zahlen auch Variablen zu ordnen. Hierzu verwenden wir das Gleichheitszeichen um einer Variablen (*links*) einem Wert (*rechts*) zu zuordnen."
]
},
{
"cell_type": "code",
"execution_count": 121,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.609873Z",
"start_time": "2019-10-27T12:26:11.597179Z"
}
},
"outputs": [],
"source": [
"a = 5"
]
},
{
"cell_type": "code",
"execution_count": 122,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.627541Z",
"start_time": "2019-10-27T12:26:11.612880Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 122,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a"
]
},
{
"cell_type": "code",
"execution_count": 123,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.643914Z",
"start_time": "2019-10-27T12:26:11.629582Z"
}
},
"outputs": [],
"source": [
"variable = 2"
]
},
{
"cell_type": "code",
"execution_count": 124,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.659328Z",
"start_time": "2019-10-27T12:26:11.646431Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 124,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a * variable"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bei der Definition von Variablen ist es wichtig auf die Reihenfolge zu achten. Dies gilt nicht nur innerhalb einer Zelle..."
]
},
{
"cell_type": "code",
"execution_count": 125,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.674632Z",
"start_time": "2019-10-27T12:26:11.662827Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"21"
]
},
"execution_count": 125,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = 4\n",
"b = 3\n",
"a = 7\n",
"\n",
"a * b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... sondern auch für die Reihenfolge in der die Code-Zellen ausgeführt werden (Angezeigt durch In []:). "
]
},
{
"cell_type": "code",
"execution_count": 126,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.690316Z",
"start_time": "2019-10-27T12:26:11.677704Z"
}
},
"outputs": [],
"source": [
"a = 7"
]
},
{
"cell_type": "code",
"execution_count": 127,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.706441Z",
"start_time": "2019-10-27T12:26:11.692773Z"
}
},
"outputs": [],
"source": [
"a = 4"
]
},
{
"cell_type": "code",
"execution_count": 128,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.721624Z",
"start_time": "2019-10-27T12:26:11.709163Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 128,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a * b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ein weiterer Vorteil (bzw. auch Nachteil) ist, dass Python eine so genannte *dynamische* Datentypenvergabe nutzt. Um besser zu verstehen was dies bedeutet gucken wir uns das Nachfolgende Beispiel an. "
]
},
{
"cell_type": "code",
"execution_count": 129,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.737726Z",
"start_time": "2019-10-27T12:26:11.721624Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 129,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = 2\n",
"b = 5\n",
"c = a * b\n",
"c"
]
},
{
"cell_type": "code",
"execution_count": 130,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.757359Z",
"start_time": "2019-10-27T12:26:11.741666Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10.0"
]
},
"execution_count": 130,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = 2\n",
"b = 5.0\n",
"c = a * b\n",
"c "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In der oberen Zelle ist **c** vom Datentyp `int` (*Integer*) was einer Ganzen Zahl entspricht. In der unteren Zelle jedoch ist **c** vom Datentype `float` (*Floating Point Number*) also eine Gleitkommazahl. Dies liegt daran das wir in der unteren Zelle **b** als Gleitkommazahl definiert haben. Um uns Arbeit abzunehmen hat Python für uns im Hintergrund dynamisch entschieden, dass somit **c** ebenfalls vom type `float` sein muss. \n",
"\n",
"Neben den primitiven Datentypen `float` und `int` gibt es noch die wichtigen Datentypen `str` (*string*) was einer Zeichenkette entspricht (z.B. Buchstaben, Wörter und Sätze), `complex` für Komplexe Zahlen und `bool` für Wahrheitswerte. Was genau Wahrheitswerte sind und für was diese verwendet werden, werdet ihr noch im **PGP2** lernen. \n",
"\n",
"Für das **PGP1** sind erstmal nur die typen `int`, `float` und `str` von Bedeutung."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zeichenketten.\n",
"\n",
"Wie eben bereits erwähnt gibt es neben den Zahlen Datentypen `int`, `float` und `complex` auch noch den Datentyp einer Zeichenkette `str`. Zeichenketten werden in Programmiersprachen vielseitig verwendet z.B. bei einer Nutzereingabe (wie dem Passwort), Dateiname bei einer Installation, oder bei Textrückgaben von Programmen. Letzteres haben wir bereits in Aufgabe 2 mit Hilfe der `print`-Funktion gesehen.\n",
"\n",
"Für das PGP-1 wollen wir uns zunächst darauf beschränken, dass Zeichenketten in so genannten **Formatstrings** dazu genutzt werden können schönere `print` Rückgaben zu erzeugen bzw. wir mit Zeichnketten Achsenbeschriftungen an Graphen anbringen können. \n",
"\n",
"Zunächst erst aber einmal eine einfache Zeichenkette:"
]
},
{
"cell_type": "code",
"execution_count": 131,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.773040Z",
"start_time": "2019-10-27T12:26:11.759880Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"'Dies ist eine Zeichenkette'"
]
},
"execution_count": 131,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"'Dies ist eine Zeichenkette'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Hierbei kann eine Zeichenkette auch alle Symbole enthalten die euer Interpreter unterstützt. In Jupyter sind dies alle gewohnten Zeichen wie Buchstaben, Zahlen, Sonderzeichen und Leerzeichen: "
]
},
{
"cell_type": "code",
"execution_count": 132,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.790081Z",
"start_time": "2019-10-27T12:26:11.776184Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0123456789 äöü *+~`´?ß-@€ python 3.7>\n"
]
}
],
"source": [
"s1 = '0123456789'\n",
"s2 = 'äöü'\n",
"s3 = '*+~`´?ß-@€'\n",
"s4 = 'python 3.7>'\n",
"\n",
"print(s1,s2,s3,s4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Einen **Formatstring** können wir über zwei Arten generieren (**Muss checken welche Python version im Hub genutzt wird**):"
]
},
{
"cell_type": "code",
"execution_count": 133,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.805410Z",
"start_time": "2019-10-27T12:26:11.792628Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dies ist Syntaxvariante eins\n",
"\n",
"Dies ist Syntaxvariante 2\n"
]
}
],
"source": [
"a = 'eins'\n",
"b = 2\n",
"\n",
"print('Dies ist Syntaxvariante {}'.format(a))\n",
"print()\n",
"print(f'Dies ist Syntaxvariante {b}') "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Neben dem Einfügen von Strings oder Zahlen in eine Zeichenkette können wir die eingefügten Werte auch formatieren:"
]
},
{
"cell_type": "code",
"execution_count": 134,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.821505Z",
"start_time": "2019-10-27T12:26:11.807484Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dies ist pi auf 4 signifikante Stellen gerundet: 3.142\n",
"\n",
"Dies ist pi auf 4 signifikante Stellen gerundet: 3.142\n"
]
}
],
"source": [
"pi = 3.1415926535\n",
"\n",
"print(f'Dies ist pi auf 4 signifikante Stellen gerundet: {pi:.4}')\n",
"print()\n",
"print('Dies ist pi auf 4 signifikante Stellen gerundet: {:.4}'.format(pi))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... oder sofern ihr eine Rückgabe lieber über mehrere Zeilen ausgeben lassen wollt könnt ihr dieswie folgt machen:"
]
},
{
"cell_type": "code",
"execution_count": 135,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.837390Z",
"start_time": "2019-10-27T12:26:11.823604Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"An einem Widerstand R wurden die folgenden Werte gemessen:\n",
"Spannung: 12.0+/-0.1 V\n",
"Strom: 0.3+/-0.01 mA\n",
"Hierraus resultiert ein Widerstand von 40.0+/-1.4 kOhm \n"
]
}
],
"source": [
"U = 12.0 #V\n",
"dU = 0.1 #V\n",
"I = 0.30 #mA\n",
"dI = 0.01 #mA\n",
"\n",
"R = U/I #kOhm \n",
"dR = R * ((dU / U)**2 + (dI / I)**2)**0.5\n",
"\n",
"print(f'''An einem Widerstand R wurden die folgenden Werte gemessen:\n",
"Spannung: {U}+/-{dU} V\n",
"Strom: {I}+/-{dI} mA\n",
"Hierraus resultiert ein Widerstand von {R}+/-{dR:.2} kOhm ''') "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Das definieren von Funktionen:\n",
"\n",
"Anstatt Berechnungen wie bei einem Taschenrechner immer wieder manuell einzugeben, ermöglicht uns eine Programmiersprache das definieren von Funktionen. Funktionen können hierbei ähnlich wie mathematische Funktionen definiert und behandelt werden. Im folgenden wollen wir uns dies im Fall des Ohmschen Gesetzt welches durch \n",
"\n",
"$U(R, I) = R \\cdot I$ \n",
"\n",
"beschrieben wird angucken. Hierbei wird die Spannung $U$ durch die Variablen $R$ und $I$ beschrieben. Dies gilt auch analog für Funktionen in einer Programmiersprache:"
]
},
{
"cell_type": "code",
"execution_count": 136,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.852541Z",
"start_time": "2019-10-27T12:26:11.839941Z"
}
},
"outputs": [],
"source": [
"def Spannung(Widerstand, Strom): # U(R,I)\n",
" return Widerstand * Strom # Wiedergabe der Funktion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Diese Funktion können wir nun auf Messdaten anwenden. Z.B. wir Messen bei einem Widerstand von $1\\,\\text{k}\\Omega$ einen Strom von $10\\,\\text{mA}$:"
]
},
{
"cell_type": "code",
"execution_count": 137,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.868415Z",
"start_time": "2019-10-27T12:26:11.855987Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"10.0"
]
},
"execution_count": 137,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Leider müssen wir hier auf die Einheiten selbst achten.\n",
"# Deshalb ist es ratsam sich die Einheiten zu den Werten zu notieren.\n",
"U = Spannung(1000, 0.01) # in V \n",
"U "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Neben mathematischen Funktionen, können Funktionen in einer Programmiersprache auch viel allgemeinere Aufgaben erfüllen bzw. komplexe Algorithmen beinhalten. Hierzu lernt ihr jedoch noch mehr in anderen Programmierkursen. Wie zum Beispiel:\n",
"\n",
"* Computer in der Wissenschaft \n",
"* Programmieren für Physiker\n",
"* Einführung in die Programmierung"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tipp: \n",
"Es ist ratsam gleich von Anfang an Funktionen zu dokumentieren. Hierzu wird in Python der sogenannte `Doc-Strings`. Sie beinhalten Informationen über die Funktion selbst ihre Verwendeten Parameter und ihrer Ausgabe. Zum Beispiel für unser Beispiel des Ohmschen Gesetzt:"
]
},
{
"cell_type": "code",
"execution_count": 138,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.884347Z",
"start_time": "2019-10-27T12:26:11.871135Z"
}
},
"outputs": [],
"source": [
"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",
" \n",
" Returns:\n",
" float: Die Berechnete Spannung in V.\n",
" '''\n",
" return Widerstand * Strom/1000"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Messtabellen in Python:\n",
"\n",
"Damit euch eine Programmiersprache wie Python Arbeit abnehmen kann, sollte es natürlich auch möglich sein größere Datenmengen z.b. die Werte einer Messtabelle in einer Variablen zu speichern. Python bietet hierfür mehrer verschiedene Konzepte alle mit unterschiedlichen Stärken und Schwächen. Die gängigsten Methoden sind listen, tuple, bzw. so genannte numpy.arrays und pandas.dataframes. Aufgrund der imitierten Zeit im PGP 1 werden wir uns hier lediglich mit zwei dieser vier Methoden auseinander setzen. \n",
"\n",
"Fangen wir zunächst mit Listen an. Eine Liste ist eine Ansammlung von Werten, welche alle den gleichen oder ganz unterschiedliche Datentypen haben können. Eine Liste kann auf zwei unterschiedliche Art und Weisen erstellt werden:"
]
},
{
"cell_type": "code",
"execution_count": 139,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.900144Z",
"start_time": "2019-10-27T12:26:11.887370Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1', 'Wert2', 'Wert3']"
]
},
"execution_count": 139,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1 = ['Wert1', 'Wert2', 'Wert3'] # Variante 1\n",
"Messwerte1"
]
},
{
"cell_type": "code",
"execution_count": 140,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.915757Z",
"start_time": "2019-10-27T12:26:11.901901Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[2, 0.9, '1']"
]
},
"execution_count": 140,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte2 = list([2, 0.9, '1']) # Variante 2\n",
"Messwerte2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sobald wir eine liste erstellt haben können wir eine ganze Reihe von unterschiedlichen Manipulationen durchführen um sie nach unserem belieben zu verändern.\n",
"\n",
"Wir können zum Beispiel die bestehende Liste um ein Wert erweitern (`append`) oder einen zusätzlichen Wert an eine beliebige Stelle in der Liste hinzufügen (`insert`)."
]
},
{
"cell_type": "code",
"execution_count": 141,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.932392Z",
"start_time": "2019-10-27T12:26:11.916752Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1', 'Wert2', 'Wert3', 'Wert5']"
]
},
"execution_count": 141,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1.append('Wert5')\n",
"Messwerte1"
]
},
{
"cell_type": "code",
"execution_count": 142,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.947965Z",
"start_time": "2019-10-27T12:26:11.935910Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1', 'Wert2', 'Wert3', 'Wert5', 'Wert4']"
]
},
"execution_count": 142,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1.insert(4, 'Wert4')\n",
"Messwerte1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ups, was ist denn in der letzten Zelle passiert? Wert4 wurde ja garnicht an Stelle 4 der Liste gesetzt, Python scheint nicht zählen zu können... \n",
"\n",
"Leider zählt Python doch richtig. In Python läuft der index von objekten in einer Liste oder ähnlichem immer von 0,1,2,3...n. Dies können wir auch ganz einfach überprüfen in dem wir unsere Liste in verschiedene \"Scheiben\" schneiden (so genanntes slicing). Dies geht wie folgt: "
]
},
{
"cell_type": "code",
"execution_count": 143,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.963385Z",
"start_time": "2019-10-27T12:26:11.950437Z"
}
},
"outputs": [],
"source": [
"NeueWerte = ['Wert1', 'Wert2', 'Wert3', 'Wert4', 'Wert5', 'Wert6'] "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Die kleinste Scheibe welche wir abschneiden können ist ein einzelner Wert:"
]
},
{
"cell_type": "code",
"execution_count": 144,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.978721Z",
"start_time": "2019-10-27T12:26:11.965411Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"'Wert1'"
]
},
"execution_count": 144,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NeueWerte[0] # Hier seht ihr, dass der erste Wert den Index 0 hat."
]
},
{
"cell_type": "code",
"execution_count": 145,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:11.994023Z",
"start_time": "2019-10-27T12:26:11.980553Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"'Wert3'"
]
},
"execution_count": 145,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wert_index_2 = NeueWerte[2] \n",
"wert_index_2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wie bei einer Pizza können wir uns natürlich auch größere Stücke nehmen."
]
},
{
"cell_type": "code",
"execution_count": 146,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.010686Z",
"start_time": "2019-10-27T12:26:11.996882Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1', 'Wert2', 'Wert3']"
]
},
"execution_count": 146,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NeueWerte[0:3] "
]
},
{
"cell_type": "code",
"execution_count": 147,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.026218Z",
"start_time": "2019-10-27T12:26:12.012554Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert3', 'Wert4', 'Wert5']"
]
},
"execution_count": 147,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NeueWerte[2:5] # Ihr seht Python behandelt den letzten Wert wie in einem offenen Intervall [2,5)"
]
},
{
"cell_type": "code",
"execution_count": 148,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.041422Z",
"start_time": "2019-10-27T12:26:12.029154Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert3', 'Wert4', 'Wert5', 'Wert6']"
]
},
"execution_count": 148,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NeueWerte[2:] # Hier werden alle Werte mit dem Index >= 2 zurück gegeben"
]
},
{
"cell_type": "code",
"execution_count": 149,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.057959Z",
"start_time": "2019-10-27T12:26:12.043979Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert4', 'Wert5', 'Wert6']"
]
},
"execution_count": 149,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NeueWerte[-3:] # Mit negativen Zahlen fangt ihr vom Ende der Liste an"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Neben `insert`, `append` und `slicing` bietet Python noch ein paar weitere Listenmanipulationen an. Mit Hilfe des `+` Operators könnt ihr die Werte in einer Liste direkt an eine andere Liste anfügen."
]
},
{
"cell_type": "code",
"execution_count": 150,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.073093Z",
"start_time": "2019-10-27T12:26:12.060135Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1',\n",
" 'Wert2',\n",
" 'Wert3',\n",
" 'Wert5',\n",
" 'Wert4',\n",
" 'Wert1',\n",
" 'Wert2',\n",
" 'Wert3',\n",
" 'Wert4',\n",
" 'Wert5',\n",
" 'Wert6']"
]
},
"execution_count": 150,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1 + NeueWerte"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Anders als `append` welches die zweite Liste als ganzes an die erste Liste anfügt:"
]
},
{
"cell_type": "code",
"execution_count": 151,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.088532Z",
"start_time": "2019-10-27T12:26:12.078147Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1',\n",
" 'Wert2',\n",
" 'Wert3',\n",
" 'Wert5',\n",
" 'Wert4',\n",
" ['Wert1', 'Wert2', 'Wert3', 'Wert4', 'Wert5', 'Wert6']]"
]
},
"execution_count": 151,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1.append(NeueWerte)\n",
"Messwerte1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aber aufgepasst bei `append` wird eure Liste an welche ihr die Daten anhängt (hier Messwerte1) direkt geändert (dies gilt auch für `insert`), während ihr beim `+` Operator die Variable überschreiben müsst damit die Änderung wirksam wird. "
]
},
{
"cell_type": "code",
"execution_count": 152,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.103764Z",
"start_time": "2019-10-27T12:26:12.091184Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['Wert1',\n",
" 'Wert2',\n",
" 'Wert3',\n",
" 'Wert5',\n",
" 'Wert4',\n",
" ['Wert1', 'Wert2', 'Wert3', 'Wert4', 'Wert5', 'Wert6'],\n",
" 'Wert1',\n",
" 'Wert2',\n",
" 'Wert3',\n",
" 'Wert4',\n",
" 'Wert5',\n",
" 'Wert6']"
]
},
"execution_count": 152,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Messwerte1 = Messwerte1 + NeueWerte\n",
"# Tipp dies könnt ihr auch einfach mit Hilfe von\n",
"# Messwerte1 += NeueWerte\n",
"Messwerte1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zwei weitere nützliche Befehle im zusammenhang von listen ist die `len`- und `range`-Funktion. \n",
"\n",
"`len` gibt euch die Länge einer Liste zurück "
]
},
{
"cell_type": "code",
"execution_count": 153,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.119046Z",
"start_time": "2019-10-27T12:26:12.106470Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['Wert1', 'Wert2', 'Wert3', 'Wert5', 'Wert4', ['Wert1', 'Wert2', 'Wert3', 'Wert4', 'Wert5', 'Wert6'], 'Wert1', 'Wert2', 'Wert3', 'Wert4', 'Wert5', 'Wert6']\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 153,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"print(Messwerte1)\n",
"len(Messwerte1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`range` erstellt euch ganzzahlige Werte zwischen zwei ganzen Zahlen "
]
},
{
"cell_type": "code",
"execution_count": 154,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.135058Z",
"start_time": "2019-10-27T12:26:12.123078Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"range(0, 5, 2)"
]
},
"execution_count": 154,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"range(0, # <-- Startwert\n",
" 5, # <-- Endwert (nicht mehr enthalten, offenes Ende)\n",
" 2 # <-- Schrittweite\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ihr könnt die `range` Rückgabe auch wieder in eine Liste umwandeln mit"
]
},
{
"cell_type": "code",
"execution_count": 155,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.150810Z",
"start_time": "2019-10-27T12:26:12.139084Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[0, 2, 4]"
]
},
"execution_count": 155,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(range(0,5,2))"
]
},
{
"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 sofern 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": 156,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.190364Z",
"start_time": "2019-10-27T12:26:12.152805Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Wert: 1\n",
"Ergebnis: 3\n",
"Wert: 2\n",
"Ergebnis: 4\n",
"Wert: 3\n",
"Ergebnis: 5\n",
"Wert: 4\n",
"Ergebnis: 6\n"
]
}
],
"source": [
"liste = [1, 2, 3, 4]\n",
"\n",
"for wert in liste:\n",
" print('Wert:', wert)\n",
" rechnung = wert + 2\n",
" print('Ergebnis:', 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 der TAB-Taste."
]
},
{
"cell_type": "code",
"execution_count": 157,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.221796Z",
"start_time": "2019-10-27T12:26:12.194886Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hier läuft das Hauptprogramm\n",
"Schleife\n",
"Wert: 1\n",
"Schleife\n",
"Wert: 2\n",
"Schleife\n",
"Wert: 3\n",
"Schleife\n",
"Wert: 4\n",
"Hier läuft wieder das Hauptprogramm\n",
"Letztes Ergebnis + 5: 11\n"
]
}
],
"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:"
]
},
{
"cell_type": "code",
"execution_count": 158,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.254001Z",
"start_time": "2019-10-27T12:26:12.227788Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[10.1, 10.5, 9.8, 8.7, 11.2]"
]
},
"execution_count": 158,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Stromwerte = [101, 105, 98, 87, 112] # mA\n",
"Spannungswerte = []# Einheit? <-- Deshlab Docstrings und Help!\n",
"Widerstand = 100 # Ohm\n",
"\n",
"for Strom in Stromwerte:\n",
" Spannungswerte.append(Spannung(Strom, Widerstand))\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": 159,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.269295Z",
"start_time": "2019-10-27T12:26:12.256646Z"
}
},
"outputs": [],
"source": [
"Spannungswerte = [Spannung(Strom, 1000) for Strom in Stromwerte]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Wir können auch über mehre 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": 160,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.286560Z",
"start_time": "2019-10-27T12:26:12.271505Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A und 0\n",
"B und 1\n",
"C und 2\n",
"D und 3\n"
]
}
],
"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": 161,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.302437Z",
"start_time": "2019-10-27T12:26:12.288197Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[335.4904, 347.3442, 343.6145, 337.275, 331.6212, 342.0115, 336.2262]\n",
"[335.4904, 347.3442, 343.6145, 337.275, 331.6212, 342.0115, 336.2262]\n"
]
}
],
"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",
"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": 162,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.318819Z",
"start_time": "2019-10-27T12:26:12.304012Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"a und 1 und x\n",
"b und 2 und y\n",
"c und 3 und z\n"
]
}
],
"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": [
"
\n",
" \n",
"#### Aufgabe 4.b.: Werte berechnen:\n",
"Kopiert eure Aufgabe 4.a. aus der Vorbereitung in das Notebook und berechnet nun für die Messwerte aus Aufgabe 4 a. die Leistung $P$ und den Widerstand $R$ sowie deren Fehler. Nutzt hierfür die ausführliche schrebweise der **for**-Schleife im Fall des Widerstands $R$ und den list-comprehension Syntax für die Leistung $P$. Fügt die berechneten Werte als neue Spalten and die Liste *daten* an. \n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Das Darstellen 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",
"\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 euch **Matplotlib** bietet auswendig zu kennen. Mit der Zeit werdet ihr ein solides Grundwissen der gängisten Befehle haben. Für alles weitere hilft euch die [Dokumentation und ihre Beispiele](http://matplotlib.org/). Des Weiteren ist insbesondere hier die **IPython Hilfe** und das **automatische Vervollständigen 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 möchten:"
]
},
{
"cell_type": "code",
"execution_count": 163,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.336089Z",
"start_time": "2019-10-27T12:26:12.321658Z"
}
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`import` läd für und aus dem package matplotlib das Modul `pyplot`. Mit Hilfe des Zusatzes `as plt` wird ein alias erstellt. Dieser Alias erspart uns im nachfolgenden Arbeit, wie wir im nachfolgenden Beispiel sehen können:"
]
},
{
"cell_type": "code",
"execution_count": 164,
"metadata": {
"ExecuteTime": {
"end_time": "2019-10-27T12:26:12.652722Z",
"start_time": "2019-10-27T12:26:12.339189Z"
}
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"