{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":91451,"databundleVersionId":11223220,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom PIL import Image\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom PIL import ImageOps\n\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:38:39.119266Z","iopub.execute_input":"2025-05-10T21:38:39.120025Z","iopub.status.idle":"2025-05-10T21:38:39.125962Z","shell.execute_reply.started":"2025-05-10T21:38:39.119993Z","shell.execute_reply":"2025-05-10T21:38:39.124872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/animal-clef-2025/metadata.csv')\n\ndf.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:13:06.485100Z","iopub.execute_input":"2025-05-10T21:13:06.485582Z","iopub.status.idle":"2025-05-10T21:13:06.612823Z","shell.execute_reply.started":"2025-05-10T21:13:06.485552Z","shell.execute_reply":"2025-05-10T21:13:06.611869Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = df[df['identity'].notnull()]\ntest = df[df['identity'].isna()]\n\nprint(train.shape) # (13074,8)\nprint(test.shape) # (2135,8)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:13:06.613699Z","iopub.execute_input":"2025-05-10T21:13:06.613940Z","iopub.status.idle":"2025-05-10T21:13:06.625121Z","shell.execute_reply.started":"2025-05-10T21:13:06.613920Z","shell.execute_reply":"2025-05-10T21:13:06.624321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# calculate number of identities\nprint(\"Total identities: \",len(train['identity'].unique()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:13:06.627445Z","iopub.execute_input":"2025-05-10T21:13:06.627781Z","iopub.status.idle":"2025-05-10T21:13:06.641604Z","shell.execute_reply.started":"2025-05-10T21:13:06.627752Z","shell.execute_reply":"2025-05-10T21:13:06.640656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# check the number of images one identity could have\nplt.figure(figsize=(12, 5))\nsns.histplot(train['identity'].value_counts(), bins=30, kde=False)\nplt.title(\"Distribution of Image Count per Identity\")\nplt.xlabel(\"Number of Images\")\nplt.ylabel(\"Number of Identities\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:13:10.025046Z","iopub.execute_input":"2025-05-10T21:13:10.025367Z","iopub.status.idle":"2025-05-10T21:13:10.458557Z","shell.execute_reply.started":"2025-05-10T21:13:10.025342Z","shell.execute_reply":"2025-05-10T21:13:10.457591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['identity'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:13:17.080203Z","iopub.execute_input":"2025-05-10T21:13:17.080872Z","iopub.status.idle":"2025-05-10T21:13:17.089974Z","shell.execute_reply.started":"2025-05-10T21:13:17.080841Z","shell.execute_reply":"2025-05-10T21:13:17.089209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def summary_table(df):\n    \"\"\"\n    summary table of species v.s. orientation\n    \"\"\"\n    summary_df = df.groupby(['orientation','species'],dropna = False).size().unstack(fill_value = 0).sort_index()\n\n    display(summary_table)\n    return summary_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:05.987654Z","iopub.execute_input":"2025-05-10T21:14:05.987950Z","iopub.status.idle":"2025-05-10T21:14:05.992821Z","shell.execute_reply.started":"2025-05-10T21:14:05.987927Z","shell.execute_reply":"2025-05-10T21:14:05.991928Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"summary_table(train) # missing species, and missing orientation founded","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:07.943890Z","iopub.execute_input":"2025-05-10T21:14:07.944197Z","iopub.status.idle":"2025-05-10T21:14:07.983241Z","shell.execute_reply.started":"2025-05-10T21:14:07.944169Z","shell.execute_reply":"2025-05-10T21:14:07.982433Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"summary_table(test) # missing values under species","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:10.017569Z","iopub.execute_input":"2025-05-10T21:14:10.018290Z","iopub.status.idle":"2025-05-10T21:14:10.032104Z","shell.execute_reply.started":"2025-05-10T21:14:10.018263Z","shell.execute_reply":"2025-05-10T21:14:10.031275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def image_show(image_id = None, species = None, orientation = None, source = df):\n    \"\"\"\n    Display images.\n    \"\"\"\n    root = '/kaggle/input/animal-clef-2025/'\n\n    if source == 'train':\n        df_used = train\n    elif source =='test':\n        df_used = test\n    else:\n        df_used = df.copy()\n        \n    if image_id:\n        rows = df_used[df_used['image_id']==image_id]\n\n    else:\n        \n        if species is not None:\n            if isinstance(species, float) and np.isnan(species):\n                df_used = df_used[df_used['species'].isna()]\n            else:\n                df_used = df_used[df_used['species'] == species]\n        \n        if orientation is not None:\n            if isinstance(orientation, float) and np.isnan(orientation):\n                df_used = df_used[df_used['orientation'].isna()]\n            else:\n                df_used = df_used[df_used['orientation'] == orientation]\n                    \n        rows = df_used.sample(n=min(16,len(df_used)))\n\n    n_rows = 4\n    n_cols = 4\n    size = (224,224)\n    selected_rows = rows.to_dict('records')\n\n    plt.figure(figsize=(12, 12))\n    for i in range(16):\n        plt.subplot(n_rows, n_cols, i + 1)\n        if i < len(selected_rows):\n            row = selected_rows[i]\n            full_path = os.path.join(root, row['path'])\n            try:\n                img = Image.open(full_path).convert('RGB')\n                img = ImageOps.fit(img, size, Image.Resampling.LANCZOS)\n            except Exception as e:\n                print(f\"[ERROR] Failed to load image at {full_path} – {e}\")\n                img = Image.new('RGB', size, (150, 150, 150))\n            title = f\"{row['identity']}\\n{row['orientation']}\"\n        else:\n            img = Image.new('RGB', size, (240, 240, 240))\n            title = \"\"\n\n        plt.imshow(img)\n        plt.title(title, fontsize=8)\n        plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:38:04.493526Z","iopub.execute_input":"2025-05-10T21:38:04.493818Z","iopub.status.idle":"2025-05-10T21:38:04.504693Z","shell.execute_reply.started":"2025-05-10T21:38:04.493796Z","shell.execute_reply":"2025-05-10T21:38:04.503769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = np.nan,source = 'train') # missing species are comfirmed as salamander","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:38:53.067005Z","iopub.execute_input":"2025-05-10T21:38:53.067867Z","iopub.status.idle":"2025-05-10T21:38:53.711271Z","shell.execute_reply.started":"2025-05-10T21:38:53.067832Z","shell.execute_reply":"2025-05-10T21:38:53.710579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# fill in the missing values for epscies column\ncondition = (df['species'].isna()) & (df['identity'].str.contains('salamander', case=False, na=False))\ndf.loc[condition, 'species'] = 'salamander'\n\ntrain = df[df['identity'].notna()].reset_index(drop=True)\ntest = df[df['identity'].isna()].reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:22.930955Z","iopub.execute_input":"2025-05-10T21:14:22.931276Z","iopub.status.idle":"2025-05-10T21:14:22.953549Z","shell.execute_reply.started":"2025-05-10T21:14:22.931217Z","shell.execute_reply":"2025-05-10T21:14:22.952664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# double check\nsummary_table(train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:25.119627Z","iopub.execute_input":"2025-05-10T21:14:25.119946Z","iopub.status.idle":"2025-05-10T21:14:25.136548Z","shell.execute_reply.started":"2025-05-10T21:14:25.119921Z","shell.execute_reply":"2025-05-10T21:14:25.135526Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"summary_table(test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:14:27.741518Z","iopub.execute_input":"2025-05-10T21:14:27.741884Z","iopub.status.idle":"2025-05-10T21:14:27.756058Z","shell.execute_reply.started":"2025-05-10T21:14:27.741853Z","shell.execute_reply":"2025-05-10T21:14:27.755346Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# check species and id distribution\n\nspecies = df['species'].unique()\n\nfor s in species:\n    print(df[df['species']==s]['identity'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:19:34.770832Z","iopub.execute_input":"2025-05-10T21:19:34.771726Z","iopub.status.idle":"2025-05-10T21:19:34.788307Z","shell.execute_reply.started":"2025-05-10T21:19:34.771688Z","shell.execute_reply":"2025-05-10T21:19:34.787591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = \"lynx\", orientation = \"right\",source = \"train\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:38:59.800601Z","iopub.execute_input":"2025-05-10T21:38:59.800914Z","iopub.status.idle":"2025-05-10T21:39:01.898128Z","shell.execute_reply.started":"2025-05-10T21:38:59.800893Z","shell.execute_reply":"2025-05-10T21:39:01.897209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = \"lynx\", orientation = \"front\",source = \"train\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:41:13.761542Z","iopub.execute_input":"2025-05-10T21:41:13.762166Z","iopub.status.idle":"2025-05-10T21:41:15.650985Z","shell.execute_reply.started":"2025-05-10T21:41:13.762140Z","shell.execute_reply":"2025-05-10T21:41:15.650111Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Some images in the lynx category are labeled as unknown for orientation, \n# but this feature is clearly visible and should be corrected.\nimage_show(species = \"lynx\", orientation = \"unknown\",source = \"train\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:47:22.288083Z","iopub.execute_input":"2025-05-10T21:47:22.288745Z","iopub.status.idle":"2025-05-10T21:47:24.216438Z","shell.execute_reply.started":"2025-05-10T21:47:22.288665Z","shell.execute_reply":"2025-05-10T21:47:24.215586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = \"lynx\", orientation = \"front\",source = \"test\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:39:48.880816Z","iopub.execute_input":"2025-05-10T21:39:48.881696Z","iopub.status.idle":"2025-05-10T21:39:51.484612Z","shell.execute_reply.started":"2025-05-10T21:39:48.881650Z","shell.execute_reply":"2025-05-10T21:39:51.483757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = \"lynx\", orientation = \"unknown\",source = \"test\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:39:19.378570Z","iopub.execute_input":"2025-05-10T21:39:19.379201Z","iopub.status.idle":"2025-05-10T21:39:22.484732Z","shell.execute_reply.started":"2025-05-10T21:39:19.379178Z","shell.execute_reply":"2025-05-10T21:39:22.483631Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = 'loggerhead turtle',orientation=np.nan, source='train')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:48:30.055165Z","iopub.execute_input":"2025-05-10T21:48:30.055504Z","iopub.status.idle":"2025-05-10T21:48:32.731138Z","shell.execute_reply.started":"2025-05-10T21:48:30.055480Z","shell.execute_reply":"2025-05-10T21:48:32.730145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = 'loggerhead turtle',orientation=np.nan, source='test')\n\n# Most NaN orientation images in the training set are full-body shots with unclear head features, \n# so we only infer orientations in the test set where head views are more visible.","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:48:41.074502Z","iopub.execute_input":"2025-05-10T21:48:41.074799Z","iopub.status.idle":"2025-05-10T21:48:43.420381Z","shell.execute_reply.started":"2025-05-10T21:48:41.074780Z","shell.execute_reply":"2025-05-10T21:48:43.419436Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_show(species = 'loggerhead turtle',orientation='left', source='train')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:48:49.753907Z","iopub.execute_input":"2025-05-10T21:48:49.754723Z","iopub.status.idle":"2025-05-10T21:48:51.912776Z","shell.execute_reply.started":"2025-05-10T21:48:49.754687Z","shell.execute_reply":"2025-05-10T21:48:51.911495Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# check for a single identity\nimport random\n\nsample_id = random.choice(train['identity'].unique())\n\nsample_rows = train[train['identity'] == sample_id].sample(n=min(10, train[train['identity'] == sample_id].shape[0]), random_state=42)\n\n\nfor idx, row in sample_rows.iterrows():\n    image_path = os.path.join('/kaggle/input/animal-clef-2025', row['path'])\n    image = Image.open(image_path).convert('RGB')\n\n    plt.figure(figsize=(4, 4))\n    plt.imshow(image)\n    plt.title(f\"ID: {row['identity']}\\nOrientation: {row['orientation']}\")\n    plt.axis('off')\n    plt.show()\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-10T21:49:23.832359Z","iopub.execute_input":"2025-05-10T21:49:23.832641Z","iopub.status.idle":"2025-05-10T21:49:24.547527Z","shell.execute_reply.started":"2025-05-10T21:49:23.832621Z","shell.execute_reply":"2025-05-10T21:49:24.546718Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Many turtle images labeled as right and topright appear visually similar, suggesting that the current orientation categories may be overly fine-grained and could benefit from merging.","metadata":{}},{"cell_type":"code","source":"train.to_csv('/kaggle/working/train.csv', index=False)\ntest.to_csv('/kaggle/working/test.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-09T04:13:57.797148Z","iopub.execute_input":"2025-05-09T04:13:57.797478Z","iopub.status.idle":"2025-05-09T04:13:57.902992Z","shell.execute_reply.started":"2025-05-09T04:13:57.797447Z","shell.execute_reply":"2025-05-09T04:13:57.902197Z"}},"outputs":[],"execution_count":null}]}