{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Classification des Micro-régions de Corse par K-NN\n", "\n", "Ce notebook implémente un système de classification des micro-régions corses basé sur l'algorithme des k plus proches voisins (k-NN). Cliquez sur la carte pour identifier la micro-région correspondante.\n", "\n", "**Fichiers nécessaires :**\n", "- `communes-de-corse-en-corse-et-francais.csv` : Liste des communes avec coordonnées GPS\n", "- `communes-par-territoire-de-projet-de-la-collectivite-territoriale-de-corse0.csv` : Territoires de projet par commune" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Installation des bibliothèques nécessaires\n", "!pip install folium pandas numpy scikit-learn --quiet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import folium\n", "from sklearn.neighbors import KNeighborsClassifier\n", "from IPython.display import display, HTML\n", "import json\n", "import re" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Chargement et préparation des données" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Chargement du fichier avec les coordonnées GPS\n", "df_coords = pd.read_csv('communes-de-corse-en-corse-et-francais.csv', \n", " sep=';', encoding='utf-8')\n", "\n", "print(f\"Fichier coordonnées: {len(df_coords)} communes\")\n", "print(\"\\nPremières lignes:\")\n", "display(df_coords.head())\n", "print(\"\\nColonnes disponibles:\")\n", "print(df_coords.columns.tolist())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Chargement du fichier avec les territoires de projet\n", "df_territoires = pd.read_csv('communes-par-territoire-de-projet-de-la-collectivite-territoriale-de-corse0.csv',\n", " sep=';', encoding='utf-8')\n", "\n", "print(f\"Fichier territoires: {len(df_territoires)} communes\")\n", "print(\"\\nPremières lignes:\")\n", "display(df_territoires.head())\n", "print(\"\\nTerritoires de projet (micro-régions):\")\n", "print(sorted(df_territoires['Territoire de projet'].unique()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Extraction des coordonnées GPS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def extract_coordinates(point_geo_str):\n", " \"\"\"\n", " Extrait latitude et longitude de la colonne Point_Geo\n", " Format attendu: \"41.984099158, 8.798384636\"\n", " \"\"\"\n", " if pd.isna(point_geo_str):\n", " return None, None\n", " \n", " try:\n", " # Supprimer les espaces et split par virgule\n", " coords = str(point_geo_str).strip().split(',')\n", " if len(coords) == 2:\n", " lat = float(coords[0].strip())\n", " lon = float(coords[1].strip())\n", " return lat, lon\n", " except:\n", " pass\n", " \n", " return None, None\n", "\n", "# Extraction des coordonnées\n", "df_coords[['Latitude', 'Longitude']] = df_coords['Point_Geo'].apply(\n", " lambda x: pd.Series(extract_coordinates(x))\n", ")\n", "\n", "# Vérification\n", "print(\"Extraction des coordonnées:\")\n", "print(f\"Communes avec coordonnées: {df_coords['Latitude'].notna().sum()}/{len(df_coords)}\")\n", "print(\"\\nExemple:\")\n", "display(df_coords[['Nom français', 'Latitude', 'Longitude']].head())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Fusion des deux fichiers" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Normalisation des noms de communes pour la jointure\n", "def normalize_commune_name(name):\n", " \"\"\"\n", " Normalise le nom d'une commune pour faciliter la jointure\n", " \"\"\"\n", " if pd.isna(name):\n", " return ''\n", " # Convertir en majuscules et supprimer les espaces multiples\n", " return str(name).upper().strip()\n", "\n", "df_coords['Commune_norm'] = df_coords['Nom français'].apply(normalize_commune_name)\n", "df_territoires['Commune_norm'] = df_territoires['Commune'].apply(normalize_commune_name)\n", "\n", "# Fusion des deux dataframes\n", "df = pd.merge(\n", " df_coords,\n", " df_territoires[['Commune_norm', 'Territoire de projet']],\n", " on='Commune_norm',\n", " how='inner'\n", ")\n", "\n", "# Renommer pour cohérence\n", "df['Commune'] = df['Nom français']\n", "\n", "print(f\"Fusion réussie: {len(df)} communes avec coordonnées ET territoire de projet\")\n", "print(\"\\nAperçu des données fusionnées:\")\n", "display(df[['Commune', 'Latitude', 'Longitude', 'Territoire de projet']].head(10))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Nettoyage: supprimer les lignes sans coordonnées\n", "df_clean = df.dropna(subset=['Latitude', 'Longitude', 'Territoire de projet']).copy()\n", "\n", "print(f\"\\n✅ Données finales: {len(df_clean)} communes prêtes pour la classification\")\n", "print(f\"\\nRépartition par micro-région:\")\n", "print(df_clean['Territoire de projet'].value_counts().sort_index())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Entraînement du modèle k-NN" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Préparation des données pour k-NN\n", "X = df_clean[['Latitude', 'Longitude']].values\n", "y = df_clean['Territoire de projet'].values\n", "\n", "# Création du modèle k-NN avec k=5 (ajustable)\n", "k = 5\n", "knn = KNeighborsClassifier(n_neighbors=k, weights='distance', metric='haversine')\n", "\n", "# Conversion des coordonnées en radians pour la distance haversine\n", "X_rad = np.radians(X)\n", "\n", "# Entraînement du modèle\n", "knn.fit(X_rad, y)\n", "\n", "print(f\"✅ Modèle k-NN entraîné avec k={k} voisins\")\n", "print(f\"📊 Nombre de micro-régions: {len(np.unique(y))}\")\n", "print(f\"\\n🗺️ Micro-régions identifiées:\")\n", "for i, region in enumerate(sorted(np.unique(y)), 1):\n", " count = (y == region).sum()\n", " print(f\" {i:2d}. {region} ({count} communes)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Création de la carte interactive avec Folium" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Couleurs pour chaque micro-région\n", "microregions = sorted(df_clean['Territoire de projet'].unique())\n", "colors = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', \n", " 'lightred', 'beige', 'darkblue', 'darkgreen', 'cadetblue', \n", " 'darkpurple', 'pink', 'lightblue', 'lightgreen', 'gray', 'black', 'lightgray']\n", "\n", "color_map = {region: colors[i % len(colors)] for i, region in enumerate(microregions)}\n", "\n", "print(\"🎨 Carte des couleurs par micro-région:\")\n", "for region, color in sorted(color_map.items()):\n", " print(f\" • {region}: {color}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Coordonnées du centre de la Corse\n", "center_lat = df_clean['Latitude'].mean()\n", "center_lon = df_clean['Longitude'].mean()\n", "\n", "print(f\"Centre de la carte: {center_lat:.4f}°N, {center_lon:.4f}°E\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Carte interactive avec prédiction au clic" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Création de la carte interactive\n", "m_interactive = folium.Map(\n", " location=[center_lat, center_lon],\n", " zoom_start=9,\n", " tiles='OpenStreetMap'\n", ")\n", "\n", "# Ajout des marqueurs pour chaque commune\n", "for idx, row in df_clean.iterrows():\n", " folium.CircleMarker(\n", " location=[row['Latitude'], row['Longitude']],\n", " radius=3,\n", " popup=f\"{row['Commune']}
{row['Territoire de projet']}
({row['Latitude']:.4f}, {row['Longitude']:.4f})\",\n", " tooltip=row['Commune'],\n", " color=color_map[row['Territoire de projet']],\n", " fill=True,\n", " fillColor=color_map[row['Territoire de projet']],\n", " fillOpacity=0.7\n", " ).add_to(m_interactive)\n", "\n", "# Préparer les données des communes pour JavaScript\n", "communes_data = df_clean[['Latitude', 'Longitude', 'Commune', 'Territoire de projet']].to_dict('records')\n", "\n", "# JavaScript pour la prédiction k-NN au clic\n", "click_js = f\"\"\"\n", "\n", "\"\"\"\n", "\n", "m_interactive.get_root().html.add_child(folium.Element(click_js))\n", "\n", "# Ajout de la légende\n", "legend_html = '''\n", "
\n", "

🗺️ Micro-régions de Corse

\n", "'''\n", "\n", "for region, color in sorted(color_map.items()):\n", " legend_html += f'

{region}

'\n", "\n", "legend_html += f'

k = {k} voisins
Distance: Haversine

'\n", "\n", "m_interactive.get_root().html.add_child(folium.Element(legend_html))\n", "\n", "# Ajout d'instructions\n", "instructions_html = '''\n", "
\n", "

🖱️ Mode d'emploi

\n", "

Cliquez n'importe où sur la carte pour prédire la micro-région.

\n", "

• Un marqueur coloré apparaît au point cliqué
\n", "• Les lignes pointillées montrent les k communes les plus proches
\n", "• Le popup affiche la prédiction détaillée

\n", "
\n", "'''\n", "\n", "m_interactive.get_root().html.add_child(folium.Element(instructions_html))\n", "\n", "print(\"\\n✅ Carte interactive créée avec succès!\")\n", "print(f\"\\n🖱️ Cliquez sur n'importe quel point de la carte pour prédire sa micro-région avec k={k} voisins.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Affichage de la carte interactive\n", "m_interactive" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7. Sauvegarde de la carte" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sauvegarder la carte interactive\n", "m_interactive.save('carte_corse_knn_interactive.html')\n", "print(\"✅ Carte sauvegardée dans 'carte_corse_knn_interactive.html'\")\n", "print(\"📁 Vous pouvez ouvrir ce fichier dans un navigateur pour une utilisation autonome.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8. Test de la prédiction (optionnel)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Fonction pour tester la prédiction sur des coordonnées spécifiques\n", "def predict_region(lat, lon, k_value=5):\n", " \"\"\"\n", " Prédit la micro-région pour des coordonnées données\n", " \"\"\"\n", " # Conversion en radians\n", " coords_rad = np.radians([[lat, lon]])\n", " \n", " # Prédiction\n", " prediction = knn.predict(coords_rad)[0]\n", " \n", " # Trouver les k plus proches voisins\n", " distances, indices = knn.kneighbors(coords_rad)\n", " \n", " # Convertir les distances de radians en km\n", " distances_km = distances[0] * 6371 # Rayon de la Terre en km\n", " \n", " print(f\"\\n📍 Coordonnées: {lat:.5f}°N, {lon:.5f}°E\")\n", " print(f\"🎯 Micro-région prédite: {prediction}\")\n", " print(f\"\\n{k_value} plus proches communes:\")\n", " \n", " for i, idx in enumerate(indices[0]):\n", " commune_info = df_clean.iloc[idx]\n", " print(f\" {i+1}. {commune_info['Commune']:30s} ({commune_info['Territoire de projet']:30s}) - {distances_km[i]:6.2f} km\")\n", " \n", " return prediction\n", "\n", "# Exemples de test\n", "print(\"=\" * 100)\n", "print(\"TESTS DE PRÉDICTION k-NN\")\n", "print(\"=\" * 100)\n", "\n", "# Test 1: Centre approximatif de la Corse (vers Corte)\n", "print(\"\\n🔍 Test 1: Centre de la Corse\")\n", "predict_region(42.15, 9.15, k)\n", "\n", "# Test 2: Nord de la Corse (Balagne/Bastia)\n", "print(\"\\n🔍 Test 2: Nord de la Corse\")\n", "predict_region(42.55, 8.85, k)\n", "\n", "# Test 3: Sud de la Corse (vers Porto-Vecchio)\n", "print(\"\\n🔍 Test 3: Sud de la Corse\")\n", "predict_region(41.65, 9.15, k)\n", "\n", "# Test 4: Ouest (vers Ajaccio)\n", "print(\"\\n🔍 Test 4: Ouest de la Corse (Ajaccio)\")\n", "predict_region(41.93, 8.74, k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9. Analyse de performance (optionnel)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Évaluation de la cohérence du modèle (cross-validation)\n", "from sklearn.model_selection import cross_val_score\n", "\n", "# Test avec différentes valeurs de k\n", "k_values = [3, 5, 7, 9, 11, 15]\n", "scores = []\n", "\n", "print(\"📊 Évaluation de la précision pour différentes valeurs de k:\\n\")\n", "print(f\"{'k':<5} {'Précision moyenne':<20} {'Écart-type':<15}\")\n", "print(\"-\" * 50)\n", "\n", "for k_val in k_values:\n", " knn_temp = KNeighborsClassifier(n_neighbors=k_val, weights='distance', metric='haversine')\n", " cv_scores = cross_val_score(knn_temp, X_rad, y, cv=5)\n", " mean_score = cv_scores.mean()\n", " std_score = cv_scores.std()\n", " scores.append(mean_score)\n", " print(f\"{k_val:<5} {mean_score:.4f} ({mean_score*100:5.2f}%) ± {std_score:.4f}\")\n", "\n", "best_k = k_values[scores.index(max(scores))]\n", "best_score = max(scores)\n", "print(\"\\n\" + \"=\" * 50)\n", "print(f\"✨ Meilleure valeur de k: {best_k} (précision: {best_score:.4f} / {best_score*100:.2f}%)\")\n", "print(\"=\" * 50)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 10. Statistiques par micro-région" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Statistiques descriptives par micro-région\n", "print(\"📈 STATISTIQUES PAR MICRO-RÉGION\\n\")\n", "print(f\"{'Micro-région':<35} {'Nb communes':<15} {'% du total'}\")\n", "print(\"=\" * 65)\n", "\n", "total_communes = len(df_clean)\n", "stats = df_clean['Territoire de projet'].value_counts().sort_index()\n", "\n", "for region, count in stats.items():\n", " pct = (count / total_communes) * 100\n", " print(f\"{region:<35} {count:<15} {pct:>5.1f}%\")\n", "\n", "print(\"=\" * 65)\n", "print(f\"{'TOTAL':<35} {total_communes:<15} 100.0%\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "✅ **Notebook k-NN Corse - Résumé**\n", "\n", "Ce notebook implémente un classificateur k-NN pour les micro-régions de Corse avec:\n", "1. ✅ Chargement des données depuis 2 fichiers CSV (coordonnées + territoires)\n", "2. ✅ Extraction automatique des coordonnées GPS depuis la colonne Point_Geo\n", "3. ✅ Fusion intelligente des deux sources de données\n", "4. ✅ Entraînement d'un modèle k-NN avec distance haversine\n", "5. ✅ Carte interactive Folium avec prédiction au clic\n", "6. ✅ Visualisation des k plus proches voisins\n", "7. ✅ Tests de performance et validation\n", "8. ✅ Export HTML pour utilisation autonome\n", "\n", "**🖱️ Utilisation:**\n", "- Cliquez n'importe où sur la carte\n", "- Un marqueur coloré apparaît avec la micro-région prédite\n", "- Des lignes pointillées montrent les k communes les plus proches\n", "- Un popup détaille la prédiction et les voisins\n", "\n", "**📁 Fichier exporté:** `carte_corse_knn_interactive.html`\n", "\n", "La carte HTML peut être ouverte dans n'importe quel navigateur pour une utilisation autonome!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.0" } }, "nbformat": 4, "nbformat_minor": 4 }