{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport librosa\nimport librosa.display\n\n# Chemin d'accès racine (à adapter si nécessaire)\nROOT = \"/kaggle/input/birdclef-2025\"\n\n# Chargement des fichiers\ntry:\n    train_df = pd.read_csv(f'{ROOT}/train.csv')\n    taxonomy_df = pd.read_csv(f'{ROOT}/taxonomy.csv')\n    sample_submission = pd.read_csv(f'{ROOT}/sample_submission.csv')\n    print(\"Chargement des DataFrames réussi.\")\nexcept FileNotFoundError as e:\n    print(f\"Erreur de chargement : {e}\")\n\n# Aperçu\nprint(\"\\nAperçu de train_df:\")\nprint(train_df.head())\nprint(f\"\\nDimensions de train_df : {train_df.shape}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compter les occurrences de chaque espèce\nlabel_counts = train_df['primary_label'].value_counts()\n\n# Afficher le Top 10 et le Bottom 10\nprint(\"--- Top 10 des espèces les plus fréquentes ---\")\nprint(label_counts.head(10))\nprint(\"\\n--- Bottom 10 des espèces les plus rares ---\")\nprint(label_counts.tail(10))\n\n# Visualisation des 50 espèces les plus fréquentes pour confirmer le déséquilibre\nplt.figure(figsize=(15, 6))\nsns.barplot(x=label_counts.head(50).index, y=label_counts.head(50).values, palette=\"viridis\")\nplt.title('Distribution des 50 Espèces les plus Fréquentes')\nplt.xlabel('Espèce (primary_label)')\nplt.ylabel('Nombre d\\'enregistrements')\nplt.xticks(rotation=90)\nplt.tight_layout()\nplt.show()\n\n# IMPLICATION : Confirme la nécessité d'oversampling et d'augmentation des données.","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- Partie A : Distribution Taxonomique ---\n\n# 1. Vérification et Nettoyage des colonnes de taxonomy_df\n# Nous assumons que les colonnes pertinentes sont 'taxon_id' (pour la liaison) et 'class' (pour la catégorie : Aves, Amphibia, etc.).\nprint(\"Colonnes de taxonomy_df :\", taxonomy_df.columns.tolist())\n\n# Correction de l'accès aux colonnes et du renommage\ntry:\n    # Tentative d'utilisation des colonnes 'taxon_id' et 'class'\n    taxonomy_df_clean = taxonomy_df[['taxon_id', 'class']].copy()\n    taxonomy_df_clean = taxonomy_df_clean.rename(columns={'class': 'class_name'})\nexcept KeyError as e:\n    # Si la colonne 'class' ou 'taxon_id' manque, nous ne pouvons pas faire la liaison.\n    # Pour l'EDA, nous allons simplement utiliser la colonne existante la plus probable pour les catégories.\n    if 'class' in taxonomy_df.columns:\n        print(\"Avertissement: 'taxon_id' manquant ou mal orthographié, utilisant 'class' pour le décompte.\")\n        taxonomy_df_clean = taxonomy_df.rename(columns={'class': 'class_name'})\n    else:\n        print(f\"Erreur fatale: Colonne de classification ('class') manquante dans taxonomy_df. Colonnes : {taxonomy_df.columns.tolist()}\")\n        taxonomy_df_clean = pd.DataFrame({'class_name': []}) # DataFrame vide pour éviter l'erreur suivante\n\n# 2. Décompte des Taxons\nif not taxonomy_df_clean.empty:\n    taxa_counts = taxonomy_df_clean['class_name'].value_counts()\n\n    plt.figure(figsize=(8, 6))\n    sns.barplot(x=taxa_counts.index, y=taxa_counts.values, palette=\"rocket\")\n    plt.title('Nombre d\\'espèces uniques par Taxon')\n    plt.ylabel(\"Nombre d'espèces uniques\")\n    plt.xlabel(\"Taxon (class_name)\")\n    plt.show()\n    print(\"IMPLICATION : Les différences de taille de classe confirment la nécessité de stratégies d'extraction de features adaptées (fréquences différentes pour chaque taxon).\")\nelse:\n    print(\"Impossible de procéder à l'analyse taxonomique sans une colonne de classe valide.\")\n\n\nprint(\"\\n\" + \"=\"*50)\nprint(\"--- Partie B : Analyse Multi-Label ---\")\n\n# 1. Fonction pour compter le nombre d'espèces secondaires\ndef count_secondary_labels(labels_str):\n    \"\"\"Compte le nombre de labels secondaires (autres espèces) dans la chaîne.\"\"\"\n    try:\n        # Évaluation sécurisée de la chaîne list-like (e.g., \"['compau', 'trokin']\")\n        labels_list = eval(labels_str)\n        # Compte les éléments de la liste qui ne sont pas la chaîne vide ([''])\n        return len([l for l in labels_list if l != ''])\n    except Exception:\n        # En cas d'erreur d'évaluation (format incorrect)\n        return 0\n\n# Applique la fonction pour obtenir le nombre de labels secondaires\ntrain_df['num_secondary_labels'] = train_df['secondary_labels'].apply(count_secondary_labels)\n\n# 2. Distribution du nombre total d'espèces (Primaire + Secondaires)\n# Le nombre total d'espèces est : 1 (primary_label) + num_secondary_labels\ntrain_df['total_species_in_clip'] = 1 + train_df['num_secondary_labels']\n\ntotal_species_counts = train_df['total_species_in_clip'].value_counts().sort_index()\n\nprint(\"\\n--- Distribution du nombre total d'espèces (Primaire + Secondaires) par enregistrement ---\")\nprint(total_species_counts)\n\n# 3. Visualisation (se concentrer sur les clips multi-espèces)\n# Nous filtrons la catégorie \"1 seule espèce\" pour mieux visualiser les co-occurrences.\nmulti_species_clips = total_species_counts[total_species_counts.index > 1]\n\n\nif not multi_species_clips.empty:\n    plt.figure(figsize=(8, 5))\n    multi_species_clips.plot(kind='bar', color='darkorange')\n    plt.title('Fréquence des enregistrements Multi-Espèces (2+ espèces)')\n    plt.xlabel(\"Nombre total d'espèces présentes\")\n    plt.ylabel(\"Fréquence\")\n    plt.xticks(rotation=0)\n    plt.show()\n    print(\"IMPLICATION : Le nombre de clips multi-espèces est faible (comparé aux clips simples). Cela justifie l'utilisation d'une **Loss Function Multi-Label (BCE)** et d'une analyse des co-occurrences pour améliorer la performance sur ces cas complexes.\")\nelse:\n    print(\"Aucun enregistrement multi-label significatif trouvé.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\n\n# --- A. Distribution Géographique ---\n\nprint(\"--- Analyse Géographique des Enregistrements (latitude/longitude) ---\")\n\n# Filtrer les coordonnées manquantes ou invalides si nécessaire pour le plotting\ndf_geo = train_df.dropna(subset=['latitude', 'longitude'])\n\nplt.figure(figsize=(10, 6))\n# Utiliser 'hue' pour différencier les collections (XC, iNat, CSA)\nsns.scatterplot(\n    x='longitude', \n    y='latitude', \n    data=df_geo, \n    alpha=0.6, \n    hue='collection', \n    palette='tab10',\n    s=20 # Taille du point\n)\nplt.title('Distribution Géographique des Enregistrements')\nplt.xlabel('Longitude')\nplt.ylabel('Latitude')\nplt.legend(title='Collection', loc='lower right')\nplt.grid(True, linestyle='--', alpha=0.5)\nplt.show()\n\n# Vérification rapide de l'étendue géographique\nif not df_geo.empty:\n    latitude_range = df_geo['latitude'].max() - df_geo['latitude'].min()\n    longitude_range = df_geo['longitude'].max() - df_geo['longitude'].min()\n    print(f\"\\nÉtendue géographique : Latitude ({latitude_range:.2f}), Longitude ({longitude_range:.2f})\")\nelse:\n    print(\"\\nAvertissement: Aucune donnée de coordonnées valide trouvée.\")\n\nprint(\"IMPLICATION : L'analyse des clusters peut suggérer des biais géographiques dans les données d'entraînement. Utiliser la géolocalisation comme feature ou s'assurer que le modèle généralise au-delà des zones denses est crucial.\")\n\n\n# --- B. Distribution de la Qualité (rating) ---\n\nprint(\"\\n\" + \"=\"*50)\nprint(\"--- Analyse de la Qualité (rating) et de la Collection ---\")\n\n# Histogramme de la distribution des notes\nplt.figure(figsize=(8, 5))\n# Utiliser des bins centrés sur les valeurs pour la clarté\nbins = np.arange(-0.5, 5.5, 1)\ntrain_df['rating'].plot(kind='hist', bins=bins, edgecolor='black', rwidth=0.8)\nplt.title('Distribution des Ratings de Qualité (0.0 à 5.0)')\nplt.xlabel('Rating (0=Non noté, 1=Basse, 5=Haute)')\nplt.ylabel('Nombre d\\'enregistrements')\nplt.xticks(np.arange(0, 6))\nplt.grid(axis='y', linestyle='--', alpha=0.7)\nplt.show()\n\n# Qualité moyenne par collection\nquality_by_collection = train_df.groupby('collection')['rating'].agg(['mean', 'median', 'count']).sort_values(by='mean', ascending=False)\nprint(\"\\nStatistiques de Rating par Collection :\")\nprint(quality_by_collection)\n\n# Comparaison des notes pour les espèces rares vs. communes (reprise des 10 plus/moins fréquentes de la Cellule 2)\n# NOTE : Ces indices dépendent de l'exécution de la Cellule 2.\nlabel_counts = train_df['primary_label'].value_counts()\ntop_10_labels = label_counts.head(10).index\nbottom_10_labels = label_counts.tail(10).index\n\nrating_common = train_df[train_df['primary_label'].isin(top_10_labels)]['rating'].mean()\nrating_rare = train_df[train_df['primary_label'].isin(bottom_10_labels)]['rating'].mean()\n\nprint(f\"\\nRating moyen des 10 espèces les plus communes : {rating_common:.2f}\")\nprint(f\"Rating moyen des 10 espèces les plus rares : {rating_rare:.2f}\")\n\nprint(\"IMPLICATION : Les ratings faibles (surtout pour les espèces rares) indiquent que le **filtrage des données** (p. ex., ne retenir que rating >= 3) et le **nettoyage du bruit** seront des étapes cruciales dans le prétraitement.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import librosa\nimport librosa.display\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pandas as pd # Assurez-vous que train_df est bien chargé\n\n# Définition du chemin racine et de la fréquence d'échantillonnage standardisée\nROOT = \"/kaggle/input/birdclef-2025\"\nSR = 32000 # Fréquence d'échantillonnage standardisée\n\n# --- Fonction pour visualiser un Mel-Spectrogramme ---\ndef plot_melspectrogram(filename, label, sr=SR):\n    \"\"\"Charge un fichier audio, calcule et affiche son Mel-Spectrogramme.\"\"\"\n    audio_path = os.path.join(ROOT, 'train_audio', filename)\n    \n    try:\n        y, _ = librosa.load(audio_path, sr=sr, mono=True)\n        \n        # Calcul du Spectrogramme Mel (paramètres types pour BirdCLEF)\n        # n_mels=128 est un bon standard.\n        M = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=1024, hop_length=320, n_mels=128)\n        M_db = librosa.power_to_db(M, ref=np.max)\n        \n        plt.figure(figsize=(12, 4))\n        librosa.display.specshow(M_db, sr=sr, x_axis='time', y_axis='mel')\n        plt.colorbar(format='%+2.0f dB')\n        plt.title(f'Mel-Spectrogramme : {label}')\n        plt.tight_layout()\n        plt.show()\n    except Exception as e:\n        print(f\"Erreur lors du traitement de {filename}: {e}\")\n        \n# --- Sélection d'échantillons pour la comparaison (corrigé) ---\n\n# 1. Exemple d'un Oiseau (Aves) - Sélection basée sur le code eBird (chaîne de caractères)\n# Utiliser un label commun\nbird_sample = train_df[train_df['primary_label'] == 'grekis'].iloc[0]\n\n# 2. Exemple d'un Non-Oiseau (Amphibien/Insecte) - Sélection basée sur l'ID numérique\n# Cherchons le premier label qui est strictement numérique.\nnon_bird_df = train_df[train_df['primary_label'].str.isnumeric()]\nif not non_bird_df.empty:\n    non_bird_sample = non_bird_df.iloc[0]\nelse:\n    # Fallback si aucun label numérique n'est trouvé\n    non_bird_sample = train_df.iloc[1] \n\n\nprint(f\"\\nExemple 1 : Oiseau Commun ({bird_sample['common_name']})\")\nplot_melspectrogram(bird_sample['filename'], bird_sample['common_name'])\n\n\nprint(f\"\\nExemple 2 : Non-Oiseau (probable Amphibien/Insecte) ({non_bird_sample['common_name']})\")\nplot_melspectrogram(non_bird_sample['filename'], non_bird_sample['common_name'])\n\n\n# 3. Exemple d'un enregistrement à faible rating ou bruité\nlow_rating_df = train_df[train_df['rating'] == 1.0]\nif not low_rating_df.empty:\n    low_rating_sample = low_rating_df.iloc[0]\n    print(f\"\\nExemple 3 : Échantillon de Faible Qualité (Rating 1.0) ({low_rating_sample['common_name']})\")\n    plot_melspectrogram(low_rating_sample['filename'], low_rating_sample['common_name'])\nelse:\n    print(\"\\nAucun échantillon avec un rating de 1.0 trouvé.\")\n\n\nprint(\"\\n--- Synthèse de l'Analyse Spectrale ---\")\nprint(\"IMPLICATION : L'analyse visuelle des spectrogrammes confirme l'occupation de **bandes de fréquences** distinctes par les différents taxons (Oiseaux vs. Amphibiens/Insectes) et la présence de **bruit de fond** dans les échantillons de faible qualité. Cela justifie l'utilisation de **SpecAugment** et l'optimisation des paramètres du Mel-Spectrogramme.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}