{"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"},{"sourceType":"kernelVersion","sourceId":242561727}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:38:13.740656Z","iopub.execute_input":"2025-06-01T12:38:13.741032Z","iopub.status.idle":"2025-06-01T12:38:15.373319Z","shell.execute_reply.started":"2025-06-01T12:38:13.741002Z","shell.execute_reply":"2025-06-01T12:38:15.372388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv'\ntrain_demo_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv'\ntest_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv'\ntest_demo_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:40:30.97128Z","iopub.execute_input":"2025-06-01T12:40:30.971605Z","iopub.status.idle":"2025-06-01T12:40:30.9768Z","shell.execute_reply.started":"2025-06-01T12:40:30.971584Z","shell.execute_reply":"2025-06-01T12:40:30.975775Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(train_path)\ntrain_demo_df = pd.read_csv(train_demo_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:40:51.314415Z","iopub.execute_input":"2025-06-01T12:40:51.314758Z","iopub.status.idle":"2025-06-01T12:41:27.754268Z","shell.execute_reply.started":"2025-06-01T12:40:51.314733Z","shell.execute_reply":"2025-06-01T12:41:27.753411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_df = train_df.merge(train_demo_df, on=\"subject\", how=\"left\")\nmerged_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:45:52.178218Z","iopub.execute_input":"2025-06-01T12:45:52.178604Z","iopub.status.idle":"2025-06-01T12:45:52.930831Z","shell.execute_reply.started":"2025-06-01T12:45:52.178578Z","shell.execute_reply":"2025-06-01T12:45:52.92994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_df.fillna(0, inplace=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:49:05.344185Z","iopub.execute_input":"2025-06-01T12:49:05.345331Z","iopub.status.idle":"2025-06-01T12:49:10.079713Z","shell.execute_reply.started":"2025-06-01T12:49:05.345265Z","shell.execute_reply":"2025-06-01T12:49:10.078611Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\nle = LabelEncoder()\nmerged_df[\"gesture\"] = le.fit_transform(merged_df[\"gesture\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:51:56.197567Z","iopub.execute_input":"2025-06-01T12:51:56.198116Z","iopub.status.idle":"2025-06-01T12:51:56.226925Z","shell.execute_reply.started":"2025-06-01T12:51:56.198089Z","shell.execute_reply":"2025-06-01T12:51:56.226015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.preprocessing import StandardScaler\nimport joblib\nfeatures = [col for col in merged_df.columns if col.startswith(('acc_', 'rot_', 'thm_', 'tof_'))]\nscaler = StandardScaler()\nmerged_df[features] = scaler.fit_transform(merged_df[features])\njoblib.dump(scaler, \"scaler.pkl\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:14:55.57153Z","iopub.execute_input":"2025-06-01T13:14:55.571999Z","iopub.status.idle":"2025-06-01T13:15:01.97422Z","shell.execute_reply.started":"2025-06-01T13:14:55.571969Z","shell.execute_reply":"2025-06-01T13:15:01.972994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\n\ndef create_sequences(merged_df, target=True):\n    X = []\n    y = []\n    demo_cols = ['adult_child', 'age', 'sex', 'handedness', 'height_cm', 'shoulder_to_wrist_cm', 'elbow_to_wrist_cm']\n    feature_cols = [col for col in merged_df.columns if col.startswith(('acc_', 'rot_', 'thm_', 'tof_'))]\n    \n    for seq_id, group in merged_df.groupby(\"sequence_id\"):\n        sequence = group[feature_cols].values\n        demo_data = group[demo_cols].iloc[0].values  # static per sequence\n        X.append((sequence, demo_data))\n        if target:\n            y.append(group[\"gesture\"].iloc[0])\n    \n    return X, y if target else X\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:58:53.43871Z","iopub.execute_input":"2025-06-01T12:58:53.439762Z","iopub.status.idle":"2025-06-01T12:58:53.447423Z","shell.execute_reply.started":"2025-06-01T12:58:53.439713Z","shell.execute_reply":"2025-06-01T12:58:53.446347Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X, y = create_sequences(merged_df)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T12:59:07.523719Z","iopub.execute_input":"2025-06-01T12:59:07.524086Z","iopub.status.idle":"2025-06-01T13:00:42.351156Z","shell.execute_reply.started":"2025-06-01T12:59:07.524063Z","shell.execute_reply":"2025-06-01T13:00:42.350043Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.sequence import pad_sequences\n\nX_seq = [seq[0] for seq in X]\nX_demo = [seq[1] for seq in X]\n\nX_seq_padded = pad_sequences(X_seq, padding='post', dtype='float32', maxlen=100)\nX_demo = np.array(X_demo)\ny = np.array(y)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:02:49.129531Z","iopub.execute_input":"2025-06-01T13:02:49.129891Z","iopub.status.idle":"2025-06-01T13:03:08.810531Z","shell.execute_reply.started":"2025-06-01T13:02:49.129866Z","shell.execute_reply":"2025-06-01T13:03:08.809447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models\n\ntime_input = layers.Input(shape=(100, X_seq_padded.shape[2]))\ndemo_input = layers.Input(shape=(X_demo.shape[1],))\n\nx = layers.Conv1D(64, 5, activation='relu')(time_input)\nx = layers.MaxPooling1D(2)(x)\nx = layers.Conv1D(128, 5, activation='relu')(x)\nx = layers.GlobalMaxPooling1D()(x)\n\n# Combine with demographics\nx = layers.Concatenate()([x, demo_input])\nx = layers.Dense(128, activation='relu')(x)\nx = layers.Dropout(0.3)(x)\noutput = layers.Dense(len(np.unique(y)), activation='softmax')(x)\n\nmodel = models.Model(inputs=[time_input, demo_input], outputs=output)\nmodel.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:03:57.48648Z","iopub.execute_input":"2025-06-01T13:03:57.487152Z","iopub.status.idle":"2025-06-01T13:03:57.646823Z","shell.execute_reply.started":"2025-06-01T13:03:57.487125Z","shell.execute_reply":"2025-06-01T13:03:57.645854Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nX_train_seq, X_val_seq, X_train_demo, X_val_demo, y_train, y_val = train_test_split(\n    X_seq_padded, X_demo, y, test_size=0.2, random_state=42\n)\n\nmodel.fit(\n    [X_train_seq, X_train_demo], y_train,\n    validation_data=([X_val_seq, X_val_demo], y_val),\n    epochs=20,\n    batch_size=32\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:04:28.609595Z","iopub.execute_input":"2025-06-01T13:04:28.610022Z","iopub.status.idle":"2025-06-01T13:06:46.739467Z","shell.execute_reply.started":"2025-06-01T13:04:28.609996Z","shell.execute_reply":"2025-06-01T13:06:46.738663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"model_tf.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:12:43.167625Z","iopub.execute_input":"2025-06-01T13:12:43.16807Z","iopub.status.idle":"2025-06-01T13:12:43.250965Z","shell.execute_reply.started":"2025-06-01T13:12:43.168038Z","shell.execute_reply":"2025-06-01T13:12:43.249799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport joblib\n\n# Load trained model\nmodel = tf.keras.models.load_model(\"model_tf.keras\")\n\n# Load pre-fitted scaler\nscaler = joblib.load(\"scaler.pkl\")\n\n# Maximum sequence length\nmax_len = 100\n\n# This function is called by Kaggle\ndef predict(sequence_df: pd.DataFrame, demo_df: pd.DataFrame) -> int:\n    # Define columns inside the function\n    feature_cols = [col for col in sequence_df.columns if col.startswith(('acc_', 'rot_', 'thm_', 'tof_'))]\n    demo_cols = ['adult_child', 'age', 'sex', 'handedness', 'height_cm', 'shoulder_to_wrist_cm', 'elbow_to_wrist_cm']\n\n    # Step 1: Merge demo info into sequence\n    for col in demo_cols:\n        sequence_df[col] = demo_df.iloc[0][col]\n\n    # Step 2: Fill NAs and scale\n    sequence_df[feature_cols] = sequence_df[feature_cols].fillna(0)\n    sequence_df[feature_cols] = scaler.transform(sequence_df[feature_cols])\n\n    # Step 3: Extract tensors\n    sequence = sequence_df[feature_cols].values.astype(np.float32)\n    demo_data = demo_df.iloc[0][demo_cols].values.astype(np.float32)\n\n    # Step 4: Pad/truncate\n    if sequence.shape[0] < max_len:\n        pad_len = max_len - sequence.shape[0]\n        pad = np.zeros((pad_len, sequence.shape[1]))\n        sequence = np.vstack((sequence, pad))\n    else:\n        sequence = sequence[:max_len]\n\n    # Step 5: Inference\n    pred = model.predict(\n        [sequence[np.newaxis, ...], demo_data[np.newaxis, ...]],\n        verbose=0\n    )\n\n    return int(np.argmax(pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:16:39.966923Z","iopub.execute_input":"2025-06-01T13:16:39.967262Z","iopub.status.idle":"2025-06-01T13:16:40.089053Z","shell.execute_reply.started":"2025-06-01T13:16:39.967237Z","shell.execute_reply":"2025-06-01T13:16:40.088075Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from kaggle_evaluation import evaluator\n\nevaluator.evaluate(predict, submission_output_path=\"submission.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-01T13:17:15.899898Z","iopub.execute_input":"2025-06-01T13:17:15.900279Z","iopub.status.idle":"2025-06-01T13:17:15.934259Z","shell.execute_reply.started":"2025-06-01T13:17:15.900245Z","shell.execute_reply":"2025-06-01T13:17:15.932758Z"}},"outputs":[],"execution_count":null}]}