{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Cell 1 - Data Loader ","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport shutil\n\nrandom_seed = 42\n\nDATA_DIR = \"/kaggle/input/competitions/birdsong-recognition\"\nAUDIO_DIR = os.path.join(DATA_DIR, \"train_audio\")\n\ntrain_csv = pd.read_csv(os.path.join(DATA_DIR, \"train.csv\"))\nprint(train_csv.shape)\n\n# sanity check filenames match what's on disk\nsample_species = train_csv.ebird_code.iloc[0]\nprint(\"CSV filename:\", train_csv[train_csv.ebird_code == sample_species].filename.iloc[0])\nprint(\"Actual files:\", os.listdir(os.path.join(AUDIO_DIR, sample_species))[:5])","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2026-09-21T10:39:47.566033Z","iopub.execute_input":"2026-09-21T10:39:47.566811Z","iopub.status.idle":"2026-09-21T10:39:49.192278Z","shell.execute_reply.started":"2026-09-21T10:39:47.566778Z","shell.execute_reply":"2026-09-21T10:39:49.191505Z"},"trusted":true,"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 2 - Per Species Stats (EDA)","metadata":{}},{"cell_type":"code","source":"counts = train_csv.groupby(\"ebird_code\").agg(\n    n_clips=(\"ebird_code\", \"count\"),\n    avg_duration=(\"duration\", \"mean\"),\n    min_duration=(\"duration\", \"min\"),\n    max_duration=(\"duration\", \"max\"),\n    avg_rating=(\"rating\", \"mean\")\n).reset_index()\n\nprint(counts.describe())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.193416Z","iopub.execute_input":"2026-09-21T10:39:49.193707Z","iopub.status.idle":"2026-09-21T10:39:49.246761Z","shell.execute_reply.started":"2026-09-21T10:39:49.193676Z","shell.execute_reply":"2026-09-21T10:39:49.24602Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 3 - Filtering Based on maximum clip duration ","metadata":{}},{"cell_type":"code","source":"MIN_CLIPS = 45\nMIN_RATING = 3.5\n\ncandidates = counts[\n    (counts.n_clips >= MIN_CLIPS) &\n    (counts.avg_rating >= MIN_RATING)\n].sort_values([\"n_clips\", \"avg_rating\"], ascending=[False, False])\n\nprint(f\"Species passing filter: {len(candidates)}\")\nprint(candidates.head(20))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.247978Z","iopub.execute_input":"2026-09-21T10:39:49.248308Z","iopub.status.idle":"2026-09-21T10:39:49.26371Z","shell.execute_reply.started":"2026-09-21T10:39:49.248281Z","shell.execute_reply":"2026-09-21T10:39:49.262643Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 4 - Selected 6 species","metadata":{}},{"cell_type":"code","source":"# Chosen for: common well-known North American species, full clip availability (100),\n# high community-rated recording quality (avg_rating > 4.5), and taxonomic/vocalization\n# diversity (corvid, thrush, swallow, sparrow-family, wren, flycatcher)\nCHOSEN_SPECIES = [\"amecro\", \"amerob\", \"astfly\", \"barswa\", \"bewwre\", \"eastow\"]\n\nchosen_summary = counts[counts.ebird_code.isin(CHOSEN_SPECIES)].sort_values(\"ebird_code\")\nprint(chosen_summary)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.264885Z","iopub.execute_input":"2026-09-21T10:39:49.265215Z","iopub.status.idle":"2026-09-21T10:39:49.283897Z","shell.execute_reply.started":"2026-09-21T10:39:49.265183Z","shell.execute_reply":"2026-09-21T10:39:49.283009Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 5 - Exact clips per class (based on duration)","metadata":{}},{"cell_type":"code","source":"N_TRAIN_POOL = 25\nN_EVAL = 25\n\ncurated_rows = []\nfor species in CHOSEN_SPECIES:\n    sub = train_csv[train_csv.ebird_code == species].copy()\n    sub = sub.sample(frac=1, random_state=random_seed).reset_index(drop=True)\n    \n    n_needed = N_TRAIN_POOL + N_EVAL\n    sub = sub.iloc[:n_needed].copy()\n    sub[\"split\"] = [\"train_pool\"] * N_TRAIN_POOL + [\"eval\"] * N_EVAL\n    curated_rows.append(sub)\n\ncurated_df = pd.concat(curated_rows, ignore_index=True)\ncurated_df = curated_df.drop(columns=[\"species\"])\ncurated_df = curated_df.rename(columns={\"ebird_code\": \"species\"})\n\nprint(curated_df.split.value_counts())\nprint(curated_df.species.value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.284932Z","iopub.execute_input":"2026-09-21T10:39:49.285234Z","iopub.status.idle":"2026-09-21T10:39:49.325469Z","shell.execute_reply.started":"2026-09-21T10:39:49.285199Z","shell.execute_reply":"2026-09-21T10:39:49.324676Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # how many curated clips would need padding (i.e. are shorter than candidate thresholds)?\n# for t in [3, 5, 8, 10]:\n#     n_short = (curated_df.duration < t).sum()\n#     pct_short = 100 * n_short / len(curated_df)\n#     print(f\"Threshold {t}s: {n_short}/{len(curated_df)} clips ({pct_short:.1f}%) would need padding\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.328212Z","iopub.execute_input":"2026-09-21T10:39:49.329707Z","iopub.status.idle":"2026-09-21T10:39:49.339801Z","shell.execute_reply.started":"2026-09-21T10:39:49.329683Z","shell.execute_reply":"2026-09-21T10:39:49.338963Z"},"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 6 - Zero padding/ Truncating Clips and Making the dataset","metadata":{}},{"cell_type":"code","source":"from pydub import AudioSegment\n\nOUT_DIR = \"/kaggle/working/curated_dataset\"\nCLIP_DURATION_SEC = 5\n\nfor split in [\"train_pool\", \"eval\"]:\n    for species in CHOSEN_SPECIES:\n        os.makedirs(os.path.join(OUT_DIR, split, species), exist_ok=True)\n\nfailed = []\nfor _, row in curated_df.iterrows():\n    src = os.path.join(AUDIO_DIR, row.species, row.filename)\n    dst = os.path.join(OUT_DIR, row.split, row.species, row.filename)\n    \n    try:\n        audio = AudioSegment.from_mp3(src)\n        target_ms = CLIP_DURATION_SEC * 1000\n        \n        if len(audio) < target_ms:\n            silence = AudioSegment.silent(duration=target_ms - len(audio))\n            audio = audio + silence\n        else:\n            audio = audio[:target_ms]\n        \n        audio.export(dst, format=\"mp3\")\n    except Exception as e:\n        failed.append((row.species, row.filename, str(e)))\n\nprint(f\"Done. {len(curated_df) - len(failed)} succeeded, {len(failed)} failed.\")\nif failed:\n    print(failed[:10])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:39:49.340814Z","iopub.execute_input":"2026-09-21T10:39:49.34119Z","iopub.status.idle":"2026-09-21T10:43:02.059171Z","shell.execute_reply.started":"2026-09-21T10:39:49.341159Z","shell.execute_reply":"2026-09-21T10:43:02.058315Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Cell 7 - Final Zip file for the dataset","metadata":{}},{"cell_type":"code","source":"curated_df[[\"filename\", \"species\", \"split\", \"duration\", \"rating\"]].to_csv(\n    os.path.join(OUT_DIR, \"manifest.csv\"), index=False\n)\n\nshutil.make_archive(\"/kaggle/working/curated_dataset\", \"zip\", OUT_DIR)\nprint(\"Zipped: /kaggle/working/curated_dataset.zip\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-21T10:58:07.526286Z","iopub.execute_input":"2026-09-21T10:58:07.526701Z","iopub.status.idle":"2026-09-21T10:58:08.160648Z","shell.execute_reply.started":"2026-09-21T10:58:07.526671Z","shell.execute_reply":"2026-09-21T10:58:08.15987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}