{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fd2f3bf7-b314-4449-95dc-f07d1a13f1c6",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-04300aa5f61f7f12",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "# 5. Programmierübung: SciPy\n",
    "\n",
    "<div style=\"display:flex;\">\n",
    "    <div style=\"text-align: left\">\n",
    "        Willkommen zur fünften Programmierübung Einführung in Python 3.\n",
    "    </div>\n",
    "    <img style=\"float: right; margin: 0px 15px 15px 0px\" src=\"https://www.python.org/static/img/python-logo-large.c36dccadd999.png?1576869008\" width=\"100\" />\n",
    "</div>\n",
    "\n",
    "Wenn Sie Fragen oder Verbesserungsvorschläge zum Inhalt oder Struktur der Notebooks haben, dann können sie eine E-Mail an Phil Keier ([p.keier@hbk-bs.de](mailto:p.keier@hbk-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) oder Martin Le ([martin.le@tu-bs.de](mailto:martin.le@tu-bs.de?subject=[SigSys]%20Feedback%20Programmierübung&amp)) schreiben.\n",
    "\n",
    "Link zu einem Python Spickzettel: [hier](https://s3.amazonaws.com/assets.datacamp.com/blog_assets/PythonForDataScience.pdf)\n",
    "\n",
    "Der Großteil des Python-Tutorials stammt aus der Veranstaltung _Deep Learning Lab_ und von [www.python-kurs.eu](https://www.python-kurs.eu/python3_kurs.php) und wurde für _Signale und Systeme_, sowie _Einführung in die Programmierung für Nicht Informatiker_ angepasst.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb677cea-65c2-486d-9442-6495b5063bd2",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-110034393ecbe731",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Was ist SciPy\n",
    "\n",
    "SciPy steht für Scientific Python und ist eine Open-Source-Bibliothek, die auf der bewährten Architektur von NumPy aufbaut. Sie bietet eine Vielzahl von Funktionen, die speziell für ingenieurtechnische und wissenschaftliche Anwendungen entwickelt wurden. In diesem Zusammenhang möchten wir uns insbesondere mit Teilen des Statistikmoduls von SciPy vertraut machen.\n",
    "\n",
    "__Für dieses Notebook schauen Sie bitte in die [SciPy Docs](https://docs.scipy.org/doc/scipy/tutorial/index.html)!!!__ Dort sind alle Funktionen beschrieben die wir hier bearbeiten und noch mehr!\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3dde4289-bc10-49fc-875d-ebc44fbf807a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-82f61f58224c5db9",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "SciPys wird meist als `sp` importiert da für diese Aufgabe nur das Statistik modul nötig ist wird einfach dieses importiert. Aufgrund des kurzen schlüssigen namens findet keine umbenennung statt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e058b67a-ab34-4685-b023-084bfa80f171",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-fe3ab9d39498717f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "from scipy import stats"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57ada7f0-5eea-45cc-b8d0-85f52c7f6b55",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-eec41b75718f0e4e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5983dc91-91f3-4b6b-b6c2-9873b9ea8db9",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-65e8f06a2b373c8c",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Lineare Regression\n",
    "\n",
    "## Motivation\n",
    "\n",
    "Die **lineare Regression** ist eine grundlegende Methode zur Modellierung von Beziehungen zwischen Variablen. Sie hilft, Zusammenhänge zu verstehen und ermöglicht Vorhersagen auf Basis vorhandener Daten. Durch die Bestimmung einer linearen Beziehung zwischen einer abhängigen und einer oder mehreren unabhängigen Variablen können wir Trends identifizieren und zukünftige Werte schätzen. Ihre Einfachheit, Effizienz und die Möglichkeit, auch bei großen Datensätzen präzise Ergebnisse zu erzielen, machen sie zu einem wertvollen Werkzeug in der Datenanalyse und ein Fundament für komplexere Modelle.\n",
    "\n",
    "Dafür geht man davon aus, dass es zwei unabhängige Variablen $x_1$ & $x_2$ gibt, für die eine Zukünftige Aussage getätigt werden soll. Hierfür wird eine fehlerminimierte Gerade $g(x)$ zwischen die Datenpunkte gelegt. Daraus resultierenden lässt sich mit $g(x)$ eine zukünftige Vorhersage in einem bestimmten Fehlerbereich tätigen.\n",
    "\n",
    "Aus der Schule sollte die Geraden Gleichung $g(x) = m\\cdot x+b$ bekannt sein. Dabei beschreibt $m$ die Steigung (Slope) & $b$ den Schnittpunkt mit der y-Achse (interception). Die **lineare regression** bietet daher die Möglichkeit diese beiden Parameter herauszufinden.\n",
    "\n",
    "Für das folgende Beispiel werden zwei (pseudo-)zufällig erzeugte Datensets generiert, auf welche dann die lineare regression angewandt wird. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "60712a1d-7a55-44f4-89e9-cae5ceab952a",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-fbb9bfd35036d55e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 19028 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "rand = np.random.default_rng(42) # RNG with fixed seed\n",
    "\n",
    "# Generate, Scale, round and sort random numbers for an increasing slope\n",
    "scalar: int = 10\n",
    "x: np.array = np.round(np.sort(rand.random(20) * scalar), decimals=2)\n",
    "y: np.array = np.round(np.sort(rand.random(20) * scalar), decimals=2)\n",
    "\n",
    "# Plot these values\n",
    "plt.title(\"Scattered Random Values\")\n",
    "plt.grid()\n",
    "plt.xlabel(\"X\")\n",
    "plt.ylabel(\"Y\")\n",
    "plt.scatter(x,y, color='g')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "083ba4ea-8e6f-4286-8049-1c8e3f22c004",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-caa85fe6a76214bf",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Aus den erzeugten Daten ist klar ersichtlich, dass diese einem Trend folgen. Mittels SciPy wollen wir diesen Trend darstellen.\n",
    "\n",
    "Dazu wird die Funktion `linregress` verwendet. Diese verlangt, wie aus der [Dokumentation](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html#scipy.stats.linregress) zu entnehmen, zwei arrays mit Daten. \n",
    "\n",
    "Die Ausgabe ist aufgespalten in 5 Parameter:\n",
    "- slope: die Steigung `m` der Geraden\n",
    "- intercept: der Punkt an dem die Gerade die y-Achse trifft oder das `b`\n",
    "- rvalue: der Pearson Korrelations Koefficient, welcher voerst ignoriert wird\n",
    "- pvalue: der p-Wert das die Nullhypothese stimmt, wird auch ignoriert\n",
    "- stderr: der Standard Error der Steigung, unter der Annahme einer Normalverteilung der Daten *(PCGs sind Normalverteilt)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9c1959d8-5bac-45fa-9aa2-47fa79943d72",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-553a583b633273d5",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "slope, intercept, _, _, stderr = stats.linregress(x,y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c2bd060-bb4c-440f-9a04-72382c9a33eb",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-111845c39238eeee",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Aus `slope` & `intercept` lässt sich folglich eine Gerade definieren:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1e30868c-f11f-43bd-a60b-200ec00e903f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-49d79f2b8dcf7315",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "def line(x: float) -> float:\n",
    "    '''\n",
    "    Evaluates the rounded line from the previous\n",
    "    evaluated linear regression model\n",
    "\n",
    "    Note: Output rounded to 2 decimal places\n",
    "    '''\n",
    "    res: float = slope * x + intercept\n",
    "    rounded: np.float64 = np.round(res, decimals=2)\n",
    "    return float(rounded)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fdd9724-5c61-471d-a121-787f2d56f132",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-a63e440ba0b57186",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Diese kann über den gesatme bereich dargestellt werden. Dazu werden die bereits bekannten x-Werte verwendet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0a59d146-4ae3-429a-a67e-7c158d5ebac7",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5239bfcad5788ae3",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34572 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Calculate the Line using vectorization\n",
    "regline: np.array = np.vectorize(line)(x)\n",
    "\n",
    "# Plot tvalues\n",
    "plt.title(\"Scattered Random Values\")\n",
    "plt.grid()\n",
    "\n",
    "plt.xlabel(\"X\")\n",
    "plt.ylabel(\"Y\")\n",
    "\n",
    "plt.scatter(x,y, color='g', label=\"Original Data\")\n",
    "plt.plot(x, regline, color='m', label=\"Fitted line\")\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "802bf60a-e402-4912-96bf-baa6cd398c6e",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-e0da661ffad2086e",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Mit diesem Model lässt sich dementsprechend die \"Zukunft\" vorhersagen. Hierfür können wir im folgenden einfach die Werte für `-1` & `11` berechnen und diese dem Plot hinzufügen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e62f6248-5813-4df0-9715-a72d3a5467f8",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-92a5bbb041e28a56",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37852 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Future Values\n",
    "fut: np.array = np.array([line(-1), line(11)])\n",
    "\n",
    "# Calculate extended Line using vectorization\n",
    "ext_range: np.array = np.arange(-1,12)\n",
    "regline: np.array = np.vectorize(line)(ext_range)\n",
    "\n",
    "# Plot values\n",
    "plt.title(\"Scattered Random Values\")\n",
    "plt.grid()\n",
    "\n",
    "plt.xlabel(\"X\")\n",
    "plt.ylabel(\"Y\")\n",
    "\n",
    "plt.scatter(x,y, color='g', label=\"Original Data\")\n",
    "plt.plot(ext_range, regline, color='m', label=\"Fitted Line\")\n",
    "plt.scatter((-1, 11), fut, color='r', label=\"Predicted Data\")\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04f002a5-4339-4225-9515-848513347697",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6c8576c776d11325",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wie zu erwarten liegen beide Werte auf der Geraden.\n",
    "\n",
    "Um deren Werte zu ermitteln lassen sich diese mittels print einfach ausgeben, dies sollte immer mit Angabe des Standard Errors erfolgen. Sonst ist unklar wie genau die Daten sind:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "563d452b-350c-4628-a97d-5d9ce268f4c4",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7677ec5dda3e8e13",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction of f(11) = 10.16, with standard deviation 0.07\n"
     ]
    }
   ],
   "source": [
    "print(\"Prediction of f(11) = {}, with standard deviation {}\".format(line(11), np.round(stderr, decimals=2)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e81b1a9-4155-4bfd-8613-2530dbe61f03",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-facd8e8ef9f4aed1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "### Aufgabe\n",
    "\n",
    "*7 Punkte*\n",
    "\n",
    "Bestimme mittels Linearer Regression die *best fit* Funktion für die beiden gegebenen Datensets `x_data` & `y_data`, unter beachtung folgender Punkte:\n",
    "\n",
    "- Plotte das Ergebnis angemessen\n",
    "- Nutze SciPys `linregress` Funktion, speichere den Output vor dem entpacken in der Variablen `l`\n",
    "- Definiere die Funktion `reg_line` mit einem Eingabeparameter\n",
    "- Bestimme die Werte für `-0.3` & `3.4` speichere diese als liste in variablen `future`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f5b801bc-450e-417b-aeea-9b69d638fb64",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-cb5b277089c75c40",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "random = np.random.default_rng(420)\n",
    "\n",
    "# 2 scuffed up One-Liners :)\n",
    "x_data: np.array = np.sort(np.round(random.random(40)*np.pi, decimals=2))\n",
    "y_data: np.array = np.flip(np.sort(np.round(random.random(40)*np.sqrt(2), decimals=2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "2645541f-c30f-487e-be65-3ec5ac22d427",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-12b5b631856ffe41",
     "locked": false,
     "points": 1,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 46732 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# BEGIN SOLUTION\n",
    "l = stats.linregress(x_data,y_data)\n",
    "slope, intercept, _, _, stderr = l\n",
    "\n",
    "def reg_line(x: float) -> float:\n",
    "    return float(np.round(slope*x+intercept,decimals=2))\n",
    "\n",
    "ext: tuple = (-0.3, 3.4)\n",
    "rl: np.array = np.vectorize(reg_line)(ext)\n",
    "\n",
    "future: list = [reg_line(ext[0]), reg_line(ext[1])]\n",
    "# Plot values\n",
    "plt.title(\"Scattered Random Values\")\n",
    "plt.grid()\n",
    "\n",
    "plt.xlabel(\"X\")\n",
    "plt.ylabel(\"Y\")\n",
    "\n",
    "plt.scatter(x_data,y_data, color='g', label=\"Original Data\")\n",
    "plt.plot(ext, rl, color='m', label=\"Fitted Line\")\n",
    "plt.scatter(ext, future, color='r', label=\"Predicted Data\")\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "plt.show()\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "aa34ed84-f200-4863-be0b-40ff5492c654",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-8f20c3645d0a9b46",
     "locked": true,
     "points": 6,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet\n",
    "\n",
    "# Check if reg_line is defined\n",
    "assert 'reg_line' in dir()\n",
    "\n",
    "# Check if reg_line gives right output\n",
    "assert reg_line(10) == -3.04\n",
    "\n",
    "# Check if future values are calculated right\n",
    "assert future == [1.51, -0.12]\n",
    "\n",
    "### BEGIN HIDDEN TESTS\n",
    "sl, ip, _, _, se = stats.linregress(x_data,y_data)\n",
    "slope, intercept, _, _, stderr = l\n",
    "\n",
    "assert slope == sl\n",
    "assert intercept == ip\n",
    "assert stderr == se\n",
    "\n",
    "### END HIDDEN TESTS"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8369e788-5862-488b-a003-d3cd31ebaa99",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-59872a1e95590378",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02656b99-d250-4a21-bc2d-46007806d81d",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-455ed6a04d0d701f",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "# Verteilungen\n",
    "\n",
    "## Probability Density Function - Normal Verteilung\n",
    "\n",
    "### Motivation\n",
    "\n",
    "Es wurden Daten über die Körperlänge eines Bienenvolkes erhoben, dementsprechend liegen diskrete Werte über die deren Körperlängen vor. Logischerweise repräsentiert dies nur das erhobene Bienenvolk, dieses arbeitet aber kontinuierlich weiter und erzeugt nachkommen. Die Population verändert sich. \n",
    "\n",
    "Daher ist anzunehmen, dass auch Werte zwischen den bereits Erhobenen auftreten können. Um dies zu Modellieren wird eine Normalverteilung benötigt.\n",
    "\n",
    "Schauen wir uns dazu erst die Gaussche Normalverteilung an, diese hat ihren Mittelpunkt bei $x=0$, bezeichnet als Erwartungswert $\\mu$, und eine Standardabweichung $\\sigma = 1$. Mathematisch ist sie definiert als $$p(x|\\mu,\\sigma)=\\frac{1}{\\sqrt(2\\pi\\sigma^2)}e^{-\\frac{(x-\\mu)^2}{2\\sigma^2}}$$\n",
    "\n",
    "SciPy bietet hierfür das `norm` objekt aus dem `stats` Modul an, dieses verlangt die beiden Parameter $\\mu$ & $\\sigma$, das auf dem `norm` Objekt kann dann die Funktion `pdf` mit einem Paramter als Stepsize oder einem Array aufgerufen werden. Nach Plotten ergibt sich folglich die Normalverteilung (von -4 bis 4):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "2803dcac-bb3b-4aff-a3c9-c0085148c376",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-54174b5f7d8309db",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34976 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "x = np.arange(-4, 4, 0.01) # Plot between -4 and 4 with stepsize 0.01\n",
    "y = stats.norm(0,1).pdf(x) # Calculate pdf with mu=0, sigma=1\n",
    "\n",
    "# Plot\n",
    "plt.title(\"Gaussche Normalverteilung\")\n",
    "plt.plot(x, y)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca2f768f-16ff-47ff-b898-fe0b8cf3511f",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-259b8d6cb9d76981",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Um herauszufinden wie viel Prozent einer Population innerhalb dieser Normalverteilung fallen wird die Funktion `ppf` (Percent Point Function) verwendet.\n",
    "\n",
    "Am Beispiel 90% der Population:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "0e5e7709-90a2-4a30-b49f-cbb243aa4e4b",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5a9ed9588ce2ea27",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "90% of the Population fall into the range 1.282.\n"
     ]
    }
   ],
   "source": [
    "percentile = stats.norm(0,1).ppf(0.9)\n",
    "print(f\"90% of the Population fall into the range {percentile:0.3f}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96afa424-d697-4c60-aa9b-4655491f4f13",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5102a53fda328278",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Um dies zu veranschaulichen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "4f5d7f54-856f-4e12-8251-d0cdc5c0f002",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-5d84b05b2808a672",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 39664 characters]",
      "text/plain": [
       "<Figure size 900x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(figsize=(9,6))\n",
    "ax.plot(x,y, color='r')\n",
    "\n",
    "# filling under the curve\n",
    "x_fill = np.arange(-4, percentile, 0.01)\n",
    "y_fill = stats.norm(0,1).pdf(x_fill)\n",
    "ax.fill_between(x_fill, y_fill, 0, color='g')\n",
    "plt.title(\"Filled Percentile under Normal Curve\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f7a26f6-36d8-4561-ad68-db6c19c7720e",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-c469fe12a69725b1",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Machen wir dies alles am Beispiel der Fuchsrote Lockensandbiene fest. Diese ist laut [Bundesministerium für Ernährung und Landwirtschaft](https://www.bmel.de/DE/themen/landwirtschaft/artenvielfalt/bienen-fuettern/wildbienen-honigbienen-und-co.html) 12-14 mm groß. Daher lässt sich annehmen das die meisten Bienen $\\mu=13mm$, mit einer Standardabweichung von $\\sigma=1mm$ haben. Wir wollen nun wissen wie groß 95% der Bienen sind, rechnen wir dies mittels `ppf` aus:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "6d57bdf0-8e75-4760-8159-d42effd9ea62",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-360e11812d2e9932",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 46332 characters]",
      "text/plain": [
       "<Figure size 900x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mu = 13 # Mean\n",
    "sigma = 1 # std deviation in mm\n",
    "\n",
    "x_norm = np.linspace(10, 16, 100) # Normaldistribution range from 10-16mm \n",
    "y_norm = stats.norm(mu, sigma).pdf(x_norm) # Calculate normal\n",
    "\n",
    "# Height of 95th percentile of bees\n",
    "percentile = stats.norm(mu,sigma).ppf(0.95)\n",
    "\n",
    "x_percentile = np.arange(x_norm[0], percentile, 0.01)\n",
    "y_percentile = stats.norm(mu,sigma).pdf(x_percentile)\n",
    "\n",
    "# Plot\n",
    "fig, ax = plt.subplots(figsize=(9,6))\n",
    "ax.plot(x_norm, y_norm, color='r')\n",
    "\n",
    "# filling under the curve\n",
    "ax.fill_between(x_percentile, y_percentile, 0, alpha=0.2, color='#FE0000')\n",
    "\n",
    "\n",
    "# Set text\n",
    "ax.text(0.5,0.25,\n",
    "        f\"95th percentile of bees fall under {percentile:.2f}mm\",\n",
    "        ha='center', va='center', transform=ax.transAxes,\n",
    "        bbox={'facecolor':'#fafafa','alpha':1,'edgecolor':'none','pad':1},\n",
    "        color='#de2e0b'\n",
    "    )\n",
    "\n",
    "# Show\n",
    "plt.title(\"Filled Percentile under Normal Curve\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9c59eb8-bfb1-46f2-bf1f-af1ffe59e190",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-7718d285f36c2fad",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "### Aufgabe \n",
    "\n",
    "*6 Punkte*\n",
    "\n",
    "Gegeben sind die nach Altersgruppe aufgeschlüsselten Durschnittskörpergrößen (in cm) von Frauen in Deutschland. (Zu finden beim [Statistischen Bundesamt](https://www.destatis.de/DE/Themen/Gesellschaft-Umwelt/Gesundheit/Gesundheitszustand-Relevantes-Verhalten/Tabellen/koerpermasse-frauen.html))\n",
    "\n",
    "Gehe wie folgt vor:\n",
    "\n",
    "- Berechne das arithmetische Mittel nutze dafür NumPy. und speichere das Ergebnis mit einer Genauigkeit von 1 Dezimalstelle nach dem Komma in der Variablen `avg_height`.\n",
    "- Gegeben ist auch die Standardabweichung von 15cm, stelle die Normalverteilung mittels `norm.pdf` auf. Speichere den Wert in `norm_height` und finde einen geeigneten linespace zum plotten.\n",
    "- Berechne folgend die Körpergröße unter die 80% aller Frauen (nach Datenset) fallen. Speichere den Wert in der Variablen `avg_percentile`.\n",
    "- Plotte das Ergebnis. Orientiere dich gerne an dem Bienenbeispiel. Finde eine geeignete Darstellung. *Tipp: Da die Y-Achse in diesem Beispiel keinen Sinn ergibt kannst du sie einfach austellen mit `plt.yticks([])`*\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "3983d224-e885-43bd-aadd-4e276a56a370",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-048d8330d555432a",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Given\n",
    "avg_height_per_woman = {\n",
    "    \"18 - 20\": 167.6,\n",
    "    \"20 - 25\": 167.7,\n",
    "    \"25 - 30\": 167.3,\n",
    "    \"30 - 35\": 167.2,\n",
    "    \"35 - 40\": 167.3,\n",
    "    \"40 - 45\": 167.5,\n",
    "    \"45 - 50\": 167.1,\n",
    "    \"50 - 55\": 167.1,\n",
    "    \"55 - 60\": 166.9,\n",
    "    \"60 - 65\": 165.4,\n",
    "    \"65 - 70\": 164.5,\n",
    "    \"70 - 75\": 163.9,\n",
    "    \"75+\": 162.8\n",
    "}\n",
    "\n",
    "avg_height = None\n",
    "norm_height = None\n",
    "avg_percentile = None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "2c2e2cf6-c743-4cb0-a329-2240205fc7da",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-a59f2b3230aad3d6",
     "locked": false,
     "points": 3,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 42256 characters]",
      "text/plain": [
       "<Figure size 900x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# BEGIN SOLUTION\n",
    "avg_height = np.round(np.mean(list(avg_height_per_woman.values())), decimals=1)\n",
    "std_sigma = 15\n",
    "\n",
    "norm_x = np.linspace(120, 220, 1000)\n",
    "norm_height = stats.norm(avg_height, std_sigma).pdf(norm_x)\n",
    "\n",
    "# Height of 80th percentile of woman heights\n",
    "avg_percentile = stats.norm(avg_height, std_sigma).ppf(0.8)\n",
    "\n",
    "x_percentile = np.arange(norm_x[0], avg_percentile, 0.01)\n",
    "y_percentile = stats.norm(avg_height, std_sigma).pdf(x_percentile)\n",
    "\n",
    "# Plot\n",
    "fig, ax = plt.subplots(figsize=(9,6))\n",
    "ax.plot(norm_x, norm_height, color='r')\n",
    "\n",
    "# filling under the curve\n",
    "ax.fill_between(x_percentile, y_percentile, 0, alpha=.5, color='#fa0000')\n",
    "\n",
    "\n",
    "# Set text\n",
    "ax.text(0.4,0.18,\n",
    "        f\"80th percentile of Womens heigth\\n fall under {avg_percentile:.1f}cm\",\n",
    "        ha='center', va='center', transform=ax.transAxes,\n",
    "        bbox={'facecolor':'#fafafa','alpha':1,'edgecolor':'none','pad':1},\n",
    "        color='#de2e0b'\n",
    "    )\n",
    "\n",
    "# Show\n",
    "plt.title(\"Woman Height Normal Distribution\")\n",
    "plt.xlabel(\"Height\")\n",
    "plt.yticks([]) # hide y\n",
    "plt.show()\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "d9f8aeae-6b51-4645-b578-19693a07ece3",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-26f141bc8b12d7fe",
     "locked": true,
     "points": 3,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet...\n",
    "import math \n",
    "\n",
    "# Check if average height is close to real value\n",
    "assert math.isclose(avg_height, 166.3, rel_tol=.2) # 1 Punkt\n",
    "\n",
    "# Check if norm height is close to real value\n",
    "assert math.isclose(np.round(np.sum(norm_height), decimals=1), 10, rel_tol=.2) # 1 Punkt\n",
    "\n",
    "# Check if percentile is close to real value\n",
    "assert math.isclose(avg_percentile, 179, rel_tol=.2) # 1 Punkt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f50b8883-e018-4a44-831a-1a3c14640955",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-6dac9e79dc305781",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "## Probabillity Mass Function - Binomial Verteilung\n",
    "\n",
    "Im Gegensatz zur Normal Verteilung folgen Binomiale Verteilungen Diskreten Werten. Dies lässt sich am besten am Beispiel zweier unabhängiger Würfel zeigen.\n",
    "\n",
    "Wir wollen im folgenden also aufzeigen wie die Wahrscheinlichkeits Verteilung zweier 6 seitiger Würfel ist. Da wir die Augenzahl beider Bestimmen führen wir eine Addition aller möglichen Werte vor. Da uns die Auftrittswahrscheinlichkeit interessiert eignet sich ein dict zum speichern der Daten. Dabei ist der Schlüssel die Augenzahl und der Wert die Autrittsanzahl:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7a58ad07-9593-4c58-9a86-23a002ef5e94",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-d852ddb9a826faa3",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 5, 9: 4, 10: 3, 11: 2, 12: 1}\n"
     ]
    }
   ],
   "source": [
    "# Two Dices\n",
    "first_dice: np.array = np.arange(1,7)\n",
    "second_dice: np.array = np.arange(1,7)\n",
    "\n",
    "# Creating a dict with all keys\n",
    "dist: dict = {el: 0 for el in range(2,13)}\n",
    "\n",
    "# Summing all possible combinations and store them inside the dict\n",
    "for el1 in first_dice:\n",
    "    for el2 in second_dice:\n",
    "        dice_roll: int = el1 + el2\n",
    "        dist[dice_roll] += 1\n",
    "\n",
    "# Print the result\n",
    "print(dist)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b2ff2d3-e8b0-45e9-9429-8db1acc5e526",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-591fc5a2d228a903",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "Wie wir erstmal sehen können folgen die Zahl einem Muster, dieses lässt sich mittels Balkendiagramm visualisieren:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f7915dad-d12a-4db1-b691-3fd53ead4027",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-b70e902f730c6e98",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 21740 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.bar(dist.keys(), dist.values(), color='#F00a00')\n",
    "plt.xticks(list(dist.keys()))\n",
    "plt.title(\"Double Dice role distribution\")\n",
    "plt.xlabel(\"Points rolled\")\n",
    "plt.ylabel(\"Number of occurrences\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3dc3bc4-b796-4ac9-be15-03c780673084",
   "metadata": {
    "nbgrader": {
     "grade": false,
     "grade_id": "cell-f6d18a41bf8cf643",
     "locked": true,
     "schema_version": 3,
     "solution": false,
     "task": false
    }
   },
   "source": [
    "### Aufgabe\n",
    "\n",
    "*7 Punkte*\n",
    "\n",
    "Gegeben sind zwei Würfel aus dem Spiel **Super Mario Party**. Es wird im folgenden angenommen das die Charaktere ihre Würfel würfeln und deren Augenzahl addiert wird.\n",
    "\n",
    "- Stelle alle Kombinationen der Gegebenen Daten auf, speichere diese als Dictionary in der Variablen `dist_mp`.\n",
    "- Exthrahiere den am dritt häufigsten Aufkommenden Wert und speicher diesen in der Variablen `best3` als tuple in der Form `(<Augenzahl>, <Auftritte>)`.\n",
    "- Plotte eine geeignete binomiale Verteilung. *Tipp: schaue dir an wie du den Plot mittels matplotlib vergrößerst*\n",
    "\n",
    "Einige der Würfel gebem dem Charakter Münzen. Finde eine logische Schlussfolgerung diese mit anderen Werten zu ersetzen. Überlege dabei welche Auswirkungen es auf die Augenzahl hat das der Charakter Münzen bekomment.\n",
    "\n",
    "|Charakter|Dice|\n",
    "|-|-|\n",
    "|Mario|1, 3, 3, 3, 5, 6|\n",
    "|Peach|0, 2, 4, 4, 4, 6|\n",
    "|Boo|-2 Coins, -2 Coins, 5, 5, 7, 7\n",
    "|Donkey Kong|+5 Coins, 0, 0, 0, 10, 10|"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "41b90981-ef18-40b4-bb03-9cd3407869b4",
   "metadata": {
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-3e706ac49be47a70",
     "locked": false,
     "points": 3,
     "schema_version": 3,
     "solution": true,
     "task": false
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'plt' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 27\u001b[0m\n\u001b[1;32m     24\u001b[0m best3: \u001b[38;5;28mtuple\u001b[39m \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m10\u001b[39m, dist_mp[\u001b[38;5;241m10\u001b[39m])\n\u001b[1;32m     26\u001b[0m \u001b[38;5;66;03m# Plot\u001b[39;00m\n\u001b[0;32m---> 27\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241m.\u001b[39mbar(dist_mp\u001b[38;5;241m.\u001b[39mkeys(), dist_mp\u001b[38;5;241m.\u001b[39mvalues(), color\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m#F00a00\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m     28\u001b[0m plt\u001b[38;5;241m.\u001b[39mxticks(\u001b[38;5;28mlist\u001b[39m(dist_mp\u001b[38;5;241m.\u001b[39mkeys()))\n\u001b[1;32m     29\u001b[0m plt\u001b[38;5;241m.\u001b[39mtitle(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDouble Dice role distribution\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[0;31mNameError\u001b[0m: name 'plt' is not defined"
     ]
    }
   ],
   "source": [
    "dist_mp: dict = None\n",
    "best3: tuple = None\n",
    "# BEGIN SOLUTION\n",
    "# define dices\n",
    "mario = [1, 3, 3, 3, 5, 6]\n",
    "peach = [0, 2, 4, 4, 4, 6]\n",
    "boo = [0, 0, 5, 5, 7, 7]\n",
    "dk = [0, 0, 0, 0, 10, 10]\n",
    "\n",
    "# Create Keys\n",
    "keys: set = {m+p+b+d for m in mario for p in peach for b in boo for d in dk}\n",
    "\n",
    "# Creating a dict with all keys\n",
    "dist_mp: dict = {k: 0 for k in keys}\n",
    "\n",
    "# Summing all possible combinations and store them inside the dict\n",
    "for m in mario:\n",
    "    for p in peach:\n",
    "        for b in boo:\n",
    "            for d in dk:\n",
    "                dice_roll: int = m+p+b+d\n",
    "                dist_mp[dice_roll] += 1\n",
    "\n",
    "best3: tuple = (10, dist_mp[10])\n",
    "\n",
    "# Plot\n",
    "plt.bar(dist_mp.keys(), dist_mp.values(), color='#F00a00')\n",
    "plt.xticks(list(dist_mp.keys()))\n",
    "plt.title(\"Double Dice role distribution\")\n",
    "plt.xlabel(\"Points rolled\")\n",
    "plt.ylabel(\"Number of occurrences\")\n",
    "plt.show()\n",
    "# END SOLUTION"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "9b142059-c19b-49c4-bc4b-02ca875b1fbf",
   "metadata": {
    "editable": true,
    "nbgrader": {
     "grade": true,
     "grade_id": "cell-cd335bd7d9a4cd4f",
     "locked": true,
     "points": 4,
     "schema_version": 3,
     "solution": false,
     "task": false
    },
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Hier werden ihre Lösungen getestet...\n",
    "# Check dist_mp 1 Punkt\n",
    "assert isinstance(dist_mp, dict), \"dist_mp isn't a dict\"\n",
    "assert len(dist_mp) == 27, \"dist_mp too small\"\n",
    "\n",
    "# Check if there is a zero value\n",
    "for key, el in dist_mp.items():\n",
    "    assert el != 0, f\"Key: {key} in dist_mp is zero, thats impossible\"\n",
    "\n",
    "### BEGIN HIDDEN TESTS\n",
    "test_dist_mp = {1: 8,\n",
    " 3: 32,\n",
    " 5: 56,\n",
    " 6: 16,\n",
    " 7: 88,\n",
    " 8: 48,\n",
    " 9: 48,\n",
    " 10: 112,\n",
    " 11: 20,\n",
    " 12: 152,\n",
    " 13: 32,\n",
    " 14: 136,\n",
    " 15: 60,\n",
    " 16: 64,\n",
    " 17: 76,\n",
    " 18: 32,\n",
    " 19: 32,\n",
    " 20: 56,\n",
    " 21: 8,\n",
    " 22: 76,\n",
    " 23: 8,\n",
    " 24: 68,\n",
    " 25: 16,\n",
    " 26: 28,\n",
    " 27: 16,\n",
    " 28: 4,\n",
    " 29: 4}\n",
    "\n",
    "# 3 Punkte\n",
    "for test, answer in zip(test_dist_mp.items(), dist_mp.items()):\n",
    "    test_k, test_v = test\n",
    "    k, v = answer\n",
    "    assert test_k == k, f\"Key missmatch should: {test_k} | is: {k}\"\n",
    "    assert test_v == v, f\"Value missmatch should: {test_v} | is: {v}\"\n",
    "\n",
    "# 1 Punkt\n",
    "assert best3[0] == 10\n",
    "assert best3[1] == 112\n",
    "### END HIDDEN TESTS"
   ]
  }
 ],
 "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",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
