{ "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Installation des bibliothèques nécessaires\n", "!pip install folium pandas numpy scikit-learn geopy --quiet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import folium\n", "from folium import plugins\n", "from sklearn.neighbors import KNeighborsClassifier\n", "from geopy.geocoders import Nominatim\n", "from geopy.extra.rate_limiter import RateLimiter\n", "import time\n", "from IPython.display import display, HTML\n", "import json" ] }, { "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 CSV\n", "df = pd.read_csv('communes-par-territoire-de-projet-de-la-collectivite-territoriale-de-corse0.csv', \n", " sep=';', encoding='utf-8')\n", "\n", "print(f\"Nombre de communes: {len(df)}\")\n", "print(\"\\nPremières lignes:\")\n", "display(df.head())\n", "print(\"\\nTerritoires de projet (micro-régions):\")\n", "print(df['Territoire de projet'].unique())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Géocodage des communes\n", "\n", "Si le fichier ne contient pas déjà les coordonnées GPS, nous les récupérons via géocodage." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Fonction pour obtenir les coordonnées GPS d'une commune\n", "def geocode_commune(commune, departement, code_postal):\n", " \"\"\"\n", " Géocode une commune corse pour obtenir ses coordonnées GPS\n", " \"\"\"\n", " geolocator = Nominatim(user_agent=\"corse_knn_classifier\")\n", " \n", " try:\n", " # Essai avec le nom de la commune et Corse\n", " query = f\"{commune}, Corse, France\"\n", " location = geolocator.geocode(query, timeout=10)\n", " \n", " if location:\n", " return location.latitude, location.longitude\n", " \n", " # Essai avec le code postal\n", " query = f\"{commune}, {code_postal}, France\"\n", " location = geolocator.geocode(query, timeout=10)\n", " \n", " if location:\n", " return location.latitude, location.longitude\n", " \n", " except Exception as e:\n", " print(f\"Erreur pour {commune}: {e}\")\n", " \n", " return None, None\n", "\n", "# Vérifier si les colonnes GPS existent déjà\n", "if 'Latitude' not in df.columns or 'Longitude' not in df.columns:\n", " print(\"Géocodage des communes en cours... (cela peut prendre quelques minutes)\")\n", " \n", " # Géocodage avec rate limiting pour respecter les limites de l'API\n", " latitudes = []\n", " longitudes = []\n", " \n", " for idx, row in df.iterrows():\n", " lat, lon = geocode_commune(row['Commune'], row['Département'], row['Code Postal'])\n", " latitudes.append(lat)\n", " longitudes.append(lon)\n", " \n", " # Affichage de la progression\n", " if (idx + 1) % 10 == 0:\n", " print(f\"Progression: {idx + 1}/{len(df)} communes géocodées\")\n", " \n", " # Pause pour respecter les limites de l'API\n", " time.sleep(1.5)\n", " \n", " df['Latitude'] = latitudes\n", " df['Longitude'] = longitudes\n", " \n", " # Sauvegarde du dataframe avec coordonnées\n", " df.to_csv('communes_corse_avec_gps.csv', sep=';', index=False, encoding='utf-8')\n", " print(\"\\nGéocodage terminé et sauvegardé dans 'communes_corse_avec_gps.csv'\")\n", "else:\n", " print(\"Les coordonnées GPS sont déjà présentes dans le fichier.\")\n", "\n", "# Supprimer les lignes sans coordonnées\n", "df_clean = df.dropna(subset=['Latitude', 'Longitude']).copy()\n", "print(f\"\\nCommunes avec coordonnées GPS: {len(df_clean)}/{len(df)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 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\"Micro-régions: {sorted(np.unique(y))}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. 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 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", "# Création de la carte\n", "m = 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']}\",\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)\n", "\n", "# Ajout d'une 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 += '
'\n", "\n", "m.get_root().html.add_child(folium.Element(legend_html))\n", "\n", "# Ajout du plugin de clic\n", "# Note: Folium ne supporte pas nativement l'interactivité côté Python en temps réel\n", "# Nous allons créer une version avec JavaScript pour la prédiction\n", "\n", "print(\"Carte de base créée avec les communes colorées par micro-région\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Carte interactive avec prédiction au clic\n", "\n", "Cette version utilise JavaScript pour permettre de cliquer sur la carte et afficher la micro-région prédite." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Création d'une nouvelle carte avec interaction JavaScript\n", "m_interactive = folium.Map(\n", " location=[center_lat, center_lon],\n", " zoom_start=9,\n", " tiles='OpenStreetMap'\n", ")\n", "\n", "# Ajout des communes\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']}\",\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", "m_interactive.get_root().html.add_child(folium.Element(legend_html))\n", "\n", "# Ajout d'instructions\n", "instructions_html = '''\n", "
\n", "

🖱️ Instructions:

\n", "

Cliquez n'importe où sur la carte pour prédire la micro-région à partir de l'algorithme k-NN.

\n", "

Les lignes pointillées montrent les k plus proches communes utilisées pour la prédiction.

\n", "
\n", "'''\n", "\n", "m_interactive.get_root().html.add_child(folium.Element(instructions_html))\n", "\n", "print(\"Carte interactive créée avec succès!\")\n", "print(f\"\\nCliquez 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": [ "## 6. 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": [ "## 7. 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}, {lon:.5f}\")\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']} ({commune_info['Territoire de projet']}) - {distances_km[i]:.2f} km\")\n", " \n", " return prediction\n", "\n", "# Exemples de test\n", "print(\"=\" * 60)\n", "print(\"TESTS DE PRÉDICTION\")\n", "print(\"=\" * 60)\n", "\n", "# Test 1: Centre approximatif de la Corse\n", "predict_region(42.15, 9.15, k)\n", "\n", "# Test 2: Nord de la Corse (Balagne)\n", "predict_region(42.55, 8.85, k)\n", "\n", "# Test 3: Sud de la Corse\n", "predict_region(41.65, 9.15, k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8. 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]\n", "scores = []\n", "\n", "print(\"Évaluation de la précision pour différentes valeurs de k:\\n\")\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", " scores.append(mean_score)\n", " print(f\"k={k_val:2d}: Précision moyenne = {mean_score:.3f} (+/- {cv_scores.std():.3f})\")\n", "\n", "# Visualisation simple\n", "print(f\"\\n✨ Meilleure valeur de k: {k_values[scores.index(max(scores))]} (précision: {max(scores):.3f})\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "Ce notebook implémente un classificateur k-NN pour les micro-régions de Corse avec:\n", "- ✅ Chargement et géocodage des communes corses\n", "- ✅ Entraînement d'un modèle k-NN avec distance haversine\n", "- ✅ Carte interactive Folium avec prédiction au clic\n", "- ✅ Visualisation des k plus proches voisins\n", "- ✅ Légende et instructions pour l'utilisateur\n", "\n", "**Utilisation:**\n", "1. Cliquez n'importe où sur la carte\n", "2. Un marqueur coloré apparaît avec la micro-région prédite\n", "3. Des lignes pointillées montrent les k communes les plus proches\n", "4. Un popup détaille la prédiction et les voisins\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 }