diff --git a/SleepStageModels.ipynb b/SleepStageModels.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..5fea506e21579de37e9781038a1a57f3f61dd16d
--- /dev/null
+++ b/SleepStageModels.ipynb
@@ -0,0 +1,973 @@
+{
+  "cells": [
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "L300ySZBqbyH"
+      },
+      "source": [
+        "# Installation"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "YSztofTQiTt7"
+      },
+      "outputs": [],
+      "source": [
+        "!pip install pyedflib\n",
+        "!pip install numpy\n",
+        "!pip install xmltodict\n",
+        "!pip install mne\n",
+        "!pip install tensorflow\n",
+        "!pip install pandas\n",
+        "!pip install scikit-learn\n",
+        "!pip install hampel\n",
+        "!pip install keras-tuner"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "D8J3VziBqm-p"
+      },
+      "source": [
+        "# Prepare data"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "2X2HY602mIwc",
+        "outputId": "4fb06705-6620-49d1-c0d2-fbedb5176c58"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Mounted at /content/drive\n"
+          ]
+        }
+      ],
+      "source": [
+        "# Mount to google drive\n",
+        "\n",
+        "from google.colab import drive\n",
+        "drive.mount('/content/drive')"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "n4FwOJ0Dm7ro"
+      },
+      "outputs": [],
+      "source": [
+        "# create Folders to store the data from google drive\n",
+        "import os\n",
+        "\n",
+        "path_to_edf_files = 'edf_files'\n",
+        "if not os.path.exists(path_to_edf_files):\n",
+        "  os.mkdir(path_to_edf_files)\n",
+        "\n",
+        "path_to_annotations = 'annotations' #/content/drive/My Drive/annotations'\n",
+        "if not os.path.exists(path_to_annotations):\n",
+        "  os.mkdir(path_to_annotations)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "v3vZ-kppi4DA"
+      },
+      "outputs": [],
+      "source": [
+        "import shutil\n",
+        "# Be carefull not to delete folder by accident (always comment out after running it)\n",
+        "#shutil.rmtree(path_to_annotations)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "hKFNmDrEmNpy"
+      },
+      "outputs": [],
+      "source": [
+        "import zipfile\n",
+        "\n",
+        "# Info: All edf and annotation Files are stored in multiple zip_files in google drive.\n",
+        "      # Root folder for edf files is path_to_all_edf_zip_folder\n",
+        "      # Root folder for annotation files is path_to_all_annotation_zip_folder\n",
+        "\n",
+        "path_to_all_edf_zip_folder = '/content/drive/My Drive/shhs2_edf_zip'\n",
+        "path_to_all_annotation_zip_folder = '/content/drive/My Drive/shhs2_annotation_zip'\n",
+        "\n",
+        "\n",
+        "# Unzip all files from path_to_all_edf_zip_folder into folder 'edf_files'\n",
+        "for filename in os.listdir(path_to_all_edf_zip_folder):\n",
+        "    if filename.endswith('.zip'):\n",
+        "        zip_path = os.path.join(path_to_all_edf_zip_folder, filename)\n",
+        "\n",
+        "        # Open ZIP-File\n",
+        "        with zipfile.ZipFile(zip_path, 'r') as zip_ref:\n",
+        "            # extract files into path_to_edf_files\n",
+        "            print(zip_path)\n",
+        "            !unzip \"$zip_path\" -d \"$path_to_edf_files\"\n",
+        "\n",
+        "        print(f'Extrahiert: {filename}')\n",
+        "\n",
+        "\n",
+        "# Unzip all files from path_to_all_annotation_zip_folder into folder 'annotations'\n",
+        "for filename in os.listdir(path_to_all_annotation_zip_folder):\n",
+        "    if filename.endswith('.zip'):\n",
+        "        zip_path = os.path.join(path_to_all_annotation_zip_folder, filename)\n",
+        "\n",
+        "        # Open ZIP-File\n",
+        "        with zipfile.ZipFile(zip_path, 'r') as zip_ref:\n",
+        "            # # extract files into path_to_annotations\n",
+        "            print(zip_path)\n",
+        "            !unzip \"$zip_path\" -d \"$path_to_annotations\"\n",
+        "\n",
+        "        print(f'Extrahiert: {filename}')\n"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "VdTlPBDxrJYL"
+      },
+      "source": [
+        "# Imports"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "V8AE7mFJjbI3"
+      },
+      "outputs": [],
+      "source": [
+        "import pyedflib\n",
+        "import numpy as np\n",
+        "import os\n",
+        "import xmltodict\n",
+        "import mne\n",
+        "import csv\n",
+        "import matplotlib.pyplot as plt\n",
+        "import pandas as pd\n",
+        "from scipy.signal import butter, lfilter, resample\n",
+        "from hampel import hampel\n",
+        "import numpy\n",
+        "import statistics\n",
+        "import pywt\n",
+        "from scipy.fft import fft, ifft, fftfreq\n",
+        "\n",
+        "from sklearn.model_selection import train_test_split\n",
+        "from sklearn.ensemble import RandomForestClassifier\n",
+        "from sklearn.neighbors import KNeighborsClassifier\n",
+        "from sklearn.metrics import classification_report, cohen_kappa_score\n",
+        "from sklearn.preprocessing import LabelEncoder\n",
+        "import imblearn\n",
+        "from collections import Counter\n",
+        "from sklearn.datasets import make_classification\n",
+        "from matplotlib import pyplot\n",
+        "from numpy import where\n",
+        "from imblearn.over_sampling import SMOTE\n",
+        "from imblearn.under_sampling import RandomUnderSampler\n",
+        "from imblearn.pipeline import Pipeline\n",
+        "from sklearn.model_selection import train_test_split, RandomizedSearchCV, cross_val_predict, cross_val_score\n",
+        "\n",
+        "from scipy.stats import skew, kurtosis\n",
+        "from tensorflow.keras.models import Sequential\n",
+        "from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense\n",
+        "from tensorflow.keras.preprocessing.sequence import pad_sequences\n",
+        "from sklearn.model_selection import train_test_split\n",
+        "from sklearn.metrics import classification_report, cohen_kappa_score\n",
+        "from sklearn.preprocessing import LabelEncoder\n",
+        "\n",
+        "from sklearn.metrics import precision_score\n",
+        "from sklearn.metrics import recall_score\n",
+        "from sklearn.metrics import f1_score\n",
+        "\n",
+        "from sklearn.metrics import cohen_kappa_score, recall_score, f1_score, classification_report\n",
+        "from tensorflow.keras.optimizers import Adam\n",
+        "import kerastuner as kt\n",
+        "from kerastuner.tuners import RandomSearch\n",
+        "\n",
+        "import traceback\n",
+        "import warnings"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "24EeYQ3DrNrt"
+      },
+      "source": [
+        "# Read available channels"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "id": "HHNMzn3Gcu3N",
+        "outputId": "0e3c6aa2-f5bd-4507-c9da-cb460d6cec06"
+      },
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "Extracting EDF parameters from /content/edf_files/shhs2-200646.edf...\n",
+            "EDF file detected\n",
+            "Setting channel info structure...\n",
+            "Creating raw.info structure...\n",
+            "['SaO2', 'H.R.', 'EEG(sec)', 'ECG', 'EMG', 'EOG(L)', 'EOG(R)', 'EEG', 'AIRFLOW', 'THOR RES', 'ABDO RES', 'POSITION', 'LIGHT', 'OX stat']\n"
+          ]
+        }
+      ],
+      "source": [
+        "for filename in os.listdir(path_to_edf_files):\n",
+        "  data = mne.io.read_raw_edf(path_to_edf_files + \"/\" + filename)\n",
+        "  raw_data = data.get_data()\n",
+        "  channel_names = data.ch_names\n",
+        "  print(channel_names)\n",
+        "  break\n",
+        "\n",
+        "# I only use PR, SaO2, Position and ABDO RES"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "DD5MaGB4rUKv"
+      },
+      "source": [
+        "# Functions"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "c6rjgpCwrYQK"
+      },
+      "outputs": [],
+      "source": [
+        "def calculate_epochs_and_remainings(total_seconds, epoch_duration):\n",
+        "    # Calculate the number of complete epochs\n",
+        "    epochs_completed = total_seconds // epoch_duration\n",
+        "\n",
+        "    # Calculate the remaining seconds\n",
+        "    remaining_seconds = total_seconds % epoch_duration\n",
+        "\n",
+        "    return epochs_completed, remaining_seconds\n",
+        "\n",
+        "# Visualize signal\n",
+        "def visualize_signal(data, title):\n",
+        "    \"\"\"\n",
+        "    Visualize EEG signal data.\n",
+        "    :param data: EEG signal data as a list or numpy array.\n",
+        "    \"\"\"\n",
+        "    plt.figure(figsize=(12, 6))\n",
+        "    plt.plot(data)\n",
+        "    plt.title(title)\n",
+        "    plt.xlabel(\"Time (in seconds)\")\n",
+        "    plt.ylabel(\"Amplitude\")\n",
+        "    plt.show()\n",
+        "\n",
+        "# Get the Min and Max Value of the singals\n",
+        "def getMinMaxValue(signalName):\n",
+        "    if signalName == \"SaO2\":\n",
+        "      return 90, 100\n",
+        "    if signalName == \"PR\":\n",
+        "      return 60, 100\n",
+        "    if signalName == \"POSITION\":\n",
+        "      return 0, 3\n",
+        "    if signalName == \"ABDO RES\":\n",
+        "      return -1, 1\n",
+        "\n",
+        "# Use min max normalization\n",
+        "def normalize(value, min_val, max_val):\n",
+        "    return (value - min_val) / (max_val - min_val)\n",
+        "\n",
+        "# Get time domain features.\n",
+        "def time_domain_features(signal):\n",
+        "    mean_val = np.mean(signal)\n",
+        "\n",
+        "    # Suppress warnings for this specific computation\n",
+        "    with warnings.catch_warnings():\n",
+        "        warnings.simplefilter(\"ignore\", category=RuntimeWarning)\n",
+        "        kurtosis_val = kurtosis(signal)\n",
+        "        skewness_val = skew(signal)\n",
+        "\n",
+        "        # Replace Nan Values with mean\n",
+        "        if np.isnan(kurtosis_val):\n",
+        "          kurtosis_val = mean_val\n",
+        "        if np.isnan(skewness_val):\n",
+        "          skewness_val = mean_val\n",
+        "\n",
+        "    std_dev = np.std(signal)\n",
+        "    variance = np.var(signal)\n",
+        "\n",
+        "    return mean_val, std_dev, variance, kurtosis_val, skewness_val\n"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "opeLCeC6rmlw"
+      },
+      "source": [
+        "# Read and Save Signals"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "NeZ9eyTNzXd2"
+      },
+      "outputs": [],
+      "source": [
+        "# Signals\n",
+        "important_signals = {\n",
+        "    'SaO2' : 0,\n",
+        "    'PR': 1,\n",
+        "    'ABDO RES': 10, # Abdomen has Sampling Rate 10\n",
+        "    'POSITION': 11\n",
+        "}\n",
+        "\n",
+        "# Variables\n",
+        "epoch_duration = 30\n",
+        "\n",
+        "# Create and open the CSV file for writing the header\n",
+        "with open('signal_data.csv', mode='w', newline='') as file:\n",
+        "    writer = csv.writer(file)\n",
+        "    writer.writerow(['stage', 'signalName', 'std', 'mean', 'variance', 'kurtosis', 'skewness'])\n",
+        "\n",
+        "\n",
+        "# Iterate over EDF files in the directory\n",
+        "print(f\"path_to_edf_files {path_to_edf_files}\")\n",
+        "\n",
+        "for filename in os.listdir(path_to_edf_files):\n",
+        "\n",
+        "  print(f\"\\n\\nEDF-File {filename}\")\n",
+        "  path_to_edf = f\"{path_to_edf_files}/{filename}\"\n",
+        "\n",
+        "  # Create xml filename\n",
+        "  filename_without_extension = filename.split(\".\")[0]\n",
+        "  xml_filename = filename_without_extension + \"-nsrr.xml\"\n",
+        "\n",
+        "  with open(f\"{path_to_annotations}/{xml_filename}\") as fd:\n",
+        "    doc = xmltodict.parse(fd.read())\n",
+        "\n",
+        "  # Create scored_events\n",
+        "  annotations = doc['PSGAnnotation']\n",
+        "  events = annotations['ScoredEvents']\n",
+        "  scored_events = events['ScoredEvent']\n",
+        "\n",
+        "  # Get the start time and duration of each sleep stage\n",
+        "  awake_times = []\n",
+        "  lite_sleep_times = []\n",
+        "  deep_sleep_times = []\n",
+        "  rem_sleep_times = []\n",
+        "\n",
+        "  for element in scored_events:\n",
+        "      if element['EventConcept'] == 'Wake|0':\n",
+        "        awake_times.append({\"start\": element[\"Start\"], \"duration\": element[\"Duration\"]})\n",
+        "      if element['EventConcept'] == 'Stage 1 sleep|1' or element['EventConcept'] == 'Stage 2 sleep|2':\n",
+        "        lite_sleep_times.append({\"start\": element[\"Start\"], \"duration\": element[\"Duration\"]})\n",
+        "      if element['EventConcept'] == 'Stage 3 sleep|3':\n",
+        "        deep_sleep_times.append({\"start\": element[\"Start\"], \"duration\": element[\"Duration\"]})\n",
+        "      if element['EventConcept'] == 'REM sleep|5':\n",
+        "        rem_sleep_times.append({\"start\": element[\"Start\"], \"duration\": element[\"Duration\"]})\n",
+        "\n",
+        "  sleep_stages = {\n",
+        "    \"awake\": awake_times,\n",
+        "    \"lite_sleep\": lite_sleep_times,\n",
+        "    \"deep_sleep\": deep_sleep_times,\n",
+        "    \"rem_sleep\": rem_sleep_times\n",
+        "  }\n",
+        "\n",
+        "  # Iterate over each EDF File\n",
+        "    # Iterate over each Sleep Stage\n",
+        "      # Iterate over each important Signal\n",
+        "        # For each Sleep Stage get start and duration\n",
+        "  try:\n",
+        "\n",
+        "    with pyedflib.EdfReader(path_to_edf) as f:\n",
+        "\n",
+        "      # Read the whole Signal and store in seperate csv File. This is explained in the bachelor Thesis.\n",
+        "      whole_signal = f.readSignal(chn=signal_index)\n",
+        "\n",
+        "      for stage, array_data in sleep_stages.items():\n",
+        "          for signal_name, signal_index in important_signals.items():\n",
+        "\n",
+        "            # get sample frequency\n",
+        "            sample_frequency = f.getSampleFrequency(signal_index)\n",
+        "\n",
+        "            for element in array_data:\n",
+        "              start_value = int(float(element['start']))\n",
+        "              duration_value = int(float(element['duration']))\n",
+        "\n",
+        "              # Read the Signal\n",
+        "              partial_signal_data = f.readSignal(chn=signal_index, start=start_value, n=duration_value)\n",
+        "\n",
+        "              # Preprocess\n",
+        "              # Downsample if necessary\n",
+        "              if sample_frequency != 1:\n",
+        "                duration = duration_value\n",
+        "                new_sample_frequency = 1\n",
+        "                new_length = duration * new_sample_frequency\n",
+        "                partial_signal_data = resample(partial_signal_data, new_length)\n",
+        "\n",
+        "              min_val, max_val = getMinMaxValue(signal_name)\n",
+        "              filtered_signal_data = normalize(filtered_signal_data, min_val, max_val)\n",
+        "\n",
+        "              # remove outliner with Hampel-Filter\n",
+        "              result = hampel(partial_signal_data, window_size=5, n_sigma=5.0)\n",
+        "              filtered_signal_data = result.filtered_data\n",
+        "\n",
+        "              # Split Signal in 30 sec epochs\n",
+        "              epochs, remainings = calculate_epochs_and_remainings(duration_value, epoch_duration)\n",
+        "\n",
+        "              # Read signal in 30 sec epochs\n",
+        "              for i in range(epochs):\n",
+        "                start_index = i * epoch_duration\n",
+        "                end_index = start_index + epoch_duration\n",
+        "\n",
+        "                signal_data = filtered_signal_data[start_index:end_index]\n",
+        "\n",
+        "                # Feature extraction\n",
+        "                mean_val, std_dev_val, variance_val, kurtosis_val, skewness_val = time_domain_features(signal_data)\n",
+        "\n",
+        "                # Write the data to the CSV file\n",
+        "                with open('signal_data.csv', mode='a', newline='') as file:\n",
+        "                  writer = csv.writer(file)\n",
+        "                  writer.writerow([stage, signal_name, mean_val, std_dev_val, variance_val, kurtosis_val, skewness_val])\n",
+        "\n",
+        "  except Exception as e:\n",
+        "    print(f\"Error {filename} {e}\")\n",
+        "    traceback.print_exc()\n",
+        "    continue\n",
+        "\n",
+        "print(\"Finished\")"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "WI1XPMn4sBoj"
+      },
+      "source": [
+        "# Preprocess"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "--ulGYtFD_03"
+      },
+      "outputs": [],
+      "source": [
+        "data = pd.read_csv('signal_data.csv')\n",
+        "data = data.dropna()\n",
+        "\n",
+        "data = data[data['signalName'] != 'POSITION'] # Remove rows with signalName Position\n",
+        "data = data[data['signalName'] != 'ABDO RES'] # Remove rows with signalName ABDO RES\n",
+        "\n",
+        "# Write the preprocessed data to a new CSV file\n",
+        "data.to_csv('preprocessed_signal_data.csv', index=False)\n",
+        "\n",
+        "data_without_stage = data.drop('stage', axis=1)\n",
+        "amount_trained_features = len(list(data_without_stage.columns))\n",
+        "print(f\"Amt Stages \\n{data['stage'].unique()}\")\n",
+        "\n",
+        "# Visualize the quantity of each stage\n",
+        "class_counts = data['stage'].value_counts()\n",
+        "class_counts.plot(kind='bar')\n",
+        "plt.xlabel('Class')\n",
+        "plt.ylabel('Amount of entries')\n",
+        "plt.title('Original Class Distribution')\n",
+        "plt.show()\n",
+        "\n",
+        "# Calculate CIF to check for imbalance dataset\n",
+        "total_entries = len(data)\n",
+        "min_stage_entries = data['stage'].value_counts().min()\n",
+        "cif = (total_entries / (2 * 4 * min_stage_entries))\n",
+        "print(f\"CIF {cif}\")"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "SH_cDL9oqMcf"
+      },
+      "outputs": [],
+      "source": [
+        "X = data[['signalName', 'std', 'mean', 'variance', 'kurtosis', 'skewness']]\n",
+        "\n",
+        "signal_name_encoder = LabelEncoder()\n",
+        "stage_encoder = LabelEncoder()\n",
+        "\n",
+        "X['signalName'] = signal_name_encoder.fit_transform(X['signalName'])\n",
+        "data['stage'] = stage_encoder.fit_transform(data['stage'])\n",
+        "\n",
+        "y = data['stage']\n",
+        "print(\"Unique values in y after encoding:\", np.unique(y))\n",
+        "\n",
+        "\n",
+        "over = SMOTE(sampling_strategy=\"not majority\") # Oversample only the minority class / The number of samples in the different classes will be equalized\n",
+        "under = RandomUnderSampler(sampling_strategy='not minority') # Undersample only the majority class\n",
+        "steps = [('o', over), ('u', under)]\n",
+        "pipeline = Pipeline(steps=steps)\n",
+        "X, y = over.fit_resample(X, y) # ONLY USE OVERSMPLE\n",
+        "counter = Counter(y)\n",
+        "print(counter)\n",
+        "\n",
+        "\n",
+        "# Plotting the class distribution\n",
+        "plt.bar(counter.keys(), counter.values())\n",
+        "plt.xlabel('Class')\n",
+        "plt.ylabel('Amount of entries')\n",
+        "plt.title('Resampled Class Distribution')\n",
+        "plt.show()\n",
+        "\n",
+        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "MmnmsE1tsOzk"
+      },
+      "source": [
+        "# Machine Learning"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "nlBhIDBO0R27"
+      },
+      "outputs": [],
+      "source": [
+        "# Random Forest\n",
+        "\n",
+        "# Hyperparameter\n",
+        "'''param_distributions = {\n",
+        "        'n_estimators': [50, 100],\n",
+        "        'max_depth': [10, 20],\n",
+        "        'min_samples_split': [10],\n",
+        "        'min_samples_leaf': [6],\n",
+        "        'bootstrap': [True]\n",
+        "}\n",
+        "\n",
+        "# RandomizedSearchCV for RF\n",
+        "#Best parameters: {'n_estimators': 100, 'min_samples_split': 5, 'min_samples_leaf': 6, 'max_depth': 20, 'bootstrap': True}\n",
+        "#Best parameters: {'n_estimators': 100, 'min_samples_split': 10, 'min_samples_leaf': 6, 'max_depth': 20, 'bootstrap': True}\n",
+        "best_params = {'n_estimators': 100, 'min_samples_split': 10, 'min_samples_leaf': 6, 'max_depth': 20, 'bootstrap': True}\n",
+        "\n",
+        "random_search = RandomizedSearchCV(\n",
+        "        estimator=RandomForestClassifier(random_state=42, class_weight='balanced'),\n",
+        "        param_distributions=param_distributions,\n",
+        "        n_iter=5,\n",
+        "        cv=5,\n",
+        "        verbose=2,\n",
+        "        random_state=42,\n",
+        "        n_jobs=-1\n",
+        ")\n",
+        "random_search.fit(X_train, y_train)\n",
+        "best_params = random_search.best_params_\n",
+        "best_score = random_search.best_score_\n",
+        "print(f\"Best parameters: {best_params}\")\n",
+        "print(f\"Best cross-validated score: {best_score}\")\n",
+        "\n",
+        "# Train\n",
+        "#rf_classifier = RandomForestClassifier(**best_params, random_state=42, class_weight='balanced') # With Hypertuning'''\n",
+        "\n",
+        "# Without Hypertuning\n",
+        "rf_classifier = RandomForestClassifier(random_state=42)\n",
+        "rf_classifier.fit(X_train, y_train)\n",
+        "\n",
+        "# Extract Feature Importance\n",
+        "feature_importances = rf_classifier.feature_importances_\n",
+        "\n",
+        "features_df = pd.DataFrame({\n",
+        "    'Feature': X.columns,\n",
+        "    'Importance': feature_importances\n",
+        "}).sort_values(by='Importance', ascending=False)\n",
+        "print(features_df)\n",
+        "\n",
+        "\n",
+        "# Make predictions on the test set\n",
+        "y_pred = rf_classifier.predict(X_test)\n",
+        "\n",
+        "# Evaluation\n",
+        "print(\"Classification report for RandomForestClassifier\")\n",
+        "print(classification_report(y_test, y_pred))\n",
+        "macro_f1 = f1_score(y_test, y_pred, average='macro')\n",
+        "print(f\"Macro-average F1 Score: {macro_f1}\")\n",
+        "kappa = cohen_kappa_score(y_test, y_pred)\n",
+        "print(f\"Cohen's Kappa: {kappa}\")"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "YGWlxbazIfMU"
+      },
+      "outputs": [],
+      "source": [
+        "# KNN\n",
+        "\n",
+        "# Hyperparameter for KNN\n",
+        "param_knn = {\n",
+        "    'n_neighbors': [3, 5, 7, 10, 15],\n",
+        "    'weights': ['uniform', 'distance'],\n",
+        "    'metric' : ['minkowski','euclidean','manhattan']\n",
+        "}\n",
+        "\n",
+        "# RandomizedSearchCV for KNN\n",
+        "knn_search = RandomizedSearchCV(\n",
+        "    estimator=KNeighborsClassifier(),\n",
+        "    param_distributions=param_knn,\n",
+        "    n_iter=5,\n",
+        "    cv=5,\n",
+        "    verbose=2,\n",
+        "    random_state=42,\n",
+        "    n_jobs=-1\n",
+        ")\n",
+        "\n",
+        "# Fit to the training data\n",
+        "knn_search.fit(X_train, y_train)\n",
+        "\n",
+        "# Retrieve the best parameters and score for KNN\n",
+        "best_params_knn = knn_search.best_params_\n",
+        "best_score_knn = knn_search.best_score_\n",
+        "\n",
+        "# Output the results for KNN\n",
+        "print(f\"Best parameters for KNN: {best_params_knn}\")\n",
+        "print(f\"Best cross-validated score for KNN: {best_score_knn}\")\n",
+        "\n",
+        "# Train KNN with the best parameters\n",
+        "knn_classifier = KNeighborsClassifier(**best_params_knn)\n",
+        "\n",
+        "# Without Hyperparameter Tuning\n",
+        "#knn_classifier = KNeighborsClassifier()\n",
+        "knn_classifier.fit(X_train, y_train)\n",
+        "\n",
+        "# Extrahieren der Feature Importance\n",
+        "feature_importances = rf_classifier.feature_importances_\n",
+        "features_df = pd.DataFrame({\n",
+        "    'Feature': X.columns,\n",
+        "    'Importance': feature_importances\n",
+        "}).sort_values(by='Importance', ascending=False)\n",
+        "print(features_df)\n",
+        "\n",
+        "\n",
+        "# Predict and evaluate KNN\n",
+        "y_pred_knn = knn_classifier.predict(X_test)\n",
+        "print(\"Classification report for KNeighborsClassifier:\")\n",
+        "print(classification_report(y_test, y_pred_knn))\n",
+        "\n",
+        "macro_f1 = f1_score(y_test, y_pred, average='macro')\n",
+        "print(f\"Macro-average F1 Score: {macro_f1}\")\n",
+        "\n",
+        "kappa_knn = cohen_kappa_score(y_test, y_pred_knn)\n",
+        "print(f\"Cohen's Kappa for KNN: {kappa_knn}\")"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "gTAMUQw8sWIl"
+      },
+      "source": [
+        "# Deep Learning"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "vgxUAedeRIkl"
+      },
+      "outputs": [],
+      "source": [
+        "# CNN\n",
+        "\n",
+        "print(\"Unique labels in training set:\", np.unique(y_train))\n",
+        "print(\"Unique labels in test set:\", np.unique(y_test))\n",
+        "\n",
+        "\n",
+        "def build_model(hp):\n",
+        "    model = Sequential()\n",
+        "    hp_filters = hp.Int('filters', min_value=32, max_value=128, step=32)\n",
+        "    hp_choice = hp.Choice('kernel_size', values=[3, 5])\n",
+        "    model.add(Conv1D(filters=hp_filters,\n",
+        "                     kernel_size=hp_choice,\n",
+        "                     activation='relu',\n",
+        "                     input_shape=(amount_trained_features, 1)))\n",
+        "    model.add(MaxPooling1D(2))\n",
+        "    model.add(Flatten())\n",
+        "    hp_unit_filter = hp.Int('units', min_value=64, max_value=128, step=32)\n",
+        "    model.add(Dense(units=hp_unit_filter, activation='relu'))\n",
+        "    model.add(Dense(len(np.unique(y)), activation='softmax'))\n",
+        "\n",
+        "    hp_learning_rate = hp.Choice('learning_rate', values = [1e-2, 1e-3])\n",
+        "    opt = Adam(learning_rate=hp_learning_rate)\n",
+        "\n",
+        "    model.compile(optimizer=opt,loss='sparse_categorical_crossentropy',\n",
+        "                  metrics=['accuracy'])\n",
+        "    return model\n",
+        "\n",
+        "tuner = RandomSearch(\n",
+        "    hypermodel=build_model,\n",
+        "    objective='val_accuracy',\n",
+        "    max_trials=4,\n",
+        "    executions_per_trial=1\n",
+        ")\n",
+        "\n",
+        "tuner.search_space_summary()\n",
+        "\n",
+        "# Start the search and get the best model\n",
+        "tuner.search(X_train, y_train, epochs=10, validation_split=0.2, batch_size=62)\n",
+        "best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]\n",
+        "\n",
+        "# Build the model with the best hyperparameters\n",
+        "model_cnn = build_model(best_hps)\n",
+        "model_cnn.fit(X_train, y_train, epochs=10, batch_size=62)\n",
+        "\n",
+        "# Make predictions\n",
+        "y_pred_probs = model_cnn.predict(X_test)\n",
+        "y_pred_classes = np.argmax(y_pred_probs, axis=1)\n",
+        "\n",
+        "test_loss, test_acc = model_cnn.evaluate(X_test, y_test, verbose=2)\n",
+        "\n",
+        "# Evaluation\n",
+        "kappa = cohen_kappa_score(y_test, y_pred_classes)\n",
+        "print(f\"Cohen's Kappa: {kappa}\")\n",
+        "\n",
+        "y_pred_labels = np.argmax(y_pred_probs, axis=1)\n",
+        "recall = recall_score(y_test, y_pred_labels, average='weighted')\n",
+        "print('Recall: %f' % recall)\n",
+        "\n",
+        "f1 = f1_score(y_test, y_pred_labels, average='weighted')\n",
+        "print('F1 score: %f' % f1)\n",
+        "\n",
+        "target_names = [str(name) for name in stage_encoder.inverse_transform([i for i in range(len(stage_encoder.classes_))])]\n",
+        "\n",
+        "print(classification_report(y_test, y_pred_classes, target_names=target_names))\n",
+        "\n",
+        "macro_f1 = f1_score(y_test, y_pred_classes, average='macro')\n",
+        "print(f\"Macro-average F1 Score: {macro_f1}\")"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "v6LAuRFgW5Kh",
+        "colab": {
+          "base_uri": "https://localhost:8080/"
+        },
+        "outputId": "f3f1f4d8-6b61-4270-9056-f810d97ae37c"
+      },
+      "outputs": [
+        {
+          "output_type": "stream",
+          "name": "stdout",
+          "text": [
+            "Reloading Tuner from ./untitled_project/tuner0.json\n",
+            "Best Hyperparameters <keras_tuner.src.engine.hyperparameters.hyperparameters.HyperParameters object at 0x7bcfdcb078b0>\n",
+            "Epoch 1/20\n",
+            "24943/24943 [==============================] - 178s 7ms/step - loss: 1.1905 - accuracy: 0.4265 - val_loss: 1.1850 - val_accuracy: 0.4321\n",
+            "Epoch 2/20\n",
+            "24943/24943 [==============================] - 167s 7ms/step - loss: 1.1816 - accuracy: 0.4326 - val_loss: 1.1785 - val_accuracy: 0.4352\n",
+            "Epoch 3/20\n",
+            "24943/24943 [==============================] - 179s 7ms/step - loss: 1.1789 - accuracy: 0.4341 - val_loss: 1.1769 - val_accuracy: 0.4356\n",
+            "Epoch 4/20\n",
+            "24943/24943 [==============================] - 173s 7ms/step - loss: 1.1771 - accuracy: 0.4354 - val_loss: 1.1745 - val_accuracy: 0.4384\n",
+            "Epoch 5/20\n",
+            "24943/24943 [==============================] - 178s 7ms/step - loss: 1.1761 - accuracy: 0.4360 - val_loss: 1.1758 - val_accuracy: 0.4359\n",
+            "Epoch 6/20\n",
+            "24943/24943 [==============================] - 174s 7ms/step - loss: 1.1752 - accuracy: 0.4364 - val_loss: 1.1764 - val_accuracy: 0.4375\n",
+            "Epoch 7/20\n",
+            "24943/24943 [==============================] - 168s 7ms/step - loss: 1.1746 - accuracy: 0.4370 - val_loss: 1.1735 - val_accuracy: 0.4356\n",
+            "Epoch 8/20\n",
+            "24943/24943 [==============================] - 166s 7ms/step - loss: 1.1742 - accuracy: 0.4374 - val_loss: 1.1729 - val_accuracy: 0.4382\n",
+            "Epoch 9/20\n",
+            "24943/24943 [==============================] - 173s 7ms/step - loss: 1.1736 - accuracy: 0.4377 - val_loss: 1.1766 - val_accuracy: 0.4369\n",
+            "Epoch 10/20\n",
+            "24943/24943 [==============================] - 180s 7ms/step - loss: 1.1732 - accuracy: 0.4381 - val_loss: 1.1713 - val_accuracy: 0.4394\n",
+            "Epoch 11/20\n",
+            "24943/24943 [==============================] - 202s 8ms/step - loss: 1.1732 - accuracy: 0.4383 - val_loss: 1.1710 - val_accuracy: 0.4398\n",
+            "Epoch 12/20\n",
+            "24943/24943 [==============================] - 167s 7ms/step - loss: 1.1729 - accuracy: 0.4384 - val_loss: 1.1740 - val_accuracy: 0.4380\n",
+            "Epoch 13/20\n",
+            "24943/24943 [==============================] - 168s 7ms/step - loss: 1.1729 - accuracy: 0.4386 - val_loss: 1.1716 - val_accuracy: 0.4379\n",
+            "Epoch 14/20\n",
+            "24943/24943 [==============================] - 170s 7ms/step - loss: 1.1726 - accuracy: 0.4384 - val_loss: 1.1749 - val_accuracy: 0.4378\n",
+            "Epoch 15/20\n",
+            "24943/24943 [==============================] - 167s 7ms/step - loss: 1.1727 - accuracy: 0.4390 - val_loss: 1.1792 - val_accuracy: 0.4383\n",
+            "Epoch 16/20\n",
+            "24943/24943 [==============================] - 167s 7ms/step - loss: 1.1736 - accuracy: 0.4387 - val_loss: 1.1728 - val_accuracy: 0.4387\n",
+            "Epoch 17/20\n",
+            "24943/24943 [==============================] - 171s 7ms/step - loss: 1.1723 - accuracy: 0.4391 - val_loss: 1.1709 - val_accuracy: 0.4399\n",
+            "Epoch 18/20\n",
+            "24943/24943 [==============================] - 172s 7ms/step - loss: 1.1723 - accuracy: 0.4393 - val_loss: 1.1723 - val_accuracy: 0.4401\n",
+            "Epoch 19/20\n",
+            "24943/24943 [==============================] - 172s 7ms/step - loss: 1.1985 - accuracy: 0.4373 - val_loss: 1.1708 - val_accuracy: 0.4393\n",
+            "Epoch 20/20\n",
+            "24943/24943 [==============================] - 168s 7ms/step - loss: 1.1884 - accuracy: 0.4374 - val_loss: 1.1779 - val_accuracy: 0.4366\n",
+            "      Feature  Importance\n",
+            "1         std    0.249967\n",
+            "4    kurtosis    0.208756\n",
+            "2        mean    0.189652\n",
+            "5    skewness    0.175992\n",
+            "3    variance    0.169360\n",
+            "0  signalName    0.006272\n",
+            "15589/15589 [==============================] - 37s 2ms/step\n",
+            "15589/15589 - 32s - loss: 1.1777 - accuracy: 0.4358 - 32s/epoch - 2ms/step\n",
+            "Cohen's Kappa: 0.2478535374116223\n",
+            "Recall: 0.435759\n",
+            "F1 score: 0.412573\n",
+            "              precision    recall  f1-score   support\n",
+            "\n",
+            "       awake       0.78      0.50      0.61    124789\n",
+            "  deep_sleep       0.39      0.64      0.49    124715\n",
+            "  lite_sleep       0.34      0.09      0.14    124949\n",
+            "   rem_sleep       0.35      0.51      0.42    124391\n",
+            "\n",
+            "    accuracy                           0.44    498844\n",
+            "   macro avg       0.46      0.44      0.41    498844\n",
+            "weighted avg       0.46      0.44      0.41    498844\n",
+            "\n",
+            "Macro-average F1 Score: 0.4126748745582194\n"
+          ]
+        },
+        {
+          "output_type": "execute_result",
+          "data": {
+            "text/plain": [
+              "\"\\nCohen's Kappa: 0.2796082316913435\\nRecall: 0.459339\\nF1 score: 0.447196\\n              precision    recall  f1-score   support\\n\\n       awake       0.86      0.54      0.66     52649\\n  deep_sleep       0.38      0.75      0.50     52242\\n  lite_sleep       0.36      0.17      0.23     52690\\n   rem_sleep       0.41      0.38      0.40     52791\\n\\n    accuracy                           0.46    210372\\n   macro avg       0.50      0.46      0.45    210372\\nweighted avg       0.50      0.46      0.45    210372\\n\\nUndersample\\nCohen's Kappa: 0.25736372829306753\\nRecall: 0.443274\\nF1 score: 0.441652\\n              precision    recall  f1-score   support\\n\\n       awake       0.85      0.49      0.62     27280\\n  deep_sleep       0.38      0.66      0.49     27386\\n  lite_sleep       0.33      0.22      0.26     27088\\n   rem_sleep       0.39      0.41      0.40     27384\\n\\n    accuracy                           0.44    109138\\n   macro avg       0.49      0.44      0.44    109138\\nweighted avg       0.49      0.44      0.44    109138\\n\\n\\nFeature  Importance\\n1         std    0.387214\\n2        mean    0.369977\\n3        maxA    0.228155\\n0  signalName    0.014655\\n3411/3411 [==============================] - 8s 2ms/step\\n3411/3411 - 8s - loss: 1.1881 - accuracy: 0.4272 - 8s/epoch - 2ms/step\\nCohen's Kappa: 0.2356531000090908\\nRecall: 0.427230\\nF1 score: 0.400917\\n              precision    recall  f1-score   support\\n\\n       awake       0.87      0.46      0.60     27280\\n  deep_sleep       0.37      0.70      0.49     27386\\n  lite_sleep       0.28      0.06      0.10     27088\\n   rem_sleep       0.36      0.48      0.41     27384\\n\\n    accuracy                           0.43    109138\\n   macro avg       0.47      0.43      0.40    109138\\nweighted avg       0.47      0.43      0.40    109138\\n\\nMacro-average F1 Score: 0.40030597312739447\\n\\n\\n\\n\\n  Feature  Importance\\n1         std    0.229538\\n0  signalName    0.179083\\n4    kurtosis    0.165762\\n3    variance    0.152368\\n2        mean    0.140518\\n5    skewness    0.132731\\n6822/6822 [==============================] - 23s 3ms/step\\n6822/6822 - 18s - loss: 1.2765 - accuracy: 0.3565 - 18s/epoch - 3ms/step\\nCohen's Kappa: 0.14185713040600867\\nRecall: 0.356494\\nF1 score: 0.350898\\n              precision    recall  f1-score   support\\n\\n       awake       0.43      0.52      0.47     54553\\n  deep_sleep       0.36      0.34      0.35     54210\\n  lite_sleep       0.28      0.35      0.31     54922\\n   rem_sleep       0.36      0.22      0.27     54591\\n\\n    accuracy                           0.36    218276\\n   macro avg       0.36      0.36      0.35    218276\\nweighted avg       0.36      0.36      0.35    218276\\n\\nMacro-average F1 Score: 0.3509835323216871\\n\\n\\nOHNE POS & ABDO\\nFeature  Importance\\n1         std    0.317718\\n2        mean    0.260348\\n3    variance    0.166735\\n4    kurtosis    0.133296\\n5    skewness    0.105235\\n0  signalName    0.016668\\n3411/3411 [==============================] - 12s 3ms/step\\n3411/3411 - 10s - loss: 1.1958 - accuracy: 0.4254 - 10s/epoch - 3ms/step\\nCohen's Kappa: 0.23302260719925538\\nRecall: 0.425370\\nF1 score: 0.377417\\n              precision    recall  f1-score   support\\n\\n       awake       0.84      0.47      0.60     27280\\n  deep_sleep       0.38      0.67      0.48     27386\\n  lite_sleep       0.31      0.00      0.00     27088\\n   rem_sleep       0.34      0.56      0.42     27384\\n\\n    accuracy                           0.43    109138\\n   macro avg       0.47      0.42      0.38    109138\\nweighted avg       0.47      0.43      0.38    109138\\n\\nMacro-average F1 Score: 0.3766119137787486\\n\""
+            ],
+            "application/vnd.google.colaboratory.intrinsic+json": {
+              "type": "string"
+            }
+          },
+          "metadata": {},
+          "execution_count": 8
+        }
+      ],
+      "source": [
+        "# LSTM\n",
+        "\n",
+        "def build_model(hp):\n",
+        "    model = Sequential([\n",
+        "        LSTM(hp.Int('units', min_value=32, max_value=128, step=32),\n",
+        "             activation='relu',\n",
+        "             input_shape=(amount_trained_features, 1)),\n",
+        "        Dense(len(np.unique(y)), activation='softmax')\n",
+        "    ])\n",
+        "\n",
+        "    model.compile(optimizer=Adam(hp.Float('learning_rate', min_value=1e-4, max_value=1e-2, sampling='LOG')),\n",
+        "                  loss='sparse_categorical_crossentropy',\n",
+        "                  metrics=['accuracy'])\n",
+        "    return model\n",
+        "\n",
+        "tuner = RandomSearch(\n",
+        "    hypermodel=build_model,\n",
+        "    objective='val_accuracy',\n",
+        "    max_trials=4,\n",
+        "    executions_per_trial=1\n",
+        ")\n",
+        "\n",
+        "tuner.search(X_train, y_train, epochs=10, validation_split=0.2, batch_size=62)\n",
+        "\n",
+        "# Get the best hyperparameters\n",
+        "best_hps = tuner.get_best_hyperparameters(num_trials=1)[0]\n",
+        "print(f\"Best Hyperparameters {best_hps}\")\n",
+        "\n",
+        "# Build the model with the best hyperparameters and train it on the data\n",
+        "model = tuner.hypermodel.build(best_hps)\n",
+        "history = model.fit(X_train, y_train, epochs=20, batch_size=64, validation_split=0.2)\n",
+        "\n",
+        "# Extrahieren der Feature Importance\n",
+        "feature_importances = rf_classifier.feature_importances_\n",
+        "features_df = pd.DataFrame({\n",
+        "    'Feature': X.columns,\n",
+        "    'Importance': feature_importances\n",
+        "}).sort_values(by='Importance', ascending=False)\n",
+        "print(features_df)\n",
+        "\n",
+        "\n",
+        "\n",
+        "y_pred_probs = model.predict(X_test)\n",
+        "y_pred_classes = np.argmax(y_pred_probs, axis=1)\n",
+        "\n",
+        "test_loss, test_acc = model.evaluate(X_test, y_test, verbose=2)\n",
+        "\n",
+        "# Evaluation\n",
+        "kappa = cohen_kappa_score(y_test, y_pred_classes)\n",
+        "print(f\"Cohen's Kappa: {kappa}\")\n",
+        "\n",
+        "y_pred_labels = np.argmax(y_pred_probs, axis=1)\n",
+        "recall = recall_score(y_test, y_pred_labels, average='weighted')\n",
+        "print('Recall: %f' % recall)\n",
+        "\n",
+        "f1 = f1_score(y_test, y_pred_labels, average='weighted')\n",
+        "print('F1 score: %f' % f1)\n",
+        "\n",
+        "target_names = [str(name) for name in stage_encoder.inverse_transform([i for i in range(len(stage_encoder.classes_))])]\n",
+        "\n",
+        "print(classification_report(y_test, y_pred_classes, target_names=target_names))\n",
+        "macro_f1 = f1_score(y_test, y_pred_classes, average='macro')\n",
+        "print(f\"Macro-average F1 Score: {macro_f1}\")"
+      ]
+    }
+  ],
+  "metadata": {
+    "colab": {
+      "collapsed_sections": [
+        "L300ySZBqbyH",
+        "D8J3VziBqm-p",
+        "VdTlPBDxrJYL",
+        "24EeYQ3DrNrt",
+        "DD5MaGB4rUKv"
+      ],
+      "provenance": []
+    },
+    "kernelspec": {
+      "display_name": "Python 3",
+      "name": "python3"
+    },
+    "language_info": {
+      "name": "python"
+    }
+  },
+  "nbformat": 4,
+  "nbformat_minor": 0
+}
\ No newline at end of file