{"cells":[{"cell_type":"markdown","metadata":{},"source":"# MABe Mouse Behavior Detection - Submission Notebook\n\nFast LightGBM model for behavior classification\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import pandas as pd\nimport numpy as np\nfrom pathlib import Path\nimport warnings\nwarnings.filterwarnings('ignore')\n\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nimport lightgbm as lgb\n\nprint(\"✅ Imports loaded successfully\")\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"class FastMouseBehaviorDetector:\n    \"\"\"Fast Mouse Behavior Detection using efficient features\"\"\"\n    \n    def __init__(self):\n        self.model = None\n        self.label_encoder = LabelEncoder()\n        self.scaler = StandardScaler()\n        \n    def load_and_process_video(self, video_id, lab_id, is_train=True):\n        \"\"\"Load and process a single video efficiently\"\"\"\n        folder = 'train_tracking' if is_train else 'test_tracking'\n        file_path = Path(f'/kaggle/input/mabe-mouse-behavior-detection/{folder}/{lab_id}/{video_id}.parquet')\n        \n        if not file_path.exists():\n            return None, None\n            \n        tracking = pd.read_parquet(file_path)\n        \n        annotations = None\n        if is_train:\n            ann_path = Path(f'/kaggle/input/mabe-mouse-behavior-detection/train_annotation/{lab_id}/{video_id}.parquet')\n            if ann_path.exists():\n                annotations = pd.read_parquet(ann_path)\n        \n        return tracking, annotations\n    \n    def extract_simple_features(self, tracking, annotations=None, sample_rate=5):\n        \"\"\"Extract simplified features from tracking data\"\"\"\n        features_list = []\n        labels_list = []\n        \n        frames = tracking['video_frame'].unique()\n        sampled_frames = frames[::sample_rate]\n        \n        for frame in sampled_frames:\n            frame_data = tracking[tracking['video_frame'] == frame]\n            \n            for mouse_id in frame_data['mouse_id'].unique():\n                mouse_frame = frame_data[frame_data['mouse_id'] == mouse_id]\n                \n                feature_dict = {\n                    'frame': frame,\n                    'mouse_id': mouse_id,\n                    'x_mean': mouse_frame['x'].mean(),\n                    'y_mean': mouse_frame['y'].mean(),\n                    'x_std': mouse_frame['x'].std(),\n                    'y_std': mouse_frame['y'].std(),\n                    'x_min': mouse_frame['x'].min(),\n                    'x_max': mouse_frame['x'].max(),\n                    'y_min': mouse_frame['y'].min(),\n                    'y_max': mouse_frame['y'].max(),\n                    'n_parts': len(mouse_frame)\n                }\n                \n                features_list.append(feature_dict)\n                \n                if annotations is not None:\n                    label = 'none'\n                    for _, ann in annotations.iterrows():\n                        if (ann['agent_id'] == mouse_id and \n                            ann['start_frame'] <= frame <= ann['stop_frame']):\n                            label = ann['action']\n                            break\n                    labels_list.append(label)\n        \n        features_df = pd.DataFrame(features_list) if features_list else None\n        return features_df, labels_list\n    \n    def train(self, n_videos=50):\n        print(f'🚀 Training on {n_videos} videos...')\n        train_df = pd.read_csv('/kaggle/input/mabe-mouse-behavior-detection/train.csv').head(n_videos)\n        \n        all_features = []\n        all_labels = []\n        \n        for idx, row in train_df.iterrows():\n            if idx % 10 == 0:\n                print(f'  Processing {idx+1}/{len(train_df)}...')\n            \n            tracking, annotations = self.load_and_process_video(\n                str(row['video_id']), row['lab_id'], is_train=True)\n            \n            if tracking is None or annotations is None:\n                continue\n            \n            features, labels = self.extract_simple_features(tracking, annotations, sample_rate=10)\n            \n            if features is not None and len(features) > 0:\n                all_features.append(features)\n                all_labels.extend(labels)\n        \n        X = pd.concat(all_features, ignore_index=True)\n        feature_cols = [col for col in X.columns if col not in ['frame', 'mouse_id']]\n        X = X[feature_cols].fillna(0)\n        y = np.array(all_labels[:len(X)])\n        \n        y_encoded = self.label_encoder.fit_transform(y)\n        X_scaled = self.scaler.fit_transform(X)\n        \n        print(f'📊 Training with {len(X)} samples...')\n        self.model = lgb.LGBMClassifier(\n            n_estimators=100, \n            learning_rate=0.1, \n            max_depth=5,\n            num_leaves=31, \n            random_state=42, \n            n_jobs=-1, \n            verbose=-1\n        )\n        self.model.fit(X_scaled, y_encoded)\n        print('✅ Training complete!')\n        return True\n    \n    def predict_video(self, video_id, lab_id):\n        \"\"\"Predict for a single video\"\"\"\n        tracking, _ = self.load_and_process_video(video_id, lab_id, is_train=False)\n        if tracking is None:\n            return []\n        \n        features, _ = self.extract_simple_features(tracking, annotations=None, sample_rate=5)\n        if features is None or len(features) == 0:\n            return []\n        \n        feature_cols = [col for col in features.columns if col not in ['frame', 'mouse_id']]\n        X = features[feature_cols].fillna(0)\n        X_scaled = self.scaler.transform(X)\n        \n        y_pred = self.model.predict(X_scaled)\n        y_pred_labels = self.label_encoder.inverse_transform(y_pred)\n        \n        predictions = []\n        features['prediction'] = y_pred_labels\n        \n        for mouse_id in features['mouse_id'].unique():\n            mouse_data = features[features['mouse_id'] == mouse_id].sort_values('frame')\n            current_behavior = None\n            start_frame = None\n            \n            for _, row in mouse_data.iterrows():\n                behavior = row['prediction']\n                frame = int(row['frame'])\n                \n                if behavior != 'none':\n                    if current_behavior is None:\n                        current_behavior = behavior\n                        start_frame = frame\n                    elif behavior != current_behavior:\n                        predictions.append({\n                            'video_id': video_id,\n                            'agent_id': f'mouse{int(mouse_id)}',\n                            'target_id': 'mouse2',\n                            'action': current_behavior,\n                            'start_frame': start_frame,\n                            'stop_frame': frame - 1\n                        })\n                        current_behavior = behavior\n                        start_frame = frame\n                else:\n                    if current_behavior is not None:\n                        predictions.append({\n                            'video_id': video_id,\n                            'agent_id': f'mouse{int(mouse_id)}',\n                            'target_id': 'mouse2',\n                            'action': current_behavior,\n                            'start_frame': start_frame,\n                            'stop_frame': frame - 1\n                        })\n                        current_behavior = None\n            \n            if current_behavior is not None:\n                predictions.append({\n                    'video_id': video_id,\n                    'agent_id': f'mouse{int(mouse_id)}',\n                    'target_id': 'mouse2',\n                    'action': current_behavior,\n                    'start_frame': start_frame,\n                    'stop_frame': int(mouse_data.iloc[-1]['frame'])\n                })\n        \n        return predictions\n\nprint(\"✅ FastMouseBehaviorDetector class defined\")\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Initialize and train detector\ndetector = FastMouseBehaviorDetector()\ndetector.train(n_videos=50)\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Generate predictions for test set\nprint(\"🔮 Generating predictions...\")\ntest_df = pd.read_csv('/kaggle/input/mabe-mouse-behavior-detection/test.csv')\nall_predictions = []\n\nfor idx, row in test_df.iterrows():\n    print(f'Predicting {idx+1}/{len(test_df)}...')\n    preds = detector.predict_video(str(row['video_id']), row['lab_id'])\n    all_predictions.extend(preds)\n\nprint(f\"✅ Generated {len(all_predictions)} predictions\")\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Create submission file\nprint(\"📝 Creating submission file...\")\n\nif len(all_predictions) > 0:\n    submission_df = pd.DataFrame(all_predictions)\n    submission_df['row_id'] = range(len(submission_df))\n    submission_df = submission_df[['row_id', 'video_id', 'agent_id', 'target_id', \n                                    'action', 'start_frame', 'stop_frame']]\nelse:\n    print(\"⚠️ No predictions, using sample submission\")\n    sample_sub = pd.read_csv('/kaggle/input/mabe-mouse-behavior-detection/sample_submission.csv')\n    submission_df = sample_sub.copy()\n\nsubmission_df.to_csv('submission.csv', index=False)\nprint(f\"✅ Submission created with {len(submission_df)} predictions\")\nprint(\"\\n📊 First few predictions:\")\nprint(submission_df.head(10))\n"}],"metadata":{"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"}},"nbformat":4,"nbformat_minor":2}