{"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"},{"sourceId":13141357,"sourceType":"datasetVersion","datasetId":8325083}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<center>\n<h2 style=\"font-weight: bold;\">🐁Social Action Recognition in Mice🐁</h2>\n</center>","metadata":{}},{"cell_type":"markdown","source":"# 1. 📁Files\n<div style=\"background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 25px; border-radius: 15px; border: 3px solid #ffd166; margin: 15px 0;\">\n<h2 style=\"text-align: center; font-weight: bold; color: #ffd166; margin-bottom: 20px;\">📁 Dataset Structure & Column Details</h2>\n\n<div style=\"background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin: 10px 0;\">\n<h3 style=\"color: #ffd166;\">🏢 <strong>Lab Folders Structure</strong></h3>\n<p>Each lab has unique tracking methods & annotation protocols:</p>\n<div style=\"display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin: 15px 0;\">\n<div style=\"background: rgba(255,255,255,0.05); padding: 10px; border-radius: 5px;\">\n<strong>🔬 Research Labs:</strong><br>\n• AdaptableSnail<br>\n• BoisterousParrot<br>\n• CautiousGiraffe<br>\n• DeliriousFly<br>\n• ElegantMink\n</div>\n<div style=\"background: rgba(255,255,255,0.05); padding: 10px; border-radius: 5px;\">\n<strong>📚 Public Datasets:</strong><br>\n• CRIM13<br>\n• CalMS21_*<br>\n• MABe22_*<br>\n• GroovyShrew<br>\n• JovialSwallow\n</div>\n</div>\n</div>\n\n<div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0;\">\n<div style=\"background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;\">\n<h4 style=\"color: #06d6a0;\">📊 <strong>train.csv Metadata Columns</strong></h4>\n<ul style=\"list-style: none; padding-left: 0;\">\n<li>• <strong>lab_id</strong> - Which research lab provided data</li>\n<li>• <strong>video_id</strong> - Unique video identifier</li>\n<li>• <strong>mouse[1-4]_strain/color/sex/id/age/condition</strong> - Mouse demographics & characteristics</li>\n<li>• <strong>frames_per_second</strong> - Video frame rate</li>\n<li>• <strong>video_duration_sec</strong> - Total video length</li>\n<li>• <strong>pix_per_cm_approx</strong> - Scale conversion</li>\n<li>• <strong>video_[width/height]_pix</strong> - Video dimensions</li>\n<li>• <strong>arena_[width/height]_cm</strong> - Arena size in cm</li>\n<li>• <strong>arena_shape/type</strong> - Experimental setup</li>\n<li>• <strong>body_parts_tracked</strong> - Which body parts are monitored</li>\n<li>• <strong>behaviors_labeled</strong> - Which behaviors are annotated</li>\n<li>• <strong>tracking_method</strong> - Computer vision method used</li>\n</ul>\n</div>\n\n<div style=\"background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px;\">\n<h4 style=\"color: #ef476f;\">🎬 <strong>Tracking Data Columns</strong></h4>\n<ul style=\"list-style: none; padding-left: 0;\">\n<li>• <strong>video_frame</strong> - Frame number in video</li>\n<li>• <strong>mouse_id</strong> - Which mouse (1, 2, 3, 4)</li>\n<li>• <strong>bodypart</strong> - Tracked body part (body_center, ear_left, ear_right, lateral_left, lateral_right, head, tail_base, tail_mid, tail_tip, nose)</li>\n<li>• <strong>x, y</strong> - Pixel coordinates of body part</li>\n</ul>\n<br>\n<h4 style=\"color: #118ab2;\">🏷️ <strong>Annotation Data Columns</strong></h4>\n<ul style=\"list-style: none; padding-left: 0;\">\n<li>• <strong>agent_id</strong> - Mouse performing the behavior</li>\n<li>• <strong>target_id</strong> - Mouse receiving the behavior (self=same as agent)</li>\n<li>• <strong>action</strong> - Behavior type (chase, avoid, attack, mount, etc.)</li>\n<li>• <strong>start_frame</strong> - Behavior start time</li>\n<li>• <strong>stop_frame</strong> - Behavior end time</li>\n</ul>\n</div>\n</div>\n\n<div style=\"background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin: 10px 0;\">\n<h4 style=\"color: #ffd166;\">🔍 <strong>Key Insights from Column Analysis</strong></h4>\n<ul style=\"list-style: none; padding-left: 0;\">\n<li>• <strong>Multiple mice tracking</strong> - Up to 4 mice per video</li>\n<li>• <strong>Detailed body part tracking</strong> - 9+ body parts per mouse</li>\n<li>• <strong>Sparse annotations</strong> - Only 864 videos have behavior labels</li>\n<li>• <strong>Variable mouse counts</strong> - Many videos have missing mouse3/mouse4 data</li>\n<li>• <strong>Standardized arena</strong> - Most use square familiar arenas</li>\n<li>• <strong>Frame-level precision</strong> - Exact behavior timing tracked</li>\n</ul>\n</div>\n\n<div style=\"text-align: center; margin-top: 15px;\">\n<p style=\"font-style: italic; color: #ffd166;\">🎯 <strong>Each lab folder contains .parquet files with tracking coordinates + behavior annotations!</strong></p>\n</div>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# 2. Train/test/submission Information","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\nimport os\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport matplotlib.animation as animation\n\nfrom PIL import Image\nfrom IPython.display import HTML\nfrom matplotlib.lines import Line2D\nfrom matplotlib.patches import Rectangle, Circle\nfrom matplotlib.offsetbox import OffsetImage, AnnotationBbox\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\ntrain = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/train.csv')\ntest = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/test.csv')\nsubmission = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/sample_submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:15.574724Z","iopub.execute_input":"2025-09-23T00:14:15.574996Z","iopub.status.idle":"2025-09-23T00:14:16.944455Z","shell.execute_reply.started":"2025-09-23T00:14:15.574973Z","shell.execute_reply":"2025-09-23T00:14:16.943676Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:17.638839Z","iopub.execute_input":"2025-09-23T00:14:17.639103Z","iopub.status.idle":"2025-09-23T00:14:17.676592Z","shell.execute_reply.started":"2025-09-23T00:14:17.639083Z","shell.execute_reply":"2025-09-23T00:14:17.675871Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:18.057841Z","iopub.execute_input":"2025-09-23T00:14:18.058128Z","iopub.status.idle":"2025-09-23T00:14:18.076337Z","shell.execute_reply.started":"2025-09-23T00:14:18.058103Z","shell.execute_reply":"2025-09-23T00:14:18.075411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:18.305038Z","iopub.execute_input":"2025-09-23T00:14:18.305456Z","iopub.status.idle":"2025-09-23T00:14:18.341982Z","shell.execute_reply.started":"2025-09-23T00:14:18.305417Z","shell.execute_reply":"2025-09-23T00:14:18.341139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all(train.columns == test.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:18.490617Z","iopub.execute_input":"2025-09-23T00:14:18.491334Z","iopub.status.idle":"2025-09-23T00:14:18.496868Z","shell.execute_reply.started":"2025-09-23T00:14:18.491303Z","shell.execute_reply":"2025-09-23T00:14:18.495932Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:20.367174Z","iopub.execute_input":"2025-09-23T00:14:20.367441Z","iopub.status.idle":"2025-09-23T00:14:20.376520Z","shell.execute_reply.started":"2025-09-23T00:14:20.367421Z","shell.execute_reply":"2025-09-23T00:14:20.375865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train annotation\npd.read_parquet('/kaggle/input/MABe-mouse-behavior-detection/train_annotation/AdaptableSnail/1212811043.parquet').head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:20.551912Z","iopub.execute_input":"2025-09-23T00:14:20.552214Z","iopub.status.idle":"2025-09-23T00:14:20.735022Z","shell.execute_reply.started":"2025-09-23T00:14:20.552190Z","shell.execute_reply":"2025-09-23T00:14:20.734334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train tracking\ntracking_data = pd.read_parquet('/kaggle/input/MABe-mouse-behavior-detection/train_tracking/AdaptableSnail/1212811043.parquet')\ntracking_data.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:20.736171Z","iopub.execute_input":"2025-09-23T00:14:20.736469Z","iopub.status.idle":"2025-09-23T00:14:21.252863Z","shell.execute_reply.started":"2025-09-23T00:14:20.736439Z","shell.execute_reply":"2025-09-23T00:14:21.252039Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Vizualizations 🖼️","metadata":{}},{"cell_type":"code","source":"plt.style.use('seaborn-v0_8')\nsns.set_palette(\"husl\")\n\ntrain = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/train.csv')\n\n# mouse image positioning function\ndef add_mouse_image(fig, ax, img_path='/kaggle/input/mousenotebook/mouse.png', position=(0.5, 0.5), zoom=0.15):\n    img = mpimg.imread(img_path)\n    x, y = position\n    \n    imagebox = OffsetImage(img, zoom=zoom)\n    ab = AnnotationBbox(imagebox, (x, y), xycoords='figure fraction', frameon=False, boxcoords=\"figure fraction\")\n    fig.add_artist(ab)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:14:22.436835Z","iopub.execute_input":"2025-09-23T00:14:22.437120Z","iopub.status.idle":"2025-09-23T00:14:22.518619Z","shell.execute_reply.started":"2025-09-23T00:14:22.437099Z","shell.execute_reply":"2025-09-23T00:14:22.517838Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9))\nfig.suptitle('Laboratory Analysis & Tracking Methods', fontsize=18, fontweight='bold', y=0.95)\n\n# Laboratory Distribution (Top 8 labs)\nlab_counts = train['lab_id'].value_counts().head(8)\ncolors = plt.cm.Set3(np.linspace(0.2, 0.8, len(lab_counts)))\n\nbars = ax1.bar(range(len(lab_counts)), lab_counts.values, color=colors, \n               edgecolor='black', alpha=0.85, linewidth=1.5)\nax1.set_title('Video Distribution by Laboratory', fontsize=14, fontweight='bold', pad=15)\nax1.set_xlabel('Laboratory ID', fontsize=12, fontweight='bold')\nax1.set_ylabel('Number of Videos', fontsize=12, fontweight='bold')\nax1.set_xticks(range(len(lab_counts)))\nax1.set_xticklabels([lab[:12] + '...' if len(lab) > 12 else lab for lab in lab_counts.index], \n                   rotation=45, ha='right')\nax1.grid(axis='y', alpha=0.3, linestyle='--')\n\n# Add value labels on bars\nfor i, bar in enumerate(bars):\n    height = bar.get_height()\n    ax1.text(bar.get_x() + bar.get_width()/2., height + 3,\n            f'{int(height)}', ha='center', va='bottom', \n            fontweight='bold', fontsize=10, color='darkblue')\n\n# Tracking Methods (Pie chart)\ntracking_counts = train['tracking_method'].value_counts()\nexplode = [0.15] * len(tracking_counts)\n\nwedges, texts, autotexts = ax2.pie(tracking_counts.values, \n                                  labels=tracking_counts.index, \n                                  autopct='%1.1f%%', \n                                  startangle=90, \n                                  explode=explode,\n                                  colors=plt.cm.Pastel2(range(len(tracking_counts))),\n                                  textprops={'fontsize': 11})\n\nax2.set_title('Mouse Tracking Methods', fontsize=14, fontweight='bold', pad=15)\n\nadd_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse3.png', position=(0.56, 0.17), zoom=0.1)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:16:31.596018Z","iopub.execute_input":"2025-09-23T00:16:31.596493Z","iopub.status.idle":"2025-09-23T00:16:32.294982Z","shell.execute_reply.started":"2025-09-23T00:16:31.596463Z","shell.execute_reply":"2025-09-23T00:16:32.294100Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9))\nfig.suptitle('Experimental Setup & Mouse Information', fontsize=18, fontweight='bold', y=0.95)\n\n# Arena Shapes Distribution\narena_shapes = train['arena_shape'].value_counts()\ncolors_shape = ['#FFD700', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']\n\nwedges, texts, autotexts = ax1.pie(arena_shapes.values, \n                                  labels=arena_shapes.index, \n                                  autopct='%1.1f%%', \n                                  startangle=45,\n                                  explode=[0.15] * len(arena_shapes),\n                                  colors=colors_shape[:len(arena_shapes)],\n                                  textprops={'fontsize': 11})\n\nax1.set_title('Arena Shapes Distribution', fontsize=14, fontweight='bold', pad=15)\n\nfor autotext in autotexts:\n    autotext.set_color('darkblue')\n    autotext.set_fontweight('bold')\n\n# Mouse Strain Analysis (Top 6 strains)\nmouse_columns = [col for col in train.columns if 'strain' in col and 'mouse' in col]\nall_strains = pd.concat([train[col] for col in mouse_columns]).dropna()\ntop_strains = all_strains.value_counts().head(6)\n\ncolors_strain = plt.cm.viridis(np.linspace(0.2, 0.8, len(top_strains)))\n\nbars = ax2.bar(range(len(top_strains)), top_strains.values, color=colors_strain,\n               edgecolor='black', alpha=0.85)\nax2.set_title('Most Common Mouse Strains', fontsize=14, fontweight='bold', pad=15)\nax2.set_xlabel('Mouse Strain', fontsize=12, fontweight='bold')\nax2.set_ylabel('Number of Occurrences', fontsize=12, fontweight='bold')\nax2.set_xticks(range(len(top_strains)))\nax2.set_xticklabels(top_strains.index, rotation=45, ha='right')\nax2.grid(axis='y', alpha=0.3, linestyle='--')\n\n# Add value labels\nfor i, bar in enumerate(bars):\n    height = bar.get_height()\n    ax2.text(bar.get_x() + bar.get_width()/2., height + 5,\n            f'{int(height)}', ha='center', va='bottom', \n            fontweight='bold', fontsize=10)\n\nadd_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse1.webp', position=(0.436, 0.2), zoom=0.53)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:19:40.693944Z","iopub.execute_input":"2025-09-23T00:19:40.694254Z","iopub.status.idle":"2025-09-23T00:19:41.119926Z","shell.execute_reply.started":"2025-09-23T00:19:40.694230Z","shell.execute_reply":"2025-09-23T00:19:41.118991Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_trajectories_and_speed(tracking_data, video_meta, frame_range=(0, 1000)):\n    # Filter data\n    df = tracking_data[\n        (tracking_data['video_frame'] >= frame_range[0]) & \n        (tracking_data['video_frame'] <= frame_range[1])\n    ].copy()\n    df = df[df['bodypart'] == 'body_center']\n\n    # Colors for mice\n    colors = {1: '#FF6B6B', 2: '#4ECDC4', 3: '#45B7D1', 4: '#96CEB4'}\n    \n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9), gridspec_kw={'width_ratios': [1, 1]})\n    fig.suptitle('Mouse Movement Analysis', fontsize=18, fontweight='bold', y=0.96)\n\n    # Trajectories\n    for mouse_id in sorted(df['mouse_id'].unique()):\n        mouse = df[df['mouse_id'] == mouse_id].sort_values('video_frame')\n        c = colors.get(mouse_id, 'gray')\n        ax1.plot(mouse['x'], mouse['y'], color=c, alpha=0.8, linewidth=2.5, label=f'Mouse {mouse_id}')\n        ax1.scatter(mouse['x'].iloc[::20], mouse['y'].iloc[::20], c=c, s=40, edgecolor='white', linewidth=0.5, zorder=5)\n\n    ax1.set(xlabel='X (pixels)', ylabel='Y (pixels)', title='Movement Trajectories')\n    ax1.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, fontsize=11)\n    ax1.grid(True, alpha=0.3)\n    ax1.set_aspect('equal', adjustable='datalim')\n    ax1.set_facecolor('#f9f9f9')\n    \n    for mouse_id in sorted(df['mouse_id'].unique()):\n        mouse = df[df['mouse_id'] == mouse_id].sort_values('video_frame').copy()\n        mouse['speed'] = np.sqrt(mouse['x'].diff()**2 + mouse['y'].diff()**2).fillna(0)\n        mouse['speed_smooth'] = mouse['speed'].rolling(15, min_periods=1).mean()\n        ax2.plot(mouse['video_frame'], mouse['speed_smooth'], \n                color=colors.get(mouse_id, 'gray'), label=f'Mouse {mouse_id}', linewidth=3)\n\n    ax2.set(xlabel='Frame', ylabel='Speed (pixels/frame)', title='Smoothed Speed Over Time')\n    ax2.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, fontsize=11)\n    ax2.grid(True, alpha=0.3)\n    ax2.set_facecolor('#f9f9f9')\n    \n    add_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse6.png', position=(0.65, 0.82), zoom=0.08)\n\n    plt.tight_layout()\n    plt.show()\n\nsample_video = train.iloc[0]\nlab_id = sample_video['lab_id']\nvideo_id = sample_video['video_id']\n\ntracking_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet'\ntracking_data = pd.read_parquet(tracking_path)\n\nplot_trajectories_and_speed(tracking_data, sample_video, frame_range=(0, 1000))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:24:01.060411Z","iopub.execute_input":"2025-09-23T00:24:01.061053Z","iopub.status.idle":"2025-09-23T00:24:02.375584Z","shell.execute_reply.started":"2025-09-23T00:24:01.061028Z","shell.execute_reply":"2025-09-23T00:24:02.374687Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_density_and_distance(tracking_data, video_meta, frame_range=(0, 1000), img_path='/kaggle/input/mousenotebook/mouse1.png'):\n    # Filter data\n    df = tracking_data[\n        (tracking_data['video_frame'] >= frame_range[0]) & \n        (tracking_data['video_frame'] <= frame_range[1])\n    ].copy()\n    df = df[df['bodypart'] == 'body_center']\n\n    # Colors\n    colors = {1: '#FF6B6B', 2: '#4ECDC4', 3: '#45B7D1', 4: '#96CEB4'}\n\n    # Create side-by-side layout\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9), gridspec_kw={'width_ratios': [1, 1.1]})\n    fig.suptitle(' Movement Density & Social Distance', fontsize=18, fontweight='bold', y=0.96)\n\n    # Movement Density\n    x = df['x'].values\n    y = df['y'].values\n\n    hb = ax1.hist2d(x, y, bins=40, cmap='hot', alpha=0.85)\n    cbar = plt.colorbar(hb[3], ax=ax1)\n    cbar.set_label('Visit Density', fontsize=11, fontweight='bold')\n\n    ax1.set(xlabel='X (pixels)', ylabel='Y (pixels)', title='Movement Density Heatmap')\n    ax1.set_aspect('equal', adjustable='datalim')\n    ax1.grid(True, alpha=0.2)\n    ax1.set_facecolor('#000000')\n    cbar.ax.tick_params(labelsize=10)\n\n    # Inter-Mouse Distances\n    mice = sorted(df['mouse_id'].unique())\n\n    if len(mice) > 1:\n        distance_stats = []\n\n        for i in range(len(mice)):\n            for j in range(i + 1, len(mice)):\n                m1 = df[df['mouse_id'] == mice[i]].set_index('video_frame')[['x', 'y']]\n                m2 = df[df['mouse_id'] == mice[j]].set_index('video_frame')[['x', 'y']]\n                merged = m1.join(m2, lsuffix='_1', rsuffix='_2', how='inner')\n\n                if len(merged) == 0:\n                    continue\n\n                dist = np.sqrt((merged['x_1'] - merged['x_2'])**2 + (merged['y_1'] - merged['y_2'])**2)\n                dist_smooth = dist.rolling(20, min_periods=1).mean()\n                distance_stats.append(dist.mean())\n\n                ax2.plot(merged.index, dist_smooth,\n                        label=f'Mouse {mice[i]} ↔ {mice[j]}',\n                        linewidth=2.5, alpha=0.85)\n\n        ax2.set(xlabel='Frame', ylabel='Distance (pixels)', title='Inter-Mouse Distance')\n        ax2.legend(loc='upper right', fontsize=10, frameon=True, fancybox=True, shadow=True)\n        ax2.grid(True, alpha=0.3)\n        ax2.set_facecolor('#f9f9f9')\n\n    else:\n        ax2.text(0.5, 0.5, 'Only one mouse detected', ha='center', va='center',\n                transform=ax2.transAxes, fontsize=13, fontweight='bold', color='#555')\n        ax2.set_title('📏 Inter-Mouse Distance', fontsize=14, fontweight='bold')\n        ax2.axis('off')\n    \n    add_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse4.png', position=(0.64, 0.2), zoom=0.35)\n\n    plt.tight_layout()\n    plt.show()\n\n\nsample_video = train.iloc[0]\nlab_id = sample_video['lab_id']\nvideo_id = sample_video['video_id']\n\ntracking_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet'\ntracking_data = pd.read_parquet(tracking_path)\n\nplot_density_and_distance(tracking_data, sample_video, frame_range=(0, 1000))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:24:18.636843Z","iopub.execute_input":"2025-09-23T00:24:18.637138Z","iopub.status.idle":"2025-09-23T00:24:19.372105Z","shell.execute_reply.started":"2025-09-23T00:24:18.637117Z","shell.execute_reply":"2025-09-23T00:24:19.371136Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_behavior_analysis(tracking_data, annotation_data, video_meta):\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9), gridspec_kw={'width_ratios': [1.2, 1]})\n    fig.suptitle('Behavior Analysis', fontsize=18, fontweight='bold', y=0.96)\n\n    # Filter body center data\n    body_center_data = tracking_data[tracking_data['bodypart'] == 'body_center']\n    colors = {1: '#FF6B6B', 2: '#4ECDC4', 3: '#45B7D1', 4: '#96CEB4'}\n\n    # Trajectories with behavior highlights\n    for mouse_id in body_center_data['mouse_id'].unique():\n        mouse = body_center_data[body_center_data['mouse_id'] == mouse_id]\n        ax1.plot(mouse['x'], mouse['y'], color=colors.get(mouse_id, 'gray'), alpha=0.3, linewidth=1, label=f'Mouse {mouse_id}')\n\n    # Highlight behaviors if annotations exist\n    if not annotation_data.empty:\n        unique_actions = annotation_data['action'].unique()\n        behavior_colors = plt.cm.Set3(np.linspace(0, 1, len(unique_actions)))\n        color_map = dict(zip(unique_actions, behavior_colors))\n\n        plotted_labels = set()\n        for _, ann in annotation_data.iterrows():\n            agent_id = int(ann['agent_id'].replace('mouse', '')) if isinstance(ann['agent_id'], str) else int(ann['agent_id'])\n            behavior_frames = tracking_data[\n                (tracking_data['video_frame'] >= ann['start_frame']) &\n                (tracking_data['video_frame'] <= ann['stop_frame']) &\n                (tracking_data['mouse_id'] == agent_id)\n            ]\n            if behavior_frames.empty:\n                continue\n\n            behavior_center = behavior_frames[behavior_frames['bodypart'] == 'body_center']\n            label = ann['action'] if ann['action'] not in plotted_labels else \"\"\n            plotted_labels.add(ann['action'])\n\n            ax1.scatter(behavior_center['x'], behavior_center['y'],\n                       c=[color_map[ann['action']]],\n                       s=60, alpha=0.8, edgecolors='black', linewidth=0.5,\n                       label=label)\n\n    ax1.set(xlabel='X (pixels)', ylabel='Y (pixels)', title='Behavior Annotations on Trajectories')\n    ax1.legend(loc='upper right', fontsize=9, frameon=True, fancybox=True, shadow=True)\n    ax1.grid(True, alpha=0.3)\n    ax1.set_aspect('equal', adjustable='datalim')\n    ax1.set_facecolor('#f9f9f9')\n\n    # Behavior statistics\n    if not annotation_data.empty:\n        behavior_stats = annotation_data.groupby('action').agg(\n            count=('start_frame', 'count'),\n            avg_duration=('start_frame', lambda x: (annotation_data.loc[x.index, 'stop_frame'] - x).mean())\n        ).round(1).sort_values('count', ascending=False)\n\n        x_pos = np.arange(len(behavior_stats))\n        bars = ax2.bar(x_pos, behavior_stats['count'],\n                      color=plt.cm.Pastel1(range(len(behavior_stats))),\n                      edgecolor='black', alpha=0.85, linewidth=0.8)\n\n        ax2.set(xlabel='Behavior Type', ylabel='Occurrences', title='Behavior Frequency')\n        ax2.set_xticks(x_pos)\n        ax2.set_xticklabels(behavior_stats.index, rotation=45, ha='right', fontsize=10)\n        ax2.grid(axis='y', alpha=0.4)\n        ax2.set_facecolor('#f9f9f9')\n\n        # Add bar labels\n        for bar in bars:\n            height = bar.get_height()\n            ax2.text(bar.get_x() + bar.get_width()/2, height + 0.1,\n                    f'{int(height)}', ha='center', va='bottom', fontweight='bold', fontsize=10)\n    else:\n        ax2.text(0.5, 0.5, 'No behavior annotations\\navailable', ha='center', va='center',\n                transform=ax2.transAxes, fontsize=13, fontweight='bold', color='#666', linespacing=1.5)\n        ax2.set_title('Behavior Frequency', fontsize=14, fontweight='bold')\n        ax2.axis('off')\n    \n    add_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse1.png', position=(0.85, 0.63), zoom=0.9)\n\n    plt.tight_layout()\n    plt.show()\n\nsample_video = train.iloc[0]\nlab_id = sample_video['lab_id']\nvideo_id = sample_video['video_id']\n\ntracking_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet'\nannotation_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_annotation/{lab_id}/{video_id}.parquet'\n\ntracking_data = pd.read_parquet(tracking_path)\nannotation_data = pd.read_parquet(annotation_path)\n\nplot_behavior_analysis(tracking_data, annotation_data, sample_video)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:25:17.910830Z","iopub.execute_input":"2025-09-23T00:25:17.911144Z","iopub.status.idle":"2025-09-23T00:25:23.295304Z","shell.execute_reply.started":"2025-09-23T00:25:17.911121Z","shell.execute_reply":"2025-09-23T00:25:23.294102Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_video = train.iloc[0]\nlab_id = sample_video['lab_id']\nvideo_id = sample_video['video_id']\n\ntracking_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet'\ntracking_data = pd.read_parquet(tracking_path)\n\nbody_center_data = tracking_data[tracking_data['bodypart'] == 'body_center']\n\ncolors = {\n    1: '#FF006E',\n    2: '#3A86FF',\n    3: '#FFA500',\n    4: '#38B000',\n}\n\nfig, (ax2, ax1) = plt.subplots(1, 2, figsize=(16, 9), gridspec_kw={'width_ratios': [1, 1.2]})\nfig.suptitle('Mouse Speed & Position Distribution', fontsize=18, fontweight='bold', y=0.96)\n\n# Speed Distribution (Boxplot)\nspeed_data = []\n\nfor mouse_id in sorted(body_center_data['mouse_id'].unique()):\n    mouse = body_center_data[body_center_data['mouse_id'] == mouse_id].sort_values('video_frame').copy()\n    mouse['speed'] = np.sqrt(mouse['x'].diff()**2 + mouse['y'].diff()**2).fillna(0)\n    mouse['mouse_id'] = mouse_id\n    speed_data.append(mouse[['mouse_id', 'speed']])\n\nif speed_data:\n    speed_df = pd.concat(speed_data, ignore_index=True)\n    palette = [colors.get(i, 'gray') for i in sorted(speed_df['mouse_id'].unique())]\n    sns.boxplot(data=speed_df, x='mouse_id', y='speed', ax=ax2,\n                palette=palette,\n                width=0.6, linewidth=1.5, fliersize=4, boxprops=dict(edgecolor=\"black\"))\n\n    ax2.set(xlabel='Mouse ID', ylabel='Speed (pixels/frame)', title='Speed Distribution by Mouse')\n    ax2.grid(axis='y', alpha=0.4, linestyle='--')\n    ax2.set_facecolor('#fafafa')\n    ax2.spines[['top', 'right']].set_visible(False)\n\n    # add mean value\n    means = speed_df.groupby('mouse_id')['speed'].mean()\n    for i, mean_val in enumerate(means):\n        ax2.text(i, mean_val + 0.3, f'μ={mean_val:.1f}',\n                ha='center', va='bottom', fontweight='bold', fontsize=11, color='#000000')\n\nelse:\n    ax2.text(0.5, 0.5, 'No speed data available', ha='center', va='center',\n             transform=ax2.transAxes, fontsize=13, fontweight='bold', color='#666')\n    ax2.set_title('Speed Distribution by Mouse')\n    ax2.axis('off')\n\n# Position Allocation (KDE)\nfor mouse_id in sorted(body_center_data['mouse_id'].unique()):\n    mouse = body_center_data[body_center_data['mouse_id'] == mouse_id]\n    color = colors.get(mouse_id, 'gray')\n    sns.kdeplot(data=mouse, x='x', y='y', ax=ax1, color=color, linewidths=3, alpha=0.65, fill=False, levels=10)\n\nax1.set(xlabel='X (pixels)', ylabel='Y (pixels)', title='Position Density (KDE)')\nlegend_elements = [Line2D([0], [0], color=colors.get(i, 'gray'), lw=4, label=f'Mouse {i}') for i in sorted(body_center_data['mouse_id'].unique())]\n\nax1.legend(handles=legend_elements,\n           loc='upper right',\n           fontsize=12,\n           frameon=True,\n           fancybox=True,\n           shadow=True,\n           ncol=1)\n\nax1.grid(True, alpha=0.3, linestyle='--')\nax1.set_aspect('equal', adjustable='datalim')\nax1.set_facecolor('#f0f4f8')\nax1.spines[['top', 'right']].set_visible(False)\n\nadd_mouse_image(fig, ax1, img_path='/kaggle/input/mousenotebook/mouse2.png', position=(0.5, 0.2), zoom=0.1)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-23T00:27:01.235127Z","iopub.execute_input":"2025-09-23T00:27:01.235866Z","iopub.status.idle":"2025-09-23T00:27:44.698002Z","shell.execute_reply.started":"2025-09-23T00:27:01.235839Z","shell.execute_reply":"2025-09-23T00:27:44.696995Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Watch video 📽️","metadata":{}},{"cell_type":"code","source":"plt.style.use('seaborn-v0_8-whitegrid')\n\ntrain = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/train.csv')\nsample_video = train.iloc[0]\nlab_id = sample_video['lab_id']\nvideo_id = sample_video['video_id']\n\ntracking_path = f'/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet'\ndf_tracking = pd.read_parquet(tracking_path)\n\n# Filter body center only\ndf = df_tracking[df_tracking['bodypart'] == 'body_center'].copy()\ndf['mouse_id'] = df['mouse_id'].astype(str)\nmouse_ids = df['mouse_id'].unique()\n\n# Assign colors\ncolors = plt.cm.Set1(np.linspace(0, 1, len(mouse_ids)))\ncolor_map = dict(zip(mouse_ids, colors))\n\n# Video dimensions\nvideo_width = sample_video['video_width_pix']\nvideo_height = sample_video['video_height_pix']\n\nfig, ax = plt.subplots(figsize=(12, 9))\nax.set_xlim(0, video_width)\nax.set_ylim(0, video_height)\nax.invert_yaxis()\nax.set_title(f'Mouse Movement Animation — Video {video_id}', fontsize=16, fontweight='bold')\nax.set_xlabel('X (pixels)', fontsize=12)\nax.set_ylabel('Y (pixels)', fontsize=12)\nax.grid(True, alpha=0.3)\n\n# Arena border\narena = Rectangle((0, 0), video_width, video_height, linewidth=2, edgecolor='black', facecolor='none', linestyle='--')\nax.add_patch(arena)\n\n# Initialize squares and trails\nsquares = {}\ntrail_lines = {}\ntrail_data = {mouse: {'x': [], 'y': []} for mouse in mouse_ids}\n\nfor mouse in mouse_ids:\n    sq = Rectangle((0, 0), 20, 20, facecolor=color_map[mouse], edgecolor='black', alpha=0.9, label=f'Mouse {mouse}')\n    ax.add_patch(sq)\n    squares[mouse] = sq\n    trail_lines[mouse], = ax.plot([], [], color=color_map[mouse], alpha=0.6, linewidth=2, linestyle='-')\n\nax.legend(loc='upper right', fontsize=11)\n\n# Animation\ndf_sorted = df.sort_values('video_frame').reset_index(drop=True)\nframes = sorted(df_sorted['video_frame'].unique())[:270]\n\ndef animate(frame):\n    updated = []\n    for mouse in mouse_ids:\n        mouse_data = df_sorted[(df_sorted['mouse_id'] == mouse) & (df_sorted['video_frame'] <= frame)]\n        if mouse_data.empty:\n            continue\n        last_pos = mouse_data.iloc[-1]\n        x, y = last_pos['x'], last_pos['y']\n        \n        # Update square position\n        squares[mouse].set_xy((x - 10, y - 10))\n        updated.append(squares[mouse])\n        \n        # Update trail\n        trail_data[mouse]['x'].append(x)\n        trail_data[mouse]['y'].append(y)\n        trail_lines[mouse].set_data(trail_data[mouse]['x'], trail_data[mouse]['y'])\n        updated.append(trail_lines[mouse])\n    \n    ax.set_title(f'Video {video_id} — Frame {int(frame)}', fontsize=14)\n    \n    return updated\n\nani = animation.FuncAnimation(fig, animate, frames=frames, interval=80, blit=True, repeat=False)\n\nplt.close(fig)\nHTML(ani.to_jshtml())","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-09-22T23:53:19.521777Z","iopub.execute_input":"2025-09-22T23:53:19.522204Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### click ▶️","metadata":{}},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #e9ecef, #dee2e6); padding: 15px; border-radius: 10px; border: 2px solid #adb5bd; margin: 15px 0;\">\n<h1 style=\"text-align: center; color: #495057; font-weight: bold; margin: 0;\">\n🙏 Thank you for your attention!\n</h1>\n</div>","metadata":{}}]}