{"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":59156,"databundleVersionId":13874099,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom pathlib import Path\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ML Libraries\nfrom sklearn.model_selection import GroupKFold, train_test_split\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier\nfrom sklearn.metrics import f1_score, classification_report\nimport lightgbm as lgb\nimport xgboost as xgb\n\n# Set random seed for reproducibility\nSEED = 42\nnp.random.seed(SEED)\n\n# Plotting style\nsns.set_style(\"whitegrid\")\nplt.rcParams['figure.figsize'] = (12, 6)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:28:25.941329Z","iopub.execute_input":"2025-10-07T20:28:25.942039Z","iopub.status.idle":"2025-10-07T20:28:25.948698Z","shell.execute_reply.started":"2025-10-07T20:28:25.942012Z","shell.execute_reply":"2025-10-07T20:28:25.947429Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Load & Prepare","metadata":{}},{"cell_type":"code","source":"# Define paths\nDATA_PATH = Path('/kaggle/input/MABe-mouse-behavior-detection')\nTRAIN_CSV = DATA_PATH / 'train.csv'\nTEST_CSV = DATA_PATH / 'test.csv'\nTRAIN_TRACKING = DATA_PATH / 'train_tracking'\nTEST_TRACKING = DATA_PATH / 'test_tracking'\nTRAIN_ANNOTATION = DATA_PATH / 'train_annotation'\n\n# Load metadata\ntrain_meta = pd.read_csv(TRAIN_CSV)\ntest_meta = pd.read_csv(TEST_CSV)\n\nprint(f\"Train videos: {len(train_meta)}\")\nprint(f\"Test videos: {len(test_meta)}\")\nprint(\"\\nTrain metadata columns:\")\nprint(train_meta.columns.tolist())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:28:33.745438Z","iopub.execute_input":"2025-10-07T20:28:33.745755Z","iopub.status.idle":"2025-10-07T20:28:33.830334Z","shell.execute_reply.started":"2025-10-07T20:28:33.745735Z","shell.execute_reply":"2025-10-07T20:28:33.828974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_tracking_data(video_id, data_path, max_frames=None):\n    \"\"\"Load tracking data for a specific video\"\"\"\n    tracking_file = data_path / f\"{video_id}.parquet\"\n    \n    if not tracking_file.exists():\n        return None\n    \n    df = pd.read_parquet(tracking_file)\n    \n    if max_frames:\n        df = df[df['video_frame'] < max_frames]\n    \n    return df\n\ndef load_annotation_data(video_id, annotation_path):\n    \"\"\"Load annotation data for a specific video\"\"\"\n    annotation_file = annotation_path / f\"{video_id}.parquet\"\n    \n    if not annotation_file.exists():\n        return None\n    \n    return pd.read_parquet(annotation_file)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:28:37.587621Z","iopub.execute_input":"2025-10-07T20:28:37.587988Z","iopub.status.idle":"2025-10-07T20:28:37.594725Z","shell.execute_reply.started":"2025-10-07T20:28:37.587965Z","shell.execute_reply":"2025-10-07T20:28:37.593559Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Exploration","metadata":{}},{"cell_type":"code","source":"def explore_data(train_meta):\n    \"\"\"Explore the training metadata\"\"\"\n\n    \n    # Lab distribution\n    print(\"\\nLab Distribution:\")\n    print(train_meta['lab_id'].value_counts())\n    \n    # Behaviors per lab\n    print(\"\\nUnique behaviors across all labs:\")\n    all_behaviors = set()\n    for behaviors in train_meta['behaviors_labeled'].dropna():\n        beh_list = eval(behaviors)\n        \n        for i in beh_list:\n            buf_behavior = i.split(',')[2]\n            all_behaviors.add(buf_behavior)\n                \n    print(f\"Total unique behaviors: {len(all_behaviors)}\")\n    print(sorted(all_behaviors))\n    \n    # Tracking methods\n    print(\"\\nTracking Methods:\")\n    print(train_meta['tracking_method'].value_counts())\n    \n    # Arena types\n    print(\"\\nArena Types:\")\n    print(train_meta['arena_type'].value_counts())\n    \n    # Video statistics\n    print(\"\\nVideo Duration Statistics:\")\n    print(train_meta['video_duration_sec'].describe())\n    \n    # Visualizations\n    fig, axes = plt.subplots(2, 2, figsize=(15, 10))\n    \n    # Lab distribution\n    train_meta['lab_id'].value_counts().plot(kind='bar', ax=axes[0, 0])\n    axes[0, 0].set_title('Videos per Lab')\n    axes[0, 0].set_xlabel('Lab ID')\n    axes[0, 0].set_ylabel('Count')\n    \n    # Video duration\n    axes[0, 1].hist(train_meta['video_duration_sec'], bins=30, edgecolor='black')\n    axes[0, 1].set_title('Video Duration Distribution')\n    axes[0, 1].set_xlabel('Duration (seconds)')\n    axes[0, 1].set_ylabel('Count')\n    \n    # FPS distribution\n    train_meta['frames_per_second'].value_counts().plot(kind='bar', ax=axes[1, 0])\n    axes[1, 0].set_title('Frames Per Second Distribution')\n    axes[1, 0].set_xlabel('FPS')\n    axes[1, 0].set_ylabel('Count')\n    \n    # Tracking method\n    train_meta['tracking_method'].value_counts().plot(kind='bar', ax=axes[1, 1])\n    axes[1, 1].set_title('Tracking Methods')\n    axes[1, 1].set_xlabel('Method')\n    axes[1, 1].set_ylabel('Count')\n    \n    plt.tight_layout()\n    plt.show()\n\nexplore_data(train_meta)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:53:23.233072Z","iopub.execute_input":"2025-10-07T20:53:23.233403Z","iopub.status.idle":"2025-10-07T20:53:24.930178Z","shell.execute_reply.started":"2025-10-07T20:53:23.233380Z","shell.execute_reply":"2025-10-07T20:53:24.929063Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Checking of video sample","metadata":{}},{"cell_type":"code","source":"# Sample: Load one video for exploration\nsample = train_meta.iloc[3]\nprint(f\"\\nLoading sample video: {sample['video_id']}\")\n\nsample_tracking = load_tracking_data(sample['video_id'], TRAIN_TRACKING / sample['lab_id'])\nsample_annotation = load_annotation_data(sample['video_id'], TRAIN_ANNOTATION / sample['lab_id'])\n\nif sample_tracking is not None:\n    print(f\"\\nTracking data shape: {sample_tracking.shape}\")\n    print(sample_tracking.head(10))\n    \nif sample_annotation is not None:\n    print(f\"\\nAnnotation data shape: {sample_annotation.shape}\")\n    print(sample_annotation.head())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:53:36.033266Z","iopub.execute_input":"2025-10-07T20:53:36.033617Z","iopub.status.idle":"2025-10-07T20:53:36.164261Z","shell.execute_reply.started":"2025-10-07T20:53:36.033593Z","shell.execute_reply":"2025-10-07T20:53:36.162935Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Feature engineering","metadata":{}},{"cell_type":"code","source":"def compute_distance(x1, y1, x2, y2):\n    \"\"\"Compute Euclidean distance\"\"\"\n    return np.sqrt((x2 - x1)**2 + (y2 - y1)**2)\n\ndef compute_velocity(df, bodypart='centroid'):\n    \"\"\"Compute velocity for a body part\"\"\"\n    df_part = df[df['bodypart'] == bodypart].sort_values('video_frame')\n    \n    dx = df_part['x'].diff()\n    dy = df_part['y'].diff()\n    velocity = np.sqrt(dx**2 + dy**2)\n    \n    return velocity\n\ndef extract_features_from_tracking(tracking_df, window_size=30):\n    \"\"\"\n    Extract features from tracking data for behavior classification\n    \n    Features include:\n    - Position statistics (mean, std, min, max)\n    - Velocity and acceleration\n    - Distance between mice\n    - Body part angles and orientations\n    - Movement patterns\n    \"\"\"\n    \n    features_list = []\n    \n    # Get unique mice and body parts\n    mice = tracking_df['mouse_id'].unique()\n    bodyparts = tracking_df['bodypart'].unique()\n    frames = sorted(tracking_df['video_frame'].unique())\n    \n    # Process in windows\n    for i in range(0, len(frames), window_size // 2):  # 50% overlap\n        window_frames = frames[i:i + window_size]\n        \n        if len(window_frames) < window_size // 2:\n            continue\n        \n        window_data = tracking_df[tracking_df['video_frame'].isin(window_frames)]\n        \n        feature_dict = {\n            'start_frame': window_frames[0],\n            'end_frame': window_frames[-1],\n            'center_frame': (window_frames[0] + window_frames[-1]) // 2\n        }\n        \n        # Features for each mouse\n        for mouse in mice:\n            mouse_data = window_data[window_data['mouse_id'] == mouse]\n            \n            for bodypart in bodyparts:\n                part_data = mouse_data[mouse_data['bodypart'] == bodypart].sort_values('video_frame')\n                \n                if len(part_data) == 0:\n                    continue\n                \n                # Position statistics\n                feature_dict[f'mouse{mouse}_{bodypart}_x_mean'] = part_data['x'].mean()\n                feature_dict[f'mouse{mouse}_{bodypart}_x_std'] = part_data['x'].std()\n                feature_dict[f'mouse{mouse}_{bodypart}_y_mean'] = part_data['y'].mean()\n                feature_dict[f'mouse{mouse}_{bodypart}_y_std'] = part_data['y'].std()\n                \n                # Velocity\n                dx = part_data['x'].diff()\n                dy = part_data['y'].diff()\n                velocity = np.sqrt(dx**2 + dy**2)\n                feature_dict[f'mouse{mouse}_{bodypart}_velocity_mean'] = velocity.mean()\n                feature_dict[f'mouse{mouse}_{bodypart}_velocity_max'] = velocity.max()\n                \n                # Acceleration\n                acceleration = velocity.diff()\n                feature_dict[f'mouse{mouse}_{bodypart}_accel_mean'] = acceleration.mean()\n        \n        # Inter-mouse features\n        if len(mice) >= 2:\n            for i, mouse1 in enumerate(mice):\n                for mouse2 in mice[i+1:]:\n                    # Distance between centroids (if available)\n                    m1_data = window_data[(window_data['mouse_id'] == mouse1) & \n                                         (window_data['bodypart'] == bodyparts[0])]\n                    m2_data = window_data[(window_data['mouse_id'] == mouse2) & \n                                         (window_data['bodypart'] == bodyparts[0])]\n                    \n                    if len(m1_data) > 0 and len(m2_data) > 0:\n                        # Merge on frame to compute distances\n                        merged = pd.merge(m1_data[['video_frame', 'x', 'y']], \n                                        m2_data[['video_frame', 'x', 'y']], \n                                        on='video_frame', suffixes=('_1', '_2'))\n                        \n                        distances = compute_distance(merged['x_1'], merged['y_1'], \n                                                    merged['x_2'], merged['y_2'])\n                        \n                        feature_dict[f'mouse{mouse1}_mouse{mouse2}_dist_mean'] = distances.mean()\n                        feature_dict[f'mouse{mouse1}_mouse{mouse2}_dist_min'] = distances.min()\n                        feature_dict[f'mouse{mouse1}_mouse{mouse2}_dist_std'] = distances.std()\n        \n        features_list.append(feature_dict)\n    \n    return pd.DataFrame(features_list)\n\n# Test feature extraction on sample video\nif sample_tracking is not None:\n    print(\"\\nExtracting features from sample video...\")\n    sample_features = extract_features_from_tracking(sample_tracking, window_size=30)\n    print(f\"Extracted {len(sample_features)} feature windows\")\n    print(f\"Number of features: {len(sample_features.columns)}\")\n    print(\"\\nSample features:\")\n    print(sample_features.head())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T20:53:56.105299Z","iopub.execute_input":"2025-10-07T20:53:56.106244Z","iopub.status.idle":"2025-10-07T20:56:07.426116Z","shell.execute_reply.started":"2025-10-07T20:53:56.106212Z","shell.execute_reply":"2025-10-07T20:56:07.425004Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preparing training data","metadata":{}},{"cell_type":"code","source":"def prepare_training_data(train_meta, tracking_path, annotation_path, \n                         max_videos=None, window_size=30):\n    \"\"\"\n    Prepare training data from videos\n    \n    Returns features (X) and labels (y)\n    \"\"\"\n\n    \n    all_features = []\n    all_labels = []\n    all_metadata = []\n    \n    if max_videos:\n        train_meta = train_meta.head(max_videos)\n    \n    for idx, sample in train_meta.iterrows():\n        video_id = sample['video_id']\n        lab_id = sample['lab_id']\n        if idx % 10 == 0:\n            print(f\"Processing video {idx+1}/{len(train_meta)}: {video_id}\")\n        \n        # Load tracking and annotation\n        tracking_path = tracking_path / f\"{lab_id}\"\n        annotation_path = annotation_path / f\"{lab_id}\"\n        tracking = load_tracking_data(video_id, tracking_path)\n        annotation = load_annotation_data(video_id, annotation_path)\n        \n        if tracking is None or annotation is None:\n            continue\n        \n        # Extract features\n        features = extract_features_from_tracking(tracking, window_size=window_size)\n        \n        if len(features) == 0:\n            continue\n        \n        # Match features with annotations\n        for _, row in annotation.iterrows():\n            start_frame = row['start_frame']\n            end_frame = row['stop_frame']\n            \n            # Find feature windows that overlap with this annotation\n            overlapping = features[\n                ((features['start_frame'] >= start_frame) & (features['start_frame'] <= end_frame)) |\n                ((features['end_frame'] >= start_frame) & (features['end_frame'] <= end_frame)) |\n                ((features['start_frame'] <= start_frame) & (features['end_frame'] >= end_frame))\n            ]\n            \n            for _, feat_row in overlapping.iterrows():\n                all_features.append(feat_row)\n                all_labels.append(row['action'])\n                all_metadata.append({\n                    'video_id': video_id,\n                    'agent_id': row['agent_id'],\n                    'target_id': row['target_id']\n                })\n    \n    X = pd.DataFrame(all_features)\n    y = pd.Series(all_labels)\n    metadata = pd.DataFrame(all_metadata)\n    \n    return X, y, metadata\n\n# Prepare training data\n\nX_train, y_train, train_metadata = prepare_training_data(\n     \n    train_meta, \n    TRAIN_TRACKING, \n    TRAIN_ANNOTATION,\n    #max_videos=20,  \n    window_size=30\n)\n\nprint(f\"\\nTraining data prepared:\")\nprint(f\"Features shape: {X_train.shape}\")\nprint(f\"Labels shape: {y_train.shape}\")\nprint(f\"\\nBehavior distribution:\")\nprint(y_train.value_counts())","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T21:00:17.434436Z","iopub.execute_input":"2025-10-07T21:00:17.434824Z","iopub.status.idle":"2025-10-07T21:03:08.883220Z","shell.execute_reply.started":"2025-10-07T21:00:17.434799Z","shell.execute_reply":"2025-10-07T21:03:08.881509Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model training - LightGBM","metadata":{}},{"cell_type":"code","source":"# Encode labels\nlabel_encoder = LabelEncoder()\ny_train_encoded = label_encoder.fit_transform(y_train)\n\n# Remove non-numeric columns for training\nfeature_cols = [col for col in X_train.columns if col not in ['start_frame', 'end_frame', 'center_frame']]\nX_train_clean = X_train[feature_cols].fillna(0)\n\nprint(f\"\\nTraining with {len(feature_cols)} features\")\n\n# Split data\nX_tr, X_val, y_tr, y_val = train_test_split(\n    X_train_clean, y_train_encoded, \n    test_size=0.2, random_state=SEED, stratify=y_train_encoded\n)\n\nprint(f\"Train set: {len(X_tr)}, Validation set: {len(X_val)}\")\n\n# Train LightGBM model\nprint(\"\\nTraining LightGBM model...\")\nlgb_params = {\n    'objective': 'multiclass',\n    'num_class': len(label_encoder.classes_),\n    'metric': 'multi_logloss',\n    'num_leaves': 31,\n    'learning_rate': 0.05,\n    'feature_fraction': 0.8,\n    'bagging_fraction': 0.8,\n    'bagging_freq': 5,\n    'verbose': -1,\n    'random_state': SEED\n}\n\ntrain_data = lgb.Dataset(X_tr, label=y_tr)\nval_data = lgb.Dataset(X_val, label=y_val, reference=train_data)\n\nmodel = lgb.train(\n    lgb_params,\n    train_data,\n    num_boost_round=500,\n    valid_sets=[train_data, val_data],\n    valid_names=['train', 'valid'],\n    callbacks=[\n        lgb.early_stopping(stopping_rounds=50),\n        lgb.log_evaluation(period=50)\n    ]\n)\n\n# Evaluate\ny_val_pred = model.predict(X_val)\ny_val_pred_classes = np.argmax(y_val_pred, axis=1)\n\nf1 = f1_score(y_val, y_val_pred_classes, average='weighted')\nprint(f\"\\nValidation F1 Score (weighted): {f1:.4f}\")\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_val, y_val_pred_classes, \n                          target_names=label_encoder.classes_, zero_division=0))\n\n# Feature importance\nfeature_importance = pd.DataFrame({\n    'feature': feature_cols,\n    'importance': model.feature_importance(importance_type='gain')\n}).sort_values('importance', ascending=False)\n\nprint(\"\\nTop 20 Important Features:\")\nprint(feature_importance.head(20))\n\n# Plot feature importance\nplt.figure(figsize=(10, 8))\nfeature_importance.head(20).plot(x='feature', y='importance', kind='barh')\nplt.title('Top 20 Feature Importance')\nplt.xlabel('Importance')\nplt.tight_layout()\nplt.show()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T21:03:31.386302Z","iopub.execute_input":"2025-10-07T21:03:31.386649Z","iopub.status.idle":"2025-10-07T21:03:42.038309Z","shell.execute_reply.started":"2025-10-07T21:03:31.386626Z","shell.execute_reply":"2025-10-07T21:03:42.037233Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predictions","metadata":{}},{"cell_type":"code","source":"def generate_predictions(model, label_encoder, test_meta, tracking_path, \n                        feature_cols, window_size=30, threshold=0.5):\n    \"\"\"Generate predictions for test videos\"\"\"\n    \n    predictions = []\n    row_id = 0\n    \n    for idx, row in test_meta.iterrows():\n        video_id = row['video_id']\n        \n        if idx % 10 == 0:\n            print(f\"Predicting video {idx+1}/{len(test_meta)}: {video_id}\")\n        \n        # Load tracking\n        tracking = load_tracking_data(video_id, tracking_path / row['lab_id'])\n        \n        if tracking is None:\n            continue\n        \n        # Extract features\n        features = extract_features_from_tracking(tracking, window_size=window_size)\n        \n        if len(features) == 0:\n            continue\n        \n        # Get features in correct order\n        X_test = features[feature_cols].fillna(0)\n        \n        # Predict\n        pred_probs = model.predict(X_test)\n        pred_classes = np.argmax(pred_probs, axis=1)\n        max_probs = np.max(pred_probs, axis=1)\n        \n        # Get behaviors labeled for this video\n        behaviors_labeled = str(row.get('behaviors_labeled', ''))\n        valid_behaviors = set()\n        beh_list = eval(behaviors)\n        for i in beh_list:\n            buf_behavior = i.split(',')[2]\n            valid_behaviors.add(buf_behavior)\n\n        \n        \n        # Convert predictions to actions\n        for i, (pred_class, prob) in enumerate(zip(pred_classes, max_probs)):\n            if prob < threshold:\n                continue\n            \n            action = label_encoder.inverse_transform([pred_class])[0]\n            \n            # Filter by valid behaviors if specified\n            if valid_behaviors and action not in valid_behaviors:\n                continue\n            \n            \n            mice = tracking['mouse_id'].unique()\n            agent_id = mice[0] if len(mice) > 0 else 1\n            target_id = mice[1] if len(mice) > 1 else agent_id\n            \n            predictions.append({\n                'row_id': row_id,\n                'video_id': video_id,\n                'agent_id': int(agent_id),\n                'target_id': int(target_id),\n                'action': action,\n                'start_frame': int(features.iloc[i]['start_frame']),\n                'stop_frame': int(features.iloc[i]['end_frame'])\n            })\n            \n            row_id += 1\n    \n    return pd.DataFrame(predictions)\n\n# Generate predictions\nprint(\"\\nGenerating predictions for test set...\")\ntest_predictions = generate_predictions(\n    model, \n    label_encoder, \n    test_meta.head(10), \n    TEST_TRACKING,\n    feature_cols,\n    window_size=30,\n    threshold=0.3\n)\n\nprint(f\"\\nGenerated {len(test_predictions)} predictions\")\nprint(test_predictions.head(10))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T21:30:14.617932Z","iopub.execute_input":"2025-10-07T21:30:14.619335Z","iopub.status.idle":"2025-10-07T21:33:06.295155Z","shell.execute_reply.started":"2025-10-07T21:30:14.619273Z","shell.execute_reply":"2025-10-07T21:33:06.293925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prepare submission\nsubmission = test_predictions[['row_id', 'video_id', 'agent_id', 'target_id', 'action', 'start_frame', 'stop_frame']]\n\nsubmission['agent_id'] = 'mouse'+submission['agent_id'].astype(str)\nsubmission['target_id'] = 'mouse'+submission['target_id'].astype(str)\n\n# Save submission\nsubmission.to_csv('submission.csv', index=False)\nprint(f\"Total predictions: {len(submission)}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-07T21:35:52.367894Z","iopub.execute_input":"2025-10-07T21:35:52.368221Z","iopub.status.idle":"2025-10-07T21:35:52.386461Z","shell.execute_reply.started":"2025-10-07T21:35:52.368201Z","shell.execute_reply":"2025-10-07T21:35:52.385370Z"}},"outputs":[],"execution_count":null}]}