From a61e837b26c970eed83356f1d592e52f50c24845 Mon Sep 17 00:00:00 2001 From: Joachim Bach Date: Thu, 2 Oct 2025 15:59:25 +0200 Subject: [PATCH] done ex1 --- PW-3/ex1/ex1-bayes-stud.ipynb | 293 +++++++++++++++++++++++++++++----- 1 file changed, 257 insertions(+), 36 deletions(-) diff --git a/PW-3/ex1/ex1-bayes-stud.ipynb b/PW-3/ex1/ex1-bayes-stud.ipynb index 4d6f60e..0b22b7b 100644 --- a/PW-3/ex1/ex1-bayes-stud.ipynb +++ b/PW-3/ex1/ex1-bayes-stud.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 468, "metadata": {}, "outputs": [], "source": [ @@ -42,7 +42,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 469, "metadata": { "pycharm": { "is_executing": false @@ -58,16 +58,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 470, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " x1 x2 y\n", + "0 34.623660 78.024693 0\n", + "1 30.286711 43.894998 0\n", + "2 35.847409 72.902198 0\n", + "3 60.182599 86.308552 1\n", + "4 79.032736 75.344376 1\n" + ] + } + ], "source": [ "X_train, y_train = read_data(\"ex1-data-train.csv\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 471, "metadata": {}, "outputs": [], "source": [ @@ -85,15 +98,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 472, "metadata": { "pycharm": { "is_executing": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "P(C0) = 0.4, P(C1) = 0.6\n" + ] + } + ], "source": [ "# TODO: Compute the priors\n", + "\n", + "unique, counts = np.unique(y_train, return_counts=True)\n", + "\n", + "P_c0 = counts[0] / (counts[0] + counts[1])\n", + "P_c1 = counts[1] / (counts[0] + counts[1])\n", + "print(f\"P(C0) = {P_c0}, P(C1) = {P_c1}\")\n", "\n" ] }, @@ -106,27 +133,62 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 473, "metadata": { "pycharm": { "is_executing": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABQcAAAINCAYAAACDAcneAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQXpJREFUeJzt3XmUVPW5LuCv6ZYGGQWFbhJGRXBABjGIkohHbpAoajRKPERBjBqDAsHrQAwqccB4PAaMionnRDwJTvEqjqhoxCEiCAhqNIDaAlGQRAUEpEF63z+MdWwBqYbqLmA/z1q1lrX3rtpf/7qMb97eVVWQJEkSAAAAAEDq1Mr3AAAAAABAfigHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJQqyvcAX1VRURHvv/9+NGjQIAoKCvI9DgBAlSVJEp988km0aNEiatXyt9idkUwKAOzMqpJHd7hy8P3334+WLVvmewwAgO22ZMmS+OY3v5nvMdgGMikAsCvIJo/ucOVggwYNIuLz4Rs2bJjnaQAAqm7VqlXRsmXLTK5h5yOTAgA7s6rk0R2uHPzibRsNGzYUxACAnZq3o+68ZFIAYFeQTR71ITgAAAAAkFLKQQAAAABIKeUgAAAAAKTUDveZgwDA1iVJEp999lls3Lgx36OkUmFhYRQVFflMQQAgteTR/MtVJlUOAsBOZv369bF06dJYu3ZtvkdJtd133z1KS0ujdu3a+R4FAKBGyaM7jlxkUuUgAOxEKioqoqysLAoLC6NFixZRu3ZtV6/VsCRJYv369fGPf/wjysrKon379lGrlk9qAQDSQR7dMeQykyoHAWAnsn79+qioqIiWLVvG7rvvnu9xUqtu3bqx2267xaJFi2L9+vVRp06dfI8EAFAj5NEdR64yqT9zA8BOyJVq+ed3AACkmSy0Y8jF78FvEgAAAABSSjkIAAAAACnlMwcBYBcx6v7XauxcY0/sVOXHJEkS55xzTtx3333x8ccfxyuvvBJdunTZ4vHvvvtutG3bNnPctGnT4sgjj4yPP/44GjduvM2zt2nTJkaMGBEjRozY5ucAAGBTNZlHI6qeSeXRzVMOAgA14vHHH4+JEyfGtGnTol27drHnnnt+7fEtW7aMpUuXbvU4AADIhjy6ecpBAKBGvP3221FaWhqHHXZYVscXFhZGSUlJNU8FAEBayKOb5zMHAYBqN3jw4Dj//PNj8eLFUVBQEG3atInHH388evXqFY0bN46mTZvGscceG2+//XbmMe+++24UFBTE3Llzt/i8L7zwQnz729+OunXrRsuWLWPYsGGxZs2azP7ly5dH//79o27dutG2bduYNGlSdf6YAADsoOTRLVMOAgDVbvz48fHLX/4yvvnNb8bSpUvj5ZdfjjVr1sTIkSNj1qxZ8fTTT0etWrXi+9//flRUVGT1nG+//XYcffTRcdJJJ8Wrr74a99xzT7zwwgtx3nnnZY4ZPHhwLFmyJJ555pm477774pZbbonly5dX148JAMAOSh7dsiqXg88991z0798/WrRoEQUFBTF58uRNjnnzzTfjuOOOi0aNGkW9evXikEMOicWLF+diXgBgJ9SoUaNo0KBB5q0Ze+21V5x00klx4oknxj777BNdunSJ3//+9/Haa6/FG2+8kdVzjh07NgYOHBgjRoyI9u3bx2GHHRY33nhj/M///E+sW7cuFixYEFOmTInbbrstDj300Dj44IPjv//7v+PTTz+t5p+W6iaPAgBVJY9uWZXLwTVr1kTnzp3j5ptv3uz+t99+O3r16hUdO3aMadOmxauvvhqjR4+OOnXqbPewAMCuY+HChXHqqadGu3btomHDhtGmTZuIiKwLnHnz5sXEiROjfv36mVvfvn2joqIiysrK4s0334yioqI4+OCDM4/p2LHjdn2zHDsGeRQAyAV59HNV/kKSfv36Rb9+/ba4/9JLL43vfe97cd1112W27b333ts2HQCwy+rfv3+0bt06brvttmjRokVUVFTEgQceGOvXr8/q8atXr45zzjknhg0btsm+Vq1axYIFC3I9MjsIeRQAyAV59HM5/czBioqKePTRR2PfffeNvn37RrNmzaJHjx6bfavHF8rLy2PVqlWVbgDAru3DDz+M+fPnxy9+8Ys46qijYr/99ouPP/64Ss/RrVu3eOONN2KfffbZ5Fa7du3o2LFjfPbZZzF79uzMY+bPnx8rVqzI8U/DjmRb8miETAoAaSOP/q8qXzn4dZYvXx6rV6+Oa6+9Nq666qr41a9+FY8//niceOKJ8cwzz8QRRxyxyWPGjh0bY8aMyeUYAGyrh4fne4L86D8+3xOkzh577BFNmzaN3/3ud1FaWhqLFy+OSy65pErPcfHFF8ehhx4a5513Xvz4xz+OevXqxRtvvBFTp06Nm266KTp06BBHH310nHPOOTFhwoQoKiqKESNGRN26davpp2JHsC15NEIm3dGNuv+1fI+wwxt7Yqd8jwCwU5FH/1dOy8Evvs3l+OOPj5/97GcREdGlS5d48cUX49Zbb91sGBs1alSMHDkyc3/VqlXRsmXLXI4FAKmwM/0fw1q1asXdd98dw4YNiwMPPDA6dOgQN954Y/Tu3Tvr5zjooIPi2WefjUsvvTS+/e1vR5Iksffee8eAAQMyx9x+++3x4x//OI444oho3rx5XHXVVTF69Ohq+InYUWxLHo2QSQEgF+TRnTOP5rQc3HPPPaOoqCj233//Stv322+/eOGFFzb7mOLi4iguLs7lGADADmjEiBExYsSIzP0+ffps8k1wSZJk/rlNmzaV7vfu3bvS/YiIQw45JJ588sktnrOkpCQeeeSRSttOO+20bRmfncS25NEImRQA0kAe3bycfuZg7dq145BDDon58+dX2r5gwYJo3bp1Lk8FAACbkEcBAKqmylcOrl69Ot56663M/bKyspg7d240adIkWrVqFRdeeGEMGDAgvvOd78SRRx4Zjz/+eDz88MMxbdq0XM4NAEBKyaMAALlT5XJw1qxZceSRR2buf/HZLIMGDYqJEyfG97///bj11ltj7NixMWzYsOjQoUP8v//3/6JXr165mxoAgNSSRwEAcqfK5eDm3l/9VUOGDIkhQ4Zs81AAALAl8igAQO7k9DMHAQAAAICdh3IQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAGzGFVdcEV26dMn3GAAApFRN5dEqf1sxALCDenh4zZ2r//iaOxcAADuHmsyjETJpjrhyEAAAAABSSjkIANSI3r17x3nnnRfnnXdeNGrUKPbcc88YPXp0JEkSERF/+MMfonv37tGgQYMoKSmJf//3f4/ly5dnHv/xxx/HwIEDY6+99oq6detG+/bt4/bbb4+IiPXr18d5550XpaWlUadOnWjdunWMHTs289gVK1bEj3/849hrr72iYcOG8W//9m8xb968SvNde+210bx582jQoEGceeaZsW7duhpYFQAAaoo8unnKQQCgxtxxxx1RVFQUM2fOjPHjx8cNN9wQ//Vf/xURERs2bIgrr7wy5s2bF5MnT4533303Bg8enHns6NGj44033ogpU6bEm2++GRMmTIg999wzIiJuvPHGeOihh+Lee++N+fPnx6RJk6JNmzaZx5588smxfPnymDJlSsyePTu6desWRx11VHz00UcREXHvvffGFVdcEddcc03MmjUrSktL45ZbbqmxdQEAoGbIo5vymYMAQI1p2bJl/PrXv46CgoLo0KFDvPbaa/HrX/86zjrrrBgyZEjmuHbt2sWNN94YhxxySKxevTrq168fixcvjq5du0b37t0jIiqFrcWLF0f79u2jV69eUVBQEK1bt87se+GFF2LmzJmxfPnyKC4ujoiI66+/PiZPnhz33XdfnH322TFu3Lg488wz48wzz4yIiKuuuiqeeuopVw8CAOxi5NFNuXIQAKgxhx56aBQUFGTu9+zZMxYuXBgbN26M2bNnR//+/aNVq1bRoEGDOOKIIyLi86AVEXHuuefG3XffHV26dImLLrooXnzxxczzDB48OObOnRsdOnSIYcOGxZNPPpnZN2/evFi9enU0bdo06tevn7mVlZXF22+/HRERb775ZvTo0aPSrD179qy2dQAAID/k0U25chAAyLt169ZF3759o2/fvjFp0qTYa6+9YvHixdG3b99Yv359RET069cvFi1aFI899lhMnTo1jjrqqBg6dGhcf/310a1btygrK4spU6bEU089Faecckr06dMn7rvvvli9enWUlpbGtGnTNjlv48aNa/YHBQBgh5TmPKocBABqzIwZMyrdf+mll6J9+/bxt7/9LT788MO49tpro2XLlhERMWvWrE0ev9dee8WgQYNi0KBB8e1vfzsuvPDCuP766yMiomHDhjFgwIAYMGBA/OAHP4ijjz46Pvroo+jWrVssW7YsioqKKr3148v222+/mDFjRpx++umVZgMAYNcij25KOQgA1JjFixfHyJEj45xzzok5c+bEb37zm/jP//zPaNWqVdSuXTt+85vfxE9+8pN4/fXX48orr6z02MsuuywOPvjgOOCAA6K8vDweeeSR2G+//SIi4oYbbojS0tLo2rVr1KpVK/70pz9FSUlJNG7cOPr06RM9e/aME044Ia677rrYd9994/33349HH300vv/970f37t1j+PDhMXjw4OjevXscfvjhMWnSpPjrX/8a7dq1y8cyAQBQTeTRTSkHAYAac/rpp8enn34a3/rWt6KwsDCGDx8eZ599dhQUFMTEiRPj5z//edx4443RrVu3uP766+O4447LPLZ27doxatSoePfdd6Nu3brx7W9/O+6+++6IiGjQoEFcd911sXDhwigsLIxDDjkkHnvssahV6/OPV37sscfi0ksvjTPOOCP+8Y9/RElJSXznO9+J5s2bR0TEgAED4u23346LLroo1q1bFyeddFKce+658cQTT9T8IgEAUG3k0U0VJEmSVPtZqmDVqlXRqFGjWLlyZTRs2DDf4wCky8PD8z1BfvQfn+8JsrZu3booKyuLtm3bRp06dfI9TpX07t07unTpEuPGjcv3KDnxdb8LeWbn53e4Yxl1/2v5HmGHN/bETvkeAUgJeXTHsqXfR1WyjG8rBgAAAICUUg4CAAAAQEr5zEEAoEZMmzYt3yMAAJBi8ujmuXIQAAAAAFJKOQgAAAAAKaUcBICdUJIk+R4h9fwOAIA0k4V2DLn4PSgHAWAnsttuu0VExNq1a/M8CV/8Dr74nQAApIE8umPJRSb1hSQAsBMpLCyMxo0bx/LlyyMiYvfdd4+CgoI8T5UuSZLE2rVrY/ny5dG4ceMoLCzM90gAADVGHt0x5DKTKgcBYCdTUlISEZEJZORH48aNM78LAIA0kUd3HLnIpMpBANjJFBQURGlpaTRr1iw2bNiQ73FSabfddnPFIACQWvLojiFXmVQ5CAA7qcLCQgUVAAB5I4/uGnwhCQAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASKkql4PPPfdc9O/fP1q0aBEFBQUxefLkLR77k5/8JAoKCmLcuHHbMSIAAPwveRQAIHeqXA6uWbMmOnfuHDfffPPXHvfAAw/ESy+9FC1atNjm4QAA4KvkUQCA3Cmq6gP69esX/fr1+9pj3nvvvTj//PPjiSeeiGOOOWabhwMAgK+SRwEAcifnnzlYUVERp512Wlx44YVxwAEH5PrpAQDga8mjAADZq/KVg1vzq1/9KoqKimLYsGFZHV9eXh7l5eWZ+6tWrcr1SAAApEhV82iETAoApFdOrxycPXt2jB8/PiZOnBgFBQVZPWbs2LHRqFGjzK1ly5a5HAkAgBTZljwaIZMCAOmV03Lw+eefj+XLl0erVq2iqKgoioqKYtGiRXHBBRdEmzZtNvuYUaNGxcqVKzO3JUuW5HIkAABSZFvyaIRMCgCkV07fVnzaaadFnz59Km3r27dvnHbaaXHGGWds9jHFxcVRXFycyzEAAEipbcmjETIpAJBeVS4HV69eHW+99VbmfllZWcydOzeaNGkSrVq1iqZNm1Y6frfddouSkpLo0KHD9k8LAEDqyaMAALlT5XJw1qxZceSRR2bujxw5MiIiBg0aFBMnTszZYAAAsDnyKABA7lS5HOzdu3ckSZL18e+++25VTwEAAFskjwIA5E5Ov5AEAAAAANh5KAcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApFSVy8Hnnnsu+vfvHy1atIiCgoKYPHlyZt+GDRvi4osvjk6dOkW9evWiRYsWcfrpp8f777+fy5kBAEgxeRQAIHeqXA6uWbMmOnfuHDfffPMm+9auXRtz5syJ0aNHx5w5c+L++++P+fPnx3HHHZeTYQEAQB4FAMidoqo+oF+/ftGvX7/N7mvUqFFMnTq10rabbropvvWtb8XixYujVatW2zYlAAD8izwKAJA7VS4Hq2rlypVRUFAQjRs33uz+8vLyKC8vz9xftWpVdY8EAECKbC2PRsikAEB6VWs5uG7durj44ovj1FNPjYYNG272mLFjx8aYMWOqc4wtGnX/a3k5745s7Imd8j0CAEDOZJNHI/KbSQGq7OHh+Z6gZvQfn+8Jao7fKXlUbd9WvGHDhjjllFMiSZKYMGHCFo8bNWpUrFy5MnNbsmRJdY0EAECKZJtHI2RSACC9quXKwS+C2KJFi+LPf/7z1/6Vtri4OIqLi6tjDAAAUqoqeTRCJgUA0ivn5eAXQWzhwoXxzDPPRNOmTXN9CgAA2CJ5FAAge1UuB1evXh1vvfVW5n5ZWVnMnTs3mjRpEqWlpfGDH/wg5syZE4888khs3Lgxli1bFhERTZo0idq1a+ducgAAUkkeBQDInSqXg7NmzYojjzwyc3/kyJERETFo0KC44oor4qGHHoqIiC5dulR63DPPPBO9e/fe9kkBACDkUQCAXKpyOdi7d+9IkmSL+79uHwAAbC95FAAgd6rt24oBAAAAgB2bchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJQqyvcAsKMbdf9r+R5hhzP2xE75HgEAAADIAVcOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSqsrl4HPPPRf9+/ePFi1aREFBQUyePLnS/iRJ4rLLLovS0tKoW7du9OnTJxYuXJireQEASDl5FAAgd6pcDq5ZsyY6d+4cN99882b3X3fddXHjjTfGrbfeGjNmzIh69epF3759Y926dds9LAAAyKMAALlTVNUH9OvXL/r167fZfUmSxLhx4+IXv/hFHH/88RER8T//8z/RvHnzmDx5cvzwhz/cvmkBAEg9eRQAIHdy+pmDZWVlsWzZsujTp09mW6NGjaJHjx4xffr0XJ4KAAA2IY8CAFRNla8c/DrLli2LiIjmzZtX2t68efPMvq8qLy+P8vLyzP1Vq1blciQAAFJkW/JohEwKAKRXTsvBbTF27NgYM2ZMvscAACDFZFIAqAEPD8/3BDWj//h8T1AlOX1bcUlJSUREfPDBB5W2f/DBB5l9XzVq1KhYuXJl5rZkyZJcjgQAQIpsSx6NkEkBgPTKaTnYtm3bKCkpiaeffjqzbdWqVTFjxozo2bPnZh9TXFwcDRs2rHQDAIBtsS15NEImBQDSq8pvK169enW89dZbmftlZWUxd+7caNKkSbRq1SpGjBgRV111VbRv3z7atm0bo0ePjhYtWsQJJ5yQy7kBAEgpeRQAIHeqXA7OmjUrjjzyyMz9kSNHRkTEoEGDYuLEiXHRRRfFmjVr4uyzz44VK1ZEr1694vHHH486derkbmoAAFJLHgUAyJ0ql4O9e/eOJEm2uL+goCB++ctfxi9/+cvtGgwAADZHHgUAyJ2cfuYgAAAAALDzUA4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASKmifA8A7LhO+Pt1m9/xcJOaHQSq28PD8z0BNan/+HxPAAAAOwxXDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAplfNycOPGjTF69Oho27Zt1K1bN/bee++48sorI0mSXJ8KAAA2IY8CAGSvKNdP+Ktf/SomTJgQd9xxRxxwwAExa9asOOOMM6JRo0YxbNiwXJ8OAAAqkUcBALKX83LwxRdfjOOPPz6OOeaYiIho06ZN3HXXXTFz5sxcnwoAADYhjwIAZC/nbys+7LDD4umnn44FCxZERMS8efPihRdeiH79+m32+PLy8li1alWlGwAAbKuq5tEImRQASK+cXzl4ySWXxKpVq6Jjx45RWFgYGzdujKuvvjoGDhy42ePHjh0bY8aMyfUYAACkVFXzaIRMmlYn/P26fI+QOw83+fr9/cfXzByQSw8Pz/cEkAo5v3Lw3nvvjUmTJsWdd94Zc+bMiTvuuCOuv/76uOOOOzZ7/KhRo2LlypWZ25IlS3I9EgAAKVLVPBohkwIA6ZXzKwcvvPDCuOSSS+KHP/xhRER06tQpFi1aFGPHjo1BgwZtcnxxcXEUFxfnegwAAFKqqnk0QiYFANIr51cOrl27NmrVqvy0hYWFUVFRketTAQDAJuRRAIDs5fzKwf79+8fVV18drVq1igMOOCBeeeWVuOGGG2LIkCG5PhUAAGxCHgUAyF7Oy8Hf/OY3MXr06PjpT38ay5cvjxYtWsQ555wTl112Wa5PBQAAm5BHAQCyl/NysEGDBjFu3LgYN25crp8aAAC2Sh4FAMhezj9zEAAAAADYOSgHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIqaJ8DwAAAAA58fDwfE8AsNNx5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWUgwAAAACQUspBAAAAAEgp5SAAAAAApJRyEAAAAABSSjkIAAAAACmlHAQAAACAlFIOAgAAAEBKKQcBAAAAIKWqpRx877334kc/+lE0bdo06tatG506dYpZs2ZVx6kAAGAT8igAQHaKcv2EH3/8cRx++OFx5JFHxpQpU2KvvfaKhQsXxh577JHrUwEAwCbkUQCA7OW8HPzVr34VLVu2jNtvvz2zrW3btrk+DQAAbJY8CgCQvZy/rfihhx6K7t27x8knnxzNmjWLrl27xm233Zbr0wAAwGbJowAA2ct5OfjOO+/EhAkTon379vHEE0/EueeeG8OGDYs77rhjs8eXl5fHqlWrKt0AAGBbVTWPRsikAEB65fxtxRUVFdG9e/e45pprIiKia9eu8frrr8ett94agwYN2uT4sWPHxpgxY3I9BgAAKVXVPBohk7Lzm1H20dfun3z/azU0SX6d8Pctr0OPtk1qcJKd09ZeR3gdsWvK+ZWDpaWlsf/++1fatt9++8XixYs3e/yoUaNi5cqVmduSJUtyPRIAAClS1TwaIZMCAOmV8ysHDz/88Jg/f36lbQsWLIjWrVtv9vji4uIoLi7O9RgAAKRUVfNohEwKAKRXzq8c/NnPfhYvvfRSXHPNNfHWW2/FnXfeGb/73e9i6NChuT4VAABsQh4FAMhezsvBQw45JB544IG466674sADD4wrr7wyxo0bFwMHDsz1qQAAYBPyKABA9nL+tuKIiGOPPTaOPfbY6nhqAADYKnkUACA7Ob9yEAAAAADYOSgHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKRUUb4HYMcy6v7X8j3CDumEv1+X7xF2KDPKPsr3CDucHm2b5HsEAHZCshfUHBkWYPNcOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACklHIQAAAAAFJKOQgAAAAAKaUcBAAAAICUUg4CAAAAQEopBwEAAAAgpZSDAAAAAJBSykEAAAAASCnlIAAAAACkVLWXg9dee20UFBTEiBEjqvtUAACwCXkUAGDLqrUcfPnll+O3v/1tHHTQQdV5GgAA2Cx5FADg61VbObh69eoYOHBg3HbbbbHHHntU12kAAGCz5FEAgK2rtnJw6NChccwxx0SfPn2+9rjy8vJYtWpVpRsAAGyvbPNohEwKAKRXUXU86d133x1z5syJl19+eavHjh07NsaMGVMdYwAAkFJVyaMRMikAkF45v3JwyZIlMXz48Jg0aVLUqVNnq8ePGjUqVq5cmbktWbIk1yMBAJAiVc2jETIpAJBeOb9ycPbs2bF8+fLo1q1bZtvGjRvjueeei5tuuinKy8ujsLAws6+4uDiKi4tzPQYAAClV1TwaIZMCAOmV83LwqKOOitdee63StjPOOCM6duwYF1988SZBDAAAckkeBQDIXs7LwQYNGsSBBx5YaVu9evWiadOmm2wHAIBck0cBALJXbd9WDAAAAADs2Krl24q/atq0aTVxGgAA2Cx5FABg81w5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACClivI9ADuPE/5+Xb5HgB3WjLKP8j3CDqdH2yb5HgEA+BdZHoAtceUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFI5LwfHjh0bhxxySDRo0CCaNWsWJ5xwQsyfPz/XpwEAgM2SRwEAspfzcvDZZ5+NoUOHxksvvRRTp06NDRs2xHe/+91Ys2ZNrk8FAACbkEcBALJXlOsnfPzxxyvdnzhxYjRr1ixmz54d3/nOd3J9OgAAqEQeBQDIXs7Lwa9auXJlREQ0adJks/vLy8ujvLw8c3/VqlXVPRIAACmytTwaIZMCAOlVreVgRUVFjBgxIg4//PA48MADN3vM2LFjY8yYMdU5BgB5MKPso3yPwE6gR9stlzWQC9nk0QiZFAByKe3/X2Dy/a9t9ZixJ3aqgUmyU63fVjx06NB4/fXX4+67797iMaNGjYqVK1dmbkuWLKnOkQAASJFs8miETAoApFe1XTl43nnnxSOPPBLPPfdcfPOb39ziccXFxVFcXFxdYwAAkFLZ5tEImRQASK+cl4NJksT5558fDzzwQEybNi3atm2b61MAAMAWyaMAANnLeTk4dOjQuPPOO+PBBx+MBg0axLJlyyIiolGjRlG3bt1cnw4AACqRRwEAspfzzxycMGFCrFy5Mnr37h2lpaWZ2z333JPrUwEAwCbkUQCA7FXL24oBACBf5FEAgOxV67cVAwAAAAA7LuUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAApVW3l4M033xxt2rSJOnXqRI8ePWLmzJnVdSoAANiEPAoAsHXVUg7ec889MXLkyLj88stjzpw50blz5+jbt28sX768Ok4HAACVyKMAANmplnLwhhtuiLPOOivOOOOM2H///ePWW2+N3XffPX7/+99Xx+kAAKASeRQAIDtFuX7C9evXx+zZs2PUqFGZbbVq1Yo+ffrE9OnTNzm+vLw8ysvLM/dXrlwZERGrVq3K9Wibnnvt6mo/x65kzbr1+R4BgF3MqrXlWz8o5yet/ozxRY5JkqTaz8WmqppHI/KXSeXR/JJvgarKS3bZCaX9f1+z+e97dWeMquTRnJeD//znP2Pjxo3RvHnzStubN28ef/vb3zY5fuzYsTFmzJhNtrds2TLXo7Gdfp3vAQAgJ35bY2f65JNPolGjRjV2Pj5X1TwaIZOmlXwLQPW4d6tH1NR/g7LJozkvB6tq1KhRMXLkyMz9ioqK+Oijj6Jp06ZRUFBQbeddtWpVtGzZMpYsWRINGzastvPszKxRdqzT1lmjrbNG2bFOW2eNslPd65QkSXzyySfRokWLnD831aMmMql/P7NnrbJnrbJnrbJnrbJnrbJnrbKXi7WqSh7NeTm45557RmFhYXzwwQeVtn/wwQdRUlKyyfHFxcVRXFxcaVvjxo1zPdYWNWzY0ItyK6xRdqzT1lmjrbNG2bFOW2eNslOd6+SKwfypah6NqNlM6t/P7Fmr7Fmr7Fmr7Fmr7Fmr7Fmr7G3vWmWbR3P+hSS1a9eOgw8+OJ5++unMtoqKinj66aejZ8+euT4dAABUIo8CAGSvWt5WPHLkyBg0aFB07949vvWtb8W4ceNizZo1ccYZZ1TH6QAAoBJ5FAAgO9VSDg4YMCD+8Y9/xGWXXRbLli2LLl26xOOPP77Jh0LnU3FxcVx++eWbvH2E/2WNsmOdts4abZ01yo512jprlB3rtOvbEfOo1132rFX2rFX2rFX2rFX2rFX2rFX2anqtCpJsvtMYAAAAANjl5PwzBwEAAACAnYNyEAAAAABSSjkIAAAAACmlHAQAAACAlNqly8EJEybEQQcdFA0bNoyGDRtGz549Y8qUKZn969ati6FDh0bTpk2jfv36cdJJJ8UHH3yQx4nz79prr42CgoIYMWJEZpt1irjiiiuioKCg0q1jx46Z/dboc++991786Ec/iqZNm0bdunWjU6dOMWvWrMz+JEnisssui9LS0qhbt2706dMnFi5cmMeJa16bNm02eS0VFBTE0KFDI8JrKSJi48aNMXr06Gjbtm3UrVs39t5777jyyivjy9+f5bUU8cknn8SIESOidevWUbdu3TjssMPi5ZdfzuxP4xo999xz0b9//2jRokUUFBTE5MmTK+3PZk0++uijGDhwYDRs2DAaN24cZ555ZqxevboGfwp2dvLntpNDt0wWrRqZNHuyaXbk06qRU7dsh82ryS7soYceSh599NFkwYIFyfz585Of//znyW677Za8/vrrSZIkyU9+8pOkZcuWydNPP53MmjUrOfTQQ5PDDjssz1Pnz8yZM5M2bdokBx10UDJ8+PDMduuUJJdffnlywAEHJEuXLs3c/vGPf2T2W6Mk+eijj5LWrVsngwcPTmbMmJG88847yRNPPJG89dZbmWOuvfbapFGjRsnkyZOTefPmJccdd1zStm3b5NNPP83j5DVr+fLllV5HU6dOTSIieeaZZ5Ik8VpKkiS5+uqrk6ZNmyaPPPJIUlZWlvzpT39K6tevn4wfPz5zjNdSkpxyyinJ/vvvnzz77LPJwoULk8svvzxp2LBh8ve//z1JknSu0WOPPZZceumlyf33359ERPLAAw9U2p/Nmhx99NFJ586dk5deeil5/vnnk3322Sc59dRTa/gnYWcmf24bOfTryaLZk0mrRjbNjnxaNXLqlu2oeXWXLgc3Z4899kj+67/+K1mxYkWy2267JX/6058y+958880kIpLp06fnccL8+OSTT5L27dsnU6dOTY444ohMKLNOn7v88suTzp07b3afNfrcxRdfnPTq1WuL+ysqKpKSkpLkP/7jPzLbVqxYkRQXFyd33XVXTYy4Qxo+fHiy9957JxUVFV5L/3LMMcckQ4YMqbTtxBNPTAYOHJgkiddSkiTJ2rVrk8LCwuSRRx6ptL1bt27JpZdeao2SZJOwlc2avPHGG0lEJC+//HLmmClTpiQFBQXJe++9V2Ozs+uRP7+eHLp1smj2ZNLtI5tunnyaPTk1eztSXt2l31b8ZRs3boy777471qxZEz179ozZs2fHhg0bok+fPpljOnbsGK1atYrp06fncdL8GDp0aBxzzDGV1iMirNOXLFy4MFq0aBHt2rWLgQMHxuLFiyPCGn3hoYceiu7du8fJJ58czZo1i65du8Ztt92W2V9WVhbLli2rtE6NGjWKHj16pGqdvmz9+vXxxz/+MYYMGRIFBQVeS/9y2GGHxdNPPx0LFiyIiIh58+bFCy+8EP369YsIr6WIiM8++yw2btwYderUqbS9bt268cILL1ijzchmTaZPnx6NGzeO7t27Z47p06dP1KpVK2bMmFHjM7Pzkz+zI4dmRxbNjky67WTTLZNPsyenbrt85tWibR975/Daa69Fz549Y926dVG/fv144IEHYv/994+5c+dG7dq1o3HjxpWOb968eSxbtiw/w+bJ3XffHXPmzKn0GQBfWLZsmXWKiB49esTEiROjQ4cOsXTp0hgzZkx8+9vfjtdff90a/cs777wTEyZMiJEjR8bPf/7zePnll2PYsGFRu3btGDRoUGYtmjdvXulxaVunL5s8eXKsWLEiBg8eHBH+ffvCJZdcEqtWrYqOHTtGYWFhbNy4Ma6++uoYOHBgRITXUkQ0aNAgevbsGVdeeWXst99+0bx587jrrrti+vTpsc8++1ijzchmTZYtWxbNmjWrtL+oqCiaNGmS2nVj28if2ZNDsyOLZk8m3Xay6ZbJp9mTU7ddPvPqLl8OdujQIebOnRsrV66M++67LwYNGhTPPvtsvsfaYSxZsiSGDx8eU6dO3aTZ53998RehiIiDDjooevToEa1bt45777036tatm8fJdhwVFRXRvXv3uOaaayIiomvXrvH666/HrbfeGoMGDcrzdDum//7v/45+/fpFixYt8j3KDuXee++NSZMmxZ133hkHHHBAzJ07N0aMGBEtWrTwWvqSP/zhDzFkyJD4xje+EYWFhdGtW7c49dRTY/bs2fkeDVJP/syOHJo9WTR7Mum2k023TD6tGjl157PLv624du3asc8++8TBBx8cY8eOjc6dO8f48eOjpKQk1q9fHytWrKh0/AcffBAlJSX5GTYPZs+eHcuXL49u3bpFUVFRFBUVxbPPPhs33nhjFBUVRfPmza3TZjRu3Dj23XffeOutt7yW/qW0tDT233//Stv222+/zFtevliLr367WdrW6QuLFi2Kp556Kn784x9ntnktfe7CCy+MSy65JH74wx9Gp06d4rTTTouf/exnMXbs2IjwWvrC3nvvHc8++2ysXr06lixZEjNnzowNGzZEu3btrNFmZLMmJSUlsXz58kr7P/vss/joo49Su25sG/kzO3LotpNFt0wm3Tay6deTT6tGTt02+cyru3w5+FUVFRVRXl4eBx98cOy2227x9NNPZ/bNnz8/Fi9eHD179szjhDXrqKOOitdeey3mzp2buXXv3j0GDhyY+WfrtKnVq1fH22+/HaWlpV5L/3L44YfH/PnzK21bsGBBtG7dOiIi2rZtGyUlJZXWadWqVTFjxoxUrdMXbr/99mjWrFkcc8wxmW1eS59bu3Zt1KpV+T9PhYWFUVFRERFeS19Vr169KC0tjY8//jieeOKJOP74463RZmSzJj179owVK1ZU+qv2n//856ioqIgePXrU+MzsOuTPzZNDt50sumUy6baRTb+efLpt5NSqyWte3eavMtkJXHLJJcmzzz6blJWVJa+++mpyySWXJAUFBcmTTz6ZJMnnX8veqlWr5M9//nMya9aspGfPnknPnj3zPHX+fflb4pLEOiVJklxwwQXJtGnTkrKysuQvf/lL0qdPn2TPPfdMli9fniSJNUqSJJk5c2ZSVFSUXH311cnChQuTSZMmJbvvvnvyxz/+MXPMtddemzRu3Dh58MEHk1dffTU5/vjjU/OV9V+2cePGpFWrVsnFF1+8yT6vpSQZNGhQ8o1vfCN55JFHkrKysuT+++9P9txzz+Siiy7KHOO1lCSPP/54MmXKlOSdd95JnnzyyaRz585Jjx49kvXr1ydJks41+uSTT5JXXnkleeWVV5KISG644YbklVdeSRYtWpQkSXZrcvTRRyddu3ZNZsyYkbzwwgtJ+/btk1NPPTVfPxI7Iflz+8ihmyeLZk8mrTrZdOvk06qRU7dsR82ru3Q5OGTIkKR169ZJ7dq1k7322is56qijMsEsSZLk008/TX76058me+yxR7L77rsn3//+95OlS5fmceIdw1dDmXVKkgEDBiSlpaVJ7dq1k2984xvJgAEDkrfeeiuz3xp97uGHH04OPPDApLi4OOnYsWPyu9/9rtL+ioqKZPTo0Unz5s2T4uLi5Kijjkrmz5+fp2nz54knnkgiYrM/u9dSkqxatSoZPnx40qpVq6ROnTpJu3btkksvvTQpLy/PHOO1lCT33HNP0q5du6R27dpJSUlJMnTo0GTFihWZ/Wlco2eeeSaJiE1ugwYNSpIkuzX58MMPk1NPPTWpX79+0rBhw+SMM85IPvnkkzz8NOys5M/tI4dunixaNTJp1cimWyefVo2cumU7al4tSJIk2fbrDgEAAACAnVXqPnMQAAAAAPicchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQdjFFBQUxOTJkyMi4t13342CgoKYO3dutTzftGnToqCgIFasWLFdM2+LK664Irp06bLF/dnMtrXnAABg28ikn5NJgZ2BchB2MoMHD44TTjhhi/uXLl0a/fr1q5Zzt2zZMpYuXRoHHnhgtTx/Tfu///f/xtNPP53VsbkMbV+ExM3dli1blpNz5NL9998f3/3ud6Np06bbHewBgF2DTJo7MunWbdiwIS6++OLo1KlT1KtXL1q0aBGnn356vP/++/keDXYJRfkeAMitkpKSanvuwsLCan3+mla/fv2oX79+3s4/f/78aNiwYaVtzZo1y9M0W7ZmzZro1atXnHLKKXHWWWflexwAYCcgk2ZPJt26tWvXxpw5c2L06NHRuXPn+Pjjj2P48OFx3HHHxaxZs/I9Huz0XDkIu5gvv+XiqzZu3BhDhgyJjh07xuLFiyMi4sEHH4xu3bpFnTp1ol27djFmzJj47LPPNvv4Lb0lZPbs2dG9e/fYfffd47DDDov58+dX2j9hwoTYe++9o3bt2tGhQ4f4wx/+UGn/4sWL4/jjj4/69etHw4YN45RTTokPPvig0jHXXnttNG/ePBo0aBBnnnlmrFu3Lqv1+LrZvvqX12nTpsW3vvWtqFevXjRu3DgOP/zwWLRoUUycODHGjBkT8+bNy/w1deLEiVmd/+s0a9YsSkpKKt1q1aoV69atiwMOOCDOPvvszLFvv/12NGjQIH7/+99HRMSHH34Yp556anzjG9+I3XffPTp16hR33XVXpefv3bt3nH/++TFixIjYY489onnz5nHbbbfFmjVr4owzzogGDRrEPvvsE1OmTPnaOU877bS47LLLok+fPtv9MwMA6SCTViaTbl8mbdSoUUydOjVOOeWU6NChQxx66KFx0003xezZszOvIWDbKQchJcrLy+Pkk0+OuXPnxvPPPx+tWrWK559/Pk4//fQYPnx4vPHGG/Hb3/42Jk6cGFdffXWVnvvSSy+N//zP/4xZs2ZFUVFRDBkyJLPvgQceiOHDh8cFF1wQr7/+epxzzjlxxhlnxDPPPBMRERUVFXH88cfHRx99FM8++2xMnTo13nnnnRgwYEDmOe6999644oor4pprrolZs2ZFaWlp3HLLLds925d99tlnccIJJ8QRRxwRr776akyfPj3OPvvsKCgoiAEDBsQFF1wQBxxwQCxdujSWLl1aab5cq1OnTkyaNCnuuOOOePDBB2Pjxo3xox/9KP7P//k/mfnXrVsXBx98cDz66KPx+uuvx9lnnx2nnXZazJw5s9Jz3XHHHbHnnnvGzJkz4/zzz49zzz03Tj755DjssMNizpw58d3vfjdOO+20WLt2bbX9PAAAX5BJZdJcZdKVK1dGQUFBNG7cOJc/NqRTAuxUBg0alBx//PFb3B8RyQMPPJAkSZKUlZUlEZE8//zzyVFHHZX06tUrWbFiRebYo446KrnmmmsqPf4Pf/hDUlpa+rXP98orryRJkiTPPPNMEhHJU089lTn+0UcfTSIi+fTTT5MkSZLDDjssOeussyqd4+STT06+973vJUmSJE8++WRSWFiYLF68OLP/r3/9axIRycyZM5MkSZKePXsmP/3pTys9R48ePZLOnTtvcR2yme3yyy/PPMeHH36YREQybdq0zT7fl4/dXl/MVq9evUq3/fffv9Jx1113XbLnnnsm5513XlJaWpr885///NrnPeaYY5ILLrggc/+II45IevXqlbn/2WefJfXq1UtOO+20zLalS5cmEZFMnz59q3N/9fcPAKSXTPo5mXRT1Z1JkyRJPv3006Rbt27Jv//7v2d1PPD1XDkIKXDqqafGmjVr4sknn4xGjRplts+bNy9++ctfZj7npH79+nHWWWfF0qVLq/RXu4MOOijzz6WlpRERsXz58oiIePPNN+Pwww+vdPzhhx8eb775ZmZ/y5Yto2XLlpn9+++/fzRu3LjSMT169Kj0HD179tzu2b6sSZMmMXjw4Ojbt2/0798/xo8fH0uXLs3qHF94/vnnK63lpEmTtnr83LlzM7fHHnus0v4LLrgg9t1337jpppvi97//fTRt2jSzb+PGjXHllVdGp06dokmTJlG/fv144oknNnlbxZd//sLCwmjatGl06tQps6158+YRsfk1AQDIJZl087N9mUy69Uy6YcOGOOWUUyJJkpgwYcJWjwe2zheSQAp873vfiz/+8Y8xffr0+Ld/+7fM9tWrV8eYMWPixBNP3OQxderUyfr5d9ttt8w/FxQURMTnb83YEVRltttvvz2GDRsWjz/+eNxzzz3xi1/8IqZOnRqHHnpoVufq3r17pc+++SLkbEnbtm2/9m0Qy5cvjwULFkRhYWEsXLgwjj766My+//iP/4jx48fHuHHjMt/aNmLEiFi/fn2l5/jyzx/x+RrsyL8vAGDXJZN+Tibd9kz6RTG4aNGi+POf/7zJF6kA28aVg5AC5557blx77bVx3HHHxbPPPpvZ3q1bt5g/f37ss88+m9xq1crN/zzst99+8Ze//KXStr/85S+x//77Z/YvWbIklixZktn/xhtvxIoVKyodM2PGjErP8dJLL+Vkvq/q2rVrjBo1Kl588cU48MAD484774yIiNq1a8fGjRu/9rF169attIYNGjTYrlmGDBkSnTp1ijvuuCMuvvjizF+tIz5fw+OPPz5+9KMfRefOnaNdu3axYMGC7TofAEB1kkmzJ5Nu6oticOHChfHUU09VuoIR2D6uHISd0MqVKzf5dramTZtWehvEV51//vmxcePGOPbYY2PKlCnRq1evuOyyy+LYY4+NVq1axQ9+8IOoVatWzJs3L15//fW46qqrcjLrhRdeGKecckp07do1+vTpEw8//HDcf//98dRTT0VERJ8+faJTp04xcODAGDduXHz22Wfx05/+NI444ojo3r17REQMHz48Bg8eHN27d4/DDz88Jk2aFH/961+jXbt2OZkxIqKsrCx+97vfxXHHHRctWrSI+fPnx8KFC+P000+PiIg2bdpEWVlZzJ07N775zW9GgwYNori4eLvOuXz58k2+4a5p06ax2267xc033xzTp0+PV199NVq2bBmPPvpoDBw4MF566aWoXbt2tG/fPu6777548cUXY4899ogbbrghPvjgg0x4zaWPPvooFi9eHO+//35ERObb9b74NjsAIJ1kUpm0pjLphg0b4gc/+EHMmTMnHnnkkdi4cWMsW7YsIj5/K3bt2rVzej5IG1cOwk5o2rRp0bVr10q3MWPGbPVxI0aMiDFjxsT3vve9ePHFF6Nv377xyCOPxJNPPhmHHHJIHHroofHrX/86WrdunbNZTzjhhBg/fnxcf/31ccABB8Rvf/vbuP3226N3794R8flbCB588MHYY4894jvf+U706dMn2rVrF/fcc0/mOQYMGBCjR4+Oiy66KA4++OBYtGhRnHvuuTmbMSJi9913j7/97W9x0kknxb777htnn312DB06NM4555yIiDjppJPi6KOPjiOPPDL22muvuOuuu7b7nB06dIjS0tJKt9mzZ8ff/va3uPDCC+OWW27JhOtbbrkl/vnPf8bo0aMjIuIXv/hFdOvWLfr27Ru9e/eOkpKSOOGEE7Z7ps156KGHomvXrnHMMcdERMQPf/jD6Nq1a9x6663Vcj4AYOcgk8qkNZVJ33vvvXjooYfi73//e3Tp0qXSrC+++GLOzwdpU5AkSZLvIQAAAACAmufKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAppRwEAAAAgJRSDgIAAABASikHAQAAACCllIMAAAAAkFLKQQAAAABIKeUgAAAAAKSUchAAAAAAUko5CAAAAAAp9f8Bg74jOTlP5s4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# TODO: Compute histograms\n", "\n", + "X_train = np.array(X_train)\n", "\n", "\n", + "x1_combined = np.column_stack((X_train[:,0], y_train))\n", + "\n", + "x1_c0 = x1_combined[y_train == 0]\n", + "x1_c1 = x1_combined[y_train == 1]\n", + "\n", + "x1_c0_hist, x1_c0_bins = np.histogram(x1_c0[:,0], bins=\"auto\")\n", + "x1_c1_hist, x1_c1_bins = np.histogram(x1_c1[:,0], bins=\"auto\")\n", + "\n", + "\n", + "x2_combined = np.column_stack((X_train[:,1], y_train))\n", + "\n", + "x2_c0 = x2_combined[y_train == 0]\n", + "x2_c1 = x2_combined[y_train == 1]\n", + "\n", + "x2_c0_hist, x2_c0_bins = np.histogram(x2_c0[:,0], bins=\"auto\")\n", + "x2_c1_hist, x2_c1_bins = np.histogram(x2_c1[:,0], bins=\"auto\")\n", + "\n", "# TODO: plot histograms\n", "\n", "plt.figure(figsize=(16,6))\n", "\n", "plt.subplot(1, 2, 1)\n", + "plt.hist(x1_c0[:,0], bins=\"auto\", alpha=0.6, label=\"failed\")\n", + "plt.hist(x1_c1[:,0], bins=\"auto\", alpha=0.6, label=\"passed\")\n", + "plt.legend()\n", "...\n", "plt.xlabel('Likelihood hist - Exam 1')\n", "\n", "plt.subplot(1, 2, 2)\n", + "plt.hist(x2_c0[:,0], bins=\"auto\", alpha=0.6, label=\"failed\")\n", + "plt.hist(x2_c1[:,0], bins=\"auto\", alpha=0.6, label=\"passed\")\n", + "plt.legend()\n", "...\n", "plt.xlabel('Likelihood hist - Exam 2')\n", "\n", @@ -142,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 474, "metadata": { "pycharm": { "is_executing": false @@ -153,7 +215,38 @@ "def likelihood_hist(x: float, hist_values: np.ndarray, bin_edges: np.ndarray) -> float:\n", " # TODO: compute likelihoods from histograms outputs\n", "\n", - " return ..." + " bin_index = np.digitize(x, bin_edges) - 1\n", + "\n", + " if bin_index >= len(hist_values) or bin_index < 0:\n", + " return 0\n", + "\n", + " # print(f\"edges = {bin_edges}\")\n", + " # print(f\"values = {hist_values}\")\n", + " # print(f\"selected bin = {bin_index}\")\n", + "\n", + " count = hist_values[bin_index]\n", + " return count/np.sum(hist_values)" + ] + }, + { + "cell_type": "code", + "execution_count": 475, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(0.4)" + ] + }, + "execution_count": 475, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hist, edges = np.histogram(x2_y0[:,0], bins=\"auto\")\n", + "likelihood_hist(45, hist, edges)" ] }, { @@ -168,34 +261,101 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 476, "metadata": { "pycharm": { "is_executing": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " x1 x2 y\n", + "0 39.196334 78.530294 0\n", + "1 40.448499 86.839470 1\n", + "2 65.571920 44.303497 0\n", + "3 79.648113 70.806564 1\n", + "4 66.260221 41.672703 0\n" + ] + } + ], "source": [ "X_test, y_test = read_data(\"ex1-data-test.csv\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 477, "metadata": { "pycharm": { "is_executing": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pred accuracy with x1 0.64\n", + "Pred accuracy with x2 0.72\n", + "Pred accuracy with x1 and x2 0.87\n" + ] + } + ], "source": [ "# TODO: predict on test set in the 3 cases described above\n", "\n", - "y_pred = []\n", + "y_pred_x1 = []\n", "\n", - "...\n", + "X_test = np.array(X_test)\n", "\n", - "accuracy_score(y_test, y_pred)" + "for i in range(len(X_test)):\n", + " P_c0_x1 = likelihood_hist(X_test[i,0], x1_c0_hist, x1_c0_bins)*P_c0\n", + " P_c1_x1 = likelihood_hist(X_test[i,0], x1_c1_hist, x1_c1_bins)*P_c1\n", + "\n", + " if P_c0_x1 > P_c1_x1:\n", + " y_pred_x1.append(0)\n", + " else:\n", + " y_pred_x1.append(1)\n", + "\n", + "print(f\"Pred accuracy with x1 {accuracy_score(y_test, y_pred_x1)}\")\n", + "\n", + "\n", + "\n", + "y_pred_x2 = []\n", + "\n", + "for i in range(len(X_test)):\n", + " P_c0_x2 = likelihood_hist(X_test[i,1], x2_c0_hist, x2_c0_bins)*P_c0\n", + " P_c1_x2 = likelihood_hist(X_test[i,1], x2_c1_hist, x2_c1_bins)*P_c1\n", + "\n", + " if P_c0_x2 > P_c1_x2:\n", + " y_pred_x2.append(0)\n", + " else:\n", + " y_pred_x2.append(1)\n", + "\n", + "print(f\"Pred accuracy with x2 {accuracy_score(y_test, y_pred_x2)}\")\n", + "\n", + "\n", + "\n", + "\n", + "y_pred_x1_x2 = []\n", + "\n", + "for i in range(len(X_test)):\n", + "# for i in range(0,1):\n", + " P_c0_x1 = likelihood_hist(X_test[i,0], x1_c0_hist, x1_c0_bins)*P_c0\n", + " P_c1_x1 = likelihood_hist(X_test[i,0], x1_c1_hist, x1_c1_bins)*P_c1\n", + " P_c0_x2 = likelihood_hist(X_test[i,1], x2_c0_hist, x2_c0_bins)*P_c0\n", + " P_c1_x2 = likelihood_hist(X_test[i,1], x2_c1_hist, x2_c1_bins)*P_c1\n", + "\n", + " if P_c0_x2*P_c0_x1 > P_c1_x1*P_c1_x2:\n", + " y_pred_x1_x2.append(0)\n", + " else:\n", + " y_pred_x1_x2.append(1)\n", + "\n", + "print(f\"Pred accuracy with x1 and x2 {accuracy_score(y_test, y_pred_x1_x2)}\")\n", + "\n" ] }, { @@ -205,13 +365,6 @@ "outputs": [], "source": [] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -223,7 +376,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "TODO: answer" + "The system with both variables is clearly the better one and this makes sense, because the chance of success of a student is clearly related to the performance of both exams, not just one. The system with one variable only can only make better decisions when the student has really highly failed or succeded an exam, which would mean the second variable could be less important. For example, if one has 0 on the first exam, it is really not probable that he will pass, even if we don't know the second grade.\n", + "\n", + "All in all, more variables in this case mean a better approximation." ] }, { @@ -242,7 +397,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 478, "metadata": { "pycharm": { "is_executing": false @@ -253,12 +408,15 @@ "def likelihood_univariate_gaussian(x: float, mean: float, var: float) -> float:\n", " # TODO: compute likelihoods from histograms outputs\n", "\n", - " return ..." + " sqrt = np.sqrt(2*np.pi*var)\n", + " exp = np.exp(-(1/(2*var)) * (x - mean)**2)\n", + "\n", + " return (1/sqrt) * exp" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 479, "metadata": { "pycharm": { "is_executing": false @@ -267,22 +425,85 @@ "outputs": [], "source": [ "# TODO: Compute mean and variance for each classes and each features (8 values)\n", + "\n", + "mean_x1_c0 = np.mean(X_train[:,0][y_train == 0])\n", + "mean_x1_c1 = np.mean(X_train[:,0][y_train == 1])\n", + "\n", + "mean_x2_c0 = np.mean(X_train[:,1][y_train == 0])\n", + "mean_x2_c1 = np.mean(X_train[:,1][y_train == 1])\n", + "\n", + "var_x1_c0 = np.var(X_train[:,0][y_train == 0])\n", + "var_x1_c1 = np.var(X_train[:,0][y_train == 1])\n", + "\n", + "var_x2_c0 = np.var(X_train[:,1][y_train == 0])\n", + "var_x2_c1 = np.var(X_train[:,1][y_train == 1])\n", + "\n", + "\n", "\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 480, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pred accuracy with x1 0.71\n", + "Pred accuracy with x2 0.72\n", + "Pred accuracy with x1 and x2 0.92\n" + ] + } + ], "source": [ "# TODO: predict on test set in the 3 cases\n", "\n", - "y_pred = []\n", + "y_pred_x1 = []\n", "\n", - "...\n", + "for i in range(len(X_test)):\n", + " P_c0_x1 = likelihood_univariate_gaussian(X_test[i,0], mean_x1_c0, var_x1_c0)*P_c0\n", + " P_c1_x1 = likelihood_univariate_gaussian(X_test[i,0], mean_x1_c1, var_x1_c1)*P_c1\n", "\n", - "accuracy_score(y_test, y_pred)" + " if P_c0_x1 > P_c1_x1:\n", + " y_pred_x1.append(0)\n", + " else:\n", + " y_pred_x1.append(1)\n", + "\n", + "print(f\"Pred accuracy with x1 {accuracy_score(y_test, y_pred_x1)}\")\n", + "\n", + "y_pred_x2 = []\n", + "\n", + "for i in range(len(X_test)):\n", + " P_c0_x2 = likelihood_univariate_gaussian(X_test[i,1], mean_x2_c0, var_x2_c0)*P_c0\n", + " P_c1_x2 = likelihood_univariate_gaussian(X_test[i,1], mean_x2_c1, var_x2_c1)*P_c1\n", + "\n", + " if P_c0_x2 > P_c1_x2:\n", + " y_pred_x2.append(0)\n", + " else:\n", + " y_pred_x2.append(1)\n", + "\n", + "print(f\"Pred accuracy with x2 {accuracy_score(y_test, y_pred_x2)}\")\n", + "\n", + "\n", + "\n", + "\n", + "y_pred_x1_x2 = []\n", + "\n", + "for i in range(len(X_test)):\n", + "# for i in range(0,1):\n", + " P_c0_x1 = likelihood_univariate_gaussian(X_test[i,0], mean_x1_c0, var_x1_c0)*P_c0\n", + " P_c1_x1 = likelihood_univariate_gaussian(X_test[i,0], mean_x1_c1, var_x1_c1)*P_c1\n", + " P_c0_x2 = likelihood_univariate_gaussian(X_test[i,1], mean_x2_c0, var_x2_c0)*P_c0\n", + " P_c1_x2 = likelihood_univariate_gaussian(X_test[i,1], mean_x2_c1, var_x2_c1)*P_c1\n", + "\n", + " if P_c0_x2*P_c0_x1 > P_c1_x1*P_c1_x2:\n", + " y_pred_x1_x2.append(0)\n", + " else:\n", + " y_pred_x1_x2.append(1)\n", + "\n", + "print(f\"Pred accuracy with x1 and x2 {accuracy_score(y_test, y_pred_x1_x2)}\")\n" ] }, { @@ -302,7 +523,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -316,7 +537,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.7" + "version": "3.12.3" }, "pycharm": { "stem_cell": {