{"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":"markdown","source":"# MABe Visualization Code\n\n#### This notebook is designed to visualize mouse behavior from the MABE dataset. It generates a video animation from a specified parquet file within the 'Train_Tracking' folder. By default, the code processes the first parquet file found in the directory.\n\n## Customization\n\n#### To analyze a different file, simply update the directory path. The code will adapt automatically to files containing fewer than four mice. You can also analyze specific segments of the recording by setting the `START_FRAME` and `END_FRAME` variables.\n\n#### **Note**: A larger frame range will result in a longer processing time.\n\n## Additional Notes\n\n#### The commented-out/hidden code is intended for observational purposes and can be modified as needed.\n\n#### Portions of this code were adapted from the notebook by analyticaobscura, which you can find here:\n#### [https://www.kaggle.com/code/analyticaobscura/mabe-v1-mouse-action-recognition](https://www.kaggle.com/code/analyticaobscura/mabe-v1-mouse-action-recognition)","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport polars as pl\nimport json\nimport os\nimport gc\nimport itertools\nfrom collections import defaultdict\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nimport matplotlib.animation as animation\nimport seaborn as sns\nfrom IPython.display import HTML\npd.options.display.max_columns = 100\ntqdm.pandas()\nBASE_PATH = \"/kaggle/input/MABe-mouse-behavior-detection/\"\nTRAIN_TRACKING_DIR = os.path.join(BASE_PATH, \"train_tracking\")\nTEST_TRACKING_DIR = os.path.join(BASE_PATH, \"test_tracking\")\nANNOTATION_DIR = os.path.join(BASE_PATH, \"train_annotation\")\ntrain_df = pd.read_csv(os.path.join(BASE_PATH, \"train.csv\"))\ntest_df = pd.read_csv(os.path.join(BASE_PATH, \"test.csv\"))\nsample_submission_df = pd.read_csv(os.path.join(BASE_PATH, \"sample_submission.csv\"))\nprint(\"ENVIRONMENT SETUP COMPLETE!\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.013350Z","iopub.execute_input":"2025-10-13T13:37:15.013719Z","iopub.status.idle":"2025-10-13T13:37:15.112204Z","shell.execute_reply.started":"2025-10-13T13:37:15.013694Z","shell.execute_reply":"2025-10-13T13:37:15.111376Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Hidden Codes Below (Optional) :-","metadata":{}},{"cell_type":"code","source":"# train_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.113277Z","iopub.execute_input":"2025-10-13T13:37:15.113561Z","iopub.status.idle":"2025-10-13T13:37:15.117640Z","shell.execute_reply.started":"2025-10-13T13:37:15.113533Z","shell.execute_reply":"2025-10-13T13:37:15.116940Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train_df.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.118533Z","iopub.execute_input":"2025-10-13T13:37:15.118799Z","iopub.status.idle":"2025-10-13T13:37:15.132517Z","shell.execute_reply.started":"2025-10-13T13:37:15.118782Z","shell.execute_reply":"2025-10-13T13:37:15.131749Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = train_df[~train_df['lab_id'].str.startswith('MABe22_')].reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.134122Z","iopub.execute_input":"2025-10-13T13:37:15.134423Z","iopub.status.idle":"2025-10-13T13:37:15.154109Z","shell.execute_reply.started":"2025-10-13T13:37:15.134403Z","shell.execute_reply":"2025-10-13T13:37:15.153082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# fig, axes = plt.subplots(2, 1, figsize = (16, 14))\n# lab_counts = train_df[\"lab_id\"].value_counts()\n# sns.barplot(x = lab_counts.index, y = lab_counts.values, ax = axes[0], palette = \"plasma\")\n# axes[0].set_title(\"Lab_id wise distribution of Videos\", fontsize = 18)\n# axes[0].set_xlabel(\"Lab IDs\")\n# axes[0].set_ylabel(\"No. of Videos\")\n# axes[0].tick_params(axis = \"x\", rotation = 45)\n\n# body_part_counts = train_df[\"body_parts_tracked\"].apply(lambda x: f\"{len(json.loads(x))} parts\").value_counts()\n# sns.barplot(x = body_part_counts.index, y = body_part_counts.values, ax = axes[1], palette = \"magma\")\n# axes[0].set_title(\"Body Parts wise distribution of Videos\", fontsize = 18)\n# axes[0].set_xlabel(\"No. of Body Parts\")\n# axes[0].set_ylabel(\"No. of Videos\")\n# axes[0].tick_params(axis = \"x\", rotation = 45)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.155039Z","iopub.execute_input":"2025-10-13T13:37:15.155338Z","iopub.status.idle":"2025-10-13T13:37:15.170136Z","shell.execute_reply.started":"2025-10-13T13:37:15.155312Z","shell.execute_reply":"2025-10-13T13:37:15.169223Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# print(\"################################################################\")\n# for i in (train_df['body_parts_tracked'].unique()):\n#     print(i)\n#     print(\"################################################################\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.171118Z","iopub.execute_input":"2025-10-13T13:37:15.171433Z","iopub.status.idle":"2025-10-13T13:37:15.186192Z","shell.execute_reply.started":"2025-10-13T13:37:15.171407Z","shell.execute_reply":"2025-10-13T13:37:15.185321Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# behaviour_durations = defaultdict(list)\n# DIFFERENT_BEHAVIOURS = {\"chase\", \"mount\", \"attack\", \"approach\", \"avoid\", \"rear\"}\n\n# print(\"Opening the PARQUET files\")\n\n# for i, row in tqdm(train_df.head(50).iterrows(), total=50):\n#     annotation_path = os.path.join(ANNOTATION_DIR, row['lab_id'], f\"{row['video_id']}.parquet\")\n#     print(annotation_path)\n#     try:\n#         annotation_df = pd.read_parquet(annotation_path)\n#         print(annotation_df)\n#         print(annotation_df.shape)\n#         print(annotation_df[\"action\"].value_counts())\n#         # if not annotation_df.empty:\n#         #     durations = annotation_df['stop_frame'] - annotation_df['start_frame']\n#         #     for action, duration in zip(annotation_df['action'], durations):\n#         #         behavior_durations[action].append(duration)\n                \n#     except FileNotFoundError:\n#         # This is a graceful way to handle videos that have tracking data but no annotations.\n#         # Our pipeline must not fail if an annotation file is missing.\n#         # print(f\"HANDLED ERROR: Annotation file not found for video {row['video_id']}. Skipping.\")\n#         pass\n#     if i == 0:\n#         break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.188070Z","iopub.execute_input":"2025-10-13T13:37:15.188326Z","iopub.status.idle":"2025-10-13T13:37:15.205117Z","shell.execute_reply.started":"2025-10-13T13:37:15.188308Z","shell.execute_reply":"2025-10-13T13:37:15.204027Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define your desired frame range here --\nSTART_FRAME = 0\nEND_FRAME = 100\n# Note : Change the train_tracking_path for the parquet file you want to check out\n\ncoords_1, coords_2, coords_3, coords_4 = [], [], [], []\n\n# This loop finds and processes the first available parquet file\n# It breaks after one successful iteration.\nfor i, row in tqdm(train_df.iterrows(), total=len(train_df)):\n    print(f\"Loading the PARQUET File...\")\n    train_tracking_path = os.path.join(TRAIN_TRACKING_DIR, row['lab_id'], f\"{row['video_id']}.parquet\")\n    \n    try:\n        train_tracking_df = pd.read_parquet(train_tracking_path)\n        print(f\"Successfully loaded: {train_tracking_path}\")\n        print(f\"Original shape: {train_tracking_df.shape}\")\n\n        filtered_df = train_tracking_df[\n            (train_tracking_df[\"video_frame\"] >= START_FRAME) & \n            (train_tracking_df[\"video_frame\"] < END_FRAME)\n        ]\n        print(f\"Shape after filtering for frames {START_FRAME}-{END_FRAME}: {filtered_df.shape}\")\n\n        for j in range(START_FRAME, END_FRAME):\n            df_1 = filtered_df[(filtered_df[\"video_frame\"] == j) & (filtered_df[\"mouse_id\"] == 1)]\n            df_2 = filtered_df[(filtered_df[\"video_frame\"] == j) & (filtered_df[\"mouse_id\"] == 2)]\n            df_3 = filtered_df[(filtered_df[\"video_frame\"] == j) & (filtered_df[\"mouse_id\"] == 3)]\n            df_4 = filtered_df[(filtered_df[\"video_frame\"] == j) & (filtered_df[\"mouse_id\"] == 4)]\n\n            coords_1.append(df_1.loc[:, [\"x\", \"y\"]])\n            coords_2.append(df_2.loc[:, [\"x\", \"y\"]])\n            coords_3.append(df_3.loc[:, [\"x\", \"y\"]])\n            coords_4.append(df_4.loc[:, [\"x\", \"y\"]])\n\n        break\n            \n    except FileNotFoundError:\n        print(f\"File not found for video {row['video_id']}. Searching for next file.\")\n        pass","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.256866Z","iopub.execute_input":"2025-10-13T13:37:15.257889Z","iopub.status.idle":"2025-10-13T13:37:15.656368Z","shell.execute_reply.started":"2025-10-13T13:37:15.257859Z","shell.execute_reply":"2025-10-13T13:37:15.655334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(coords_1))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.657891Z","iopub.execute_input":"2025-10-13T13:37:15.658175Z","iopub.status.idle":"2025-10-13T13:37:15.662624Z","shell.execute_reply.started":"2025-10-13T13:37:15.658155Z","shell.execute_reply":"2025-10-13T13:37:15.661904Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# print(df_1)\n# print(df_2)\n# print(df_3)\n# print(df_4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.663647Z","iopub.execute_input":"2025-10-13T13:37:15.663996Z","iopub.status.idle":"2025-10-13T13:37:15.683558Z","shell.execute_reply.started":"2025-10-13T13:37:15.663976Z","shell.execute_reply":"2025-10-13T13:37:15.682644Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(8, 8))\nax.set_aspect('equal')\nax.set_title(\"DataFrame Animation\")\nax.set_xlabel(\"X Coordinate\")\nax.set_ylabel(\"Y Coordinate\")\nax.set_xlim(0, 1200)\nax.set_ylim(0, 1200)\n\nscatter_1 = ax.scatter(coords_1[0]['x'], coords_1[0]['y'], s=50, c='red', label='Mouse 1')\nscatter_2 = ax.scatter(coords_2[0]['x'], coords_2[0]['y'], s=50, c='blue', label='Mouse 2')\nscatter_3 = ax.scatter(coords_3[0]['x'], coords_3[0]['y'], s=50, c='green', label='Mouse 3')\nscatter_4 = ax.scatter(coords_4[0]['x'], coords_4[0]['y'], s=50, c='purple', label='Mouse 4')\n\nax.legend(loc='upper right')\n\ndef update(frame):\n    \"\"\"Updates the positions of all four scatter plots for each frame.\"\"\"\n    \n    # Update Mouse 1\n    new_pos_1 = coords_1[frame][['x', 'y']].values\n    scatter_1.set_offsets(new_pos_1)\n    \n    # Update Mouse 2\n    new_pos_2 = coords_2[frame][['x', 'y']].values\n    scatter_2.set_offsets(new_pos_2)\n    \n    # Update Mouse 3\n    new_pos_3 = coords_3[frame][['x', 'y']].values\n    scatter_3.set_offsets(new_pos_3)\n    \n    # Update Mouse 4\n    new_pos_4 = coords_4[frame][['x', 'y']].values\n    scatter_4.set_offsets(new_pos_4)\n    \n    return scatter_1, scatter_2, scatter_3, scatter_4\n\nani = animation.FuncAnimation(\n    fig=fig, \n    func=update, \n    frames=len(coords_1),\n    interval=200,\n    blit=True\n)\n\nHTML(ani.to_html5_video())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T13:37:15.684498Z","iopub.execute_input":"2025-10-13T13:37:15.684825Z","iopub.status.idle":"2025-10-13T13:37:29.768282Z","shell.execute_reply.started":"2025-10-13T13:37:15.684764Z","shell.execute_reply":"2025-10-13T13:37:29.767303Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}