{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":242561727,"sourceType":"kernelVersion"},{"sourceId":248455010,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CMI - Detect behavior with sensor data","metadata":{}},{"cell_type":"markdown","source":"## 1. Import","metadata":{}},{"cell_type":"markdown","source":"### 1.1 Import basic libraries","metadata":{}},{"cell_type":"code","source":"import os\n\nimport pandas as pd\npd.options.display.max_columns = 100\n\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport polars as pl\n\nimport warnings\nwarnings.simplefilter(action='ignore', category=FutureWarning)\nwarnings.filterwarnings(\"ignore\", category=RuntimeWarning)\n\nimport sys\nsys.path.append('../Data/cmi-detect-behavior-with-sensor-data')\n\nimport kaggle_evaluation.cmi_inference_server\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:25:55.973013Z","iopub.execute_input":"2025-07-02T13:25:55.973310Z","iopub.status.idle":"2025-07-02T13:26:01.063010Z","shell.execute_reply.started":"2025-07-02T13:25:55.973280Z","shell.execute_reply":"2025-07-02T13:26:01.061998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install /kaggle/input/pip-install-dependencies-cmi/scikit_base-0.12.3-py3-none-any.whl\n!pip install /kaggle/input/pip-install-dependencies-cmi/scikit_learn-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install /kaggle/input/pip-install-dependencies-cmi/sktime-0.38.1-py3-none-any.whl","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:26:01.065078Z","iopub.execute_input":"2025-07-02T13:26:01.065992Z","iopub.status.idle":"2025-07-02T13:26:21.839737Z","shell.execute_reply.started":"2025-07-02T13:26:01.065960Z","shell.execute_reply":"2025-07-02T13:26:21.838503Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 1.2 Import data","metadata":{}},{"cell_type":"code","source":"# Training data\ndf_train = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ndf_train_demo = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n\n# Save copy to use for model training only\ndf_train_copy = df_train.copy()\ndf_train_demo_copy = df_train_demo.copy()\n\n# Testing data\ndf_test = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\")\ndf_test_demo = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:26:21.841255Z","iopub.execute_input":"2025-07-02T13:26:21.841572Z","iopub.status.idle":"2025-07-02T13:27:00.228586Z","shell.execute_reply.started":"2025-07-02T13:26:21.841539Z","shell.execute_reply":"2025-07-02T13:27:00.226892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.230113Z","iopub.execute_input":"2025-07-02T13:27:00.230529Z","iopub.status.idle":"2025-07-02T13:27:00.335893Z","shell.execute_reply.started":"2025-07-02T13:27:00.230490Z","shell.execute_reply":"2025-07-02T13:27:00.334788Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. EDA","metadata":{}},{"cell_type":"code","source":"cmap = sns.color_palette(\"inferno\", 4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.336937Z","iopub.execute_input":"2025-07-02T13:27:00.337257Z","iopub.status.idle":"2025-07-02T13:27:00.344123Z","shell.execute_reply.started":"2025-07-02T13:27:00.337217Z","shell.execute_reply":"2025-07-02T13:27:00.342815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prepare a reference table \n\ndf_ref = df_train[['sequence_id', 'subject', 'gesture', 'sequence_type']].drop_duplicates()\ndf_ref.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.345127Z","iopub.execute_input":"2025-07-02T13:27:00.345405Z","iopub.status.idle":"2025-07-02T13:27:00.541729Z","shell.execute_reply.started":"2025-07-02T13:27:00.345385Z","shell.execute_reply":"2025-07-02T13:27:00.540912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Extract unique gestures\nunique_gestures = df_ref.gesture.unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.544406Z","iopub.execute_input":"2025-07-02T13:27:00.544717Z","iopub.status.idle":"2025-07-02T13:27:00.550755Z","shell.execute_reply.started":"2025-07-02T13:27:00.544690Z","shell.execute_reply":"2025-07-02T13:27:00.549609Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1 Acceleration","metadata":{}},{"cell_type":"code","source":"import random\n\ndef subplot_timeseries(axes, x_data, y_data, ax, x_1, x_2, x_3, x_4, title=None, x_label=None, y_label=None):\n    \n    # Background\n    axes[ax].fill_between(x=x_1, y1=max(y_data)+1, y2=min(y_data)-1,\n                        color=cmap[0], alpha=0.3)\n    axes[ax].fill_between(x=x_2, y1=max(y_data)+1, y2=min(y_data)-1,\n                        color=cmap[1], alpha=0.3)\n    axes[ax].fill_between(x=x_3, y1=max(y_data)+1, y2=min(y_data)-1,\n                        color=cmap[2], alpha=0.3)\n    axes[ax].fill_between(x=x_4, y1=max(y_data)+1, y2=min(y_data)-1,\n                        color=cmap[3], alpha=0.3)\n\n    # Line plot\n    sns.lineplot(x=x_data, y=y_data,\n                 color=\"black\",\n                 ax=axes[ax])\n\n    # Axes\n    axes[ax].set_title(title)\n    axes[ax].set_xlabel(x_label)\n    axes[ax].set_ylabel(y_label)\n    \n\ndef plot_acceleration(df, seq_id):\n\n    # Extract gesture phase\n    x_1 = df.sequence_counter[df.behavior == \"Relaxes and moves hand to target location\"]\n    x_2 = df.sequence_counter[df.behavior == \"Moves hand to target location\"]\n    x_3 = df.sequence_counter[df.behavior == \"Hand at target location\"]\n    x_4 = df.sequence_counter[df.behavior == \"Performs gesture\"]\n    \n    # Set-up plot\n    fig, axes = plt.subplots(1, 3, figsize=(12, 3))\n\n    subplot_timeseries(axes, df.sequence_counter, df.acc_x, 0, \n                       x_1, x_2, x_3, x_4,\n                      \"Acceleration in x\", \"Sequence counter\", \"Acceleration\")\n    subplot_timeseries(axes, df.sequence_counter, df.acc_y, 1, \n                       x_1, x_2, x_3, x_4,\n                      \"Acceleration in y\", \"Sequence counter\", \"Acceleration\")\n    subplot_timeseries(axes, df.sequence_counter, df.acc_z, 2, \n                       x_1, x_2, x_3, x_4,\n                      \"Acceleration in z\", \"Sequence counter\", \"Acceleration\")\n\n    fig.suptitle(f\"Acceleration Data for Sequence {seq_id}: {df.reset_index().gesture[0]}\", fontsize=12)\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.551860Z","iopub.execute_input":"2025-07-02T13:27:00.552544Z","iopub.status.idle":"2025-07-02T13:27:00.569243Z","shell.execute_reply.started":"2025-07-02T13:27:00.552504Z","shell.execute_reply":"2025-07-02T13:27:00.568146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for gesture in unique_gestures:\n    df_gesture = df_train[df_train.gesture == gesture].reset_index()\n    rand_n = random.randint(0, df_gesture.shape[0])\n    seq_id = df_gesture.sequence_id[rand_n]\n    df_id = df_gesture[df_gesture.sequence_id == seq_id]\n    plot_acceleration(df_id, seq_id)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:00.570345Z","iopub.execute_input":"2025-07-02T13:27:00.570679Z","iopub.status.idle":"2025-07-02T13:27:12.898460Z","shell.execute_reply.started":"2025-07-02T13:27:00.570652Z","shell.execute_reply":"2025-07-02T13:27:12.896990Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 2.1.1 Acceleration allignment","metadata":{}},{"cell_type":"code","source":"from scipy.interpolate import interp1d\n\ndef norm_serie(seq_id, group, col, len_total, start_p2, start_p3):\n\n    # Extract values\n    times = group['sequence_counter'].values\n    values = group[col].values\n    \n    # Start of phase 2 and 3\n    t_start_p2 = group.sequence_counter[(group['behavior'] == 'Hand at target location')].iloc[0]\n    t_start_p3 = group.sequence_counter[(group['behavior'] == 'Performs gesture')].iloc[0]\n\n    # Time min and max\n    t_min = times.min()\n    t_max = times.max()\n    \n    # Normalize time in 3 segments\n    norm_time = []\n    for t in times:\n        if t < t_start_p2: # Moves hand to position\n            norm = (t - t_min) / (t_start_p2 - t_min) * start_p2\n        elif t <= t_start_p3: # Hand at position\n            norm = ((t - t_start_p2) / (t_start_p3 - t_start_p2)) * (start_p3 - start_p2) + start_p2\n        else: # Performs action\n            norm = ((t - t_start_p3) / (t_max - t_start_p3)) * (1 - start_p3) + start_p3\n        norm_time.append(norm)\n\n    # Interpolate to fixed-length time axis\n    f = interp1d(norm_time, values, kind='linear', bounds_error=False, fill_value=\"extrapolate\")\n    x_out = np.linspace(0, 1, len_total)\n    y_interp = f(x_out)\n    return y_interp\n\n\ndef allign_series(df, col):\n\n    # Get general statistics (median phase length (moves, relaxes, perfoms gesture etc))\n    len_p1 = round(np.median(df_gesture[(df_gesture.behavior == 'Relaxes and moves hand to target location') | (df_gesture.behavior == 'Moves hand to target location')].groupby(['sequence_id', 'behavior']).size().values))\n    len_p2 = round(np.median(df_gesture[(df_gesture.behavior == 'Hand at target location')].groupby(['sequence_id', 'behavior']).size().values))\n    len_p3 = round(np.median(df_gesture[(df_gesture.behavior == 'Performs gesture')].groupby(['sequence_id', 'behavior']).size().values))\n    len_total = len_p1 + len_p2 + len_p3\n\n    # Calculate relative starting points of p2 and p3\n    start_p2 = len_p2 / len_total\n    start_p3 = (len_p2 + len_p3) / len_total\n\n    # Initialize alligned series\n    aligned_series = []\n\n    for seq_id, group in df.groupby(\"sequence_id\"):\n        aligned_series.append(norm_serie(seq_id, group, col, len_total, start_p2, start_p3))\n\n    return aligned_series, start_p2, start_p3\n\n\ndef subplot_alligned(aligned_series, start_p2, start_p3, axes, ax, title, x_label, y_label, spagetti=True):\n\n    # Prepare data\n    aligned_array = np.vstack(aligned_series)\n    mean_vals = aligned_array.mean(axis=0)\n    std_vals = aligned_array.std(axis=0)\n    x = np.linspace(0, 1, aligned_array.shape[1])\n\n    # Plot background\n    axes[ax].axvline(start_p2, linestyle='--', color=[0.5, 0.5, 0.5])\n    axes[ax].axvline(start_p3, linestyle='--', color=[0.5, 0.5, 0.5])\n\n    if spagetti:\n        # Plot data\n        for i, series in enumerate(aligned_array):\n            axes[ax].plot(x, series, color=cmap[3], alpha=0.5)\n        \n        axes[ax].plot(x, mean_vals, color='black')\n    else: \n        # Plot data\n        axes[ax].plot(x, mean_vals, color='black')\n        axes[ax].fill_between(x, mean_vals - std_vals, mean_vals + std_vals, alpha=0.3, color='gray')\n\n    # Axes\n    axes[ax].set_title(title)\n    axes[ax].set_xlabel(x_label)\n    axes[ax].set_ylabel(y_label)\n    \n\ndef plot_alligned_acc(df, gesture):\n\n    # Get alligned series\n    aligned_acc_x, start_p2, start_p3 = allign_series(df_gesture, 'acc_x')\n    aligned_acc_y, _, _ = allign_series(df_gesture, 'acc_y')\n    aligned_acc_z, _, _ = allign_series(df_gesture, 'acc_z')\n    \n    # Set up figure\n    fig, axes = plt.subplots(1, 3, figsize=(12, 3))\n\n    subplot_alligned(aligned_acc_x, start_p2, start_p3, axes, 0,\n                    \"Acceleration in x\", \"Sequence counter\", \"Acceleration\")\n    subplot_alligned(aligned_acc_y, start_p2, start_p3, axes, 1,\n                    \"Acceleration in y\", \"Sequence counter\", \"Acceleration\")\n    subplot_alligned(aligned_acc_z, start_p2, start_p3, axes, 2,\n                    \"Acceleration in z\", \"Sequence counter\", \"Acceleration\")\n    \n    fig.suptitle(f\"Acceleration Data: {gesture}\", fontsize=12)\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:12.899430Z","iopub.execute_input":"2025-07-02T13:27:12.899673Z","iopub.status.idle":"2025-07-02T13:27:12.917993Z","shell.execute_reply.started":"2025-07-02T13:27:12.899655Z","shell.execute_reply":"2025-07-02T13:27:12.916356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random.seed(2025)\n\nfor gesture in unique_gestures:\n\n    df_gesture = df_train[df_train.gesture == gesture].reset_index()\n    seq_ids = list(df_gesture.sequence_id.unique())\n    \n    N = 10\n    seq_ids_sampled = random.sample(seq_ids, N)\n    df_gesture = df_gesture[df_gesture.sequence_id.isin(seq_ids_sampled)]\n    \n    plot_alligned_acc(df_gesture, gesture)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:27:45.930843Z","iopub.execute_input":"2025-07-02T13:27:45.931267Z","iopub.status.idle":"2025-07-02T13:27:58.882518Z","shell.execute_reply.started":"2025-07-02T13:27:45.931236Z","shell.execute_reply":"2025-07-02T13:27:58.881502Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.3 Rotation","metadata":{}},{"cell_type":"code","source":"def plot_alligned_rot(df, gesture):\n\n    # Get alligned series\n    aligned_rot_x, start_p2, start_p3 = allign_series(df_gesture, 'rot_x')\n    aligned_rot_y, _, _ = allign_series(df_gesture, 'rot_y')\n    aligned_rot_z, _, _ = allign_series(df_gesture, 'rot_z')\n    aligned_rot_w, _, _ = allign_series(df_gesture, 'rot_w')\n    \n    # Set up figure\n    fig, axes = plt.subplots(1, 4, figsize=(16, 3))\n\n    subplot_alligned(aligned_rot_x, start_p2, start_p3, axes, 0,\n                    \"Rotation in x\", \"Sequence counter\", \"Acceleration\")\n    subplot_alligned(aligned_rot_y, start_p2, start_p3, axes, 1,\n                    \"Rotation in y\", \"Sequence counter\", \"Acceleration\")\n    subplot_alligned(aligned_rot_z, start_p2, start_p3, axes, 2,\n                    \"Rotation in z\", \"Sequence counter\", \"Acceleration\")\n    subplot_alligned(aligned_rot_w, start_p2, start_p3, axes, 3,\n                    \"Rotation in w\", \"Sequence counter\", \"Acceleration\")\n    \n    fig.suptitle(f\"Rotation Data: {gesture}\", fontsize=12)\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:02.553581Z","iopub.execute_input":"2025-07-02T13:28:02.553968Z","iopub.status.idle":"2025-07-02T13:28:02.561373Z","shell.execute_reply.started":"2025-07-02T13:28:02.553943Z","shell.execute_reply":"2025-07-02T13:28:02.560376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for gesture in unique_gestures:\n\n    df_gesture = df_train[df_train.gesture == gesture].reset_index()\n    seq_ids = list(df_gesture.sequence_id.unique())\n    \n    N = 10\n    seq_ids_sampled = random.sample(seq_ids, N)\n    df_gesture = df_gesture[df_gesture.sequence_id.isin(seq_ids_sampled)]\n    \n    plot_alligned_rot(df_gesture, gesture)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:04.134594Z","iopub.execute_input":"2025-07-02T13:28:04.134976Z","iopub.status.idle":"2025-07-02T13:28:20.085676Z","shell.execute_reply.started":"2025-07-02T13:28:04.134952Z","shell.execute_reply":"2025-07-02T13:28:20.084786Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.4 Temperature","metadata":{}},{"cell_type":"code","source":"thm_cols = [col for col in df_train.columns if 'thm' in col]\ndf_temp = df_train[['sequence_id', 'sequence_counter', 'subject', 'behavior', 'gesture', 'sequence_type'] + thm_cols]\ndf_temp = df_temp.replace(-1, np.nan)\ndf_temp['thm_mean'] = df_temp[thm_cols].mean(axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:25.029982Z","iopub.execute_input":"2025-07-02T13:28:25.030299Z","iopub.status.idle":"2025-07-02T13:28:25.434584Z","shell.execute_reply.started":"2025-07-02T13:28:25.030277Z","shell.execute_reply":"2025-07-02T13:28:25.433597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cmap = sns.color_palette(\"inferno\", 5)\n\ndef plot_temperature(group, gesture, ax):\n\n    # Get breakpoints between phases\n    t_start_p2 = group.sequence_counter[(group['behavior'] == 'Hand at target location')].iloc[0]\n    t_start_p3 = group.sequence_counter[(group['behavior'] == 'Performs gesture')].iloc[0]\n\n    # Plot background\n    ax.axvline(t_start_p2, linestyle='--', color=[0.5, 0.5, 0.5])\n    ax.axvline(t_start_p3, linestyle='--', color=[0.5, 0.5, 0.5])\n\n    # Plot data\n    for i, series in enumerate(['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']):\n        ax.plot(group['sequence_counter'], group[series], color=cmap[i], alpha=0.5)\n    \n    ax.plot(group['sequence_counter'], group['thm_mean'], color='black', linewidth=2)\n\n    # Axes\n    ax.set_title(gesture)\n    ax.set_xlabel('Time (sequence counter)')\n    ax.set_ylabel('Temperature')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:25.491992Z","iopub.execute_input":"2025-07-02T13:28:25.492316Z","iopub.status.idle":"2025-07-02T13:28:25.500691Z","shell.execute_reply.started":"2025-07-02T13:28:25.492294Z","shell.execute_reply":"2025-07-02T13:28:25.499757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, axes = plt.subplots(3, 6, figsize=(16, 9))\naxes = axes.flatten()\n\nfor i, gesture in enumerate(unique_gestures):\n\n    df_gesture = df_temp[df_temp.gesture == gesture].reset_index()\n    seq_ids = list(df_gesture.sequence_id.unique())\n\n    seq_ids_sampled = random.sample(seq_ids, 1)\n    df_gesture = df_gesture[df_gesture.sequence_id.isin(seq_ids_sampled)]\n    \n    plot_temperature(df_gesture, gesture, axes[i])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:27.809484Z","iopub.execute_input":"2025-07-02T13:28:27.809856Z","iopub.status.idle":"2025-07-02T13:28:31.494136Z","shell.execute_reply.started":"2025-07-02T13:28:27.809795Z","shell.execute_reply":"2025-07-02T13:28:31.493201Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.5 Time of flight","metadata":{}},{"cell_type":"markdown","source":"#### 2.5.1 All data per gesture ","metadata":{}},{"cell_type":"code","source":"tof_cols = [col for col in df_train.columns if 'tof' in col]\ndf_tof = df_train[['sequence_id', 'sequence_counter', 'subject', 'behavior', 'gesture', 'sequence_type'] + tof_cols]\ndf_tof = df_tof.replace(-1, np.nan)\ndf_tof['tof_mean'] = df_tof[tof_cols].mean(axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:31.495319Z","iopub.execute_input":"2025-07-02T13:28:31.495602Z","iopub.status.idle":"2025-07-02T13:28:36.921542Z","shell.execute_reply.started":"2025-07-02T13:28:31.495581Z","shell.execute_reply":"2025-07-02T13:28:36.920478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cmap = sns.color_palette(\"inferno\", 321)\n\ndef plot_tof(group, gesture, ax):\n\n    tof_cols = [col for col in group.columns if 'tof' in col]\n    \n    # Get breakpoints between phases\n    t_start_p2 = group.sequence_counter[(group['behavior'] == 'Hand at target location')].iloc[0]\n    t_start_p3 = group.sequence_counter[(group['behavior'] == 'Performs gesture')].iloc[0]\n\n    # Plot background\n    ax.axvline(t_start_p2, linestyle='--', color=[0.5, 0.5, 0.5])\n    ax.axvline(t_start_p3, linestyle='--', color=[0.5, 0.5, 0.5])\n\n    # Plot data\n    for i, series in enumerate(tof_cols):\n        ax.plot(group['sequence_counter'], group[series], color=cmap[i], alpha=0.5)\n    \n    ax.plot(group['sequence_counter'], group['tof_mean'], color='black', linewidth=2)\n\n    # Axes\n    ax.set_title(gesture)\n    ax.set_xlabel('Time (sequence counter)')\n    ax.set_ylabel('Time of flight')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:36.922621Z","iopub.execute_input":"2025-07-02T13:28:36.923000Z","iopub.status.idle":"2025-07-02T13:28:36.934450Z","shell.execute_reply.started":"2025-07-02T13:28:36.922971Z","shell.execute_reply":"2025-07-02T13:28:36.933391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random.seed(2025)\n\nfig, axes = plt.subplots(3, 6, figsize=(16, 9))\naxes = axes.flatten()\n\nfor i, gesture in enumerate(unique_gestures):\n\n    df_gesture = df_tof[df_tof.gesture == gesture].reset_index()\n    seq_ids = list(df_gesture.sequence_id.unique())\n\n    seq_ids_sampled = random.sample(seq_ids, 1)\n    df_gesture = df_gesture[df_gesture.sequence_id.isin(seq_ids_sampled)]\n    \n    plot_tof(df_gesture, gesture, axes[i])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:36.936104Z","iopub.execute_input":"2025-07-02T13:28:36.936812Z","iopub.status.idle":"2025-07-02T13:28:48.119632Z","shell.execute_reply.started":"2025-07-02T13:28:36.936786Z","shell.execute_reply":"2025-07-02T13:28:48.118185Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.5.2 Data by sensor","metadata":{}},{"cell_type":"code","source":"cmap = sns.color_palette(\"inferno\", 321)\n\ndef plot_tof_by_sensor(group, gesture):\n\n    fig, axes = plt.subplots(1, 5, figsize=(16, 3))\n\n    tof_cols_1 = [col for col in group.columns if 'tof_1' in col]\n    tof_cols_2 = [col for col in group.columns if 'tof_2' in col]\n    tof_cols_3 = [col for col in group.columns if 'tof_3' in col]\n    tof_cols_4 = [col for col in group.columns if 'tof_4' in col]\n    tof_cols_5 = [col for col in group.columns if 'tof_5' in col]\n    \n    # Get breakpoints between phases\n    t_start_p2 = group.sequence_counter[(group['behavior'] == 'Hand at target location')].iloc[0]\n    t_start_p3 = group.sequence_counter[(group['behavior'] == 'Performs gesture')].iloc[0]\n\n    # Plot background\n    for i, tof_cols in enumerate([tof_cols_1, tof_cols_2, tof_cols_3, tof_cols_4, tof_cols_5]):\n        axes[i].axvline(t_start_p2, linestyle='--', color=[0.5, 0.5, 0.5])\n        axes[i].axvline(t_start_p3, linestyle='--', color=[0.5, 0.5, 0.5])\n    \n        # Plot data\n        for j, series in enumerate(tof_cols):\n            axes[i].plot(group['sequence_counter'], group[series], color=cmap[(i*64)+j], alpha=0.5)    \n        # Axes\n        axes[i].set_title(f'Sensor {i+1}')\n        axes[i].set_xlabel('Time (sequence counter)')\n        axes[i].set_ylabel('Time of flight')\n    \n    fig.suptitle(f\"TOF Data: {gesture}\", fontsize=12)\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:48.121483Z","iopub.execute_input":"2025-07-02T13:28:48.121906Z","iopub.status.idle":"2025-07-02T13:28:48.139366Z","shell.execute_reply.started":"2025-07-02T13:28:48.121875Z","shell.execute_reply":"2025-07-02T13:28:48.138502Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random.seed(2025)\n\n\nfor i, gesture in enumerate(unique_gestures):\n\n    df_gesture = df_tof[df_tof.gesture == gesture].reset_index()\n    seq_ids = list(df_gesture.sequence_id.unique())\n\n    seq_ids_sampled = random.sample(seq_ids, 1)\n    df_gesture = df_gesture[df_gesture.sequence_id.isin(seq_ids_sampled)]\n    \n    plot_tof_by_sensor(df_gesture, gesture)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:28:48.141145Z","iopub.execute_input":"2025-07-02T13:28:48.141428Z","iopub.status.idle":"2025-07-02T13:29:11.198339Z","shell.execute_reply.started":"2025-07-02T13:28:48.141406Z","shell.execute_reply":"2025-07-02T13:29:11.197373Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Time serie transformations","metadata":{}},{"cell_type":"markdown","source":"### 3.1 SummaryTransformer","metadata":{}},{"cell_type":"code","source":"# Clean up the df (only keep mean for thm and tof data)\n\n# Extract col names\ntof_cols = [col for col in df_train.columns if 'tof_' in col]\nthm_cols = [col for col in df_train.columns if 'thm_' in col]\n\n# replace -1 with NaN\ndf_train = df_train.replace(-1, np.nan)\n\n# Calculate mean\ndf_train['mean_tof'] = df_train[tof_cols].mean(axis=1)\ndf_train['mean_thm'] = df_train[thm_cols].mean(axis=1)\n\n# Drop original columns\ndf_train_clean = df_train.drop(labels=tof_cols+thm_cols, axis=1)\n\n# Convert to nested format for feature extraction\nts_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w', 'mean_tof', 'mean_thm']\n\n# Convert to nested format using groupby 'id'\nX_nested = (\n    df_train_clean[ts_cols + ['sequence_id']]\n    .groupby('sequence_id', group_keys=False)[ts_cols]\n    .apply(lambda group: pd.Series({col: pd.Series(group[col].values) for col in ts_cols}))\n)\n\nX_nested.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:39:13.198269Z","iopub.execute_input":"2025-07-02T13:39:13.198610Z","iopub.status.idle":"2025-07-02T13:39:27.238206Z","shell.execute_reply.started":"2025-07-02T13:39:13.198585Z","shell.execute_reply":"2025-07-02T13:39:27.237130Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sktime.transformations.series.summarize import SummaryTransformer\n\ntransformer = SummaryTransformer(summary_function=('mean', 'std', 'min', 'max', 'skew', 'kurt', 'mad'), \n                                 quantiles=None)\nX_nested_transformed = transformer.fit_transform(X_nested)\n\nX_nested_transformed","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T13:44:52.199378Z","iopub.execute_input":"2025-07-02T13:44:52.199729Z","iopub.status.idle":"2025-07-02T13:47:56.986996Z","shell.execute_reply.started":"2025-07-02T13:44:52.199678Z","shell.execute_reply":"2025-07-02T13:47:56.985931Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_transformed = df_ref.join(X_nested_transformed, on='sequence_id')\ndf_transformed['gesture_encoded'] = pd.factorize(df_transformed['gesture'])[0]\ndf_transformed.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:04:28.770039Z","iopub.execute_input":"2025-07-02T14:04:28.770348Z","iopub.status.idle":"2025-07-02T14:04:28.827008Z","shell.execute_reply.started":"2025-07-02T14:04:28.770327Z","shell.execute_reply":"2025-07-02T14:04:28.826047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ts_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w', 'mean_tof', 'mean_thm']\n\nfor col in ts_cols:\n    cols = [c for c in df_transformed.columns if col in c]\n\n    fig, axes = plt.subplots(1, 7, figsize=(20, 3))\n    for i, c in enumerate(cols):\n        sns.boxplot(data=df_transformed, x='gesture_encoded', y=c,\n                   ax=axes[i])\n        axes[i].set_title(c)\n    \n    plt.tight_layout()\n    plt.show()\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:05:35.598943Z","iopub.execute_input":"2025-07-02T14:05:35.599285Z","iopub.status.idle":"2025-07-02T14:06:01.211789Z","shell.execute_reply.started":"2025-07-02T14:05:35.599266Z","shell.execute_reply":"2025-07-02T14:06:01.210277Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 3.2 Catch22","metadata":{}},{"cell_type":"code","source":"# Sample the training data to speed-up\ndef balanced_sample(df, target_col, total_samples, random_state=42):\n    classes = df[target_col].unique()\n    n_classes = len(classes)\n    samples_per_class = total_samples // n_classes\n    \n    balanced_parts = []\n    for cls in classes:\n        subset = df[df[target_col] == cls]\n        \n        if len(subset) < samples_per_class:\n            raise ValueError(f\"Not enough samples in class '{cls}' to take {samples_per_class}\")\n        \n        balanced_sample = subset.sample(n=samples_per_class, random_state=random_state)\n        balanced_parts.append(balanced_sample)\n    \n    return pd.concat(balanced_parts).sample(frac=1, random_state=random_state).reset_index(drop=True)\n\n# Select sequence ids (balance gesture class)\ndf_seq_id = df_train.groupby(\"sequence_id\").first().reset_index()\ndf_seq_id_balanced = balanced_sample(df_seq_id, target_col=\"gesture\", total_samples=90)\nseq_ids_to_keep = df_seq_id_balanced[\"sequence_id\"]\nsubjects_to_keep = df_seq_id_balanced[\"subject\"]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:27:20.341939Z","iopub.execute_input":"2025-07-02T14:27:20.342255Z","iopub.status.idle":"2025-07-02T14:27:22.354351Z","shell.execute_reply.started":"2025-07-02T14:27:20.342236Z","shell.execute_reply":"2025-07-02T14:27:22.353577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Sample subset for plotting\ndf_plot = df_train[df_train.sequence_id.isin(seq_ids_to_keep)]\n\n# Drop original columns\ndf_plot_clean = df_plot.drop(labels=tof_cols+thm_cols, axis=1)\n\n# Convert to nested format for feature extraction\nts_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w', 'mean_tof', 'mean_thm']\n\n# Convert to nested format using groupby 'id'\nX_nested = (\n    df_plot_clean[ts_cols + ['sequence_id']]\n    .groupby('sequence_id', group_keys=False)[ts_cols]\n    .apply(lambda group: pd.Series({col: pd.Series(group[col].values) for col in ts_cols}))\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:28:20.581115Z","iopub.execute_input":"2025-07-02T14:28:20.581490Z","iopub.status.idle":"2025-07-02T14:28:20.725543Z","shell.execute_reply.started":"2025-07-02T14:28:20.581420Z","shell.execute_reply":"2025-07-02T14:28:20.724804Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sktime.transformations.panel.catch22 import Catch22\n\nclass SafeCatch22(Catch22):\n    def _transform_case(self, X, f_idx):\n        try:\n            return super()._transform_case(X, f_idx)\n        except Exception:\n            # Return array of NaNs if Catch22 fails on a series\n            return np.full((1, len(f_idx)), np.nan)\n\n# Use SafeCatch22\ncatch22 = SafeCatch22()\nX_catch22 = catch22.fit_transform(X_nested)\n\nX_catch22","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:32:13.887024Z","iopub.execute_input":"2025-07-02T14:32:13.887512Z","iopub.status.idle":"2025-07-02T14:32:21.465217Z","shell.execute_reply.started":"2025-07-02T14:32:13.887481Z","shell.execute_reply":"2025-07-02T14:32:21.464155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_catch22 = df_ref.join(X_catch22, on='sequence_id', how='right')\ndf_catch22['gesture_encoded'] = pd.factorize(df_catch22['gesture'])[0]\ndf_catch22.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:35:58.907910Z","iopub.execute_input":"2025-07-02T14:35:58.908319Z","iopub.status.idle":"2025-07-02T14:35:58.991307Z","shell.execute_reply.started":"2025-07-02T14:35:58.908287Z","shell.execute_reply":"2025-07-02T14:35:58.990110Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for col in ts_cols:\n    cols = [c for c in df_catch22.columns if col in c]\n\n    fig, axes = plt.subplots(11, 2, figsize=(20,22))\n    axes = axes.flatten()\n    \n    for i, c in enumerate(cols):\n        sns.boxplot(data=df_catch22, x='gesture_encoded', y=c,\n                   ax=axes[i])\n        axes[i].set_title(c)\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-02T14:38:09.242863Z","iopub.execute_input":"2025-07-02T14:38:09.243208Z","iopub.status.idle":"2025-07-02T14:39:20.426438Z","shell.execute_reply.started":"2025-07-02T14:38:09.243187Z","shell.execute_reply":"2025-07-02T14:39:20.425212Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Data preprocessing","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}