diff --git a/aufgaben/p1/kennzahlen.ipynb b/aufgaben/p1/kennzahlen.ipynb
index b788a56b17556d665796493a9e7685e163009722..420082b9d2d152bb99c99b72b474f1387152fcbb 100644
--- a/aufgaben/p1/kennzahlen.ipynb
+++ b/aufgaben/p1/kennzahlen.ipynb
@@ -19,8 +19,8 @@
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:09.952587Z",
-     "end_time": "2023-04-23T23:18:10.173372Z"
+     "start_time": "2023-06-05T15:52:16.665218Z",
+     "end_time": "2023-06-05T15:52:16.852775Z"
     }
    }
   },
@@ -442,13 +442,13 @@
     }
    ],
    "source": [
-    "print(create_bewegung('gehen').get_bewegung_data())"
+    "print(create_bewegung('gehen').get_characterisation())"
    ],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:10.173372Z",
-     "end_time": "2023-04-23T23:18:10.621223Z"
+     "start_time": "2023-06-05T15:52:16.852775Z",
+     "end_time": "2023-06-05T15:52:17.392881Z"
     }
    }
   },
@@ -870,13 +870,13 @@
     }
    ],
    "source": [
-    "print(create_bewegung('joggen').get_bewegung_data())"
+    "print(create_bewegung('joggen').get_characterisation())"
    ],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:10.621223Z",
-     "end_time": "2023-04-23T23:18:11.195891Z"
+     "start_time": "2023-06-05T15:52:17.392881Z",
+     "end_time": "2023-06-05T15:52:17.918323Z"
     }
    }
   },
@@ -1298,13 +1298,13 @@
     }
    ],
    "source": [
-    "print(create_bewegung('jumpingjack').get_bewegung_data())"
+    "print(create_bewegung('jumpingjack').get_characterisation())"
    ],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:11.195891Z",
-     "end_time": "2023-04-23T23:18:11.714719Z"
+     "start_time": "2023-06-05T15:52:17.918323Z",
+     "end_time": "2023-06-05T15:52:18.431863Z"
     }
    }
   },
@@ -1726,13 +1726,13 @@
     }
    ],
    "source": [
-    "print(create_bewegung('kniebeuge').get_bewegung_data())"
+    "print(create_bewegung('kniebeuge').get_characterisation())"
    ],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:11.714719Z",
-     "end_time": "2023-04-23T23:18:12.209380Z"
+     "start_time": "2023-06-05T15:52:18.431863Z",
+     "end_time": "2023-06-05T15:52:18.974459Z"
     }
    }
   },
@@ -2154,13 +2154,13 @@
     }
    ],
    "source": [
-    "print(create_bewegung('treppe').get_bewegung_data())"
+    "print(create_bewegung('treppe').get_characterisation())"
    ],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-04-23T23:18:12.209380Z",
-     "end_time": "2023-04-23T23:18:12.715561Z"
+     "start_time": "2023-06-05T15:52:18.974459Z",
+     "end_time": "2023-06-05T15:52:19.456134Z"
     }
    }
   }
diff --git a/aufgaben/p10/evaluation.py b/aufgaben/p10/evaluation.py
index 69f5e507894d3aaa894ddced696e6e0844d0eafe..10a706e23c7d0f781c31a3a3ce4f1baae069bd7b 100644
--- a/aufgaben/p10/evaluation.py
+++ b/aufgaben/p10/evaluation.py
@@ -8,7 +8,7 @@ from algorithm.decision_tree.decision_tree import DecisionTree
 from algorithm.k_nearest_neighbors.k_nearest_neighbors_algorithm import KNearestNeighborsAlgorithm
 from aufgaben.p4.testdata import get_evaluation_data
 from aufgaben.p6.error_rate import ErrorRate
-from aufgaben.p6.multiclass_error_rate import Multiclass_ErrorRate
+from aufgaben.p6.multiclass_error_rate import MulticlassErrorRate
 from features.standard_deviation import standard_deviation
 from features.arithmetic_mean import arithmetic_mean
 from features.median import median
@@ -40,11 +40,11 @@ def evaluate():
     # DecisionTree
     print("\nDecision Tree:")
     evaluate_algorithm(training_data, test_data,
-                       DecisionTree(entropy_threshold=0.5, number_segments=25, print_after_train=True), Multiclass_ErrorRate(classes))
+                       DecisionTree(entropy_threshold=0.5, number_segments=25, print_after_train=True), MulticlassErrorRate(classes))
 
     # KNN
     print("\nKNN")
-    evaluate_algorithm(training_data, test_data, KNearestNeighborsAlgorithm(), Multiclass_ErrorRate(classes), {'distance': euclidean_distance, 'k': 5})
+    evaluate_algorithm(training_data, test_data, KNearestNeighborsAlgorithm(), MulticlassErrorRate(classes), {'distance': euclidean_distance, 'k': 5})
 
     # PLA
     print("\nPLA")
@@ -52,7 +52,7 @@ def evaluate():
     threshold = 0.5
     perceptron = Perceptron(weights, threshold, numpy.tanh)
     train(perceptron, training_data, 10000, 0.1)
-    fehlerrate = Multiclass_ErrorRate(classes)
+    fehlerrate = MulticlassErrorRate(classes)
     for features, correct_class in test_data:
         result = perceptron.classify(features)
         fehlerrate.evaluate(correct_class, result)
diff --git a/aufgaben/p3/plotter.py b/aufgaben/p3/plotter.py
index 73a67546eda9753610359fc3e7eb8ea4bcd6a973..c660198e392270610b184d9c7cd624b19b772504 100644
--- a/aufgaben/p3/plotter.py
+++ b/aufgaben/p3/plotter.py
@@ -1,6 +1,6 @@
+import plot.plotter as plotter
 from features.moving_average import moving_average
 from korpus import create_bewegung
-import plot.plotter as plotter
 
 
 def test_plotter_sensoren():
diff --git a/aufgaben/p4/binary_classification.py b/aufgaben/p4/binary_classification.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd92478684b6ddfdd10f08abb81ba2383eda9a5d
--- /dev/null
+++ b/aufgaben/p4/binary_classification.py
@@ -0,0 +1,33 @@
+import math
+
+from features.standard_deviation import standard_deviation
+from testdata.constants import Acc_Y
+from testdata.testdata import get_features, label_testdata
+
+CLASS_JOGGEN = 1
+CLASS_KNIEBEUGE = -1
+
+SENSOR_FUSS = 1
+
+
+def binary_classification_feature(window_size=30):
+    """ Testdaten für P4 und P5 """
+
+    personen = [
+        (1, 1)  # Gruppe 1, Person 1
+    ]
+
+    # Standardabweichung in Y-Richtung sollte beim Joggen hoch und bei Kniebeuge gering sein
+    joggen_feature = get_features(standard_deviation, window_size, 'joggen', SENSOR_FUSS, Acc_Y, personen)
+    kniebeuge_feature = get_features(standard_deviation, window_size, 'kniebeuge', SENSOR_FUSS, Acc_Y, personen)
+
+    return joggen_feature, kniebeuge_feature
+
+
+def binary_classification_testdata():
+    joggen_feature, kniebeuge_feature = binary_classification_feature(150)
+
+    joggen_training, joggen_test = label_testdata(joggen_feature, CLASS_JOGGEN, math.inf)
+    kniebeuge_training, kniebeuge_test = label_testdata(kniebeuge_feature, CLASS_KNIEBEUGE, math.inf)
+
+    return joggen_training + kniebeuge_training, joggen_test + kniebeuge_test
diff --git a/aufgaben/p4/feature_plot.ipynb b/aufgaben/p4/feature_plot.ipynb
index 480414313f1bd04792d4f70a3d0fc71775216fde..882b3b13b12620459f86f790f73c71cf90a54512 100644
--- a/aufgaben/p4/feature_plot.ipynb
+++ b/aufgaben/p4/feature_plot.ipynb
@@ -2,12 +2,12 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
    "metadata": {
     "collapsed": true,
     "ExecuteTime": {
-     "start_time": "2023-05-08T13:40:15.203707Z",
-     "end_time": "2023-05-08T13:40:17.815747Z"
+     "start_time": "2023-06-05T15:54:07.443928Z",
+     "end_time": "2023-06-05T15:54:09.651516Z"
     }
    },
    "outputs": [
@@ -22,16 +22,16 @@
     {
      "data": {
       "text/plain": "<IPython.core.display.HTML object>",
-      "text/html": "<div id='79c3758f-e5f1-44b7-a889-2b1b07a9b1d2'></div>"
+      "text/html": "<div id='6170268f-673a-4725-9cf6-9ca12000f4f3'></div>"
      },
      "metadata": {},
      "output_type": "display_data"
     }
    ],
    "source": [
+    "from aufgaben.p4.binary_classification import binary_classification_feature\n",
     "%matplotlib notebook\n",
     "import matplotlib.pyplot as plotter\n",
-    "from aufgaben.p4.testdata import binary_classification_feature\n",
     "\n",
     "joggen_feature, kniebeuge_feature = binary_classification_feature(150)\n",
     "\n",
@@ -44,14 +44,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
    "outputs": [],
    "source": [],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-05-08T13:40:17.815747Z",
-     "end_time": "2023-05-08T13:40:17.843945Z"
+     "start_time": "2023-06-05T15:54:09.651516Z",
+     "end_time": "2023-06-05T15:54:09.678169Z"
     }
    }
   }
diff --git a/aufgaben/p5/apply_pla.py b/aufgaben/p5/apply_pla.py
index 991f982da6459dbdc62e4e4a201563002c47dff8..85b13b20790f31983fa9ff94e813421f153b2a33 100644
--- a/aufgaben/p5/apply_pla.py
+++ b/aufgaben/p5/apply_pla.py
@@ -4,12 +4,13 @@ import numpy
 
 from algorithm.pla.perceptron import Perceptron
 from algorithm.pla.perceptron_learning_algorithm import train
-from aufgaben.p4.testdata import get_labeled_testdata, CLASS_JOGGEN, CLASS_KNIEBEUGE
+from aufgaben.p4.binary_classification import binary_classification_testdata
 from aufgaben.p6.error_rate import ErrorRate
+from testdata.testdata import CLASS_JOGGEN, CLASS_KNIEBEUGE
 
 
 def apply_pla():
-    test_data, training_data = get_labeled_testdata()
+    test_data, training_data = binary_classification_testdata()
 
     # Erstelle ein Perzeptron
     weights = [random.random()]
diff --git a/aufgaben/p5/draw_results.ipynb b/aufgaben/p5/draw_results.ipynb
index 3fa3ee91bdf6664d6a9e6d303784247d32283491..290a4de20a51ffafc1ce8d71f7f17f33f4aa2bfe 100644
--- a/aufgaben/p5/draw_results.ipynb
+++ b/aufgaben/p5/draw_results.ipynb
@@ -6,23 +6,41 @@
    "metadata": {
     "collapsed": true,
     "ExecuteTime": {
-     "start_time": "2023-05-21T01:33:55.307530Z",
-     "end_time": "2023-05-21T01:33:57.564890Z"
+     "start_time": "2023-06-05T15:54:37.919141Z",
+     "end_time": "2023-06-05T15:54:40.388783Z"
     }
    },
    "outputs": [
     {
-     "ename": "TypeError",
-     "evalue": "slice indices must be integers or None or have an __index__ method",
-     "output_type": "error",
-     "traceback": [
-      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
-      "\u001B[1;31mTypeError\u001B[0m                                 Traceback (most recent call last)",
-      "\u001B[1;32m<ipython-input-1-dc2cba0b48b6>\u001B[0m in \u001B[0;36m<module>\u001B[1;34m\u001B[0m\n\u001B[0;32m      3\u001B[0m \u001B[1;32mimport\u001B[0m \u001B[0mmatplotlib\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mpyplot\u001B[0m \u001B[1;32mas\u001B[0m \u001B[0mplotter\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      4\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m----> 5\u001B[1;33m \u001B[0mjoggen\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mkniebeuge\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mgrenzwert\u001B[0m  \u001B[1;33m=\u001B[0m \u001B[0mapply_pla\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m      6\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m      7\u001B[0m \u001B[0mplotter\u001B[0m\u001B[1;33m.\u001B[0m\u001B[0mscatter\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mjoggen\u001B[0m\u001B[1;33m,\u001B[0m \u001B[1;33m[\u001B[0m\u001B[1;36m0\u001B[0m\u001B[1;33m]\u001B[0m \u001B[1;33m*\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mjoggen\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mlabel\u001B[0m\u001B[1;33m=\u001B[0m\u001B[1;34m\"Joggen\"\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n",
-      "\u001B[1;32mD:\\Informatik-Workspace\\PyCharm\\FH-Münster\\MEML\\aufgaben\\p5\\apply_pla.py\u001B[0m in \u001B[0;36mapply_pla\u001B[1;34m()\u001B[0m\n\u001B[0;32m     10\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m     11\u001B[0m \u001B[1;32mdef\u001B[0m \u001B[0mapply_pla\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m:\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 12\u001B[1;33m     \u001B[0mtest_data\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mtraining_data\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mget_labeled_testdata\u001B[0m\u001B[1;33m(\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m     13\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m     14\u001B[0m     \u001B[1;31m# Erstelle ein Perzeptron\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n",
-      "\u001B[1;32mD:\\Informatik-Workspace\\PyCharm\\FH-Münster\\MEML\\aufgaben\\p4\\testdata.py\u001B[0m in \u001B[0;36mget_labeled_testdata\u001B[1;34m()\u001B[0m\n\u001B[0;32m     13\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m     14\u001B[0m     \u001B[1;31m# Wir nehmen nur DATA_LIMIT an Daten (sonst ist K-Nearest-Neighbors zu langsam)\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[1;32m---> 15\u001B[1;33m     \u001B[0mjoggen_feature\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mjoggen_feature\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mmin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mDATA_LIMIT\u001B[0m\u001B[1;33m/\u001B[0m\u001B[1;36m2\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mjoggen_feature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0m\u001B[0;32m     16\u001B[0m     \u001B[0mkniebeuge_feature\u001B[0m \u001B[1;33m=\u001B[0m \u001B[0mkniebeuge_feature\u001B[0m\u001B[1;33m[\u001B[0m\u001B[1;33m:\u001B[0m \u001B[0mmin\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mDATA_LIMIT\u001B[0m \u001B[1;33m/\u001B[0m \u001B[1;36m2\u001B[0m\u001B[1;33m,\u001B[0m \u001B[0mlen\u001B[0m\u001B[1;33m(\u001B[0m\u001B[0mkniebeuge_feature\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m)\u001B[0m\u001B[1;33m]\u001B[0m\u001B[1;33m\u001B[0m\u001B[1;33m\u001B[0m\u001B[0m\n\u001B[0;32m     17\u001B[0m \u001B[1;33m\u001B[0m\u001B[0m\n",
-      "\u001B[1;31mTypeError\u001B[0m: slice indices must be integers or None or have an __index__ method"
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Gesamt-Erfolgsrate: 1.0\n",
+      "Gesamt-Fehlerrate: 0.0\n",
+      "\n",
+      "Typ               Absolut    Relativ\n",
+      "--------------  ---------  ---------\n",
+      "True-Positive         630   0.515548\n",
+      "True-Negative         592   0.484452\n",
+      "False-Positive          0   0\n",
+      "False-Negative          0   0\n"
      ]
+    },
+    {
+     "data": {
+      "text/plain": "<IPython.core.display.Javascript object>",
+      "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n    if (typeof WebSocket !== 'undefined') {\n        return WebSocket;\n    } else if (typeof MozWebSocket !== 'undefined') {\n        return MozWebSocket;\n    } else {\n        alert(\n            'Your browser does not have WebSocket support. ' +\n                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n                'Firefox 4 and 5 are also supported but you ' +\n                'have to enable WebSockets in about:config.'\n        );\n    }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n    this.id = figure_id;\n\n    this.ws = websocket;\n\n    this.supports_binary = this.ws.binaryType !== undefined;\n\n    if (!this.supports_binary) {\n        var warnings = document.getElementById('mpl-warnings');\n        if (warnings) {\n            warnings.style.display = 'block';\n            warnings.textContent =\n                'This browser does not support binary websocket messages. ' +\n                'Performance may be slow.';\n        }\n    }\n\n    this.imageObj = new Image();\n\n    this.context = undefined;\n    this.message = undefined;\n    this.canvas = undefined;\n    this.rubberband_canvas = undefined;\n    this.rubberband_context = undefined;\n    this.format_dropdown = undefined;\n\n    this.image_mode = 'full';\n\n    this.root = document.createElement('div');\n    this.root.setAttribute('style', 'display: inline-block');\n    this._root_extra_style(this.root);\n\n    parent_element.appendChild(this.root);\n\n    this._init_header(this);\n    this._init_canvas(this);\n    this._init_toolbar(this);\n\n    var fig = this;\n\n    this.waiting = false;\n\n    this.ws.onopen = function () {\n        fig.send_message('supports_binary', { value: fig.supports_binary });\n        fig.send_message('send_image_mode', {});\n        if (fig.ratio !== 1) {\n            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n        }\n        fig.send_message('refresh', {});\n    };\n\n    this.imageObj.onload = function () {\n        if (fig.image_mode === 'full') {\n            // Full images could contain transparency (where diff images\n            // almost always do), so we need to clear the canvas so that\n            // there is no ghosting.\n            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n        }\n        fig.context.drawImage(fig.imageObj, 0, 0);\n    };\n\n    this.imageObj.onunload = function () {\n        fig.ws.close();\n    };\n\n    this.ws.onmessage = this._make_on_message_function(this);\n\n    this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n    var titlebar = document.createElement('div');\n    titlebar.classList =\n        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n    var titletext = document.createElement('div');\n    titletext.classList = 'ui-dialog-title';\n    titletext.setAttribute(\n        'style',\n        'width: 100%; text-align: center; padding: 3px;'\n    );\n    titlebar.appendChild(titletext);\n    this.root.appendChild(titlebar);\n    this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n    var fig = this;\n\n    var canvas_div = (this.canvas_div = document.createElement('div'));\n    canvas_div.setAttribute(\n        'style',\n        'border: 1px solid #ddd;' +\n            'box-sizing: content-box;' +\n            'clear: both;' +\n            'min-height: 1px;' +\n            'min-width: 1px;' +\n            'outline: 0;' +\n            'overflow: hidden;' +\n            'position: relative;' +\n            'resize: both;'\n    );\n\n    function on_keyboard_event_closure(name) {\n        return function (event) {\n            return fig.key_event(event, name);\n        };\n    }\n\n    canvas_div.addEventListener(\n        'keydown',\n        on_keyboard_event_closure('key_press')\n    );\n    canvas_div.addEventListener(\n        'keyup',\n        on_keyboard_event_closure('key_release')\n    );\n\n    this._canvas_extra_style(canvas_div);\n    this.root.appendChild(canvas_div);\n\n    var canvas = (this.canvas = document.createElement('canvas'));\n    canvas.classList.add('mpl-canvas');\n    canvas.setAttribute('style', 'box-sizing: content-box;');\n\n    this.context = canvas.getContext('2d');\n\n    var backingStore =\n        this.context.backingStorePixelRatio ||\n        this.context.webkitBackingStorePixelRatio ||\n        this.context.mozBackingStorePixelRatio ||\n        this.context.msBackingStorePixelRatio ||\n        this.context.oBackingStorePixelRatio ||\n        this.context.backingStorePixelRatio ||\n        1;\n\n    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n        'canvas'\n    ));\n    rubberband_canvas.setAttribute(\n        'style',\n        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n    );\n\n    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n    if (this.ResizeObserver === undefined) {\n        if (window.ResizeObserver !== undefined) {\n            this.ResizeObserver = window.ResizeObserver;\n        } else {\n            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n            this.ResizeObserver = obs.ResizeObserver;\n        }\n    }\n\n    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n        var nentries = entries.length;\n        for (var i = 0; i < nentries; i++) {\n            var entry = entries[i];\n            var width, height;\n            if (entry.contentBoxSize) {\n                if (entry.contentBoxSize instanceof Array) {\n                    // Chrome 84 implements new version of spec.\n                    width = entry.contentBoxSize[0].inlineSize;\n                    height = entry.contentBoxSize[0].blockSize;\n                } else {\n                    // Firefox implements old version of spec.\n                    width = entry.contentBoxSize.inlineSize;\n                    height = entry.contentBoxSize.blockSize;\n                }\n            } else {\n                // Chrome <84 implements even older version of spec.\n                width = entry.contentRect.width;\n                height = entry.contentRect.height;\n            }\n\n            // Keep the size of the canvas and rubber band canvas in sync with\n            // the canvas container.\n            if (entry.devicePixelContentBoxSize) {\n                // Chrome 84 implements new version of spec.\n                canvas.setAttribute(\n                    'width',\n                    entry.devicePixelContentBoxSize[0].inlineSize\n                );\n                canvas.setAttribute(\n                    'height',\n                    entry.devicePixelContentBoxSize[0].blockSize\n                );\n            } else {\n                canvas.setAttribute('width', width * fig.ratio);\n                canvas.setAttribute('height', height * fig.ratio);\n            }\n            canvas.setAttribute(\n                'style',\n                'width: ' + width + 'px; height: ' + height + 'px;'\n            );\n\n            rubberband_canvas.setAttribute('width', width);\n            rubberband_canvas.setAttribute('height', height);\n\n            // And update the size in Python. We ignore the initial 0/0 size\n            // that occurs as the element is placed into the DOM, which should\n            // otherwise not happen due to the minimum size styling.\n            if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n                fig.request_resize(width, height);\n            }\n        }\n    });\n    this.resizeObserverInstance.observe(canvas_div);\n\n    function on_mouse_event_closure(name) {\n        return function (event) {\n            return fig.mouse_event(event, name);\n        };\n    }\n\n    rubberband_canvas.addEventListener(\n        'mousedown',\n        on_mouse_event_closure('button_press')\n    );\n    rubberband_canvas.addEventListener(\n        'mouseup',\n        on_mouse_event_closure('button_release')\n    );\n    rubberband_canvas.addEventListener(\n        'dblclick',\n        on_mouse_event_closure('dblclick')\n    );\n    // Throttle sequential mouse events to 1 every 20ms.\n    rubberband_canvas.addEventListener(\n        'mousemove',\n        on_mouse_event_closure('motion_notify')\n    );\n\n    rubberband_canvas.addEventListener(\n        'mouseenter',\n        on_mouse_event_closure('figure_enter')\n    );\n    rubberband_canvas.addEventListener(\n        'mouseleave',\n        on_mouse_event_closure('figure_leave')\n    );\n\n    canvas_div.addEventListener('wheel', function (event) {\n        if (event.deltaY < 0) {\n            event.step = 1;\n        } else {\n            event.step = -1;\n        }\n        on_mouse_event_closure('scroll')(event);\n    });\n\n    canvas_div.appendChild(canvas);\n    canvas_div.appendChild(rubberband_canvas);\n\n    this.rubberband_context = rubberband_canvas.getContext('2d');\n    this.rubberband_context.strokeStyle = '#000000';\n\n    this._resize_canvas = function (width, height, forward) {\n        if (forward) {\n            canvas_div.style.width = width + 'px';\n            canvas_div.style.height = height + 'px';\n        }\n    };\n\n    // Disable right mouse context menu.\n    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n        event.preventDefault();\n        return false;\n    });\n\n    function set_focus() {\n        canvas.focus();\n        canvas_div.focus();\n    }\n\n    window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'mpl-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'mpl-button-group';\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'mpl-button-group';\n            continue;\n        }\n\n        var button = (fig.buttons[name] = document.createElement('button'));\n        button.classList = 'mpl-widget';\n        button.setAttribute('role', 'button');\n        button.setAttribute('aria-disabled', 'false');\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n        var icon_img = document.createElement('img');\n        icon_img.src = '_images/' + image + '.png';\n        icon_img.srcset = '_images/' + image + '_large.png 2x';\n        icon_img.alt = tooltip;\n        button.appendChild(icon_img);\n\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    var fmt_picker = document.createElement('select');\n    fmt_picker.classList = 'mpl-widget';\n    toolbar.appendChild(fmt_picker);\n    this.format_dropdown = fmt_picker;\n\n    for (var ind in mpl.extensions) {\n        var fmt = mpl.extensions[ind];\n        var option = document.createElement('option');\n        option.selected = fmt === mpl.default_extension;\n        option.innerHTML = fmt;\n        fmt_picker.appendChild(option);\n    }\n\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n    // which will in turn request a refresh of the image.\n    this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n    properties['type'] = type;\n    properties['figure_id'] = this.id;\n    this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n    if (!this.waiting) {\n        this.waiting = true;\n        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    var format_dropdown = fig.format_dropdown;\n    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n    fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n    var size = msg['size'];\n    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n        fig._resize_canvas(size[0], size[1], msg['forward']);\n        fig.send_message('refresh', {});\n    }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n    var x0 = msg['x0'] / fig.ratio;\n    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n    var x1 = msg['x1'] / fig.ratio;\n    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n    x0 = Math.floor(x0) + 0.5;\n    y0 = Math.floor(y0) + 0.5;\n    x1 = Math.floor(x1) + 0.5;\n    y1 = Math.floor(y1) + 0.5;\n    var min_x = Math.min(x0, x1);\n    var min_y = Math.min(y0, y1);\n    var width = Math.abs(x1 - x0);\n    var height = Math.abs(y1 - y0);\n\n    fig.rubberband_context.clearRect(\n        0,\n        0,\n        fig.canvas.width / fig.ratio,\n        fig.canvas.height / fig.ratio\n    );\n\n    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n    // Updates the figure title.\n    fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n    var cursor = msg['cursor'];\n    switch (cursor) {\n        case 0:\n            cursor = 'pointer';\n            break;\n        case 1:\n            cursor = 'default';\n            break;\n        case 2:\n            cursor = 'crosshair';\n            break;\n        case 3:\n            cursor = 'move';\n            break;\n    }\n    fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n    fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n    // Request the server to send over a new figure.\n    fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n    fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n    for (var key in msg) {\n        if (!(key in fig.buttons)) {\n            continue;\n        }\n        fig.buttons[key].disabled = !msg[key];\n        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n    }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n    if (msg['mode'] === 'PAN') {\n        fig.buttons['Pan'].classList.add('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    } else if (msg['mode'] === 'ZOOM') {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.add('active');\n    } else {\n        fig.buttons['Pan'].classList.remove('active');\n        fig.buttons['Zoom'].classList.remove('active');\n    }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Called whenever the canvas gets updated.\n    this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n    return function socket_on_message(evt) {\n        if (evt.data instanceof Blob) {\n            var img = evt.data;\n            if (img.type !== 'image/png') {\n                /* FIXME: We get \"Resource interpreted as Image but\n                 * transferred with MIME type text/plain:\" errors on\n                 * Chrome.  But how to set the MIME type?  It doesn't seem\n                 * to be part of the websocket stream */\n                img.type = 'image/png';\n            }\n\n            /* Free the memory for the previous frames */\n            if (fig.imageObj.src) {\n                (window.URL || window.webkitURL).revokeObjectURL(\n                    fig.imageObj.src\n                );\n            }\n\n            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n                img\n            );\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        } else if (\n            typeof evt.data === 'string' &&\n            evt.data.slice(0, 21) === 'data:image/png;base64'\n        ) {\n            fig.imageObj.src = evt.data;\n            fig.updated_canvas_event();\n            fig.waiting = false;\n            return;\n        }\n\n        var msg = JSON.parse(evt.data);\n        var msg_type = msg['type'];\n\n        // Call the  \"handle_{type}\" callback, which takes\n        // the figure and JSON message as its only arguments.\n        try {\n            var callback = fig['handle_' + msg_type];\n        } catch (e) {\n            console.log(\n                \"No handler for the '\" + msg_type + \"' message type: \",\n                msg\n            );\n            return;\n        }\n\n        if (callback) {\n            try {\n                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n                callback(fig, msg);\n            } catch (e) {\n                console.log(\n                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n                    e,\n                    e.stack,\n                    msg\n                );\n            }\n        }\n    };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n    //this section is from http://www.quirksmode.org/js/events_properties.html\n    var targ;\n    if (!e) {\n        e = window.event;\n    }\n    if (e.target) {\n        targ = e.target;\n    } else if (e.srcElement) {\n        targ = e.srcElement;\n    }\n    if (targ.nodeType === 3) {\n        // defeat Safari bug\n        targ = targ.parentNode;\n    }\n\n    // pageX,Y are the mouse positions relative to the document\n    var boundingRect = targ.getBoundingClientRect();\n    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n    return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n    return Object.keys(original).reduce(function (obj, key) {\n        if (typeof original[key] !== 'object') {\n            obj[key] = original[key];\n        }\n        return obj;\n    }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n    var canvas_pos = mpl.findpos(event);\n\n    if (name === 'button_press') {\n        this.canvas.focus();\n        this.canvas_div.focus();\n    }\n\n    var x = canvas_pos.x * this.ratio;\n    var y = canvas_pos.y * this.ratio;\n\n    this.send_message(name, {\n        x: x,\n        y: y,\n        button: event.button,\n        step: event.step,\n        guiEvent: simpleKeys(event),\n    });\n\n    /* This prevents the web browser from automatically changing to\n     * the text insertion cursor when the button is pressed.  We want\n     * to control all of the cursor setting manually through the\n     * 'cursor' event from matplotlib */\n    event.preventDefault();\n    return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n    // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n    // Prevent repeat events\n    if (name === 'key_press') {\n        if (event.key === this._key) {\n            return;\n        } else {\n            this._key = event.key;\n        }\n    }\n    if (name === 'key_release') {\n        this._key = null;\n    }\n\n    var value = '';\n    if (event.ctrlKey && event.key !== 'Control') {\n        value += 'ctrl+';\n    }\n    else if (event.altKey && event.key !== 'Alt') {\n        value += 'alt+';\n    }\n    else if (event.shiftKey && event.key !== 'Shift') {\n        value += 'shift+';\n    }\n\n    value += 'k' + event.key;\n\n    this._key_event_extra(event, name);\n\n    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n    return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n    if (name === 'download') {\n        this.handle_save(this, null);\n    } else {\n        this.send_message('toolbar_button', { name: name });\n    }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n    this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n    // Create a \"websocket\"-like object which calls the given IPython comm\n    // object with the appropriate methods. Currently this is a non binary\n    // socket, so there is still some room for performance tuning.\n    var ws = {};\n\n    ws.binaryType = comm.kernel.ws.binaryType;\n    ws.readyState = comm.kernel.ws.readyState;\n    function updateReadyState(_event) {\n        if (comm.kernel.ws) {\n            ws.readyState = comm.kernel.ws.readyState;\n        } else {\n            ws.readyState = 3; // Closed state.\n        }\n    }\n    comm.kernel.ws.addEventListener('open', updateReadyState);\n    comm.kernel.ws.addEventListener('close', updateReadyState);\n    comm.kernel.ws.addEventListener('error', updateReadyState);\n\n    ws.close = function () {\n        comm.close();\n    };\n    ws.send = function (m) {\n        //console.log('sending', m);\n        comm.send(m);\n    };\n    // Register the callback with on_msg.\n    comm.on_msg(function (msg) {\n        //console.log('receiving', msg['content']['data'], msg);\n        var data = msg['content']['data'];\n        if (data['blob'] !== undefined) {\n            data = {\n                data: new Blob(msg['buffers'], { type: data['blob'] }),\n            };\n        }\n        // Pass the mpl event to the overridden (by mpl) onmessage function.\n        ws.onmessage(data);\n    });\n    return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n    // This is the function which gets called when the mpl process\n    // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n    var id = msg.content.data.id;\n    // Get hold of the div created by the display call when the Comm\n    // socket was opened in Python.\n    var element = document.getElementById(id);\n    var ws_proxy = comm_websocket_adapter(comm);\n\n    function ondownload(figure, _format) {\n        window.open(figure.canvas.toDataURL());\n    }\n\n    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n    // web socket which is closed, not our websocket->open comm proxy.\n    ws_proxy.onopen();\n\n    fig.parent_element = element;\n    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n    if (!fig.cell_info) {\n        console.error('Failed to find cell for figure', id, fig);\n        return;\n    }\n    fig.cell_info[0].output_area.element.on(\n        'cleared',\n        { fig: fig },\n        fig._remove_fig_handler\n    );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n    var width = fig.canvas.width / fig.ratio;\n    fig.cell_info[0].output_area.element.off(\n        'cleared',\n        fig._remove_fig_handler\n    );\n    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n    // Update the output cell to use the data from the current canvas.\n    fig.push_to_output();\n    var dataURL = fig.canvas.toDataURL();\n    // Re-enable the keyboard manager in IPython - without this line, in FF,\n    // the notebook keyboard shortcuts fail.\n    IPython.keyboard_manager.enable();\n    fig.parent_element.innerHTML =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n    fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n    fig.send_message('closing', msg);\n    // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n    // Turn the data on the canvas into data in the output cell.\n    var width = this.canvas.width / this.ratio;\n    var dataURL = this.canvas.toDataURL();\n    this.cell_info[1]['text/html'] =\n        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n    // Tell IPython that the notebook contents must change.\n    IPython.notebook.set_dirty(true);\n    this.send_message('ack', {});\n    var fig = this;\n    // Wait a second, then push the new image to the DOM so\n    // that it is saved nicely (might be nice to debounce this).\n    setTimeout(function () {\n        fig.push_to_output();\n    }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n    var fig = this;\n\n    var toolbar = document.createElement('div');\n    toolbar.classList = 'btn-toolbar';\n    this.root.appendChild(toolbar);\n\n    function on_click_closure(name) {\n        return function (_event) {\n            return fig.toolbar_button_onclick(name);\n        };\n    }\n\n    function on_mouseover_closure(tooltip) {\n        return function (event) {\n            if (!event.currentTarget.disabled) {\n                return fig.toolbar_button_onmouseover(tooltip);\n            }\n        };\n    }\n\n    fig.buttons = {};\n    var buttonGroup = document.createElement('div');\n    buttonGroup.classList = 'btn-group';\n    var button;\n    for (var toolbar_ind in mpl.toolbar_items) {\n        var name = mpl.toolbar_items[toolbar_ind][0];\n        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n        var image = mpl.toolbar_items[toolbar_ind][2];\n        var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n        if (!name) {\n            /* Instead of a spacer, we start a new button group. */\n            if (buttonGroup.hasChildNodes()) {\n                toolbar.appendChild(buttonGroup);\n            }\n            buttonGroup = document.createElement('div');\n            buttonGroup.classList = 'btn-group';\n            continue;\n        }\n\n        button = fig.buttons[name] = document.createElement('button');\n        button.classList = 'btn btn-default';\n        button.href = '#';\n        button.title = name;\n        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n        button.addEventListener('click', on_click_closure(method_name));\n        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n        buttonGroup.appendChild(button);\n    }\n\n    if (buttonGroup.hasChildNodes()) {\n        toolbar.appendChild(buttonGroup);\n    }\n\n    // Add the status bar.\n    var status_bar = document.createElement('span');\n    status_bar.classList = 'mpl-message pull-right';\n    toolbar.appendChild(status_bar);\n    this.message = status_bar;\n\n    // Add the close button to the window.\n    var buttongrp = document.createElement('div');\n    buttongrp.classList = 'btn-group inline pull-right';\n    button = document.createElement('button');\n    button.classList = 'btn btn-mini btn-primary';\n    button.href = '#';\n    button.title = 'Stop Interaction';\n    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n    button.addEventListener('click', function (_evt) {\n        fig.handle_close(fig, {});\n    });\n    button.addEventListener(\n        'mouseover',\n        on_mouseover_closure('Stop Interaction')\n    );\n    buttongrp.appendChild(button);\n    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n    var fig = event.data.fig;\n    if (event.target !== this) {\n        // Ignore bubbled events from children.\n        return;\n    }\n    fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n    // this is important to make the div 'focusable\n    el.setAttribute('tabindex', 0);\n    // reach out to IPython and tell the keyboard manager to turn it's self\n    // off when our div gets focus\n\n    // location in version 3\n    if (IPython.notebook.keyboard_manager) {\n        IPython.notebook.keyboard_manager.register_events(el);\n    } else {\n        // location in version 2\n        IPython.keyboard_manager.register_events(el);\n    }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n    var manager = IPython.notebook.keyboard_manager;\n    if (!manager) {\n        manager = IPython.keyboard_manager;\n    }\n\n    // Check for shift+enter\n    if (event.shiftKey && event.which === 13) {\n        this.canvas_div.blur();\n        // select the cell after this one\n        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n        IPython.notebook.select(index + 1);\n    }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n    fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n    // Return the cell and output element which can be found *uniquely* in the notebook.\n    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n    // IPython event is triggered only after the cells have been serialised, which for\n    // our purposes (turning an active figure into a static one), is too late.\n    var cells = IPython.notebook.get_cells();\n    var ncells = cells.length;\n    for (var i = 0; i < ncells; i++) {\n        var cell = cells[i];\n        if (cell.cell_type === 'code') {\n            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n                var data = cell.output_area.outputs[j];\n                if (data.data) {\n                    // IPython >= 3 moved mimebundle to data attribute of output\n                    data = data.data;\n                }\n                if (data['text/html'] === html_output) {\n                    return [cell, data, j];\n                }\n            }\n        }\n    }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n    IPython.notebook.kernel.comm_manager.register_target(\n        'matplotlib',\n        mpl.mpl_figure_comm\n    );\n}\n"
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": "<IPython.core.display.HTML object>",
+      "text/html": "<div id='da85e23d-8b51-4723-8359-8d708f1882d0'></div>"
+     },
+     "metadata": {},
+     "output_type": "display_data"
     }
    ],
    "source": [
@@ -42,14 +60,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 1,
    "outputs": [],
    "source": [],
    "metadata": {
     "collapsed": false,
     "ExecuteTime": {
-     "start_time": "2023-05-21T01:33:57.533653Z",
-     "end_time": "2023-05-21T01:33:57.564890Z"
+     "start_time": "2023-06-05T15:54:40.379763Z",
+     "end_time": "2023-06-05T15:54:40.392790Z"
     }
    }
   }
diff --git a/aufgaben/p6/multiclass_error_rate.py b/aufgaben/p6/multiclass_error_rate.py
index 0cecb3beebe7ed30cbddddabab2d47502e629987..a6571f3f706e395abf4ca4081faa8a58e366ace4 100644
--- a/aufgaben/p6/multiclass_error_rate.py
+++ b/aufgaben/p6/multiclass_error_rate.py
@@ -1,22 +1,20 @@
-from tabulate import tabulate
-
-
-class Multiclass_ErrorRate:
-
+class MulticlassErrorRate:
     evaluations = {}
 
-    def __init__(self, classes: list):
-        for clazz in classes:
-            self.evaluations[clazz] = [0, 0]
-
     def evaluate(self, expected_class: float, actual_class: float):
+        if expected_class not in self.evaluations.keys():
+            self.evaluations[expected_class] = [0, 0]
+
         if expected_class == actual_class:
             self.evaluations[expected_class][0] += 1
         else:
             self.evaluations[expected_class][1] += 1
 
     def print_table(self):
-        for clazz in self.evaluations.keys():
-            print(f"Klasse: {clazz}. Korrekt: {self.evaluations[clazz][0]}. Inkorrekt: {self.evaluations[clazz][1]}")
-        error_rate = sum([clazz[1] for clazz in self.evaluations.values()]) / (sum([clazz[0] for clazz in self.evaluations.values()]) + sum([clazz[1] for clazz in self.evaluations.values()]))
+        for class_name in self.evaluations.keys():
+            print(
+                f"Klasse: {class_name}. Korrekt: {self.evaluations[class_name][0]}. Inkorrekt: {self.evaluations[class_name][1]}")
+        error_rate = sum([class_name[1] for class_name in self.evaluations.values()]) / (
+                    sum([clazz[0] for clazz in self.evaluations.values()]) + sum(
+                [clazz[1] for clazz in self.evaluations.values()]))
         print(f"Errorrate: {error_rate}")
diff --git a/aufgaben/p7/apply_k_nearest_neighbors.py b/aufgaben/p7/apply_k_nearest_neighbors.py
index 2ba80a5da08dc62d69ca562a77e1dff2ce8d3542..93892e2448f6e11ea099a6a9db27f10862ec3ec5 100644
--- a/aufgaben/p7/apply_k_nearest_neighbors.py
+++ b/aufgaben/p7/apply_k_nearest_neighbors.py
@@ -1,18 +1,18 @@
 from algorithm.k_nearest_neighbors.distance_measure.euclidean_distance import euclidean_distance
 from algorithm.k_nearest_neighbors.k_nearest_neighbors_algorithm import KNearestNeighborsAlgorithm
-from aufgaben.p4.testdata import get_labeled_testdata
-from aufgaben.p6.error_rate import ErrorRate
+from aufgaben.p6.multiclass_error_rate import MulticlassErrorRate
+from aufgaben.p7.multiple_class_classification import multiple_classes_testdata
 
 
 def apply_k_nearest_neighbors():
-    test_data, training_data = get_labeled_testdata()
+    test_data, training_data = multiple_classes_testdata()
 
     # Trainiere den K-Nearest-Neighbors-Algorithm
     algorithm = KNearestNeighborsAlgorithm()
     algorithm.train(training_data)
 
     # Vergleiche alle Ergebnisse mit der erwarteten Klasse
-    fehlerrate = ErrorRate()
+    fehlerrate = MulticlassErrorRate()
     for features, correct_class in test_data:
         result = algorithm.classify(features, euclidean_distance, 10)
         fehlerrate.evaluate(correct_class, result)
diff --git a/aufgaben/p7/multiple_class_classification.py b/aufgaben/p7/multiple_class_classification.py
new file mode 100644
index 0000000000000000000000000000000000000000..9cd44cfa9e7ab28d8264d554b6dbbe0bf896fe3e
--- /dev/null
+++ b/aufgaben/p7/multiple_class_classification.py
@@ -0,0 +1,33 @@
+from features.standard_deviation import standard_deviation
+from testdata.constants import Acc_Y
+from testdata.testdata import get_features, label_testdata
+
+SENSOR_FUSS = 1
+
+
+def multiple_classes_features(window_size=30):
+    """ Testdaten für P7 """
+
+    personen = [
+        (2, 1),  # Gruppe 2, Person 1
+        (2, 2)  # Gruppe 2, Person 2
+    ]
+
+    joggen_feature = get_features(standard_deviation, window_size, 'joggen', SENSOR_FUSS, Acc_Y, personen)
+    gehen_feature = get_features(standard_deviation, window_size, 'gehen', SENSOR_FUSS, Acc_Y, personen)
+    jumpingjack_feature = get_features(standard_deviation, window_size, 'jumpingjack', SENSOR_FUSS, Acc_Y, personen)
+
+    return joggen_feature, gehen_feature, jumpingjack_feature
+
+
+def multiple_classes_testdata():
+    joggen_feature, gehen_feature, jumpingjack_feature = multiple_classes_features(100)
+
+    joggen_training, joggen_test = label_testdata(joggen_feature, 'JOGGEN', 500)
+    gehen_training, gehen_test = label_testdata(joggen_feature, 'GEHEN', 500)
+    jumpingjack_training, jumpingjack_test = label_testdata(jumpingjack_feature, 'JUMPINGJACK', 500)
+
+    trainings_data = joggen_training + gehen_training + jumpingjack_training
+    test_data = joggen_test + gehen_test + jumpingjack_test
+
+    return trainings_data, test_data
diff --git a/aufgaben/p9/apply_decision_tree.py b/aufgaben/p9/apply_decision_tree.py
index fdc8ba4918539bad52d53189e4ee464b97e63467..762db887e9fbec7c5699749e5e75c517adfeeda8 100644
--- a/aufgaben/p9/apply_decision_tree.py
+++ b/aufgaben/p9/apply_decision_tree.py
@@ -4,7 +4,7 @@ import numpy
 
 from algorithm.decision_tree.decision_tree import DecisionTree
 from aufgaben.p4.testdata import get_labeled_testdata
-from aufgaben.p6.multiclass_error_rate import Multiclass_ErrorRate
+from aufgaben.p6.multiclass_error_rate import MulticlassErrorRate
 
 
 def apply_decision_tree():
@@ -15,7 +15,7 @@ def apply_decision_tree():
     algorithm.train(None, training_data)
 
     # Vergleiche alle Ergebnisse mit der erwarteten Klasse
-    fehlerrate = Multiclass_ErrorRate()
+    fehlerrate = MulticlassErrorRate()
     for features, correct_class in test_data:
         result = algorithm.classify(features)
         fehlerrate.evaluate(correct_class, result)
diff --git a/testdata/__init__.py b/testdata/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/testdata/constants.py b/testdata/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9311a71f945e939c2f9027ce928a81709a74ada
--- /dev/null
+++ b/testdata/constants.py
@@ -0,0 +1,9 @@
+Acc_X = 0
+Acc_Y = 1
+Acc_Z = 2
+Gyr_X = 3
+Gyr_Y = 4
+Gyr_Z = 5
+Mag_X = 6
+Mag_Y = 7
+Mag_Z = 8
diff --git a/aufgaben/p4/testdata.py b/testdata/testdata.py
similarity index 50%
rename from aufgaben/p4/testdata.py
rename to testdata/testdata.py
index cb4ef8eef41d639361c8c1e059d4b1027dd6988c..4a7818d272c478beb89a020e16b5918b654350ba 100644
--- a/aufgaben/p4/testdata.py
+++ b/testdata/testdata.py
@@ -2,8 +2,7 @@ from math import floor
 
 from features.moving_feature import moving_feature
 from features.standard_deviation import standard_deviation
-from features.arithmetic_mean import arithmetic_mean
-from korpus import create_bewegung, create_bewegung_two_person
+from korpus import create_bewegung
 
 CLASS_JOGGEN = 1
 CLASS_KNIEBEUGE = -1
@@ -16,65 +15,49 @@ DATA_LIMIT = 1000
 DATA_LIMIT_PER_TYPE = floor(DATA_LIMIT / 2)
 
 
-def binary_classification_feature(window_size=30):
-    # Wir holen uns den Höhensensor des Fußes (Bleibt bei Kniebeugen gleich und bewegt sich beim Laufen)
-    joggen_values = create_bewegung('joggen').messungen[1].sensoren[1].werte
-    kniebeuge_values = create_bewegung('kniebeuge').messungen[1].sensoren[1].werte
-    gehen_values = create_bewegung('gehen').messungen[1].sensoren[1].werte
+def get_features(feature_function, window_size, bewegungs_name, messung, sensor, personen: list):
+    values = []
 
-    # Berechne die Standardabweichung (Bei Kniebeugen gering, bei Joggen hoch)
-    joggen_feature = moving_feature(arithmetic_mean, window_size, joggen_values)
-    kniebeuge_feature = moving_feature(arithmetic_mean, window_size, kniebeuge_values)
-    gehen_feature = moving_feature(arithmetic_mean, window_size, gehen_values)
+    for person in personen:
+        bewegung = create_bewegung(bewegungs_name, gruppe=person[0], person=person[1])
+        values += list(bewegung.messungen[messung].sensoren[sensor].werte)
 
-    return joggen_feature, kniebeuge_feature, gehen_feature
+    return moving_feature(feature_function, window_size, values)
 
 
 def classification_evaluation(window_size=30, func=standard_deviation):
     messung = 0
     sensor = 0
-    joggen_values = create_bewegung_two_person('joggen', person=1).messungen[messung].sensoren[sensor].werte
-    kniebeuge_values = create_bewegung_two_person('gehen', person=1).messungen[messung].sensoren[sensor].werte
-    jj_values = create_bewegung_two_person('jumpingjack', person=1).messungen[messung].sensoren[sensor].werte
-    joggen_values2 = create_bewegung_two_person('joggen', person=2).messungen[messung].sensoren[sensor].werte
-    kniebeuge_values2 = create_bewegung_two_person('gehen', person=2).messungen[messung].sensoren[sensor].werte
-    jj_values2 = create_bewegung_two_person('jumpingjack', person=2).messungen[messung].sensoren[sensor].werte
+    joggen_values = create_bewegung('joggen', person=1).messungen[messung].sensoren[sensor].werte
+    kniebeuge_values = create_bewegung('gehen', person=1).messungen[messung].sensoren[sensor].werte
+    jj_values = create_bewegung('jumpingjack', person=1).messungen[messung].sensoren[sensor].werte
+    joggen_values2 = create_bewegung('joggen', person=2).messungen[messung].sensoren[sensor].werte
+    kniebeuge_values2 = create_bewegung('gehen', person=2).messungen[messung].sensoren[sensor].werte
+    jj_values2 = create_bewegung('jumpingjack', person=2).messungen[messung].sensoren[sensor].werte
 
     # Berechne die Standardabweichung (Bei Kniebeugen gering, bei Joggen hoch)
-    joggen_feature = moving_feature(func, window_size, list(joggen_values)+list(joggen_values2))
+    joggen_feature = moving_feature(func, window_size, list(joggen_values) + list(joggen_values2))
     kniebeuge_feature = moving_feature(func, window_size, list(kniebeuge_values) + list(kniebeuge_values2))
     jj_feature = moving_feature(func, window_size, list(jj_values) + list(jj_values2))
 
     return joggen_feature, kniebeuge_feature, jj_feature
 
 
-def get_labeled_testdata():
-    # Hole die aus den Sensordaten berechneten Merkmale
-    joggen_feature, kniebeuge_feature, gehen_feature = binary_classification_feature(200)
-
+def label_testdata(features, label, data_limit):
     # Wir nehmen nur DATA_LIMIT an Daten (sonst ist K-Nearest-Neighbors zu langsam)
-    joggen_feature = joggen_feature[: min(DATA_LIMIT_PER_TYPE, len(joggen_feature))]
-    kniebeuge_feature = kniebeuge_feature[: min(DATA_LIMIT_PER_TYPE, len(kniebeuge_feature))]
-    gehen_feature = gehen_feature[: min(DATA_LIMIT_PER_TYPE, len(gehen_feature))]
+    features = features[: min(data_limit, len(features))]
 
     # Wandel Liste an Merkmalen in einzelne Merkmalsvektoren um
-    joggen_vector = ([element] for element in joggen_feature)
-    kniebeugen_vector = ([element] for element in kniebeuge_feature)
-    gehen_vector = ([element] for element in gehen_feature)
+    feature_vector = ([element] for element in features)
 
     # Weise den Trainingsdaten eine Klasse zu
-    # 0 = Kniebeuge, 1 = Joggen
-    training_data_joggen = list(zip(joggen_vector, [CLASS_JOGGEN] * len(joggen_feature)))
-    training_data_kniebeuge = list(zip(kniebeugen_vector, [CLASS_KNIEBEUGE] * len(kniebeuge_feature)))
-    training_data_gehen = list(zip(gehen_vector, [CLASS_GEHEN] * len(gehen_feature)))
+    data = list(zip(feature_vector, [label] * len(features)))
 
     # Wir nehmen 90 % der Testdaten zum Trainieren und 10 % zum Testen
-    delimiter_joggen = floor(len(joggen_feature) * TRAINING_DATA_PERCENTAGE)
-    delimiter_kniebeuge = floor(len(kniebeuge_feature) * TRAINING_DATA_PERCENTAGE)
-    delimiter_gehen = floor(len(gehen_feature) * TRAINING_DATA_PERCENTAGE)
+    delimiter = floor(len(features) * TRAINING_DATA_PERCENTAGE)
 
-    training_data = training_data_joggen[:delimiter_joggen] + training_data_kniebeuge[:delimiter_kniebeuge] + training_data_gehen[:delimiter_gehen]
-    test_data = training_data_joggen[delimiter_joggen:] + training_data_kniebeuge[delimiter_kniebeuge:] + training_data_gehen[delimiter_gehen:]
+    training_data = data[:delimiter]
+    test_data = data[delimiter:]
 
     return test_data, training_data
 
@@ -104,7 +87,6 @@ def get_evaluation_data(window_size, feature_list: []):
         for i in range(len(jj_feature)):
             jj_vector[i].append(jj_feature[i])
 
-
     # Weise den Trainingsdaten eine Klasse zu
     # 0 = Kniebeuge, 1 = Joggen
     training_data_joggen = list(zip(joggen_vector, [CLASS_JOGGEN] * len(joggen_vector)))
@@ -116,8 +98,9 @@ def get_evaluation_data(window_size, feature_list: []):
     delimiter_kniebeuge = floor(len(kniebeugen_vector) * TRAINING_DATA_PERCENTAGE)
     delimiter_jj = floor(len(jj_vector) * TRAINING_DATA_PERCENTAGE)
 
-    training_data = training_data_joggen[:delimiter_joggen] + training_data_kniebeuge[:delimiter_kniebeuge] + training_data_jj[:delimiter_jj]
-    test_data = training_data_joggen[delimiter_joggen:] + training_data_kniebeuge[delimiter_kniebeuge:] + training_data_jj[delimiter_jj:]
+    training_data = training_data_joggen[:delimiter_joggen] + training_data_kniebeuge[
+                                                              :delimiter_kniebeuge] + training_data_jj[:delimiter_jj]
+    test_data = training_data_joggen[delimiter_joggen:] + training_data_kniebeuge[
+                                                          delimiter_kniebeuge:] + training_data_jj[delimiter_jj:]
 
     return test_data, training_data
-