{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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"},{"sourceId":12540981,"sourceType":"datasetVersion","datasetId":7917146}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\n!pip install --no-index --find-links=/kaggle/input/cmi-hexo-agent-run-1-data/wheels/wheels scikit-learn==1.7.1\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:08:57.854756Z","iopub.execute_input":"2025-07-22T06:08:57.855593Z","iopub.status.idle":"2025-07-22T06:09:07.517270Z","shell.execute_reply.started":"2025-07-22T06:08:57.855554Z","shell.execute_reply":"2025-07-22T06:09:07.515983Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport polars as pl\nimport pickle\nimport os\nfrom typing import Dict, Any\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:23.529932Z","iopub.execute_input":"2025-07-22T06:09:23.530622Z","iopub.status.idle":"2025-07-22T06:09:24.533980Z","shell.execute_reply.started":"2025-07-22T06:09:23.530587Z","shell.execute_reply":"2025-07-22T06:09:24.532987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_models():\n    \"\"\"Load trained models and configuration\"\"\"\n    with open('/kaggle/input/cmi-hexo-agent-run-1-data/binary_model.pkl', 'rb') as f:\n        binary_model = pickle.load(f)\n    \n    with open('/kaggle/input/cmi-hexo-agent-run-1-data/multiclass_model.pkl', 'rb') as f:\n        multiclass_model = pickle.load(f)\n    \n    with open('/kaggle/input/cmi-hexo-agent-run-1-data/feature_cols.pkl', 'rb') as f:\n        feature_cols = pickle.load(f)\n    \n    with open('/kaggle/input/cmi-hexo-agent-run-1-data/target_gestures.pkl', 'rb') as f:\n        target_gestures = pickle.load(f)\n    \n    return binary_model, multiclass_model, feature_cols, target_gestures","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:28.298962Z","iopub.execute_input":"2025-07-22T06:09:28.299414Z","iopub.status.idle":"2025-07-22T06:09:28.305441Z","shell.execute_reply.started":"2025-07-22T06:09:28.299390Z","shell.execute_reply":"2025-07-22T06:09:28.304382Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_sequence_features(seq_data_pl, demographics_pl):\n    \"\"\"Create features for a single sequence\"\"\"\n    # Convert to pandas for easier processing\n    seq_data = seq_data_pl.to_pandas()\n    demographics = demographics_pl.to_pandas() if len(demographics_pl) > 0 else pd.DataFrame()\n    \n    if len(seq_data) == 0:\n        return None\n    \n    # Basic sequence info\n    features = {\n        'sequence_length': len(seq_data)\n    }\n    \n    # Get subject info\n    if 'subject' in seq_data.columns:\n        subject = seq_data['subject'].iloc[0]\n        if len(demographics) > 0 and 'subject' in demographics.columns:\n            demo_row = demographics[demographics['subject'] == subject]\n            if len(demo_row) > 0:\n                for col in ['adult_child', 'age', 'sex', 'handedness', 'height_cm', \n                           'shoulder_to_wrist_cm', 'elbow_to_wrist_cm']:\n                    if col in demo_row.columns:\n                        features[col] = demo_row[col].iloc[0]\n    \n    # Fill missing demographic features with defaults\n    demo_defaults = {\n        'adult_child': 1, 'age': 25, 'sex': 1, 'handedness': 1, \n        'height_cm': 170, 'shoulder_to_wrist_cm': 52, 'elbow_to_wrist_cm': 25\n    }\n    for key, default in demo_defaults.items():\n        if key not in features:\n            features[key] = default\n    \n    # IMU features (always available)\n    imu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    for col in imu_cols:\n        if col in seq_data.columns:\n            values = seq_data[col].dropna()\n            if len(values) > 0:\n                features[f'{col}_mean'] = values.mean()\n                features[f'{col}_std'] = values.std()\n                features[f'{col}_min'] = values.min()\n                features[f'{col}_max'] = values.max()\n                features[f'{col}_range'] = values.max() - values.min()\n            else:\n                features[f'{col}_mean'] = 0\n                features[f'{col}_std'] = 0\n                features[f'{col}_min'] = 0\n                features[f'{col}_max'] = 0\n                features[f'{col}_range'] = 0\n    \n    # Thermopile features (may be missing)\n    thm_cols = [f'thm_{i}' for i in range(1, 6)]\n    has_thm_data = False\n    for col in thm_cols:\n        if col in seq_data.columns:\n            values = seq_data[col].dropna()\n            if len(values) > 0:\n                has_thm_data = True\n                features[f'{col}_mean'] = values.mean()\n                features[f'{col}_std'] = values.std()\n                features[f'{col}_min'] = values.min()\n                features[f'{col}_max'] = values.max()\n            else:\n                features[f'{col}_mean'] = np.nan\n                features[f'{col}_std'] = np.nan\n                features[f'{col}_min'] = np.nan\n                features[f'{col}_max'] = np.nan\n        else:\n            features[f'{col}_mean'] = np.nan\n            features[f'{col}_std'] = np.nan\n            features[f'{col}_min'] = np.nan\n            features[f'{col}_max'] = np.nan\n    \n    features['has_thm_data'] = has_thm_data\n    \n    # Time-of-flight features (may be missing) - sample subset\n    tof_sample_cols = [f'tof_1_v{i}' for i in [0, 15, 31, 47, 63]] + [f'tof_3_v{i}' for i in [0, 31, 63]]\n    has_tof_data = False\n    for col in tof_sample_cols:\n        if col in seq_data.columns:\n            values = seq_data[col].dropna()\n            values = values[values != -1]  # Remove no-response values\n            if len(values) > 0:\n                has_tof_data = True\n                features[f'{col}_mean'] = values.mean()\n                features[f'{col}_std'] = values.std()\n            else:\n                features[f'{col}_mean'] = np.nan\n                features[f'{col}_std'] = np.nan\n        else:\n            features[f'{col}_mean'] = np.nan\n            features[f'{col}_std'] = np.nan\n    \n    features['has_tof_data'] = has_tof_data\n    \n    # Phase-specific features\n    for phase in ['Transition', 'Pause', 'Gesture']:\n        if 'behavior' in seq_data.columns:\n            phase_data = seq_data[seq_data['behavior'].str.contains(phase, na=False)]\n        else:\n            phase_data = pd.DataFrame()\n            \n        features[f'{phase.lower()}_length'] = len(phase_data)\n        \n        if len(phase_data) > 0:\n            for col in ['acc_x', 'acc_y', 'acc_z']:\n                if col in phase_data.columns:\n                    values = phase_data[col].dropna()\n                    if len(values) > 0:\n                        features[f'{phase.lower()}_{col}_mean'] = values.mean()\n                        features[f'{phase.lower()}_{col}_std'] = values.std()\n                    else:\n                        features[f'{phase.lower()}_{col}_mean'] = 0\n                        features[f'{phase.lower()}_{col}_std'] = 0\n                else:\n                    features[f'{phase.lower()}_{col}_mean'] = 0\n                    features[f'{phase.lower()}_{col}_std'] = 0\n        else:\n            for col in ['acc_x', 'acc_y', 'acc_z']:\n                features[f'{phase.lower()}_{col}_mean'] = 0\n                features[f'{phase.lower()}_{col}_std'] = 0\n    \n    return features","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:28.575333Z","iopub.execute_input":"2025-07-22T06:09:28.575988Z","iopub.status.idle":"2025-07-22T06:09:28.594157Z","shell.execute_reply.started":"2025-07-22T06:09:28.575956Z","shell.execute_reply":"2025-07-22T06:09:28.593165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load models at module level\nprint(\"Loading trained models...\")\ntry:\n    BINARY_MODEL, MULTICLASS_MODEL, FEATURE_COLS, TARGET_GESTURES = load_models()\n    print(\"Models loaded successfully!\")\nexcept Exception as e:\n    print(f\"Error loading models: {e}\")\n    BINARY_MODEL = MULTICLASS_MODEL = FEATURE_COLS = TARGET_GESTURES = None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:28.810457Z","iopub.execute_input":"2025-07-22T06:09:28.810795Z","iopub.status.idle":"2025-07-22T06:09:30.107256Z","shell.execute_reply.started":"2025-07-22T06:09:28.810772Z","shell.execute_reply":"2025-07-22T06:09:30.106354Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"\n    Predict gesture for a single sequence.\n    \n    Args:\n        sequence: Polars DataFrame with sensor data for one sequence\n        demographics: Polars DataFrame with demographics data\n    \n    Returns:\n        str: Predicted gesture name\n    \"\"\"\n    \n    if BINARY_MODEL is None or MULTICLASS_MODEL is None:\n        # Fallback prediction\n        return 'Text on phone'\n    \n    try:\n        # Create features\n        features_dict = create_sequence_features(sequence, demographics)\n        \n        if features_dict is None:\n            return 'Text on phone'\n        \n        # Convert to DataFrame with proper feature columns\n        features_df = pd.DataFrame([features_dict])\n        \n        # Ensure all required features are present\n        for col in FEATURE_COLS:\n            if col not in features_df.columns:\n                features_df[col] = 0\n        \n        # Select and order features to match training\n        X = features_df[FEATURE_COLS].copy()\n        X = X.fillna(0)\n        \n        # Get predictions from both models\n        binary_pred = BINARY_MODEL.predict(X)[0]\n        multiclass_pred = MULTICLASS_MODEL.predict(X)[0]\n        \n        # If binary model says it's non-target, return a non-target gesture\n        if binary_pred == 0:\n            # Return the multiclass prediction if it's non_target, otherwise a common non-target gesture\n            if multiclass_pred == 'non_target':\n                return 'Text on phone'  # Most common non-target gesture\n            else:\n                return 'Text on phone'  # Default to common non-target\n        else:\n            # Binary model says it's target, use multiclass prediction\n            if multiclass_pred in TARGET_GESTURES:\n                return multiclass_pred\n            else:\n                # Fallback to most common target gesture\n                return 'Neck - scratch'\n    \n    except Exception as e:\n        print(f\"Error in prediction: {e}\")\n        return 'Text on phone'  # Safe fallback","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:30.108896Z","iopub.execute_input":"2025-07-22T06:09:30.109323Z","iopub.status.idle":"2025-07-22T06:09:30.118700Z","shell.execute_reply.started":"2025-07-22T06:09:30.109298Z","shell.execute_reply":"2025-07-22T06:09:30.117779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"! cp -r /kaggle/input/cmi-detect-behavior-with-sensor-data/kaggle_evaluation .","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-22T06:09:30.119770Z","iopub.execute_input":"2025-07-22T06:09:30.120702Z","iopub.status.idle":"2025-07-22T06:09:30.322585Z","shell.execute_reply.started":"2025-07-22T06:09:30.120666Z","shell.execute_reply":"2025-07-22T06:09:30.321118Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\n\ninference_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-07-22T06:09:30.325091Z","iopub.execute_input":"2025-07-22T06:09:30.325457Z","iopub.status.idle":"2025-07-22T06:09:32.427816Z","shell.execute_reply.started":"2025-07-22T06:09:30.325427Z","shell.execute_reply":"2025-07-22T06:09:32.426493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}