{"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":"# 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-06-23T09:47:22.791077Z","iopub.execute_input":"2025-06-23T09:47:22.791372Z","iopub.status.idle":"2025-06-23T09:47:25.117263Z","shell.execute_reply.started":"2025-06-23T09:47:22.791341Z","shell.execute_reply":"2025-06-23T09:47:25.116352Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Import Library","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom scipy.stats import skew, kurtosis, iqr\nfrom sklearn.metrics import classification_report, accuracy_score\n\n# Deep learning libraries\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import LSTM, Dense, Dropout\nfrom tensorflow.keras.utils import to_categorical","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:47:25.118954Z","iopub.execute_input":"2025-06-23T09:47:25.119358Z","iopub.status.idle":"2025-06-23T09:47:43.925396Z","shell.execute_reply.started":"2025-06-23T09:47:25.119334Z","shell.execute_reply":"2025-06-23T09:47:43.924513Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# IMPORTANT: Define your target behaviors (BFRB-like gestures) here.\n# Any behavior NOT in this list will be classified as 'non_target' for the submission file.\n# Replace these examples with the actual target behaviors from your dataset.\ntarget_behaviors = ['bfrb_type_A', 'bfrb_type_B'] # Example BFRB gesture types\n# Assuming other behaviors like 'walking', 'sitting', 'eating' etc. would be non-target\n\n# Define expected sensor columns for the Helios device\n# IMPORTANT: Adjust these lists to match the exact column names in your 'your_actual_data.csv'\n# If a sensor type is not present in your training data, it will be handled.\nIMU_SENSOR_COLUMNS = ['acc_x', 'acc_y', 'acc_z', 'gyro_x', 'gyro_y', 'gyro_z', 'mag_x', 'mag_y', 'mag_z']\nTHERMOPILE_SENSOR_COLUMNS = ['therm_1', 'therm_2', 'therm_3', 'therm_4'] # Example thermopile columns\nTOF_SENSOR_COLUMNS = ['tof_distance', 'tof_signal_strength'] # Example Time-of-Flight columns\n\nALL_EXPECTED_SENSOR_COLUMNS = IMU_SENSOR_COLUMNS + THERMOPILE_SENSOR_COLUMNS + TOF_SENSOR_COLUMNS","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:47:43.926411Z","iopub.execute_input":"2025-06-23T09:47:43.927123Z","iopub.status.idle":"2025-06-23T09:47:43.932527Z","shell.execute_reply.started":"2025-06-23T09:47:43.927087Z","shell.execute_reply":"2025-06-23T09:47:43.931640Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:47:43.933625Z","iopub.execute_input":"2025-06-23T09:47:43.933973Z","iopub.status.idle":"2025-06-23T09:48:19.273819Z","shell.execute_reply.started":"2025-06-23T09:47:43.933943Z","shell.execute_reply":"2025-06-23T09:48:19.272885Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.columns[1:50]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:48:19.275903Z","iopub.execute_input":"2025-06-23T09:48:19.276194Z","iopub.status.idle":"2025-06-23T09:48:19.285558Z","shell.execute_reply.started":"2025-06-23T09:48:19.276162Z","shell.execute_reply":"2025-06-23T09:48:19.284459Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. Load Actual Sensor Data¶","metadata":{}},{"cell_type":"code","source":"data_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv' # <--- CHANGE THIS TO YOUR DATA FILE PATH\n\ntry:\n    df = pd.read_csv(data_path)\n    # Check for core columns needed for windowing and labeling\n    core_required_columns = ['timestamp', 'behavior'] + [col for col in IMU_SENSOR_COLUMNS if col in df.columns]\n    if not all(col in df.columns for col in core_required_columns):\n        print(f\"Warning: Missing some core IMU columns or 'timestamp'/'behavior'. Please check your CSV header.\")\n        print(f\"Expected: {core_required_columns} + any thermopile/ToF columns\")\n        # Do not exit immediately, but proceed with available columns.\n        # This will lead to empty features for missing sensors, which is desired for inference on partial data.\n\n    print(f\"Data loaded successfully from: {data_path}\")\n    print(\"Sample Data Head:\")\n    print(df.head())\n    print(f\"Total number of samples: {len(df)}\")\n    print(f\"Columns found in data: {df.columns.tolist()}\")\n    print(\"\\n\")\nexcept FileNotFoundError:\n    print(f\"Error: Data file not found at '{data_path}'. Please check the path.\")\n    print(\"Exiting script. Please provide a valid data_path.\")\n    exit()\nexcept Exception as e:\n    print(f\"An unexpected error occurred while loading data: {e}\")\n    exit()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:48:19.286785Z","iopub.execute_input":"2025-06-23T09:48:19.287067Z","iopub.status.idle":"2025-06-23T09:48:48.843843Z","shell.execute_reply.started":"2025-06-23T09:48:19.287042Z","shell.execute_reply":"2025-06-23T09:48:48.842661Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Feature Engineering","metadata":{}},{"cell_type":"code","source":"\nwindow_size = 50 # Number of data points in each window (e.g., 50 samples)\noverlap = 25     # Number of overlapping data points (e.g., 25 samples)\n\nfeatures = []\nlabels = []\nwindow_indices = []\n\n# Helper function to extract statistical features for a single sensor series\ndef extract_sensor_features(series):\n    if series.empty:\n        # Return zeros for all features if series is empty (e.g., sensor not present)\n        # This list must match the number of features extracted per sensor type below\n        return [0.0] * 12 # 12 features per sensor (mean, std, min, max, median, var, rms, sma, ptp, iqr, skew, kurt)\n    return [\n        series.mean(), series.std(), series.min(), series.max(),\n        series.median(), series.var(), np.sqrt(np.mean(series**2)),\n        series.abs().sum(), np.ptp(series), iqr(series),\n        skew(series), kurtosis(series)\n    ]\n\n# The total number of features we expect from all sensors\n# (12 features * number of sensors in ALL_EXPECTED_SENSOR_COLUMNS)\nTOTAL_FEATURE_DIMENSION = len(ALL_EXPECTED_SENSOR_COLUMNS) * 12\n\nfor i in range(0, len(df) - window_size, window_size - overlap):\n    window = df.iloc[i : i + window_size]\n\n    # Ensure the window has enough data points and a consistent label\n    if len(window) == window_size and window['behavior'].nunique() == 1:\n        current_window_features = []\n        for col in ALL_EXPECTED_SENSOR_COLUMNS:\n            if col in window.columns:\n                current_window_features.extend(extract_sensor_features(window[col]))\n            else:\n                # If sensor column is not in this window/dataframe, fill with zeros\n                current_window_features.extend([0.0] * 12) # 12 features per sensor\n\n        # Ensure the feature vector has the consistent total dimension\n        if len(current_window_features) == TOTAL_FEATURE_DIMENSION:\n            features.append(current_window_features)\n            labels.append(window['behavior'].iloc[0])\n            window_indices.append(i) # Store the starting index of the window\n        else:\n            print(f\"Warning: Feature dimension mismatch at window {i}. Expected {TOTAL_FEATURE_DIMENSION}, got {len(current_window_features)}. Skipping.\")\nif not features:\n    print(\"No features extracted. Please ensure your data is large enough for the chosen window size and overlap.\")\n    exit()\n\nX = np.array(features)\ny = np.array(labels)\nwindow_ids = np.array(window_indices)\n\nprint(f\"Extracted {X.shape[0]} windows with {X.shape[1]} features each.\")\nprint(f\"Labels shape: {y.shape}\")\nprint(\"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:48:48.845159Z","iopub.execute_input":"2025-06-23T09:48:48.845486Z","iopub.status.idle":"2025-06-23T09:49:01.725047Z","shell.execute_reply.started":"2025-06-23T09:48:48.845450Z","shell.execute_reply":"2025-06-23T09:49:01.724115Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Encode Labels and Split Data¶","metadata":{}},{"cell_type":"code","source":"\ntry:\n    # Ensure y is a numpy array and not empty\n    if not isinstance(y, np.ndarray) or y.size == 0:\n        print(\"Error: 'y' (labels) is not a valid numpy array or is empty when trying to encode. Check data loading and feature engineering steps.\")\n        exit()\n\n    label_encoder = LabelEncoder()\n    y_encoded = label_encoder.fit_transform(y)\n    num_classes = len(label_encoder.classes_)\n    print(f\"Detected {num_classes} behavior classes: {label_encoder.classes_}\")\n\n    y_categorical = to_categorical(y_encoded, num_classes=num_classes)\n\n    if len(X) < 2 or num_classes < 2:\n        print(\"Not enough samples or unique classes to split data. Need at least 2 samples and 2 unique classes.\")\n        print(\"Consider adjusting window_size/overlap or providing more data with varied behaviors.\")\n        exit()\n\n    X_train, X_test, y_train, y_test, window_ids_train, window_ids_test = train_test_split(\n        X, y_categorical, window_ids, test_size=0.3, random_state=42, stratify=y_encoded\n    )\n    print(f\"Training features shape: {X_train.shape}\")\n    print(f\"Testing features shape: {X_test.shape}\")\n    print(f\"Training labels shape: {y_train.shape}\")\n    print(f\"Testing labels shape: {y_test.shape}\")\n    print(f\"Testing window IDs shape: {window_ids_test.shape}\")\n    print(\"\\n\")\n\nexcept NameError:\n    print(\"Critical Error: 'y' is not defined before starting label encoding. This indicates an issue in the preceding 'Feature Engineering' section or data loading, or an unstable environment.\")\n    print(\"Please ensure that 'your_actual_data.csv' exists, contains sufficient data, and has a 'behavior' column for labels.\")\n    exit()\nexcept Exception as e:\n    print(f\"An unexpected error occurred during label encoding or data splitting: {e}\")\n    exit()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:01.726036Z","iopub.execute_input":"2025-06-23T09:49:01.726312Z","iopub.status.idle":"2025-06-23T09:49:01.741533Z","shell.execute_reply.started":"2025-06-23T09:49:01.726291Z","shell.execute_reply":"2025-06-23T09:49:01.740590Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Feature Scaling (for Deep Learning)","metadata":{}},{"cell_type":"code","source":"scaler = StandardScaler()\nX_train_scaled = scaler.fit_transform(X_train)\nX_test_scaled = scaler.transform(X_test)\nprint(\"Features scaled.\")\nprint(\"\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:01.742791Z","iopub.execute_input":"2025-06-23T09:49:01.743169Z","iopub.status.idle":"2025-06-23T09:49:01.790344Z","shell.execute_reply.started":"2025-06-23T09:49:01.743137Z","shell.execute_reply":"2025-06-23T09:49:01.789481Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Reshape Data for LSTM Input","metadata":{}},{"cell_type":"code","source":"\ntimesteps = 1\ninput_features = X_train_scaled.shape[1] # This will now be TOTAL_FEATURE_DIMENSION\n\nX_train_reshaped = X_train_scaled.reshape(X_train_scaled.shape[0], timesteps, input_features)\nX_test_reshaped = X_test_scaled.reshape(X_test_scaled.shape[0], timesteps, input_features)\n\nprint(f\"Reshaped training features shape: {X_train_reshaped.shape}\")\nprint(f\"Reshaped testing features shape: {X_test_reshaped.shape}\")\nprint(\"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:01.791296Z","iopub.execute_input":"2025-06-23T09:49:01.791614Z","iopub.status.idle":"2025-06-23T09:49:01.797243Z","shell.execute_reply.started":"2025-06-23T09:49:01.791567Z","shell.execute_reply":"2025-06-23T09:49:01.796278Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Define and Train the LSTM Model","metadata":{}},{"cell_type":"code","source":"\nmodel = Sequential()\nmodel.add(LSTM(units=100, input_shape=(timesteps, input_features), activation='relu'))\nmodel.add(Dropout(0.2))\nmodel.add(Dense(units=num_classes, activation='softmax'))\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\n\nhistory = model.fit(\n    X_train_reshaped, y_train,\n    epochs=50,\n    batch_size=32,\n    validation_split=0.1,\n    verbose=1\n)\n\nprint(\"\\nModel training complete.\")\nprint(\"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:01.798286Z","iopub.execute_input":"2025-06-23T09:49:01.798667Z","iopub.status.idle":"2025-06-23T09:49:16.628954Z","shell.execute_reply.started":"2025-06-23T09:49:01.798637Z","shell.execute_reply":"2025-06-23T09:49:16.628097Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluate the LSTM Model","metadata":{}},{"cell_type":"code","source":"print(\"Evaluating the LSTM Model on the Test Set...\")\nloss, accuracy = model.evaluate(X_test_reshaped, y_test, verbose=0)\n\nprint(f\"Test Loss: {loss:.4f}\")\nprint(f\"Test Accuracy: {accuracy:.4f}\")\nprint(\"\\n\")\n\ny_pred_probs = model.predict(X_test_reshaped)\ny_pred_classes = np.argmax(y_pred_probs, axis=1)\ny_true_classes = np.argmax(y_test, axis=1)\n\nprint(\"Classification Report on Test Set (LSTM):\")\ntarget_names_for_report = label_encoder.inverse_transform(np.arange(num_classes))\nprint(classification_report(y_true_classes, y_pred_classes, target_names=target_names_for_report))\nprint(\"\\n\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:16.629959Z","iopub.execute_input":"2025-06-23T09:49:16.630235Z","iopub.status.idle":"2025-06-23T09:49:17.340212Z","shell.execute_reply.started":"2025-06-23T09:49:16.630212Z","shell.execute_reply":"2025-06-23T09:49:17.339087Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Make Predictions on New Data","metadata":{}},{"cell_type":"code","source":"print(\"Making Predictions on New Data (for a single new window)...\")\nif len(df) >= window_size:\n    # Simulating a new window, potentially with missing sensor data if you manually\n    # create a sub-dataframe with only IMU for example.\n    new_sensor_data_for_prediction = df.iloc[len(df) - window_size:] # Takes last window\n\n    new_features_for_prediction = []\n    if len(new_sensor_data_for_prediction) == window_size:\n        current_prediction_features = []\n        for col in ALL_EXPECTED_SENSOR_COLUMNS:\n            if col in new_sensor_data_for_prediction.columns:\n                current_prediction_features.extend(extract_sensor_features(new_sensor_data_for_prediction[col]))\n            else:\n                current_prediction_features.extend([0.0] * 12)\n\n        if len(current_prediction_features) == TOTAL_FEATURE_DIMENSION:\n            new_features_for_prediction.append(current_prediction_features)\n        else:\n             print(f\"Warning: Prediction feature dimension mismatch. Expected {TOTAL_FEATURE_DIMENSION}, got {len(current_prediction_features)}. Skipping.\")\n    if new_features_for_prediction:\n        X_new_prediction_scaled = scaler.transform(np.array(new_features_for_prediction))\n        X_new_prediction_reshaped = X_new_prediction_scaled.reshape(1, timesteps, input_features)\n\n        predicted_probs = model.predict(X_new_prediction_reshaped)\n        predicted_class_index = np.argmax(predicted_probs, axis=1)\n        predicted_behavior = label_encoder.inverse_transform(predicted_class_index)\n\n        # Apply contest-specific mapping for single prediction\n        if predicted_behavior[0] not in target_behaviors:\n            predicted_behavior[0] = 'non_target'\n\n        print(f\"Predicted behavior for a new window of data: {predicted_behavior[0]}\")\n    else:\n        print(\"Could not generate features for prediction (window size mismatch or other issue).\")\nelse:\n    print(\"Not enough total data samples to simulate a 'new' window for prediction.\")\nprint(\"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:17.341177Z","iopub.execute_input":"2025-06-23T09:49:17.341446Z","iopub.status.idle":"2025-06-23T09:49:17.465773Z","shell.execute_reply.started":"2025-06-23T09:49:17.341426Z","shell.execute_reply":"2025-06-23T09:49:17.464895Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Generate Submission File (Contest Ready)","metadata":{}},{"cell_type":"code","source":"print(\"Generating Submission File (Contest Ready)...\")\n\n# Inverse transform predicted classes back to original behavior names\npredicted_behavior_names_full = label_encoder.inverse_transform(y_pred_classes)\n\n# Apply contest rule: collapse non-target behaviors into 'non_target' class\nfinal_submission_gestures = []\nfor behavior_name in predicted_behavior_names_full:\n    if behavior_name in target_behaviors:\n        final_submission_gestures.append(behavior_name)\n    else:\n        final_submission_gestures.append('non_target')\n\n# Create a DataFrame for the submission file\n# 'Window_ID' maps to 'sequence_id' as per contest requirements\nsubmission_df = pd.DataFrame({\n    'sequence_id': window_ids_test, # Using window_ids_test as sequence_id\n    'gesture': final_submission_gestures # Column name changed to 'gesture'\n})\n\n# Sort by sequence_id for a cleaner submission\nsubmission_df = submission_df.sort_values(by='sequence_id').reset_index(drop=True)\n\n# Define the submission file name\nsubmission_file_name = 'submission_predictions_contest.csv' # Changed filename for clarity\nsubmission_df.to_csv(submission_file_name, index=False)\n\nprint(f\"Submission file '{submission_file_name}' created successfully.\")\nprint(\"Sample of the submission file head:\")\nprint(submission_df.head())\nprint(\"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T09:49:17.468451Z","iopub.execute_input":"2025-06-23T09:49:17.468834Z"}},"outputs":[],"execution_count":null}]}