{"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":"markdown","source":"# EDA which makes sense for the MABe Challenge - Social Action Recognition in Mice\n\nLet's postpone the descriptive statistics. Isn't the most interesting part the visualization of the mice?\n\nReference\n- [Competition](https://www.kaggle.com/competitions/MABe-mouse-behavior-detection)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-27T12:46:47.837201Z","iopub.execute_input":"2025-09-27T12:46:47.837536Z","iopub.status.idle":"2025-09-27T12:46:50.571278Z","shell.execute_reply.started":"2025-09-27T12:46:47.837510Z","shell.execute_reply":"2025-09-27T12:46:50.570240Z"},"_kg_hide-input":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/MABe-mouse-behavior-detection/train.csv')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-27T12:46:50.573138Z","iopub.execute_input":"2025-09-27T12:46:50.573760Z","iopub.status.idle":"2025-09-27T12:46:50.714670Z","shell.execute_reply.started":"2025-09-27T12:46:50.573733Z","shell.execute_reply":"2025-09-27T12:46:50.713462Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Visualizing the mice\n\nWe create a class `Visualizer`, which plots a single frame of a video so that the mice can be recognized.","metadata":{}},{"cell_type":"code","source":"class Visualizer():\n    \"\"\"A class for visualizing single frames of mouse videos.\n\n    From https://www.kaggle.com/code/ambrosm/mabe-eda-which-makes-sense\n    \"\"\"\n    paws = ['forepaw_left', 'forepaw_right', 'hindpaw_left', 'hindpaw_right']\n    head = ['ear_left', 'ear_right', 'nose', 'ear_left']\n\n    def __init__(self, train):\n        \"\"\"Initialize a visualizer.\n        \n        Parameters:\n        train: pandas DataFrame read from train.csv\n        \"\"\"\n        self.train = train\n    \n    def load_video(self, train_idx):\n        \"\"\"Load the specified video into the visualizer\"\"\"\n        self.train_idx = train_idx\n        lab_id = self.train.iloc[train_idx].lab_id\n        video_id = self.train.iloc[train_idx].video_id\n        path = f\"/kaggle/input/MABe-mouse-behavior-detection/train_tracking/{lab_id}/{video_id}.parquet\"\n        self.video_name = path.split('/')[-1].split('.')[0]\n        self.vid = pd.read_parquet(path)\n        try:\n            self.annot = pd.read_parquet(path.replace('train_tracking', 'train_annotation'))\n        except FileNotFoundError:\n            self.annot = None\n        self.pvid = self.vid.pivot(columns=['mouse_id', 'bodypart'], index='video_frame', values=['x', 'y'])\n        self.bodyparts = set(self.pvid.loc[self.pvid.index[0], ('x', 1)].index)\n        # print(self.bodyparts)\n        self.n_mouses = len(np.unique(self.pvid.columns.get_level_values('mouse_id')))\n\n    def __len__(self):\n        \"\"\"Frame count of video\"\"\"\n        return len(self.pvid)\n\n    def plot_frame(self, frame_idx):\n        \"\"\"Plot the selected frame of the previously loaded video\"\"\"\n        video_frame = self.pvid.index[frame_idx]\n        if (self.pvid.loc[video_frame] == 0).all():\n            print(f\"{self.train_idx}.{frame_idx} is empty.\")\n            return\n        for mouse, color in enumerate(['g', 'b', 'orange', 'brown'][:self.n_mouses]):\n            mouse_id = mouse + 1\n            mx = self.pvid.loc[video_frame, ('x', mouse_id)].copy()\n            my = self.pvid.loc[video_frame, ('y', mouse_id)].copy()\n\n            # Plot the head\n            # Every mouse has ear_left and ear_right\n            if 'nose' in mx.index and mx['nose'] != 0:\n                plt.fill(mx[self.head], my[self.head], color=color, alpha=0.5)\n                plt.scatter([mx['nose']], [my['nose']], s=100, color=color)\n            else:\n                plt.plot(mx[['ear_left', 'ear_right']], my[['ear_left', 'ear_right']], color=color)\n            if 'head' not in mx.index:\n                mx['head'] = mx[['ear_left', 'ear_right']].mean()\n                my['head'] = my[['ear_left', 'ear_right']].mean()\n\n            # Plot the body and tail\n            # Every mouse has tail_base, but it can be 0\n            parts_list = ['head']\n            if 'neck' in mx.index and mx['neck'] != 0:\n                parts_list.append('neck')\n            if 'body_center' in mx.index and mx['body_center'] != 0:\n                parts_list.append('body_center')\n            if mx['tail_base'] != 0:\n                parts_list.append('tail_base')\n            if 'tail_tip' in mx.index and mx['tail_tip'] != 0:\n                parts_list.append('tail_tip')\n            plt.plot(mx[parts_list], my[parts_list], color=color)\n\n            # Plot the width of the body\n            if 'lateral_right' in mx.index:\n                plt.plot(mx[['lateral_right', 'lateral_left']], my[['lateral_right', 'lateral_left']], color=color)\n                \n            # Plot the hip\n            if 'hip_right' in mx.index:\n                plt.plot(mx[['hip_right', 'hip_left']], my[['hip_right', 'hip_left']], color=color)\n                \n            # Plot the paws\n            if 'forepaw_left' in mx.index:\n                plt.scatter(mx[self.paws], my[self.paws], color=color)\n\n        if self.annot is not None:\n            actions = set(self.annot.action[(self.annot.start_frame <= video_frame) & (video_frame <= self.annot.stop_frame)])\n            if len(actions) == 0:\n                actions = ''\n        else:\n            actions = ''\n        plt.title(f'{self.train_idx}.{frame_idx} {actions}')\n        plt.gca().set_aspect('equal')\n        plt.show()\n\nvisualizer = Visualizer(train)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-27T12:46:50.716058Z","iopub.execute_input":"2025-09-27T12:46:50.716447Z","iopub.status.idle":"2025-09-27T12:46:50.737199Z","shell.execute_reply.started":"2025-09-27T12:46:50.716390Z","shell.execute_reply":"2025-09-27T12:46:50.736103Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Let's look at a few video frames. Some of the frames have action annotations, which you can see in the title of the diagrams.","metadata":{}},{"cell_type":"code","source":"visualizer.load_video(5772)\nvisualizer.plot_frame(0)\nvisualizer.plot_frame(500)\n\nvisualizer.load_video(484)\nfor i in range(0, len(visualizer), len(visualizer) // 3):\n    visualizer.plot_frame(i)\n\nvisualizer.load_video(397)\nfor i in range(0, len(visualizer), len(visualizer) // 3):\n    visualizer.plot_frame(i)\n\nvisualizer.load_video(428)\nfor i in range(0, len(visualizer), len(visualizer) // 3):\n    visualizer.plot_frame(i)\n\nvisualizer.load_video(8669)\nfor i in range(0, len(visualizer), len(visualizer) // 3):\n    visualizer.plot_frame(i)\n\nvisualizer.load_video(306)\nfor i in range(0, len(visualizer), len(visualizer) // 3):\n    visualizer.plot_frame(i)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-27T12:50:23.377333Z","iopub.execute_input":"2025-09-27T12:50:23.377792Z","iopub.status.idle":"2025-09-27T12:50:27.140628Z","shell.execute_reply.started":"2025-09-27T12:50:23.377764Z","shell.execute_reply":"2025-09-27T12:50:27.139624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}