{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.17","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":31042,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"pip install polars","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:25.611091Z","iopub.execute_input":"2025-05-30T16:22:25.611918Z","iopub.status.idle":"2025-05-30T16:22:29.121190Z","shell.execute_reply.started":"2025-05-30T16:22:25.611886Z","shell.execute_reply":"2025-05-30T16:22:29.116150Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport polars as pl\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.model_selection import StratifiedShuffleSplit\nfrom sklearn.metrics import f1_score\nfrom collections import Counter\nimport os\n\nimport kaggle_evaluation.cmi_inference_server","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:29.123277Z","iopub.execute_input":"2025-05-30T16:22:29.123515Z","iopub.status.idle":"2025-05-30T16:22:29.135160Z","shell.execute_reply.started":"2025-05-30T16:22:29.123491Z","shell.execute_reply":"2025-05-30T16:22:29.128744Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"NumPy: For numerical operations.\n\nPandas: Standard library for tabular data processing.\n\nPolars: Fast DataFrame library that can be more performant than pandas for large datasets.\n\nLabelEncoder: Converts categorical labels to integers.\n\nRandomForestClassifier: A robust, interpretable ensemble model.\n\nStratifiedShuffleSplit: Ensures even class distribution in train/test splits.\n\nf1_score: Common metric for classification, especially with class imbalance.\n\nCounter: For analyzing class distributions or any counts.\n\nos: For file system operations.","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ntest_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\")\ntest_demographics_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\")\n\ntrain_df = train_df.loc[train_df['sequence_type'] == 'Target'].reset_index(drop = True)\n\nsensor_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n\nall_sensor_cols = [col for col in train_df.columns if any(s in col for s in ['acc_', 'rot_', 'thm_', 'tof_'])]\n\ntrain_df[all_sensor_cols] = train_df[all_sensor_cols].fillna(-1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:29.137001Z","iopub.execute_input":"2025-05-30T16:22:29.137252Z","iopub.status.idle":"2025-05-30T16:22:50.868809Z","shell.execute_reply.started":"2025-05-30T16:22:29.137228Z","shell.execute_reply":"2025-05-30T16:22:50.865303Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\ntrain_df['encoded_gesture'] = le.fit_transform(train_df['gesture'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:50.871784Z","iopub.execute_input":"2025-05-30T16:22:50.872215Z","iopub.status.idle":"2025-05-30T16:22:50.940020Z","shell.execute_reply.started":"2025-05-30T16:22:50.872190Z","shell.execute_reply":"2025-05-30T16:22:50.935402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in train_df.columns:\n    print(i)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:50.942187Z","iopub.execute_input":"2025-05-30T16:22:50.942429Z","iopub.status.idle":"2025-05-30T16:22:50.955436Z","shell.execute_reply.started":"2025-05-30T16:22:50.942406Z","shell.execute_reply":"2025-05-30T16:22:50.950003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def competition_metric(y_true, y_pred, le_instance, all_original_gestures):\n    bfrb_gestures = [g for g in all_original_gestures if g in le_instance.classes_]\n    \n    # Binary F1: assuming 'Target' is 1, 'Non-Target' is 0\n    # Since we only trained on 'Target', our model will always predict a BFRB gesture.\n    # Therefore, y_pred_binary will effectively always be 1 for a model trained this way.\n    # This metric part might be misleading on a validation set that only contains 'Target' gestures.\n    # For a proper binary F1, the true labels would need to include non-BFRB types.\n    y_true_binary = np.ones_like(y_true, dtype=int) # All are 'Target' in this filtered dataset\n    y_pred_binary = np.ones_like(y_pred, dtype=int) # Model predicts only BFRB if trained on 'Target'\n    binary_f1 = f1_score(y_true_binary, y_pred_binary, average='binary', pos_label=1, zero_division=0)\n\n    # Macro F1: specific gesture classification\n    # This is calculated only over the BFRB gestures.\n    macro_f1 = f1_score(y_true, y_pred, average='macro', zero_division=0)\n\n    final_score = (binary_f1 + macro_f1) / 2\n    return final_score\n\nunique_sequences = train_df[['sequence_id', 'encoded_gesture']].drop_duplicates()\nunique_sequence_ids = unique_sequences['sequence_id']\nunique_sequence_targets = unique_sequences['encoded_gesture']\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:50.957207Z","iopub.execute_input":"2025-05-30T16:22:50.957432Z","iopub.status.idle":"2025-05-30T16:22:50.996718Z","shell.execute_reply.started":"2025-05-30T16:22:50.957411Z","shell.execute_reply":"2025-05-30T16:22:50.992837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"splitter = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)\nfor train_idx, val_idx in splitter.split(unique_sequence_ids, unique_sequence_targets):\n    train_seq_ids = unique_sequence_ids.iloc[train_idx]\n    val_seq_ids = unique_sequence_ids.iloc[val_idx]\n\nX_train_df = train_df[train_df['sequence_id'].isin(train_seq_ids)]\nX_val_df = train_df[train_df['sequence_id'].isin(val_seq_ids)]\n\ny_train = X_train_df['encoded_gesture']\ny_val = X_val_df['encoded_gesture']\n\nX_train = X_train_df[sensor_cols]\nX_val = X_val_df[sensor_cols]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:22:50.998071Z","iopub.execute_input":"2025-05-30T16:22:50.998304Z","iopub.status.idle":"2025-05-30T16:22:51.747444Z","shell.execute_reply.started":"2025-05-30T16:22:50.998282Z","shell.execute_reply":"2025-05-30T16:22:51.741748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rf_model = RandomForestClassifier(n_estimators=1000, max_depth=8, random_state=42, n_jobs=-1)\nrf_model.fit(X_train, y_train)\n\ny_val_pred = rf_model.predict(X_val)\n\nall_original_gestures_in_train = train_df['gesture'].unique()\nvalidation_score = competition_metric(y_val, y_val_pred, le, all_original_gestures_in_train)\nprint(f\"Validation Score: {validation_score:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:23:48.443322Z","iopub.execute_input":"2025-05-30T16:23:48.443734Z","iopub.status.idle":"2025-05-30T16:23:59.083977Z","shell.execute_reply.started":"2025-05-30T16:23:48.443702Z","shell.execute_reply":"2025-05-30T16:23:59.077320Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    sequence_pd = sequence.to_pandas()\n    sequence_pd[all_sensor_cols] = sequence_pd[all_sensor_cols].fillna(-1)\n    X_inference = sequence_pd[sensor_cols]\n    \n    # Get predictions for all rows in the sequence\n    all_predicted_label_ids = rf_model.predict(X_inference)\n    \n    # Find the most common predicted label ID\n    # Counter returns a list of (element, count) tuples, sorted by count.\n    # We want the element of the first tuple.\n    most_common_label_id = Counter(all_predicted_label_ids).most_common(1)[0][0]\n    \n    # Convert the most common numerical label back to the original gesture string\n    predicted_gesture_str = le.inverse_transform([most_common_label_id])[0]\n    \n    return predicted_gesture_str","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:23:59.084890Z","iopub.execute_input":"2025-05-30T16:23:59.085137Z","iopub.status.idle":"2025-05-30T16:23:59.098120Z","shell.execute_reply.started":"2025-05-30T16:23:59.085113Z","shell.execute_reply":"2025-05-30T16:23:59.092838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    inference_server.run_local_gateway(\n        data_paths=(\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv',\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv',\n        )\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T16:23:59.100632Z","iopub.execute_input":"2025-05-30T16:23:59.100875Z","iopub.status.idle":"2025-05-30T16:24:00.631615Z","shell.execute_reply.started":"2025-05-30T16:23:59.100852Z","shell.execute_reply":"2025-05-30T16:24:00.623428Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}