{
  "cells": [
    {
      "cell_type": "raw",
      "metadata": {
        "vscode": {
          "languageId": "raw"
        }
      },
      "source": [
        "# PhysioNet VGRF - End-to-End Analysis\n",
        "\n",
        "This notebook walks through loading PhysioNet VGRF gait data, creating sliding windows, extracting features, preparing classification data, training a classifier, and visualizing results.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# Setup imports\n",
        "import os\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "import seaborn as sns\n",
        "\n",
        "from gaitsetpy.dataset import PhysioNetLoader\n",
        "from gaitsetpy.features import PhysioNetFeatureExtractor\n",
        "from gaitsetpy.classification.models import RandomForestModel\n",
        "\n",
        "DATA_DIR = os.path.join('data', 'physionet')\n",
        "print(f\"Data dir: {DATA_DIR}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 1) Load dataset\n",
        "loader = PhysioNetLoader()\n",
        "data, names = loader.load_data(DATA_DIR)\n",
        "labels = loader.get_labels()\n",
        "print(f\"Loaded {len(data)} files. Controls={labels.count('Co')} Patients={labels.count('Pt')}\")\n",
        "\n",
        "if data:\n",
        "    display(data[0].head())\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 2) Create sliding windows\n",
        "windows = loader.create_sliding_windows(data, names, window_size=600, step_size=100)\n",
        "print(f\"Window sets created: {len(windows)}\")\n",
        "if windows:\n",
        "    total_windows = sum(w['metadata']['num_windows'] for w in windows if 'metadata' in w)\n",
        "    print(f\"Total windows: {total_windows}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 3) Extract features\n",
        "extractor = PhysioNetFeatureExtractor(verbose=True)\n",
        "all_features = []\n",
        "for window_dict in windows:\n",
        "    if 'windows' in window_dict:\n",
        "        feats = extractor.extract_features(window_dict['windows'], fs=loader.metadata['sampling_frequency'])\n",
        "        all_features.append({\n",
        "            'name': window_dict['name'],\n",
        "            'features': feats,\n",
        "            'metadata': window_dict.get('metadata', {})\n",
        "        })\n",
        "print(f\"Extracted from {len(all_features)} files\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 4) Prepare classification data\n",
        "X = []\n",
        "y = []\n",
        "for file_features in all_features:\n",
        "    file_name = file_features['name']\n",
        "    feats = file_features['features']\n",
        "    metadata = file_features.get('metadata', {})\n",
        "    label = metadata.get('label', 'Co' if 'Co' in file_name else 'Pt')\n",
        "    for sensor_features in feats:\n",
        "        sensor_data = sensor_features['features']\n",
        "        vec = []\n",
        "        for feature_name, feature_values in sensor_data.items():\n",
        "            if isinstance(feature_values, list):\n",
        "                if len(feature_values) > 0:\n",
        "                    import numpy as _np\n",
        "                    if isinstance(feature_values[0], (list, _np.ndarray)):\n",
        "                        flat = []\n",
        "                        for val in feature_values:\n",
        "                            if isinstance(val, (list, _np.ndarray)):\n",
        "                                flat.extend(val)\n",
        "                            else:\n",
        "                                flat.append(val)\n",
        "                        vec.append(_np.mean(flat))\n",
        "                    else:\n",
        "                        vec.append(_np.mean(feature_values))\n",
        "                else:\n",
        "                    vec.append(0)\n",
        "            else:\n",
        "                vec.append(feature_values)\n",
        "        if vec:\n",
        "            X.append(vec)\n",
        "            y.append(label)\n",
        "import numpy as _np\n",
        "X = _np.array(X)\n",
        "y = _np.array(y)\n",
        "print(f\"X shape: {X.shape}, y: {dict(zip(*_np.unique(y, return_counts=True)))}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 5) Train RandomForest and evaluate\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "from sklearn.metrics import classification_report, confusion_matrix\n",
        "\n",
        "if len(X) > 0:\n",
        "    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42)\n",
        "    scaler = StandardScaler()\n",
        "    X_train_s = scaler.fit_transform(X_train)\n",
        "    X_test_s = scaler.transform(X_test)\n",
        "\n",
        "    rf = RandomForestModel()\n",
        "    feature_dicts_train = [{'name': 'sample', 'features': {f'f{i}': X_train_s[:, i].tolist() for i in range(X_train_s.shape[1])}, 'annotations': y_train.tolist()}]\n",
        "    feature_dicts_test = [{'name': 'sample', 'features': {f'f{i}': X_test_s[:, i].tolist() for i in range(X_test_s.shape[1])}, 'annotations': y_test.tolist()}]\n",
        "\n",
        "    rf.train(feature_dicts_train, test_size=0.0, validation_split=False)\n",
        "    metrics = rf.evaluate(feature_dicts_test, detailed_report=True)\n",
        "    print(f\"Accuracy: {metrics['accuracy']:.3f}\")\n",
        "\n",
        "    cm = confusion_matrix(y_test, rf.model.predict(X_test_s))\n",
        "    import seaborn as sns\n",
        "    import matplotlib.pyplot as plt\n",
        "    plt.figure(figsize=(6,5))\n",
        "    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Co','Pt'], yticklabels=['Co','Pt'])\n",
        "    plt.title('Confusion Matrix - PhysioNet')\n",
        "    plt.ylabel('True')\n",
        "    plt.xlabel('Pred')\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "    print(\"\\nClassification report:\\n\", classification_report(y_test, rf.model.predict(X_test_s), target_names=['Co','Pt']))\n",
        "else:\n",
        "    print(\"No samples to train.\")\n"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
