{"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"},{"sourceId":549375,"sourceType":"modelInstanceVersion","modelInstanceId":416862,"modelId":434569}],"dockerImageVersionId":31040,"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\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\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-09-01T02:01:05.429472Z","iopub.execute_input":"2025-09-01T02:01:05.430139Z","iopub.status.idle":"2025-09-01T02:01:05.439976Z","shell.execute_reply.started":"2025-09-01T02:01:05.430108Z","shell.execute_reply":"2025-09-01T02:01:05.439024Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport lightgbm as lgb\nfrom sklearn.model_selection import GroupKFold\nimport pickle\n\n# Load datasets\ntrain_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ndemographics_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n\n# 📊 Feature Engineering Function\ndef create_features(df, demographics_df):\n    \"\"\"\n    Groups data by sequence_id and calculates features.\n    This function is identical to the one used in the submission notebook.\n    \"\"\"\n    \n    # Merge demographics data\n    df = pd.merge(df, demographics_df, on='subject', how='left')\n\n    # Create 'has_full_sensors' feature\n    df['has_full_sensors'] = df['thm_1'].notna().astype(int)\n\n    # Define columns for feature engineering\n    IMU_COLS = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    THERMOPILE_COLS = [f'thm_{i}' for i in range(1, 6)]\n    TOF_COLS = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]\n    DEMOGRAPHIC_COLS = list(demographics_df.drop('subject', axis=1).columns)\n\n    # 1. IMU Features\n    df['acc_mag'] = np.sqrt(df['acc_x']**2 + df['acc_y']**2 + df['acc_z']**2)\n    df['rot_mag'] = np.sqrt(df['rot_w']**2 + df['rot_x']**2 + df['rot_y']**2 + df['rot_z']**2)\n\n    features = []\n    \n    # Group by sequence_id to calculate aggregate statistics\n    agg_features = df.groupby('sequence_id').agg(\n        # Statistical features for all relevant columns\n        mean=pd.NamedAgg(column='acc_mag', aggfunc='mean'),\n        std=pd.NamedAgg(column='acc_mag', aggfunc='std'),\n        max=pd.NamedAgg(column='acc_mag', aggfunc='max'),\n        min=pd.NamedAgg(column='acc_mag', aggfunc='min'),\n        # Add more features as needed (e.g., median, IQR for all columns)\n    ).reset_index()\n\n    features.append(agg_features)\n    \n    # 2. TOF Features\n    # Note: Replace -1 with a suitable value or handle separately. Here, we count them.\n    for col in TOF_COLS:\n        df[f'{col}_is_negative1'] = (df[col] == -1).astype(int)\n    \n    tof_agg = df.groupby('sequence_id').agg(\n        # Count of -1s\n        **{f'tof_neg1_count_{i}': ('tof_1_v0_is_negative1', 'sum') for i in range(1, 6)}\n        # Add more features for TOF, e.g., mean, std of the pixel data\n    ).reset_index()\n\n    features.append(tof_agg)\n\n    # 3. Demographics and Sensor Presence\n    demo_and_presence = df.groupby('sequence_id').first()[DEMOGRAPHIC_COLS + ['has_full_sensors']].reset_index()\n    features.append(demo_and_presence)\n\n    # Merge all features into a single dataframe\n    final_features = features[0]\n    for feat_df in features[1:]:\n        final_features = pd.merge(final_features, feat_df, on='sequence_id', how='left')\n\n    # Clean up and prepare for model\n    final_features.replace([np.inf, -np.inf], np.nan, inplace=True)\n    final_features.fillna(0, inplace=True)\n\n    return final_features\n\n\n# Create features for training data\nprint(f\"Original train_df size: {train_df.shape}\")\nX_features = create_features(train_df, demographics_df)\nprint(f\"X_features size after creation: {X_features.shape}\")\n\ny_target = train_df.groupby('sequence_id')['gesture'].first()\nsubjects = train_df.groupby('sequence_id')['subject'].first()\n\n# The critical section to debug\nprint(f\"Number of unique sequences in y_target: {y_target.shape[0]}\")\nprint(f\"Number of unique sequences in X_features: {X_features.shape[0]}\")\n\n# Set 'sequence_id' as index for proper alignment\n# This is a key step that may be missing in your create_features function.\nX_features.set_index('sequence_id', inplace=True)\ny_target.index = y_target.index.astype(str)\nX_features.index = X_features.index.astype(str)\n\n# Now, align X and y by their common sequence_id.\ncommon_sequences = X_features.index.intersection(y_target.index)\n\nprint(f\"Number of common sequences: {len(common_sequences)}\")\n\n# Final alignment\nif len(common_sequences) == 0:\n    raise ValueError(\"No common sequences found. Your feature engineering function is likely dropping all data.\")\n\nX = X_features.loc[common_sequences]\ny = y_target.loc[common_sequences]\nsubjects = subjects.loc[common_sequences]\n\nprint(f\"Final X size for training: {X.shape}\")\nprint(f\"Final y size for training: {y.shape}\")\n\n# 🤖 Model Training\ncategorical_features = ['adult_child', 'sex', 'handedness', 'has_full_sensors']\nlgbm = lgb.LGBMClassifier(objective='multiclass', random_state=42, n_estimators=1000)\n\n# GroupKFold Cross-Validation\ngkf = GroupKFold(n_splits=5)\nfor fold, (train_idx, val_idx) in enumerate(gkf.split(X, y, groups=subjects)):\n    X_train, y_train = X.iloc[train_idx], y.iloc[train_idx]\n    X_val, y_val = X.iloc[val_idx], y.iloc[val_idx]\n\n    lgbm.fit(X_train, y_train, \n             eval_set=[(X_val, y_val)], \n             eval_metric='multi_logloss', \n             callbacks=[lgb.early_stopping(100)],\n             categorical_feature=categorical_features,\n             )\n\n# Save the trained model\nwith open('model.pkl', 'wb') as f:\n    pickle.dump(lgbm, f)\n\nprint(\"Model trained and saved as 'model.pkl'\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-01T02:09:57.545756Z","iopub.execute_input":"2025-09-01T02:09:57.546169Z","iopub.status.idle":"2025-09-01T02:10:37.016574Z","shell.execute_reply.started":"2025-09-01T02:09:57.546141Z","shell.execute_reply":"2025-09-01T02:10:37.015587Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This script is a complete Kaggle submission file.\n# It reads the test data, applies the necessary feature engineering,\n# uses a pre-trained model to make predictions, and saves the output\n# to a 'submission.csv' file, which Kaggle uses for scoring.\n\nimport pandas as pd\nimport numpy as np\nimport lightgbm as lgb\nimport pickle\nimport os\nimport sys\n\n# Define file paths for the input data and the pre-trained model.\n# These paths are standard for Kaggle competition notebooks.\nTEST_DATA_PATH = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv'\nDEMOGRAPHICS_DATA_PATH = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv'\n# IMPORTANT: You must change the path below to match the directory where\n# you have saved your pre-trained 'model.pkl' file.\nMODEL_PATH = '/kaggle/working/model.pkl'\n\n# 1. Load Data and Model\nprint(\"Loading data and model...\")\ntry:\n    # Load the test data and demographics.\n    test_df = pd.read_csv(TEST_DATA_PATH)\n    demographics_df = pd.read_csv(DEMOGRAPHICS_DATA_PATH)\n    \n    # Load the pre-trained LightGBM model.\n    with open(MODEL_PATH, 'rb') as f:\n        model = pickle.load(f)\n\n    # --- FIX START ---\n    # Add a check to ensure the loaded object is a valid model with a 'predict' method.\n    if not hasattr(model, 'predict'):\n        print(\"Error: The loaded object from 'model.pkl' does not have a '.predict()' method.\")\n        print(\"Please ensure you are pickling the trained model object itself, not an array of predictions.\")\n        sys.exit(1)\n    # --- FIX END ---\n\nexcept FileNotFoundError as e:\n    print(f\"Error: {e}. Please ensure that the data and model files are correctly included.\")\n    sys.exit(1)\n\nprint(\"Data and model loaded successfully.\")\n\n# 2. Feature Engineering\nprint(\"Starting feature engineering...\")\n\n# Merge the test data and demographics.\nfull_test_df = pd.merge(test_df, demographics_df, on='subject', how='left')\n\n# Define columns for feature engineering.\nTOF_COLS = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]\nDEMOGRAPHIC_COLS = list(demographics_df.drop('subject', axis=1).columns)\n\n# Calculate magnitude features for accelerometer and gyroscope.\nfull_test_df['acc_mag'] = np.sqrt(full_test_df['acc_x']**2 + full_test_df['acc_y']**2 + full_test_df['acc_z']**2)\nfull_test_df['rot_mag'] = np.sqrt(full_test_df['rot_w']**2 + full_test_df['rot_x']**2 + full_test_df['rot_y']**2 + full_test_df['rot_z']**2)\n\n# Create a feature indicating if all sensors are present.\nfull_test_df['has_full_sensors'] = full_test_df['thm_1'].notna().astype(int)\n\n# Group the data by 'sequence_id' to generate aggregate features.\nfeatures = []\nagg_features = full_test_df.groupby('sequence_id').agg(\n    mean=pd.NamedAgg(column='acc_mag', aggfunc='mean'),\n    std=pd.NamedAgg(column='acc_mag', aggfunc='std'),\n    max=pd.NamedAgg(column='acc_mag', aggfunc='max'),\n    min=pd.NamedAgg(column='acc_mag', aggfunc='min'),\n).reset_index()\nfeatures.append(agg_features)\n\n# Handle the specific case of -1 values in ToF sensors.\nfor col in TOF_COLS:\n    full_test_df[f'{col}_is_negative1'] = (full_test_df[col] == -1).astype(int)\n\n# Count the number of -1 values per sequence for each ToF sensor.\ntof_agg = full_test_df.groupby('sequence_id').agg(\n    **{f'tof_neg1_count_{i}': ('tof_1_v0_is_negative1', 'sum') for i in range(1, 6)}\n).reset_index()\nfeatures.append(tof_agg)\n\n# Aggregate demographic and sensor presence features.\ndemo_and_presence = full_test_df.groupby('sequence_id').first()[DEMOGRAPHIC_COLS + ['has_full_sensors']].reset_index()\nfeatures.append(demo_and_presence)\n\n# Merge all the engineered feature sets into a single DataFrame.\nfinal_features = features[0]\nfor feat_df in features[1:]:\n    final_features = pd.merge(final_features, feat_df, on='sequence_id', how='left')\n\n# Replace any infinite values with NaN and then fill NaNs with 0.\nfinal_features.replace([np.inf, -np.inf], np.nan, inplace=True)\nfinal_features.fillna(0, inplace=True)\n\nprint(\"Feature engineering complete.\")\n\n# 3. Make Predictions\nprint(\"Making predictions...\")\n\n# Get the list of feature columns for prediction.\ntraining_columns = list(final_features.drop('sequence_id', axis=1).columns)\n\n# Use the loaded model to predict the gesture.\npredictions = model.predict(final_features[training_columns])\n\nprint(\"Predictions complete.\")\n\n# 4. Create and Save Submission File\nprint(\"Creating submission file...\")\n\n# Create the submission DataFrame with 'sequence_id' and predicted 'gesture'.\nsubmission_df = pd.DataFrame({\n    'sequence_id': final_features['sequence_id'],\n    'gesture': predictions\n})\n\n# Save the DataFrame to a CSV file. The file name must be 'submission.csv' for Kaggle.\nsubmission_df.to_csv('submission.csv', index=False)\n\nprint(\"Submission file 'submission.csv' created successfully.\")\nprint(\"Script finished.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-01T03:37:31.139280Z","iopub.execute_input":"2025-09-01T03:37:31.139626Z","iopub.status.idle":"2025-09-01T03:37:31.385906Z","shell.execute_reply.started":"2025-09-01T03:37:31.139601Z","shell.execute_reply":"2025-09-01T03:37:31.385006Z"}},"outputs":[],"execution_count":null}]}