{"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":13719397,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\n\"\"\"\nMouse Behavior Detection Competition - Simple Metadata-Only Baseline\nA minimal baseline that uses only metadata to create valid predictions\n\"\"\"\n\nimport pandas as pd\nimport numpy as np\nimport json\nfrom typing import List, Dict\nimport warnings\nwarnings.filterwarnings('ignore')\n\nclass SimpleMouseBehaviorPredictor:\n    def __init__(self):\n        self.lab_behaviors = {}\n        self.behavior_durations = {}\n        \n    def load_data(self, data_dir: str = '/kaggle/input/MABe-mouse-behavior-detection'):\n        \"\"\"Load training and test data\"\"\"\n        print(\"Loading metadata...\")\n        self.train_df = pd.read_csv(f'{data_dir}/train.csv')\n        self.test_df = pd.read_csv(f'{data_dir}/test.csv')\n        \n        print(f\"Training videos: {len(self.train_df)}\")\n        print(f\"Test videos: {len(self.test_df)}\")\n        \n        # Analyze behaviors from training metadata\n        self.analyze_training_behaviors()\n        \n    def analyze_training_behaviors(self):\n        \"\"\"Analyze behavior patterns from training metadata only\"\"\"\n        print(\"Analyzing behavior patterns from metadata...\")\n        \n        lab_behavior_counts = {}\n        \n        for idx, row in self.train_df.iterrows():\n            lab_id = row['lab_id']\n            \n            if lab_id not in lab_behavior_counts:\n                lab_behavior_counts[lab_id] = {}\n            \n            try:\n                behaviors_labeled = json.loads(row['behaviors labeled'])\n                \n                for behavior_key in behaviors_labeled:\n                    # Parse behavior key: \"agent_id,target_id,action\"\n                    parts = behavior_key.split(',')\n                    if len(parts) == 3:\n                        action = parts[2]\n                        if action not in lab_behavior_counts[lab_id]:\n                            lab_behavior_counts[lab_id][action] = 0\n                        lab_behavior_counts[lab_id][action] += 1\n                        \n            except:\n                continue\n        \n        # Get most common behaviors per lab\n        self.lab_behaviors = {}\n        for lab_id, behavior_counts in lab_behavior_counts.items():\n            if behavior_counts:\n                # Sort by frequency\n                sorted_behaviors = sorted(behavior_counts.items(), key=lambda x: x[1], reverse=True)\n                self.lab_behaviors[lab_id] = [behavior for behavior, count in sorted_behaviors]\n            \n        print(\"Behavior analysis complete!\")\n        for lab, behaviors in self.lab_behaviors.items():\n            print(f\"{lab}: {len(behaviors)} behaviors - {behaviors[:3]}{'...' if len(behaviors) > 3 else ''}\")\n            \n    def get_default_duration(self, action: str) -> tuple:\n        \"\"\"Get default start/stop frames for different actions\"\"\"\n        # Default durations based on typical behavior patterns\n        duration_map = {\n            'sniff': (30, 60),\n            'attack': (15, 45),\n            'chase': (45, 90),\n            'mount': (30, 90),\n            'groom': (60, 120),\n            'approach': (20, 50),\n            'escape': (15, 45),\n            'rear': (20, 60),\n            'avoid': (20, 50),\n            'defend': (20, 60),\n        }\n        \n        # Default for unknown actions\n        default_duration = (30, 80)\n        \n        for key_action, (min_dur, max_dur) in duration_map.items():\n            if key_action in action.lower():\n                return (min_dur, max_dur)\n                \n        return default_duration\n    \n    def predict_behaviors_for_video(self, video_info: pd.Series) -> List[Dict]:\n        \"\"\"Generate behavior predictions for a single video using only metadata\"\"\"\n        predictions = []\n        \n        video_id = video_info['video_id']\n        lab_id = video_info['lab_id']\n        \n        print(f\"Processing video {video_id} from lab {lab_id}\")\n        \n        # Parse behaviors that should be labeled\n        try:\n            behaviors_labeled_str = str(video_info['behaviors labeled'])\n            if behaviors_labeled_str and behaviors_labeled_str != 'nan':\n                behaviors_labeled = json.loads(behaviors_labeled_str)\n            else:\n                behaviors_labeled = []\n        except Exception as e:\n            print(f\"Error parsing behaviors for video {video_id}: {e}\")\n            behaviors_labeled = []\n            \n        # If no specific behaviors are labeled, create basic predictions using lab patterns\n        if not behaviors_labeled:\n            lab_behaviors = self.lab_behaviors.get(lab_id, ['sniff'])\n            if lab_behaviors:\n                # Create a simple prediction with most common behavior for this lab\n                action = lab_behaviors[0]\n                min_dur, max_dur = self.get_default_duration(action)\n                \n                # Use video duration info if available\n                try:\n                    fps = float(video_info['frames per second'])\n                    duration_sec = float(video_info['video duration (sec)'])\n                    total_frames = int(fps * duration_sec)\n                    \n                    # Place prediction in middle third of video\n                    start_frame = total_frames // 3\n                    end_frame = min(start_frame + max_dur, total_frames - 10)\n                except:\n                    # Fallback frame values\n                    start_frame = 100\n                    end_frame = 200\n                \n                predictions.append({\n                    'video_id': video_id,\n                    'agent_id': 1,  # Default mouse IDs\n                    'target_id': 2,\n                    'action': action,\n                    'start_frame': start_frame,\n                    'stop_frame': end_frame\n                })\n            \n            return predictions\n        \n        # Process each labeled behavior\n        for behavior_key in behaviors_labeled:\n            try:\n                parts = behavior_key.split(',')\n                if len(parts) != 3:\n                    continue\n                    \n                agent_id = int(parts[0])\n                target_id = int(parts[1])\n                action = parts[2]\n                \n                # Get reasonable duration for this action\n                min_dur, max_dur = self.get_default_duration(action)\n                duration = np.random.randint(min_dur, max_dur + 1)\n                \n                # Calculate frame positions based on video info\n                try:\n                    fps = float(video_info['frames per second'])\n                    duration_sec = float(video_info['video duration (sec)'])\n                    total_frames = int(fps * duration_sec)\n                    \n                    # Randomly place behavior in video (avoid first and last 10%)\n                    earliest_start = int(0.1 * total_frames)\n                    latest_start = int(0.9 * total_frames) - duration\n                    \n                    if latest_start <= earliest_start:\n                        start_frame = earliest_start\n                    else:\n                        start_frame = np.random.randint(earliest_start, latest_start)\n                        \n                    stop_frame = min(start_frame + duration, total_frames - 1)\n                    \n                except Exception as e:\n                    print(f\"Error calculating frames for video {video_id}: {e}\")\n                    # Fallback frame calculation\n                    start_frame = np.random.randint(50, 500)\n                    stop_frame = start_frame + duration\n                \n                predictions.append({\n                    'video_id': video_id,\n                    'agent_id': agent_id,\n                    'target_id': target_id,\n                    'action': action,\n                    'start_frame': int(start_frame),\n                    'stop_frame': int(stop_frame)\n                })\n                \n            except Exception as e:\n                print(f\"Error processing behavior {behavior_key} for video {video_id}: {e}\")\n                continue\n                \n        return predictions\n        \n    def generate_predictions(self) -> pd.DataFrame:\n        \"\"\"Generate predictions for all test videos\"\"\"\n        print(\"Generating predictions...\")\n        \n        all_predictions = []\n        \n        for idx, row in self.test_df.iterrows():\n            predictions = self.predict_behaviors_for_video(row)\n            all_predictions.extend(predictions)\n            \n        print(f\"Generated {len(all_predictions)} predictions\")\n        \n        # Ensure we have at least some predictions\n        if not all_predictions:\n            print(\"Creating fallback predictions...\")\n            \n            for idx, row in self.test_df.iterrows():\n                all_predictions.append({\n                    'video_id': row['video_id'],\n                    'agent_id': 1,\n                    'target_id': 2,\n                    'action': 'sniff',\n                    'start_frame': 100,\n                    'stop_frame': 200\n                })\n        \n        # Create DataFrame\n        predictions_df = pd.DataFrame(all_predictions)\n        predictions_df['row_id'] = range(len(predictions_df))\n        \n        return predictions_df\n        \n    def create_submission(self, predictions_df: pd.DataFrame) -> pd.DataFrame:\n        \"\"\"Create submission file in correct format\"\"\"\n        required_columns = ['row_id', 'video_id', 'agent_id', 'target_id', 'action', 'start_frame', 'stop_frame']\n        \n        # Ensure all columns exist\n        for col in required_columns:\n            if col not in predictions_df.columns:\n                print(f\"Missing required column: {col}\")\n                return pd.DataFrame(columns=required_columns)\n        \n        submission = predictions_df[required_columns].copy()\n        \n        # Ensure correct data types\n        submission['row_id'] = submission['row_id'].astype(int)\n        submission['video_id'] = submission['video_id'].astype(int)\n        submission['agent_id'] = submission['agent_id'].astype(int)\n        submission['target_id'] = submission['target_id'].astype(int)\n        submission['start_frame'] = submission['start_frame'].astype(int)\n        submission['stop_frame'] = submission['stop_frame'].astype(int)\n        \n        return submission\n\ndef main():\n    \"\"\"Main pipeline execution\"\"\"\n    # Data directory - adjust path as needed\n    data_dir = '/kaggle/input/MABe-mouse-behavior-detection'\n    \n    try:\n        # Initialize predictor\n        predictor = SimpleMouseBehaviorPredictor()\n        \n        # Load data and analyze\n        predictor.load_data(data_dir)\n        \n        # Generate predictions\n        predictions_df = predictor.generate_predictions()\n        \n        # Create submission\n        submission_df = predictor.create_submission(predictions_df)\n        \n        print(f\"\\nSubmission shape: {submission_df.shape}\")\n        print(f\"Unique videos: {submission_df['video_id'].nunique()}\")\n        print(f\"Unique actions: {submission_df['action'].nunique()}\")\n        print(\"\\nAction distribution:\")\n        print(submission_df['action'].value_counts())\n        \n        print(\"\\nSubmission sample:\")\n        print(submission_df.head())\n        \n        # Validate submission\n        if len(submission_df) == 0:\n            raise ValueError(\"Empty submission!\")\n            \n        # Save submission\n        submission_df.to_csv('submission.csv', index=False)\n        print(\"\\nSubmission saved as 'submission.csv'\")\n        \n        return submission_df\n        \n    except Exception as e:\n        print(f\"Error in main pipeline: {e}\")\n        \n        # Create absolute minimal fallback\n        print(\"Creating minimal fallback submission...\")\n        \n        try:\n            test_df = pd.read_csv(f'{data_dir}/test.csv')\n            \n            fallback_predictions = []\n            for idx, row in test_df.iterrows():\n                fallback_predictions.append({\n                    'row_id': idx,\n                    'video_id': row['video_id'],\n                    'agent_id': 1,\n                    'target_id': 2,\n                    'action': 'sniff',\n                    'start_frame': 100,\n                    'stop_frame': 200\n                })\n            \n            fallback_df = pd.DataFrame(fallback_predictions)\n            fallback_df.to_csv('submission.csv', index=False)\n            print(\"Minimal fallback submission saved!\")\n            \n        except Exception as fallback_error:\n            print(f\"Even fallback failed: {fallback_error}\")\n            \n            # Ultimate fallback\n            ultra_minimal = pd.DataFrame({\n                'row_id': [0],\n                'video_id': [1],\n                'agent_id': [1],\n                'target_id': [2],\n                'action': ['sniff'],\n                'start_frame': [100],\n                'stop_frame': [200]\n            })\n            ultra_minimal.to_csv('submission.csv', index=False)\n            print(\"Ultra-minimal submission created!\")\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-19T17:00:00.034276Z","iopub.execute_input":"2025-09-19T17:00:00.034596Z","iopub.status.idle":"2025-09-19T17:00:01.059957Z","shell.execute_reply.started":"2025-09-19T17:00:00.034572Z","shell.execute_reply":"2025-09-19T17:00:01.058803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}