Décisions méthodologiques

Toutes les règles structurantes du dashboard, classées par catégorie.

Chaque décision donne la règle, la raison (↳), et les alternatives écartées (✗) quand pertinentes.


🏗️ Architecture

Choix de stack, hébergement, contraintes structurantes du site.

Quarto Dashboard en R, déploiement statique

Cartes via leaflet, viz via ggplot2 + ggiraph ou SVG natif, Sankey via networkD3. Pipeline data en R (duckdb + arrow + sf).
Document lisible cellule par cellule, pas un HTML/JS opaque. Quarto = literate computing. Pas de serveur Shiny → hébergeable sur GitHub Pages statique.
HTML/JS pur (pas un notebook) · Shiny serveur (pas statique) · Shinylive WebR (gardé en option).

Multi-page sous 100 MB par fichier

Une page Quarto par thématique. Plafond cible : 45 MB HTML par page.
GitHub Pages plafonne à 100 MB par fichier. Un dashboard mono-page atteindrait 200+ MB.
Implémentation : R/setup_dashboard.R est sourcé par chaque page, charge les données conditionnellement (if (carb_ready) …). htmlwidgets n'embarque que les objets passés aux widgets de la page.

Périmètre géographique : Métropole + 5 DROM en cartouches

DROM = 971 Guadeloupe, 972 Martinique, 973 Guyane, 974 La Réunion, 976 Mayotte. Convention IGN.
Inclusivité sans dégrader la lisibilité de la carte principale.
Carte unique étirée sur Atlantique/Indien (illisible).

🗺️ Cartographie

Palettes, encodages, gestion des cas particuliers (PLM, DROM, légendes dynamiques).

Législatives : famille gagnante + opacité par marge

Variable cartographiée : famille gagnante au tour décisif (NFP/ENS/RN/LR/Divers). Opacité proportionnelle à la marge (faible marge → couleur pâle).
Le multipartisme se rend bien par couleur catégorielle. L'opacité ajoute la force de la victoire sans surcharger.

Municipales : helper dédié choropleth_municipales()

Différences vs Législatives :
  • Palette "Divers" boostée : olive #A18047 au lieu du gris national. Justification : 88% T1 + 81% T2 gagnés par "Divers" (LDIV/LDVG/LDVD/LREG) — invisibles sinon.
  • Stroke différencié : T1 → orange #F0A500 (0,6 px), T2 → blanc (0,3 px). L'œil distingue immédiatement le tour décisif.
  • Opacité fixe 0,85 pour toutes les communes avec famille, pas de fade par marge.
La marge moyenne T1 (47%) écraserait T2 (12%) ; un fade serait illisible.

Paris / Lyon / Marseille : règle par viz

Données MI agrègent au niveau commune (75056, 69123, 13055), GeoJSON expose par arrondissement (75101-75120, etc.).
  • Élections : résultat ville mère répercuté sur tous les arrondissements.
  • Mobilité Sankey : consolidation 751xx75056 avant agrégation (sinon 20 "Paris arrondissements" pollueraient le Sankey).
  • Profil radar : filtre les communes avec < 5/8 axes renseignés (exclut les codes PLM consolidés peu renseignés).
Sommer pop + revenus + APL des arrondissements vers la commune mère — demanderait un script PLM dédié (à faire ultérieurement).

Légendes dynamiques (page Démographie)

Quand l'utilisateur toggle entre "Population", "Densité INSEE", "Chômage", la légende swap entre format continu (5 breaks log-quantile) et format catégoriel (6 niveaux ordinaux).
Un même barème continu n'a aucun sens pour une variable catégorielle.

DROM cartouches : re-fit bounds à l'expansion

R/map_helpers.R::.add_resize_handler() capture les bounds initiaux puis ré-applique fitBounds() après chaque invalidateSize(). Observe aussi le parent .card (Quarto fullscreen).
Sans re-fit, leaflet garde l'échelle calculée pour 150×150 px. Un DROM agrandi en fullscreen restait minuscule au centre.

📊 Données par source

Une décision par source : règles métier, filtres, snapshots. 13 sources au total.

📥 Élections Législatives 2024 — T1 fallback

Min. Intérieur · 35 000 communes
Variable cartographiée = famille gagnante au tour décisif. Pour les ~6 000 communes décidées au 1er tour (majorité absolue + seuil d'inscrits), on bascule au résultat T1 ; colonne tour trace la nature du résultat affiché.
Mapping nuance MI → 5 familles :
  • NFP : UG, FI, SOC, COM, VEC, ECO
  • ENS : ENS, HOR
  • LR : LR, UDI, UDR
  • RN : RN, UXD, EXD
  • Divers : DVD, DVG, DVC, DSV, REG, EXG, DIV

📥 Municipales 2026 — T1 + T2 combinés

Min. Intérieur · ~3 300 communes
T2 prioritaire (1 526 communes) + T1 fallback (1 779 communes décidées au 1er tour) = ~3 300 communes ≥ 1 000 hab. Les ~32 000 communes < 1 000 hab. utilisent le scrutin majoritaire individuel → absentes du fichier source, affichées en beige neutre.
Mapping nuance liste (préfixe L) → famille : LUG/LFI/LSOC/LCOM/LVEC/LECO = NFP ; LUC = ENS ; LLR/LUDI/LUDR/LUD = LR ; LRN/LUXD/LEXD = RN ; LDIV/LDVG/LDVD/LDVC/LREG = Divers.

📥 DVF Immobilier — filtres standards

DGFiP · 655 000 ventes 2021-2024
Filtres :
  • nature_mutation = "Vente"
  • type_local ∈ {"Appartement", "Maison"}
  • surface_reelle_bati > 9 m²
  • Bornes : 200 €/m² < x < 30 000 €/m²
  • Mutations mono-local uniquement
  • Médiane comme stat d'agrégation (robuste outliers)
  • N < 5 ventes/an → estimation_insuffisante, grisée
Convention DVF standard. Mono-local évite la répartition prix sur lots. Médiane robuste.
Moyenne (sensible outliers) · Répartition proportionnelle (bruit non traçable).

📥 Atmo Air — agrégation 30 jours glissants

Atmo France · WFS · ~700 stations
Snapshot agrégé sur 30 jours glissants au lieu du jour J. EPCI expansion pour Bretagne (Air Breizh), Occitanie (Atmo Occitanie), Pays de la Loire (Air PdL) qui publient par EPCI.
L'indice journalier varie fortement (pollens, canicule). 30 j lisse les NA et reflète l'exposition chronique.
DROM couverts partiellement : Guadeloupe + Martinique seulement.

📥 SSMSI Délinquance — secret statistique

SSMSI 2025 · ~18 000 communes après secret
8 indicateurs ‰ habitants : cambriolages, vols personnes, vols véhicules, violences sexuelles, dégradations, trafic stup, usage stup, total.
Secret stat : taux non publié si < 5 faits cumulés sur 3 ans → ~17 000 communes (les plus petites) sont en gris "non publié". Échelle log_quantile sur la carte (distribution très skewée).

📥 BAAC Accidents — taux ‰ habitants

Min. Intérieur · 11 099 communes · 2024
Compte d'accidents corporels par commune, normalisé en ‰ habitants (joint avec pop_latest).

📥 DGFiP Finances communales

DGFiP · ~35 000 communes · 2024
Recettes, charges, dette, CAF en €/habitant. Taux de 3 taxes locales : habitation, foncier bâti, foncier non bâti.

📥 APL DREES — seuil désert médical

DREES · ~35 000 communes · 2023
5 professions : médecins (+ ≤65 ans), infirmières, sages-femmes, dentistes, kinés.
APL médecins généralistes : palette divergente avec seuil 2,5 consultations/an/hab = désert médical (DREES). Couleur change de rouge à vert au passage du seuil.

📥 Espérance de vie INSEE — join par nom

INSEE · ~28 000 communes · 2024
INSEE-EV ne publie pas le code commune. Join par nom commune + département normalisé → 81,6% taux de match. Ajouté à la page Santé.

📥 FiLoSoFi Revenus INSEE

INSEE · ~30 000 communes · 2021
Revenu médian disponible, déciles D1/D9, indice de Gini, % ménages imposés, part prestations vs activité.
CSV séparateur point-virgule (français), filtrage des très petites communes par INSEE (secret stat).

📥 INSEE Populations + Strates de taille

INSEE · ~35 000 communes · 2017-2021
5 strates de taille INSEE :
  • Très petite (< 500 hab.)
  • Rurale (500 - 2 000)
  • Bourg / périurbain (2 000 - 10 000)
  • Ville moyenne (10 000 - 50 000)
  • Grande ville (≥ 50 000)
La densité urbaine est le principal confondant. Stratifier permet d'isoler les corrélations intra-strate.
Fallback proxy pour communes sans pop publiée : quartiles d'inscrits sur listes électorales.

📥 INSEE Grille de densité (typologie 6 niveaux)

INSEE · ~35 000 communes · 2024
Variable catégorielle ordinale rural → urbain :
  1. Rural non périurbain
  2. Rural périurbain
  3. Petites villes
  4. Centres urbains intermédiaires
  5. Ceintures urbaines
  6. Grands centres urbains
Mappée en palette catégorielle (page Démographie). Sert aussi de variable continue densite_rang (1-6) dans Explorer + Bivariée.

📥 DARES Chômage DEFM A+B+C

DARES · ~7 400 communes · 1 trimestre
Demandeurs d'Emploi en Fin de Mois cat. A+B+C. Normalisé en taux ‰ habitants via defm_abc / pop_latest * 1000.
Couverture limitée : ~7 400 communes seulement (DARES ne publie pas pour toutes les petites).

📥 RARE/CITEPA Empreinte carbone — approche conso

RARE · ~34 800 communes · base 2018
Empreinte carbone par habitant en approche consommation (EXIOBASE × CITEPA). Médiane FR ≈ 7,9 tCO₂eq/hab/an.
Reflète le mode de vie (logement, transport, alimentation, biens), indépendamment de la localisation des usines. Une grande ville pollue là où sont ses usines (souvent banlieue) ; l'empreinte conso lui attribue son vrai poids.
Empreinte territoriale (émissions sur le sol) — biaise les communes industrielles.

📥 INSEE MOBPRO — codes TRANS RP 2019

INSEE · ~34 800 communes · RP 2019
⚠️ INSEE a révisé le codage TRANS au RP 2017+. Codes actuels :
  • 1 Pas de transport
  • 2 Marche à pied
  • 3 Vélo (y compris VAE)
  • 4 2-roues motorisés
  • 5 Voiture / camion / fourgonnette
  • 6 Transports en commun
Un bug initial a confondu TRANS=5 avec TC → médiane voiture à 0% sur la carte. Fix appliqué et codes documentés ici pour éviter la récidive.
Indicateurs dérivés : % emploi local, % voiture/TC/vélo/marche, top destination + flux.

📥 Agence Bio Agriculture

Agence Bio · 23 100 communes · 2024
Surface bio totale = AB certifiée + en conversion (C1 + C2 + C3). Nombre d'opérateurs = exploitations + transformateurs + distributeurs.
Fichier opérateurs en long (1 ligne par production × activité) → agrégé par sum(nboperateur) par commune × année.
Couverture : 23 100 communes (les ~12 000 sans aucune exploitation bio n'apparaissent pas dans le fichier source). Échelle log-quantile sur la carte (distribution skewée, médiane 47 ha, max 16 817).

🔬 Analyse

Stratification, métriques de corrélation, filtres de tautologies.

Spearman par défaut, Pearson alternatif

Toutes les corrélations calculées en Spearman (rang) par défaut. Pearson disponible en toggle pour comparaison.
Distributions prix m², chômage, délinquance, bio ont des queues lourdes. Spearman est monotone et robuste aux outliers.

Top 25 corrélations — filtrage des tautologies

Le top affiché à droite de la heatmap Explorer exclut les paires intra-thématique. 12 groupes thématiques servent de filtre.
Sans filtre, le top serait saturé par des tautologies :
  • del_total × del_vols_pers (ρ = 0,98) — total inclut les vols
  • fin_charges × fin_recettes (ρ = 0,99) — budget équilibré
  • ctx_log_inscrits × ctx_log_pop (ρ = 0,99) — inscrits ≈ pop adulte
  • revenu_median × revenu_d9 (ρ = 0,95) — bougent ensemble
Les corrélations vraiment intéressantes traversent les sujets : revenu × vote, densité × prix, CO₂ × mobilité.

Bivariate choropleth — palette Stevens 3×3

Encodage 2 variables en terciles × terciles → 9 classes (palette Stevens 2015). Les coins donnent les associations extrêmes ; le centre rassemble les "moyennes".
16 paires pré-calculées côté R (scripts/05_merge_and_explore.R). 1 176 combinaisons disponibles côté JS via selectors X/Y.

Stratification : 5 strates × variable continue densite_rang

Les corrélations sont pré-calculées par paire × méthode × strate (Spearman/Pearson × 5 strates pop + Toutes) → 15 912 lignes dans correlations.parquet.
Variable alternative densite_rang (1-6, INSEE typologie 6 niveaux) disponible dans heatmap et Bivariée mais pas (encore) en strate Explorer.

📈 Visualisations

Choix d’encodage pour les 4 viz dérivées (radar, Sankey, small multiples, scatter Explorer).

Profil radar — 8 dimensions en rang centile

8 axes retenus, critère couverture ≥ 30 000 communes ET diversité thématique :
  1. Population (log)
  2. Revenu médian
  3. APL médecins
  4. Recettes communales €/hab
  5. Délinquance totale ‰
  6. Empreinte CO₂/hab
  7. % voiture mobilité
  8. Densité INSEE (rang 1-6)
Encodage en rang centile national (0-100) plutôt que valeur brute → lecture "rond ou écrasé" indépendante des unités. 50 = médiane FR.
Filtre : communes avec < 5/8 axes renseignés sont exclues du lookup (cas PLM consolidés).

Sankey mobilité — Top 100 flux + PLM consolidé

Préfixage des nœuds : "R · Lyon" pour résidence, "T · Lyon" pour destination. Sinon networkD3 collapse les deux côtés et perd l'orientation.
Pipeline (scripts/16_mobilite_flows.R) :
  1. Consolidation PLM (751xx75056, 6938x69123, 132xx13055)
  2. Top 50 destinations par poids entrant total
  3. Filtre COMMUNE != DCLT (exclut flux intra-commune)
  4. Top 200 flux ≥ 100 actifs, puis top 100 pour le rendu

Small multiples départements — 96 tuiles métropole

Régex strict ^(0[1-9]|[1-8][0-9]|9[0-5]|2[AB])$ pour le filtre — exclut ZX (Saint-Martin), ZZ (Français hors de France) et 97x/98x (DROM, séparés).
Indicateur continu : médiane communale du département. Famille dominante : plurality (pas majorité absolue) des communes du département.
9 indicateurs sélectionnables, 8 palettes différentes selon le sens de l'indicateur (séquentielle, divergente, catégorielle).

Explorer scatter — LOESS approx + ρ live

Échantillon stratifié (max 8 000 communes) pour ne pas saturer le SVG. ρ Spearman recalculé en JS sur l'échantillon courant.
Courbe "LOESS-like" : moyenne mobile sur 30 quantiles de X. Pas un vrai LOESS (trop coûteux côté JS) mais suffisant pour donner la tendance.

🔄 Fraîcheur des données & complétions écartées

Quelles sources se rafraîchissent automatiquement, lesquelles sont figées par une limite amont, et quelles extensions ont été tentées puis écartées parce qu’elles auraient introduit un indicateur trompeur (principe d’objectivité : mieux vaut pas d’indicateur qu’un indicateur faux).

Rafraîchissement automatique mensuel

Le workflow .github/workflows/refresh-data.yml (cron, 1ᵉʳ du mois) re-télécharge les sources à URL stable et recommit les parquets modifiés : Atmo air (30 j glissants — la seule qui change vraiment souvent), SSMSI, DGFiP, APL, BAAC, carbone, FiLoSoFi, populations, bio. Scripts idempotents (cache des bruts).
Un dashboard de données publiques se périme ; l'automatisation évite la dérive sans intervention manuelle. Les versions de packages sont figées (renv.lock) pour qu'un refresh ne change jamais le comportement.

Sources figées par une limite amont (refresh manuel)

  • DEFM chômage : la DARES publie le fichier communal sans URL stable réutilisable (export depuis leur portail). Le millésime affiché dépend du CSV déposé manuellement dans data/raw/dares/.
  • MOBPRO mobilité : dernier fichier détail commune × commune diffusé par l'INSEE = RP 2019 (décalage usuel ~3 ans). Pas de millésime plus récent au format détail à la date du build.
  • Municipales 2026 : scrutin ponctuel, pas de refresh.

✗ Complétions tentées puis écartées

  • Gaz + électricité par commune (bloc G complet) — Agence ORE publie un CSV de 800 MB ; l'API Tabular data.gouv le rejette et le stream complet est disproportionné pour le gain. Seul le carbone (approche conso) est retenu pour le bloc Énergie.
  • % SAU en bio (bloc K) — deux pistes rejetées : (1) la SAU Agreste RA2020 republiée par ZABAL est en format long multi-millésimes à couverture partielle (~10 k lignes, non national) ; (2) normaliser la surface bio par l'aire communale donne des ratios > 100 % car l'Agence Bio attribue la surface au siège de l'opérateur, pas à la localisation des parcelles — dénominateur et numérateur ne portent pas sur le même territoire. On conserve donc la surface bio brute (ha) plutôt qu'un ratio trompeur.
  • Espérance de vie — élargir le match au-delà de 81,6 % — la source INSEE ne publie que ~1 680 communes principales ; améliorer l'appariement de noms ne lèverait pas ce plafond de couverture. Gain marginal, non poursuivi.

Historique des règles

  • 2026-05-25 — Décisions initiales : Quarto + leaflet, périmètre métropole+DROM, élections 2024 T2 + T1 fallback, DVF filtres standard, Atmo WFS + EPCI expansion, stratification 4 strates pop INSEE.
  • 2026-05-26 matin — Multi-page split GitHub Pages, ajout SSMSI + BAAC, DGFiP, APL 5 pros + EV INSEE, FiLoSoFi, grille densité INSEE 6 levels, DEFM, Municipales 2026 T1+T2. Passage 4 → 5 strates pop INSEE.
  • 2026-05-26 après-midi — Blocs G (carbone RARE/CITEPA), I (mobilité MOBPRO 2019), K (Agence Bio). Helper municipales dédié. Fix démographie click+legend, fix DROM resize fitBounds. Viz radar profil, Sankey mobilité, small multiples départements. Codes TRANS RP 2019 documentés post-bug.