{
  "cells": [
    {
      "cell_type": "raw",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "# Daphnet Freezing of Gait (FoG) - End-to-End Demo\n",
        "\n",
        "This notebook demonstrates how to use `gaitsetpy` with the Daphnet FoG dataset.\n",
        "\n",
        "What you'll learn:\n",
        "- Load Daphnet dataset using `DaphnetLoader`\n",
        "- Create sliding windows\n",
        "- Extract gait features (time/frequency/statistical)\n",
        "- Train and evaluate multiple classifiers (RF, MLP, LSTM, BiLSTM, CNN)\n",
        "\n",
        "Prerequisites:\n",
        "- Internet access to download dataset (first run)\n",
        "- Optional: PyTorch installed to run LSTM/BiLSTM/CNN\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Data directory: c:\\Users\\ahars\\Desktop\\Funstuff\\coding\\alohomora_labs\\gaitSetPy\\examples\\notebooks\\data\n"
          ]
        }
      ],
      "source": [
        "# Setup: imports and paths\n",
        "import os\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "\n",
        "from gaitsetpy.dataset.daphnet import DaphnetLoader\n",
        "from gaitsetpy.features.gait_features import extract_gait_features\n",
        "from gaitsetpy.classification.models import get_classification_model\n",
        "\n",
        "DATA_DIR = os.path.join(os.path.dirname(\"..\"), \"data\")\n",
        "print(f\"Data directory: {os.path.abspath(DATA_DIR)}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Downloading Daphnet dataset to: data\\daphnet.zip\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "Download Progress: 21.4MiB [00:08, 2.60MiB/s]\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Download completed successfully! File saved to: data\\daphnet.zip\n",
            "Loaded 17 subjects. Example file: S01R01.txt\n"
          ]
        },
        {
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>shank</th>\n",
              "      <th>shank_h_fd</th>\n",
              "      <th>shank_v</th>\n",
              "      <th>shank_h_l</th>\n",
              "      <th>thigh</th>\n",
              "      <th>thigh_h_fd</th>\n",
              "      <th>thigh_v</th>\n",
              "      <th>thigh_h_l</th>\n",
              "      <th>trunk</th>\n",
              "      <th>trunk_h_fd</th>\n",
              "      <th>trunk_v</th>\n",
              "      <th>trunk_h_l</th>\n",
              "      <th>annotations</th>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>time</th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "      <th></th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>15</th>\n",
              "      <td>973.304166</td>\n",
              "      <td>70</td>\n",
              "      <td>39</td>\n",
              "      <td>-970</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>31</th>\n",
              "      <td>973.304166</td>\n",
              "      <td>70</td>\n",
              "      <td>39</td>\n",
              "      <td>-970</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>46</th>\n",
              "      <td>963.120449</td>\n",
              "      <td>60</td>\n",
              "      <td>49</td>\n",
              "      <td>-960</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>62</th>\n",
              "      <td>963.120449</td>\n",
              "      <td>60</td>\n",
              "      <td>49</td>\n",
              "      <td>-960</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>78</th>\n",
              "      <td>962.091991</td>\n",
              "      <td>50</td>\n",
              "      <td>39</td>\n",
              "      <td>-960</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0.0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "           shank  shank_h_fd  shank_v  shank_h_l  thigh  thigh_h_fd  thigh_v  \\\n",
              "time                                                                           \n",
              "15    973.304166          70       39       -970    0.0           0        0   \n",
              "31    973.304166          70       39       -970    0.0           0        0   \n",
              "46    963.120449          60       49       -960    0.0           0        0   \n",
              "62    963.120449          60       49       -960    0.0           0        0   \n",
              "78    962.091991          50       39       -960    0.0           0        0   \n",
              "\n",
              "      thigh_h_l  trunk  trunk_h_fd  trunk_v  trunk_h_l  annotations  \n",
              "time                                                                 \n",
              "15            0    0.0           0        0          0            0  \n",
              "31            0    0.0           0        0          0            0  \n",
              "46            0    0.0           0        0          0            0  \n",
              "62            0    0.0           0        0          0            0  \n",
              "78            0    0.0           0        0          0            0  "
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# 1) Load Daphnet dataset\n",
        "loader = DaphnetLoader()\n",
        "data, names = loader.load_data(DATA_DIR)\n",
        "print(f\"Loaded {len(data)} subjects. Example file: {names[0] if names else 'N/A'}\")\n",
        "\n",
        "# Quick peek\n",
        "if data:\n",
        "    display(data[0].head())\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created sliding windows for 17 subjects.\n",
            "Prepared 221 sensor windows.\n"
          ]
        }
      ],
      "source": [
        "# 2) Create sliding windows\n",
        "windows = loader.create_sliding_windows(data, names, window_size=192, step_size=32)\n",
        "print(f\"Created sliding windows for {len(windows)} subjects.\")\n",
        "\n",
        "# Flatten windows into a list of sensor windows for feature extraction\n",
        "sensor_windows = []\n",
        "for subject in windows:\n",
        "    for sensor in subject['windows']:\n",
        "        sensor_windows.append(sensor)\n",
        "\n",
        "print(f\"Prepared {len(sensor_windows)} sensor windows.\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "🚀 GaitFeatureExtractor initialized successfully!\n",
            "📊 Default configuration: {'time_domain': True, 'frequency_domain': True, 'statistical': True, 'ar_order': 3}\n",
            "\n",
            "============================================================\n",
            "🔍 STARTING GAIT FEATURE EXTRACTION\n",
            "============================================================\n",
            "📈 Total sensors/windows to process: 221\n",
            "🔊 Sampling frequency: 64 Hz\n",
            "⏱️  Time domain features: ✅\n",
            "🌊 Frequency domain features: ✅\n",
            "📊 Statistical features: ✅\n",
            "🔄 Auto-regression order: 3\n",
            "------------------------------------------------------------\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "🔍 Processing Sensors:   0%|          | 0/221 [00:00<?, ?sensor/s, Current=shank, Windows=2895]INFO:gaitsetpy.features.gait_features:🎯 Processing sensor: shank\n",
            "INFO:gaitsetpy.features.gait_features:📦 Number of windows: 2895\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ⏱️  Extracting time domain features for shank...\n",
            "    🔍 Computing time domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Time domain: 12 feature types, 34740 total features\n",
            "  🌊 Extracting frequency domain features for shank...\n",
            "    🔍 Computing frequency domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Frequency domain: 8 feature types, 23160 total features\n",
            "  📊 Extracting statistical features for shank...\n",
            "    🔍 Computing statistical features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "      🔗 Computing correlation features...\n",
            "  ✅ Statistical: 5 feature types, 14475 total features\n",
            "  🔄 Extracting auto-regression coefficients for shank...\n",
            "    🔍 Computing auto-regression coefficients (order=3)...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "🔍 Processing Sensors:   0%|          | 1/221 [00:16<1:00:25, 16.48s/sensor, Current=shank_h_fd, Windows=2895]INFO:gaitsetpy.features.gait_features:🎯 Processing sensor: shank_h_fd\n",
            "INFO:gaitsetpy.features.gait_features:📦 Number of windows: 2895\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Auto-regression: 1 feature types, 2895 total features\n",
            "  🎯 Total features extracted for shank: 75270\n",
            "  📋 Feature types: ['mean', 'std', 'variance', 'rms', 'range', 'median', 'mode', 'mean_absolute_value', 'median_absolute_deviation', 'peak_height', 'zero_crossing_rate', 'energy', 'dominant_frequency', 'peak_frequency', 'power_spectral_entropy', 'principal_harmonic_frequency', 'stride_times', 'step_time', 'cadence', 'freezing_index', 'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation', 'ar_coefficients']\n",
            "----------------------------------------\n",
            "  ⏱️  Extracting time domain features for shank_h_fd...\n",
            "    🔍 Computing time domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Time domain: 12 feature types, 34740 total features\n",
            "  🌊 Extracting frequency domain features for shank_h_fd...\n",
            "    🔍 Computing frequency domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Frequency domain: 8 feature types, 23160 total features\n",
            "  📊 Extracting statistical features for shank_h_fd...\n",
            "    🔍 Computing statistical features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "      🔗 Computing correlation features...\n",
            "  ✅ Statistical: 5 feature types, 14475 total features\n",
            "  🔄 Extracting auto-regression coefficients for shank_h_fd...\n",
            "    🔍 Computing auto-regression coefficients (order=3)...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "🔍 Processing Sensors:   1%|          | 2/221 [00:30<54:33, 14.95s/sensor, Current=shank_v, Windows=2895]     INFO:gaitsetpy.features.gait_features:🎯 Processing sensor: shank_v\n",
            "INFO:gaitsetpy.features.gait_features:📦 Number of windows: 2895\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Auto-regression: 1 feature types, 2895 total features\n",
            "  🎯 Total features extracted for shank_h_fd: 75270\n",
            "  📋 Feature types: ['mean', 'std', 'variance', 'rms', 'range', 'median', 'mode', 'mean_absolute_value', 'median_absolute_deviation', 'peak_height', 'zero_crossing_rate', 'energy', 'dominant_frequency', 'peak_frequency', 'power_spectral_entropy', 'principal_harmonic_frequency', 'stride_times', 'step_time', 'cadence', 'freezing_index', 'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation', 'ar_coefficients']\n",
            "----------------------------------------\n",
            "  ⏱️  Extracting time domain features for shank_v...\n",
            "    🔍 Computing time domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Time domain: 12 feature types, 34740 total features\n",
            "  🌊 Extracting frequency domain features for shank_v...\n",
            "    🔍 Computing frequency domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Frequency domain: 8 feature types, 23160 total features\n",
            "  📊 Extracting statistical features for shank_v...\n",
            "    🔍 Computing statistical features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "      🔗 Computing correlation features...\n",
            "  ✅ Statistical: 5 feature types, 14475 total features\n",
            "  🔄 Extracting auto-regression coefficients for shank_v...\n",
            "    🔍 Computing auto-regression coefficients (order=3)...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "🔍 Processing Sensors:   1%|▏         | 3/221 [00:43<52:06, 14.34s/sensor, Current=shank_h_l, Windows=2895]INFO:gaitsetpy.features.gait_features:🎯 Processing sensor: shank_h_l\n",
            "INFO:gaitsetpy.features.gait_features:📦 Number of windows: 2895\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Auto-regression: 1 feature types, 2895 total features\n",
            "  🎯 Total features extracted for shank_v: 75270\n",
            "  📋 Feature types: ['mean', 'std', 'variance', 'rms', 'range', 'median', 'mode', 'mean_absolute_value', 'median_absolute_deviation', 'peak_height', 'zero_crossing_rate', 'energy', 'dominant_frequency', 'peak_frequency', 'power_spectral_entropy', 'principal_harmonic_frequency', 'stride_times', 'step_time', 'cadence', 'freezing_index', 'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation', 'ar_coefficients']\n",
            "----------------------------------------\n",
            "  ⏱️  Extracting time domain features for shank_h_l...\n",
            "    🔍 Computing time domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Time domain: 12 feature types, 34740 total features\n",
            "  🌊 Extracting frequency domain features for shank_h_l...\n",
            "    🔍 Computing frequency domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Frequency domain: 8 feature types, 23160 total features\n",
            "  📊 Extracting statistical features for shank_h_l...\n",
            "    🔍 Computing statistical features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "      🔗 Computing correlation features...\n",
            "  ✅ Statistical: 5 feature types, 14475 total features\n",
            "  🔄 Extracting auto-regression coefficients for shank_h_l...\n",
            "    🔍 Computing auto-regression coefficients (order=3)...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "🔍 Processing Sensors:   2%|▏         | 4/221 [00:58<52:21, 14.48s/sensor, Current=thigh, Windows=2895]    INFO:gaitsetpy.features.gait_features:🎯 Processing sensor: thigh\n",
            "INFO:gaitsetpy.features.gait_features:📦 Number of windows: 2895\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Auto-regression: 1 feature types, 2895 total features\n",
            "  🎯 Total features extracted for shank_h_l: 75270\n",
            "  📋 Feature types: ['mean', 'std', 'variance', 'rms', 'range', 'median', 'mode', 'mean_absolute_value', 'median_absolute_deviation', 'peak_height', 'zero_crossing_rate', 'energy', 'dominant_frequency', 'peak_frequency', 'power_spectral_entropy', 'principal_harmonic_frequency', 'stride_times', 'step_time', 'cadence', 'freezing_index', 'skewness', 'kurtosis', 'entropy', 'interquartile_range', 'correlation', 'ar_coefficients']\n",
            "----------------------------------------\n",
            "  ⏱️  Extracting time domain features for thigh...\n",
            "    🔍 Computing time domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Time domain: 12 feature types, 34740 total features\n",
            "  🌊 Extracting frequency domain features for thigh...\n",
            "    🔍 Computing frequency domain features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  ✅ Frequency domain: 8 feature types, 23160 total features\n",
            "  📊 Extracting statistical features for thigh...\n",
            "    🔍 Computing statistical features...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "      🔗 Computing correlation features...\n",
            "  ✅ Statistical: 5 feature types, 14475 total features\n",
            "  🔄 Extracting auto-regression coefficients for thigh...\n",
            "    🔍 Computing auto-regression coefficients (order=3)...\n"
          ]
        },
        {
          "name": "stderr",
          "output_type": "stream",
          "text": []
        }
      ],
      "source": [
        "# 3) Extract gait features\n",
        "fs = loader.metadata.get('sampling_frequency', 64)\n",
        "features = extract_gait_features(sensor_windows, fs, time_domain=True, frequency_domain=True, statistical=True, verbose=True)\n",
        "print(f\"Extracted features for {len(features)} sensors.\")\n",
        "\n",
        "# Prepare feature_dicts grouped by sensor, keep lists per-window\n",
        "feature_dicts = []\n",
        "annotation_sensor = None\n",
        "for sensor in features:\n",
        "    if sensor['name'] == 'annotations':\n",
        "        annotation_sensor = sensor\n",
        "        break\n",
        "\n",
        "for sensor in features:\n",
        "    if sensor['name'] != 'annotations':\n",
        "        n_windows = len(next(iter(sensor['features'].values())))\n",
        "        feature_dicts.append({\n",
        "            'name': sensor['name'],\n",
        "            'features': sensor['features'],\n",
        "            'annotations': annotation_sensor['annotations'][:n_windows] if annotation_sensor else [0]*n_windows\n",
        "        })\n",
        "\n",
        "print(f\"Prepared {len(feature_dicts)} feature dictionaries for classification.\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 4) Train and evaluate multiple models\n",
        "model_names = ['random_forest', 'mlp', 'lstm', 'bilstm', 'cnn']\n",
        "\n",
        "for model_name in model_names:\n",
        "    print(f\"\\n=== Training model: {model_name.upper()} ===\")\n",
        "    try:\n",
        "        model = get_classification_model(model_name)\n",
        "    except Exception as e:\n",
        "        print(f\"Skipping {model_name}: {e}\")\n",
        "        continue\n",
        "    try:\n",
        "        model.train(feature_dicts, test_size=0.2, validation_split=True)\n",
        "        results = model.evaluate(feature_dicts, detailed_report=True)\n",
        "        print(f\"Results for {model_name}:\\n\", {k: (v if k!='classification_report' else 'see detailed report') for k, v in results.items()})\n",
        "    except Exception as e:\n",
        "        print(f\"Error training/evaluating {model_name}: {e}\")\n"
      ]
    },
    {
      "cell_type": "raw",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "## Notes\n",
        "- If PyTorch is not installed, LSTM, BiLSTM, and CNN will be skipped.\n",
        "- Feature preparation relies on `preprocess_features` to stack per-window lists.\n",
        "- You can adjust `window_size`, `step_size`, and feature extractor flags for experiments.\n"
      ]
    },
    {
      "cell_type": "raw",
      "metadata": {},
      "source": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.13.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
