{"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,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Configure visualizations\nsns.set(style=\"whitegrid\")\nplt.rcParams[\"figure.figsize\"] = (14, 6)\nimport os\n\nimport pandas as pd\n\n\nimport kaggle_evaluation.cmi_inference_server","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:29:42.629250Z","iopub.execute_input":"2025-06-01T13:29:42.629522Z","iopub.status.idle":"2025-06-01T13:29:47.719436Z","shell.execute_reply.started":"2025-06-01T13:29:42.629492Z","shell.execute_reply":"2025-06-01T13:29:47.718514Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")  # Replace with your actual path\ntrain_demo = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:29:47.720438Z","iopub.execute_input":"2025-06-01T13:29:47.720993Z","iopub.status.idle":"2025-06-01T13:30:22.876671Z","shell.execute_reply.started":"2025-06-01T13:29:47.720961Z","shell.execute_reply":"2025-06-01T13:30:22.875811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Train Shape:\", train.shape)\nprint(\"Train Demographics Shape:\", train_demo.shape)\n\n# Display column types and null values\nprint(train.info())\nprint(train_demo.info())\n\n# Unique labels\nprint(\"Unique gestures:\", train['gesture'].unique())\nprint(\"Unique behaviors:\", train['behavior'].unique())\nprint(\"Unique orientations:\", train['orientation'].unique())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:26.168407Z","iopub.execute_input":"2025-06-01T13:30:26.169086Z","iopub.status.idle":"2025-06-01T13:30:26.317360Z","shell.execute_reply.started":"2025-06-01T13:30:26.169058Z","shell.execute_reply":"2025-06-01T13:30:26.316440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_demo.describe(include='all'))\n\n# Distribution of subjects, age, sex\nsns.countplot(data=train_demo, x='sex')\nplt.title(\"Sex Distribution\")\nplt.show()\n\nsns.histplot(train_demo['age'], bins=20, kde=True)\nplt.title(\"Age Distribution\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:30.902540Z","iopub.execute_input":"2025-06-01T13:30:30.902899Z","iopub.status.idle":"2025-06-01T13:30:31.544084Z","shell.execute_reply.started":"2025-06-01T13:30:30.902874Z","shell.execute_reply":"2025-06-01T13:30:31.543144Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Gesture distribution\nsns.countplot(data=train, y='gesture', order=train['gesture'].value_counts().index)\nplt.title(\"Gesture Frequency\")\nplt.show()\n\n# Target vs Non-target\nsns.countplot(data=train, x='sequence_type')\nplt.title(\"Target vs Non-Target Gestures\")\nplt.show()\n\n# Orientation distribution\nsns.countplot(data=train, y='orientation', order=train['orientation'].value_counts().index)\nplt.title(\"Body Orientation\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:35.462352Z","iopub.execute_input":"2025-06-01T13:30:35.462714Z","iopub.status.idle":"2025-06-01T13:30:37.130807Z","shell.execute_reply.started":"2025-06-01T13:30:35.462673Z","shell.execute_reply":"2025-06-01T13:30:37.129826Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"example_seq_id = train['sequence_id'].unique()[0]\nexample_seq = train[train['sequence_id'] == example_seq_id]\n\nplt.plot(example_seq['sequence_counter'], example_seq['acc_x'], label='acc_x')\nplt.plot(example_seq['sequence_counter'], example_seq['acc_y'], label='acc_y')\nplt.plot(example_seq['sequence_counter'], example_seq['acc_z'], label='acc_z')\nplt.title(f\"Accelerometer Data for Sequence {example_seq_id}\")\nplt.xlabel(\"Sequence Step\")\nplt.ylabel(\"Acceleration (m/s^2)\")\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:40.967940Z","iopub.execute_input":"2025-06-01T13:30:40.968250Z","iopub.status.idle":"2025-06-01T13:30:41.345156Z","shell.execute_reply.started":"2025-06-01T13:30:40.968225Z","shell.execute_reply":"2025-06-01T13:30:41.344266Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tof_cols = [col for col in train.columns if col.startswith('tof_')]\n\nmissing_tof = (train[tof_cols] == -1).mean().sort_values(ascending=False) * 100\nmissing_tof.head(30).plot(kind='bar', title=\"Top ToF Pixels with Missing Signals (-1)\")\nplt.ylabel(\"% Missing\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:44.525410Z","iopub.execute_input":"2025-06-01T13:30:44.525745Z","iopub.status.idle":"2025-06-01T13:30:45.819151Z","shell.execute_reply.started":"2025-06-01T13:30:44.525721Z","shell.execute_reply":"2025-06-01T13:30:45.818311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.manifold import TSNE\nfrom sklearn.decomposition import PCA\nimport umap\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Aggregate features per sequence\nagg_features = train.groupby(\"sequence_id\").agg({\n    'acc_x': 'mean', 'acc_y': 'mean', 'acc_z': 'mean',\n    'rot_x': 'mean', 'rot_y': 'mean', 'rot_z': 'mean',\n    'thm_1': 'mean', 'thm_2': 'mean', 'thm_3': 'mean', 'thm_4': 'mean', 'thm_5': 'mean'\n}).reset_index()\n\n# Add gesture labels\ngesture_labels = train.groupby(\"sequence_id\")[\"gesture\"].first().reset_index()\nagg_features = agg_features.merge(gesture_labels, on=\"sequence_id\")\n\n# Drop rows with any NaN values\nagg_features_clean = agg_features.dropna()\n\n# Dimensionality reduction with UMAP\nreducer = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42)\nembedding_umap = reducer.fit_transform(\n    agg_features_clean.drop([\"sequence_id\", \"gesture\"], axis=1)\n)\n\n# Plotting\nplt.figure(figsize=(10, 6))\nsns.scatterplot(\n    x=embedding_umap[:, 0],\n    y=embedding_umap[:, 1],\n    hue=agg_features_clean[\"gesture\"],\n    palette=\"tab20\",\n    s=50\n)\nplt.title(\"UMAP projection of aggregated sensor features by gesture\")\nplt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:30:48.958198Z","iopub.execute_input":"2025-06-01T13:30:48.958501Z","iopub.status.idle":"2025-06-01T13:32:10.367495Z","shell.execute_reply.started":"2025-06-01T13:30:48.958477Z","shell.execute_reply":"2025-06-01T13:32:10.366688Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seq_subject_map = train.groupby(\"sequence_id\")[\"subject\"].first().value_counts()\nseq_subject_map.plot(kind='bar', figsize=(12, 5), title=\"Number of Sequences per Subject\")\nplt.xlabel(\"Subject\")\nplt.ylabel(\"Sequence Count\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:32:26.975918Z","iopub.execute_input":"2025-06-01T13:32:26.976549Z","iopub.status.idle":"2025-06-01T13:32:28.155422Z","shell.execute_reply.started":"2025-06-01T13:32:26.976523Z","shell.execute_reply":"2025-06-01T13:32:28.154493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.cluster import KMeans\nfrom sklearn.preprocessing import StandardScaler\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Select features and replace NaN values with column means\nX = agg_features.drop([\"sequence_id\", \"gesture\"], axis=1)\nX_filled = X.fillna(X.mean())\n\n# Standardize the features\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X_filled)\n\n# Apply KMeans clustering\nkmeans = KMeans(n_clusters=8, random_state=42)\nagg_features['cluster'] = kmeans.fit_predict(X_scaled)\n\n# Visualize first four features\nsns.pairplot(agg_features, hue='cluster', vars=X.columns[:4])\nplt.suptitle(\"Clustering of Sequences Using KMeans\", y=1.02)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:32:33.676232Z","iopub.execute_input":"2025-06-01T13:32:33.676556Z","iopub.status.idle":"2025-06-01T13:32:47.810307Z","shell.execute_reply.started":"2025-06-01T13:32:33.676531Z","shell.execute_reply":"2025-06-01T13:32:47.809050Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"example_sequence = train[train[\"sequence_id\"] == train[\"sequence_id\"].unique()[0]]\n\nfig, axs = plt.subplots(4, 1, figsize=(15, 10), sharex=True)\n\naxs[0].plot(example_sequence[\"acc_x\"], label=\"acc_x\")\naxs[0].plot(example_sequence[\"acc_y\"], label=\"acc_y\")\naxs[0].plot(example_sequence[\"acc_z\"], label=\"acc_z\")\naxs[0].set_title(\"IMU - Accelerometer\")\naxs[0].legend()\n\naxs[1].plot(example_sequence[\"rot_x\"], label=\"rot_x\")\naxs[1].plot(example_sequence[\"rot_y\"], label=\"rot_y\")\naxs[1].plot(example_sequence[\"rot_z\"], label=\"rot_z\")\naxs[1].set_title(\"IMU - Rotation\")\naxs[1].legend()\n\naxs[2].plot(example_sequence[[\"thm_1\", \"thm_2\", \"thm_3\", \"thm_4\", \"thm_5\"]])\naxs[2].set_title(\"Thermopile Sensors\")\n\naxs[3].imshow(\n    example_sequence[[f\"tof_1_v{i}\" for i in range(64)]].values.T,\n    aspect='auto', cmap='viridis', interpolation='none'\n)\naxs[3].set_title(\"Time-of-Flight Sensor 1 - 8x8 Grid Over Time\")\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:32:58.457052Z","iopub.execute_input":"2025-06-01T13:32:58.457354Z","iopub.status.idle":"2025-06-01T13:32:59.472800Z","shell.execute_reply.started":"2025-06-01T13:32:58.457335Z","shell.execute_reply":"2025-06-01T13:32:59.471905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Calculate mean per phase per feature\nphase_summary = train.groupby('behavior')[['acc_x', 'acc_y', 'acc_z', 'thm_1', 'tof_1_v0']].mean().reset_index()\n\n# Melt for easier plotting\nphase_melted = phase_summary.melt(id_vars='behavior', var_name='feature', value_name='mean_value')\n\nplt.figure(figsize=(10, 6))\nsns.barplot(data=phase_melted, x='feature', y='mean_value', hue='behavior')\nplt.title(\"Sensor Mean per Feature by Behavior Phase\")\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:33:08.957226Z","iopub.execute_input":"2025-06-01T13:33:08.957978Z","iopub.status.idle":"2025-06-01T13:33:09.376295Z","shell.execute_reply.started":"2025-06-01T13:33:08.957950Z","shell.execute_reply":"2025-06-01T13:33:09.375330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import random\n\n# Pick a random sequence\nseq_id = random.choice(train['sequence_id'].unique())\ndf_seq = train[train['sequence_id'] == seq_id]\n\n# Plot time-series for a few key sensors\nplt.figure(figsize=(14, 5))\nfor col in ['acc_x', 'acc_y', 'acc_z']:\n    plt.plot(df_seq['sequence_counter'], df_seq[col], label=col)\n\nplt.title(f\"Accelerometer Signals for Sequence {seq_id} ({df_seq['behavior'].iloc[-1]})\")\nplt.xlabel(\"Time (sequence_counter)\")\nplt.ylabel(\"Acceleration\")\nplt.legend()\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:33:22.632353Z","iopub.execute_input":"2025-06-01T13:33:22.633123Z","iopub.status.idle":"2025-06-01T13:33:23.053631Z","shell.execute_reply.started":"2025-06-01T13:33:22.633091Z","shell.execute_reply":"2025-06-01T13:33:23.052640Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.impute import SimpleImputer\n\n# Prepare training data\nX = agg_features.drop([\"sequence_id\", \"gesture\"], axis=1)\ny = agg_features[\"gesture\"]\nX = X.fillna(X.mean())\n\n# Train classifier\nrf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf.fit(X, y)\n\n# Plot top features\nimportances = pd.Series(rf.feature_importances_, index=X.columns)\ntop_features = importances.sort_values(ascending=False).head(10)\n\nplt.figure(figsize=(8, 5))\ntop_features.plot(kind='barh')\nplt.title(\"Top 10 Important Features (Random Forest)\")\nplt.gca().invert_yaxis()\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:33:30.983409Z","iopub.execute_input":"2025-06-01T13:33:30.983695Z","iopub.status.idle":"2025-06-01T13:33:34.955280Z","shell.execute_reply.started":"2025-06-01T13:33:30.983676Z","shell.execute_reply":"2025-06-01T13:33:34.954289Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report\n\nsensor_groups = {\n    'IMU only': ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z'],\n    'Thermopiles': ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'],\n    'ToF avg': [f\"tof_{i}_v{j}\" for i in range(1, 6) for j in range(64)]\n}\n\n# Compute average per sensor (ToF is dense)\nagg_features_tof = train.groupby(\"sequence_id\").agg({\n    **{col: 'mean' for col in train.columns if \"tof\" in col}\n}).reset_index()\nagg_features = agg_features.merge(agg_features_tof, on=\"sequence_id\", how='left')\n\nfor name, cols in sensor_groups.items():\n    cols_existing = [col for col in cols if col in agg_features.columns]\n    X = agg_features[cols_existing].fillna(0)\n    y = agg_features[\"gesture\"]\n\n    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n    clf = RandomForestClassifier(random_state=42)\n    clf.fit(X_train, y_train)\n    print(f\"\\n{name} Only:\")\n    print(classification_report(y_test, clf.predict(X_test)))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:34:35.983454Z","iopub.execute_input":"2025-06-01T13:34:35.983848Z","iopub.status.idle":"2025-06-01T13:34:55.010867Z","shell.execute_reply.started":"2025-06-01T13:34:35.983819Z","shell.execute_reply":"2025-06-01T13:34:55.009763Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Gesture Specific EDA\n","metadata":{}},{"cell_type":"code","source":"print(train[\"gesture\"].dropna().unique())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:10.060495Z","iopub.execute_input":"2025-06-01T13:35:10.060889Z","iopub.status.idle":"2025-06-01T13:35:10.133473Z","shell.execute_reply.started":"2025-06-01T13:35:10.060863Z","shell.execute_reply":"2025-06-01T13:35:10.132761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Clean whitespace just in case\ntrain[\"gesture\"] = train[\"gesture\"].str.strip()\n\n# Define gestures for comparison\ntarget_gesture = \"Above ear - pull hair\"\nnon_target_gesture = \"Glasses on/off\"\n\n# Filter rows for the two gestures\ndf_target = train[train[\"gesture\"] == target_gesture]\ndf_non_target = train[train[\"gesture\"] == non_target_gesture]\n\n# Print basic info\nprint(\"Target gesture sequences:\", df_target[\"sequence_id\"].nunique())\nprint(\"Non-target gesture sequences:\", df_non_target[\"sequence_id\"].nunique())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:13.187225Z","iopub.execute_input":"2025-06-01T13:35:13.187538Z","iopub.status.idle":"2025-06-01T13:35:13.432140Z","shell.execute_reply.started":"2025-06-01T13:35:13.187514Z","shell.execute_reply":"2025-06-01T13:35:13.431394Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\nfeatures_to_compare = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z']\n\nfor feature in features_to_compare:\n    plt.figure(figsize=(8, 4))\n    sns.histplot(data=df_target, x=feature, hue=\"gesture\", element=\"step\", stat=\"density\", common_norm=False)\n    plt.title(f\"Distribution of {feature} by Gesture\")\n    plt.tight_layout()\n    plt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:19.974091Z","iopub.execute_input":"2025-06-01T13:35:19.974420Z","iopub.status.idle":"2025-06-01T13:35:22.282359Z","shell.execute_reply.started":"2025-06-01T13:35:19.974397Z","shell.execute_reply":"2025-06-01T13:35:22.281131Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Select gestures to compare\ntarget_gesture = \"Above ear - pull hair\"\nnon_target_gesture = \"Glasses on/off\"\n\n# Ensure consistent whitespace handling\ntrain[\"gesture\"] = train[\"gesture\"].str.strip()\n\n# Filter the DataFrame for selected gestures\ndf_target = train[train[\"gesture\"] == target_gesture]\ndf_non_target = train[train[\"gesture\"] == non_target_gesture]\n\n# Plot acceleration comparison across gestures\nplt.figure(figsize=(16, 6))\nfor i, axis in enumerate(['acc_x', 'acc_y', 'acc_z']):\n    plt.subplot(1, 3, i + 1)\n    sns.kdeplot(df_target[axis], label=target_gesture, fill=True)\n    sns.kdeplot(df_non_target[axis], label=non_target_gesture, fill=True)\n    plt.title(f\"{axis} Distribution\")\n    plt.xlabel(axis)\n    plt.ylabel(\"Density\")\n    plt.legend()\nplt.suptitle(\"Acceleration Comparison Between Gestures\")\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:31.098177Z","iopub.execute_input":"2025-06-01T13:35:31.098587Z","iopub.status.idle":"2025-06-01T13:35:33.317875Z","shell.execute_reply.started":"2025-06-01T13:35:31.098552Z","shell.execute_reply":"2025-06-01T13:35:33.316687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train[['acc_x', 'acc_y', 'acc_z']] = train[['acc_x', 'acc_y', 'acc_z']].fillna(train[['acc_x', 'acc_y', 'acc_z']].mean())\n\n# Compute magnitude of acceleration vector\ntrain['acc_magnitude'] = (train['acc_x']**2 + train['acc_y']**2 + train['acc_z']**2)**0.5\n\n# Aggregate mean acceleration magnitude by gesture\ngesture_acc = train.groupby('gesture')['acc_magnitude'].mean().sort_values(ascending=False).reset_index()\n\n# Plot\nplt.figure(figsize=(12, 6))\nsns.barplot(data=gesture_acc, x='gesture', y='acc_magnitude', palette='viridis')\nplt.xticks(rotation=90)\nplt.title(\"Average Acceleration Magnitude per Gesture\")\nplt.ylabel(\"Mean Acceleration Magnitude\")\nplt.xlabel(\"Gesture\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:43.311455Z","iopub.execute_input":"2025-06-01T13:35:43.311763Z","iopub.status.idle":"2025-06-01T13:35:43.825407Z","shell.execute_reply.started":"2025-06-01T13:35:43.311743Z","shell.execute_reply":"2025-06-01T13:35:43.824521Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fill missing values in thermopile sensor columns\nthm_cols = ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']\ntrain[thm_cols] = train[thm_cols].fillna(train[thm_cols].mean())\n\n# Compute average temperature across thermopiles\ntrain['thm_avg'] = train[thm_cols].mean(axis=1)\n\n# Group by gesture and calculate mean\ngesture_thm = train.groupby('gesture')['thm_avg'].mean().sort_values(ascending=False).reset_index()\n\n# Plot\nplt.figure(figsize=(12, 6))\nsns.barplot(data=gesture_thm, x='gesture', y='thm_avg', palette='coolwarm')\nplt.xticks(rotation=90)\nplt.title(\"Average Temperature (Thermopiles) per Gesture\")\nplt.ylabel(\"Mean Temperature (°C)\")\nplt.xlabel(\"Gesture\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:51.272568Z","iopub.execute_input":"2025-06-01T13:35:51.272915Z","iopub.status.idle":"2025-06-01T13:35:51.886984Z","shell.execute_reply.started":"2025-06-01T13:35:51.272892Z","shell.execute_reply":"2025-06-01T13:35:51.886024Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fill missing values in thermopile sensor columns\nthm_cols = ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']\ntrain[thm_cols] = train[thm_cols].fillna(train[thm_cols].mean())\n\n# Compute average temperature across thermopiles\ntrain['thm_avg'] = train[thm_cols].mean(axis=1)\n\n# Group by gesture and calculate mean\ngesture_thm = train.groupby('gesture')['thm_avg'].mean().sort_values(ascending=False).reset_index()\n\n# Plot\nplt.figure(figsize=(12, 6))\nsns.barplot(data=gesture_thm, x='gesture', y='thm_avg', palette='coolwarm')\nplt.xticks(rotation=90)\nplt.title(\"Average Temperature (Thermopiles) per Gesture\")\nplt.ylabel(\"Mean Temperature (°C)\")\nplt.xlabel(\"Gesture\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:35:58.303617Z","iopub.execute_input":"2025-06-01T13:35:58.304287Z","iopub.status.idle":"2025-06-01T13:35:58.926237Z","shell.execute_reply.started":"2025-06-01T13:35:58.304256Z","shell.execute_reply":"2025-06-01T13:35:58.925223Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compute phase-wise means\ntrain['thm_avg'] = train[['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']].mean(axis=1)\ntrain['tof_avg'] = train[tof_cols].mean(axis=1)\n\n# Melt for plotting\nmelted = train.melt(id_vars=['behavior'], value_vars=['thm_avg', 'tof_avg'], var_name='Sensor', value_name='Value')\n\nplt.figure(figsize=(10, 6))\nsns.boxplot(data=melted, x='behavior', y='Value', hue='Sensor')\nplt.title(\"Sensor Readings by Sequence Phase (Behavior)\")\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:36:07.724887Z","iopub.execute_input":"2025-06-01T13:36:07.725207Z","iopub.status.idle":"2025-06-01T13:36:12.118389Z","shell.execute_reply.started":"2025-06-01T13:36:07.725184Z","shell.execute_reply":"2025-06-01T13:36:12.117503Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from scipy.stats import skew, kurtosis, entropy\nfrom sklearn.linear_model import LinearRegression\nimport numpy as np\nimport pandas as pd\nfrom scipy.signal import find_peaks\nfrom scipy.fft import fft\n\ndef compute_features(df):\n    feature_list = []\n\n    for seq_id, group in df.groupby(\"sequence_id\"):\n        feats = {\"sequence_id\": seq_id}\n\n        for sensor in ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z',\n                       'thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']:\n\n            # Clean data: replace -1 only if applicable (not general)\n            x = group[sensor].copy()\n            x = x.replace(-1, np.nan)  # Replace -1 by NaN if -1 is invalid\n            x = x.dropna()\n\n            # If empty after dropna, fill with zeros or skip\n            if len(x) == 0:\n                x = np.array([0])\n            else:\n                x = x.values\n\n            feats[f\"{sensor}_mean\"] = np.mean(x)\n            feats[f\"{sensor}_std\"] = np.std(x)\n            feats[f\"{sensor}_min\"] = np.min(x)\n            feats[f\"{sensor}_max\"] = np.max(x)\n\n            # Only calculate skew/kurtosis if variance > 0 and length > 2\n            if len(x) > 2 and np.std(x) > 1e-6:\n                feats[f\"{sensor}_skew\"] = skew(x)\n                feats[f\"{sensor}_kurtosis\"] = kurtosis(x)\n            else:\n                feats[f\"{sensor}_skew\"] = 0\n                feats[f\"{sensor}_kurtosis\"] = 0\n\n            feats[f\"{sensor}_energy\"] = np.sum(x ** 2)\n            feats[f\"{sensor}_zerocross\"] = ((np.diff(np.sign(x)) != 0).sum())\n            peaks, _ = find_peaks(x, height=np.mean(x) + np.std(x))\n            feats[f\"{sensor}_n_peaks\"] = len(peaks)\n\n            probs, _ = np.histogram(x, bins=10, density=True)\n            feats[f\"{sensor}_entropy\"] = entropy(probs + 1e-6)\n\n        # Similar clean-up for magnitude computations:\n        acc_mag = np.sqrt(group[\"acc_x\"]**2 + group[\"acc_y\"]**2 + group[\"acc_z\"]**2)\n        acc_mag = acc_mag.replace(-1, np.nan).dropna().values\n        if len(acc_mag) == 0:\n            acc_mag = np.array([0])\n        feats[\"acc_mag_mean\"] = np.mean(acc_mag)\n\n        rot_mag = np.sqrt(group[\"rot_x\"]**2 + group[\"rot_y\"]**2 + group[\"rot_z\"]**2)\n        rot_mag = rot_mag.replace(-1, np.nan).dropna().values\n        if len(rot_mag) == 0:\n            rot_mag = np.array([0])\n        feats[\"rot_mag_mean\"] = np.mean(rot_mag)\n\n        # FFT dominant frequency with checks:\n        for sensor in ['acc_x', 'acc_y', 'acc_z']:\n            x = group[sensor].replace(-1, np.nan).dropna().values\n            if len(x) == 0:\n                feats[f\"{sensor}_fft_dom_freq\"] = 0\n            else:\n                freqs = np.abs(fft(x))\n                feats[f\"{sensor}_fft_dom_freq\"] = np.argmax(freqs[:len(freqs)//2])\n\n        # TOF processing: same, handle missing properly\n        tof_cols = [col for col in group.columns if col.startswith(\"tof_\")]\n        if tof_cols:\n            tof_data = group[tof_cols].replace(-1, 0).fillna(0).values\n            feats[\"tof_active_pixels\"] = np.sum(tof_data > 0)\n            feats[\"tof_mean_intensity\"] = np.mean(tof_data[tof_data > 0]) if np.sum(tof_data > 0) > 0 else 0\n            if tof_data.shape[1] >= 64:\n                mask = (tof_data[:, :64] > 0).sum(axis=0)\n                if np.sum(mask) > 0:\n                    tof_centroid_x = np.average([i % 8 for i in range(64)], weights=mask)\n                    tof_centroid_y = np.average([i // 8 for i in range(64)], weights=mask)\n                else:\n                    tof_centroid_x, tof_centroid_y = 0, 0\n                feats[\"tof_centroid_x\"] = tof_centroid_x\n                feats[\"tof_centroid_y\"] = tof_centroid_y\n        else:\n            feats[\"tof_active_pixels\"] = 0\n            feats[\"tof_mean_intensity\"] = 0\n            feats[\"tof_centroid_x\"] = 0\n            feats[\"tof_centroid_y\"] = 0\n\n        # Linear trend with checks:\n        for sensor in ['acc_x', 'acc_y', 'acc_z']:\n            x = group[sensor].replace(-1, np.nan).dropna().values\n            if len(x) > 1:\n                lr = LinearRegression().fit(np.arange(len(x)).reshape(-1, 1), x)\n                feats[f\"{sensor}_trend\"] = lr.coef_[0]\n            else:\n                feats[f\"{sensor}_trend\"] = 0\n\n        feature_list.append(feats)\n\n    return pd.DataFrame(feature_list)\n\n# Usage:\ndf_features = compute_features(train)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:46:17.477186Z","iopub.execute_input":"2025-06-01T13:46:17.477548Z","iopub.status.idle":"2025-06-01T13:50:29.509980Z","shell.execute_reply.started":"2025-06-01T13:46:17.477523Z","shell.execute_reply":"2025-06-01T13:50:29.509044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_features.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:50:56.267669Z","iopub.execute_input":"2025-06-01T13:50:56.268050Z","iopub.status.idle":"2025-06-01T13:50:56.299602Z","shell.execute_reply.started":"2025-06-01T13:50:56.268027Z","shell.execute_reply":"2025-06-01T13:50:56.298693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Example: df_labels has columns ['sequence_id', 'target']\ndf_labels = train[['sequence_id', 'gesture']].drop_duplicates()\n\n# Merge features and labels on sequence_id\ndf_all = df_features.merge(df_labels, on='sequence_id', how='inner')\n\nX = df_all.drop(columns=['sequence_id', 'gesture'])\ny = df_all['gesture']\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:58:29.013821Z","iopub.execute_input":"2025-06-01T13:58:29.014537Z","iopub.status.idle":"2025-06-01T13:58:29.115558Z","shell.execute_reply.started":"2025-06-01T13:58:29.014502Z","shell.execute_reply":"2025-06-01T13:58:29.114754Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, mean_squared_error\n\n# Split train/val\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)\n\n# Initialize model (classification example)\nmodel = RandomForestClassifier(n_estimators=100, random_state=42)\n\n# Train\nmodel.fit(X_train, y_train)\n\n# Predict and evaluate\ny_pred = model.predict(X_val)\n\n# For classification\nprint(\"Validation Accuracy:\", accuracy_score(y_val, y_pred))\n\n# For regression, use:\n# print(\"Validation RMSE:\", np.sqrt(mean_squared_error(y_val, y_pred)))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:58:49.829657Z","iopub.execute_input":"2025-06-01T13:58:49.830014Z","iopub.status.idle":"2025-06-01T13:58:57.506240Z","shell.execute_reply.started":"2025-06-01T13:58:49.829991Z","shell.execute_reply":"2025-06-01T13:58:57.505507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import accuracy_score, f1_score\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.preprocessing import LabelEncoder\n\n# Models\nfrom sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier\nfrom xgboost import XGBClassifier\nfrom lightgbm import LGBMClassifier\nfrom catboost import CatBoostClassifier\nfrom sklearn.linear_model import LogisticRegression, RidgeClassifier, SGDClassifier\nfrom sklearn.svm import SVC, LinearSVC\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.naive_bayes import GaussianNB, BernoulliNB\n\n# Your features and target\nX = df_features.drop(columns=[\"sequence_id\"])\ny = train[[\"sequence_id\", \"gesture\"]].drop_duplicates().set_index(\"sequence_id\").loc[df_features[\"sequence_id\"]][\"gesture\"].values\n\n# Step 1: Encode string labels into integers\nle = LabelEncoder()\ny_encoded = le.fit_transform(y)  # Converts to integer labels 0 ... N-1\n\n# Optional: Get mapping of label to class name\nlabel_mapping = dict(zip(le.classes_, le.transform(le.classes_)))\nprint(\"Label mapping:\", label_mapping)\n# Standardize features\nscaler = StandardScaler()\nX_scaled = scaler.fit_transform(X)\n\n# Models dictionary\nmodels = {\n    \"RandomForest\": RandomForestClassifier(n_estimators=100, random_state=42),\n    \"GradientBoosting\": GradientBoostingClassifier(random_state=42),\n    \"XGBoost\": XGBClassifier(use_label_encoder=False, eval_metric='mlogloss', random_state=42),\n    \"LightGBM\": LGBMClassifier(random_state=42),\n    \"CatBoost\": CatBoostClassifier(verbose=0, random_state=42),\n    \"LogisticRegression\": LogisticRegression(max_iter=1000, random_state=42),\n    \"RidgeClassifier\": RidgeClassifier(),\n    \"SGDClassifier\": SGDClassifier(loss='log_loss', max_iter=1000, random_state=42),\n    \"SVC_RBF\": SVC(kernel='rbf', probability=True, random_state=42),\n    \"LinearSVC\": LinearSVC(max_iter=1000, random_state=42),\n    \"KNN\": KNeighborsClassifier(n_neighbors=5),\n    \"GaussianNB\": GaussianNB(),\n    \"BernoulliNB\": BernoulliNB()\n}\n\n# Cross-validation\nkf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\nresults = []\n\nprint(\"Training models...\\n\")\nfor name, model in models.items():\n    accs, f1s = [], []\n\n    for train_idx, val_idx in kf.split(X_scaled, y_encoded):\n        X_train, X_val = X_scaled[train_idx], X_scaled[val_idx]\n        y_train, y_val = y_encoded[train_idx], y_encoded[val_idx]\n\n        model.fit(X_train, y_train)\n        preds = model.predict(X_val)\n\n        accs.append(accuracy_score(y_val, preds))\n        f1s.append(f1_score(y_val, preds, average='macro'))\n\n    print(f\"{name:15s} | Accuracy: {np.mean(accs):.4f} | F1 Macro: {np.mean(f1s):.4f}\")\n    results.append({\n        \"Model\": name,\n        \"Accuracy\": np.mean(accs),\n        \"F1_macro\": np.mean(f1s)\n    })\n\n# Results DataFrame\nresults_df = pd.DataFrame(results).sort_values(by=\"F1_macro\", ascending=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T15:09:42.246988Z","iopub.execute_input":"2025-06-01T15:09:42.247558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}