{
 "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\"<b>{row['Commune']}</b><br>{row['Territoire de projet']}<br><small>({row['Latitude']:.4f}, {row['Longitude']:.4f})</small>\",\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",
    "<script>\n",
    "// Données des communes\n",
    "var communesData = {json.dumps(communes_data)};\n",
    "\n",
    "// Carte des couleurs\n",
    "var colorMap = {json.dumps(color_map)};\n",
    "\n",
    "// Fonction pour calculer la distance haversine\n",
    "function haversineDistance(lat1, lon1, lat2, lon2) {{\n",
    "    const R = 6371; // Rayon de la Terre en km\n",
    "    const dLat = (lat2 - lat1) * Math.PI / 180;\n",
    "    const dLon = (lon2 - lon1) * Math.PI / 180;\n",
    "    const a = Math.sin(dLat/2) * Math.sin(dLat/2) +\n",
    "              Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *\n",
    "              Math.sin(dLon/2) * Math.sin(dLon/2);\n",
    "    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n",
    "    return R * c;\n",
    "}}\n",
    "\n",
    "// Fonction k-NN\n",
    "function predictRegion(lat, lon, k) {{\n",
    "    // Calculer les distances\n",
    "    var distances = communesData.map(function(commune) {{\n",
    "        return {{\n",
    "            distance: haversineDistance(lat, lon, commune.Latitude, commune.Longitude),\n",
    "            region: commune['Territoire de projet'],\n",
    "            commune: commune.Commune\n",
    "        }};\n",
    "    }});\n",
    "    \n",
    "    // Trier par distance\n",
    "    distances.sort((a, b) => a.distance - b.distance);\n",
    "    \n",
    "    // Prendre les k plus proches\n",
    "    var kNearest = distances.slice(0, k);\n",
    "    \n",
    "    // Vote pondéré par l'inverse de la distance\n",
    "    var votes = {{}};\n",
    "    kNearest.forEach(function(neighbor) {{\n",
    "        var weight = 1 / (neighbor.distance + 0.001); // +0.001 pour éviter division par 0\n",
    "        if (votes[neighbor.region]) {{\n",
    "            votes[neighbor.region] += weight;\n",
    "        }} else {{\n",
    "            votes[neighbor.region] = weight;\n",
    "        }}\n",
    "    }});\n",
    "    \n",
    "    // Trouver la région gagnante\n",
    "    var maxVote = 0;\n",
    "    var predictedRegion = '';\n",
    "    for (var region in votes) {{\n",
    "        if (votes[region] > maxVote) {{\n",
    "            maxVote = votes[region];\n",
    "            predictedRegion = region;\n",
    "        }}\n",
    "    }}\n",
    "    \n",
    "    return {{\n",
    "        region: predictedRegion,\n",
    "        neighbors: kNearest\n",
    "    }};\n",
    "}}\n",
    "\n",
    "// Variable pour stocker le marqueur de prédiction\n",
    "var predictionMarker = null;\n",
    "var neighborLines = [];\n",
    "\n",
    "// Attendre que la carte soit chargée\n",
    "setTimeout(function() {{\n",
    "    var maps = document.querySelectorAll('.folium-map');\n",
    "    if (maps.length > 0) {{\n",
    "        var mapElement = maps[maps.length - 1];\n",
    "        var leafletMap = mapElement._leaflet_map;\n",
    "        \n",
    "        if (leafletMap) {{\n",
    "            leafletMap.on('click', function(e) {{\n",
    "                var lat = e.latlng.lat;\n",
    "                var lon = e.latlng.lng;\n",
    "                \n",
    "                // Prédiction avec k={k}\n",
    "                var result = predictRegion(lat, lon, {k});\n",
    "                \n",
    "                // Supprimer l'ancien marqueur et lignes\n",
    "                if (predictionMarker) {{\n",
    "                    leafletMap.removeLayer(predictionMarker);\n",
    "                }}\n",
    "                neighborLines.forEach(function(line) {{\n",
    "                    leafletMap.removeLayer(line);\n",
    "                }});\n",
    "                neighborLines = [];\n",
    "                \n",
    "                // Créer le popup avec informations détaillées\n",
    "                var popupContent = '<div style=\"min-width: 200px;\">' +\n",
    "                    '<h4 style=\"margin: 5px 0; color: ' + colorMap[result.region] + ';\">🎯 ' + result.region + '</h4>' +\n",
    "                    '<p style=\"margin: 5px 0; font-size: 11px;\"><b>Coordonnées cliquées:</b><br>' + \n",
    "                    'Lat: ' + lat.toFixed(5) + '°<br>Lon: ' + lon.toFixed(5) + '°</p>' +\n",
    "                    '<hr style=\"margin: 5px 0;\">' +\n",
    "                    '<p style=\"margin: 5px 0; font-size: 11px;\"><b>{k} plus proches communes:</b></p>' +\n",
    "                    '<ul style=\"margin: 5px 0; padding-left: 20px; font-size: 10px;\">';\n",
    "                \n",
    "                result.neighbors.forEach(function(neighbor, i) {{\n",
    "                    popupContent += '<li><b>' + neighbor.commune + '</b> (' + neighbor.distance.toFixed(2) + ' km)</li>';\n",
    "                }});\n",
    "                \n",
    "                popupContent += '</ul></div>';\n",
    "                \n",
    "                // Ajouter le nouveau marqueur\n",
    "                predictionMarker = L.marker([lat, lon], {{\n",
    "                    icon: L.divIcon({{\n",
    "                        className: 'prediction-marker',\n",
    "                        html: '<div style=\"background-color: ' + colorMap[result.region] + \n",
    "                              '; width: 20px; height: 20px; border-radius: 50%; ' +\n",
    "                              'border: 3px solid white; box-shadow: 0 0 10px rgba(0,0,0,0.5);\"></div>',\n",
    "                        iconSize: [20, 20]\n",
    "                    }})\n",
    "                }}).addTo(leafletMap);\n",
    "                \n",
    "                predictionMarker.bindPopup(popupContent, {{maxWidth: 300}}).openPopup();\n",
    "                \n",
    "                // Ajouter des lignes vers les k plus proches voisins\n",
    "                result.neighbors.forEach(function(neighbor) {{\n",
    "                    var commune = communesData.find(c => c.Commune === neighbor.commune);\n",
    "                    if (commune) {{\n",
    "                        var line = L.polyline(\n",
    "                            [[lat, lon], [commune.Latitude, commune.Longitude]],\n",
    "                            {{\n",
    "                                color: 'gray',\n",
    "                                weight: 1,\n",
    "                                opacity: 0.5,\n",
    "                                dashArray: '5, 5'\n",
    "                            }}\n",
    "                        ).addTo(leafletMap);\n",
    "                        neighborLines.push(line);\n",
    "                    }}\n",
    "                }});\n",
    "            }});\n",
    "            \n",
    "            console.log('✅ Gestionnaire de clic k-NN activé');\n",
    "            console.log('📊 ' + communesData.length + ' communes chargées');\n",
    "        }}\n",
    "    }}\n",
    "}}, 1000);\n",
    "</script>\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",
    "<div style=\"position: fixed; \n",
    "            top: 10px; right: 10px; width: 250px; max-height: 85vh; overflow-y: auto;\n",
    "            background-color: white; border:2px solid grey; z-index:9999; border-radius: 5px;\n",
    "            font-size:12px; padding: 10px; box-shadow: 0 0 15px rgba(0,0,0,0.2);\">\n",
    "<p style=\"margin-bottom: 8px; font-weight: bold; font-size: 14px;\">🗺️ Micro-régions de Corse</p>\n",
    "'''\n",
    "\n",
    "for region, color in sorted(color_map.items()):\n",
    "    legend_html += f'<p style=\"margin: 3px 0;\"><i class=\"fa fa-circle\" style=\"color:{color}\"></i> {region}</p>'\n",
    "\n",
    "legend_html += f'<hr style=\"margin: 8px 0;\"><p style=\"margin: 3px 0; font-size: 11px; color: #666;\">k = {k} voisins<br>Distance: Haversine</p></div>'\n",
    "\n",
    "m_interactive.get_root().html.add_child(folium.Element(legend_html))\n",
    "\n",
    "# Ajout d'instructions\n",
    "instructions_html = '''\n",
    "<div style=\"position: fixed; \n",
    "            bottom: 10px; left: 10px; width: 320px; \n",
    "            background-color: white; border:2px solid grey; z-index:9999; border-radius: 5px;\n",
    "            font-size:13px; padding: 12px; box-shadow: 0 0 15px rgba(0,0,0,0.2);\">\n",
    "<p style=\"margin: 0 0 8px 0; font-weight: bold;\">🖱️ Mode d'emploi</p>\n",
    "<p style=\"margin: 5px 0; line-height: 1.4;\"><b>Cliquez</b> n'importe où sur la carte pour prédire la micro-région.</p>\n",
    "<p style=\"margin: 5px 0; line-height: 1.4; font-size: 11px;\">• Un marqueur coloré apparaît au point cliqué<br>\n",
    "• Les lignes pointillées montrent les k communes les plus proches<br>\n",
    "• Le popup affiche la prédiction détaillée</p>\n",
    "</div>\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
}
