oo
Mon classement Partager

Spécification de la méthode Icioola (v1.0)

Version 1.0 · publiée le 2026-08-16 · licence CC BY 4.0

Ce document décrit comment Icioola transforme des données publiques en notes, en classements et en lettres A–E. Il est écrit pour être réimplémenté : chaque formule est accompagnée d'un exemple numérique, et chaque exemple figure dans les vecteurs dorés (web/src/engine/golden/vecteurs.json), rejoués à chaque build côté TypeScript et côté Python.

Si vous réimplémentez cette spécification et que vous ne retrouvez pas ces nombres, l'un de nous deux a tort — et c'est vérifiable.

Ce que cette spec n'est pas. Ce n'est pas une justification. Les choix décrits ici sont défendables, pas neutres ; là où ils sont discutables, la section « Questions ouvertes » le dit plutôt que de le maquiller.

0. Vue d'ensemble

Icioola produit deux grandeurs différentes, et les confondre est l'erreur la plus courante :

Le matchLa note de qualité de vie
Répond à« où moi devrais-je vivre ? »« quelle est la qualité de cette commune ? »
Dépend devos priorités, votre budget, votre trajetrien — elle est la même pour tout le monde
Calculéedans le navigateur, à chaque changementune fois, au build
Publiée dansvotre classement personnelles palmarès, les fiches, l'API

Les deux partagent la même agrégation non compensatoire (§3) et la même normalisation (§2).


1. Les entrées

1.1 Indicateurs

Un indicateur est une valeur publique rattachée à une commune, avec son millésime. Aucune valeur n'est estimée, interpolée ou imputée à la source : ce qui n'est pas publié reste absent.

Chaque indicateur porte un sens favorable. Pour la plupart, plus grand est meilleur ; pour certains, l'inverse — prix, loyers, délinquance enregistrée, bruit, NO₂, taxe foncière, distance au grand espace naturel. Ce sens est déclaré, jamais deviné.

1.2 Strates de taille

Une commune n'est comparable qu'à des communes de taille voisine : un village n'a pas les équipements d'une métropole, et la délinquance _enregistrée_ par habitant dépend fortement de l'activité d'un centre-ville.

StratePopulation
villagemoins de 10 000 habitants
moyenne10 000 à 49 999
grande50 000 et plus

Population inconnue ⇒ village (la strate la plus peuplée en nombre de communes, donc celle qui fait le moins d'hypothèse).

1.3 Ce qui n'est jamais une entrée

Ces données sont affichées sur les fiches, comme faits bruts, et n'entrent dans aucun score, aucun classement, aucun filtre :

La ligne de partage. Est notable un risque attaché au territoire, publié par l'État, avec information obligatoire de l'acquéreur ou du locataire : inondation, Seveso, nucléaire, argiles, rupture de barrage, arrêtés de catastrophe naturelle. Est interdite toute caractérisation des habitants : la scorer reviendrait à noter une commune sur qui y vit.

Un test exécuté à chaque build lit le code source du moteur et échoue si l'une de ces clés y apparaît. La liste est aussi publiée dans les vecteurs dorés (exclusions.jamaisScore).


2. Normalisation : de la valeur au percentile

Une valeur brute n'est pas comparable d'un indicateur à l'autre (2 100 €/m² contre 11,3/20). Chaque indicateur est donc converti en percentile 0–100 où 100 est toujours favorable.

  1. Winsorisation aux percentiles 2 et 98 : les valeurs extrêmes sont ramenées à ces bornes. Sans elle, une seule commune aberrante écrase toute l'échelle.
  2. Rang parmi les valeurs présentes du groupe : p = round(rang / (n − 1) × 100), où rang est le nombre de valeurs strictement inférieures.
  3. Inversion si l'indicateur est de sens inverse : p ← 100 − p.
  4. Groupe de moins de deux valeurs ⇒ p = 50 (aucune comparaison possible).
  5. Valeur absente ⇒ percentile absent, jamais 0.

Le groupe est la strate de taille (§1.2) pour la note globale, la région pour les éditions locales. Un percentile n'a de sens qu'accompagné de son assiette — c'est pourquoi elle est affichée à l'écran.

Taux à faibles effectifs. Les taux rapportés à la population (délinquance) sont d'abord lissés par Bayes empirique (modèle Gamma-Poisson) : un taux mesuré sur peu d'exposition est rétréci vers la moyenne de sa strate. Sans ce lissage, les micro-communes où presque rien n'est enregistré occupent mécaniquement les premières places de « la plus sûre ».

3. Agrégation : la moyenne géométrique pondérée

C'est le cœur de la méthode.

match = exp( Σ wᵢ · ln(sᵢ) / Σ wᵢ )

sᵢ est le score 0–100 du critère i et wᵢ son poids.

Deux règles de bord, non négociables :

Pourquoi géométrique

Parce qu'un point faible ne doit pas être rachetable. Sur le vecteur doré « NON-COMPENSATOIRE » — poids 0,6 / 0,3 / 0,1, scores 0 / 100 / 100 :

MéthodeRésultat
Moyenne arithmétique pondérée40,0 — « moyen »
Moyenne géométrique pondérée6,31 — « rédhibitoire »

Une commune catastrophique sur ce qui compte le plus au demandeur ne doit pas ressortir « moyenne ». C'est ce qui sépare Icioola d'un score additif, où tout se rachète.

Couverture

En même temps que le match, on calcule :

couverture = Σ wᵢ des critères AVEC donnée / Σ wᵢ demandés

Une couverture de 0,4 signifie que 60 % du poids demandé repose sur des médianes imputées. Elle est affichée, et le fait que la priorité n°1 soit manquante est signalé séparément — c'est le cas où le résultat est le plus trompeur.


4. Les poids : d'un ordre à des nombres

L'utilisateur classe ses critères ; il ne saisit pas de pourcentages. Les poids en sont dérivés par la méthode ROC (Rank-Order Centroid, Barron & Barrett 1996), le meilleur estimateur des poids quand on ne connaît que l'ordre :

wᵢ = (1/n) · Σ_{j=i}^{n} 1/j        (i compté à partir de 1, n = nombre de critères classés)
npoids
11
30,6111 · 0,2778 · 0,1111
50,4567 · 0,2567 · 0,1567 · 0,0900 · 0,0400

La somme vaut 1 par construction.

Pourquoi pas des pourcentages éditables, alors que c'est la demande spontanée : parce qu'ils doivent sommer à 100 (bouger l'un déplace tous les autres), parce que 13 % contre 15 % ne veut rien dire perceptivement, et surtout parce qu'ils entrent en conflit avec le classement — si l'on édite un pourcentage puis qu'on reclasse un critère, l'un des deux gestes doit être ignoré. Le pourcentage reste affiché, toujours dérivé, jamais saisi.

Un multiplicateur à trois crans (×0,5, ×1, ×2) module un critère sans toucher à l'ordre. Les poids sont ensuite renormalisés à somme 1 : sans cette renormalisation, un même « ×2 » n'aurait pas le même effet selon le nombre de critères classés.


5. Budget et trajet : des filtres, pas des critères

Le prix et le temps de trajet n'entrent pas dans la moyenne comme les autres critères.

Budget — score personnel, plein tant que le prix reste bien sous le budget, nul à 35 % au dessus :

score = clamp01( (budget − prix) / (0,35 × budget) ) × 100

Trajet — plein jusqu'à 60 % du temps maximum accepté, puis décroissance linéaire :

idéal  = 0,6 × max
score  = 100                              si minutes ≤ idéal
       = 0                                si minutes ≥ max
       = (max − minutes) / (max − idéal) × 100   entre les deux

Les 60 % évitent la falaise « 30 minutes parfait, 31 minutes nul » sans demander un second réglage. Le trajet retenu est le temps porte-à-porte brut, jamais dilué par le télétravail : un seuil « ≤ 75 min » qui laisse passer des trajets réels de trois heures deux jours par semaine est un seuil qui ment.

Le trajet entre dans la moyenne uniquement lorsqu'un lieu de travail est renseigné, avec un poids proportionnel aux jours de présence. Le budget reste un filtre pur en toutes circonstances.

Le temps ne se monétise jamais. Un trajet est exprimé en heures de vie, jamais converti en euros. Une heure de trajet ne vaut pas le même prix pour tout le monde, et prétendre le contraire transformerait un arbitrage personnel en calcul objectif qu'il n'est pas.

6. La note globale de qualité de vie

Fixe, identique pour tout le monde, publiée sur chaque fiche et dans les palmarès.

Piliers (obligatoires) : sécurité, accès aux généralistes, commerces du quotidien, culture. Bonus (compté s'il existe) : note au brevet.

note = min( 100, exp( Σ ln(max(1, pᵢ)) / n ) )     sur les percentiles pᵢ disponibles

où les percentiles sont calculés par strate, sur une assiette nationale unique.

6.1 Le prix n'y entre pas

Un logement cher n'est pas un défaut de qualité de vie : c'est un coût, et ce coût dépend du budget de chacun. Mélanger les deux produit des classements absurdes dans les deux sens. Le prix est affiché à côté de la note, jamais fondu dedans.

6.2 Éligibilité explicite

Une commune est classée si — et seulement si :

  1. les quatre piliers sont mesurés ; et
  2. ses taux sont statistiquement exploitables (au moins 200 habitants).

Sinon elle garde sa fiche et n'apparaît dans aucun classement. Elle n'est pas dernière : elle est absente. Sans cette règle, le « n°1 de France » serait un hameau où aucun fait n'a jamais été enregistré.

6.3 Le rang national se calcule sur le rang relatif

Les strates n'ont pas la même taille : 33 827 villages, 914 communes moyennes, 134 grandes. Le maximum d'un échantillon croît avec sa taille — le meilleur de 33 827 villages atteint mécaniquement un percentile plus extrême que le meilleur de 134 grandes villes. Classer sur la note brute donnait un top 8 composé à 100 % de villages.

Le rang national est donc calculé sur le rang relatif dans la strate :

relatif = 100 × (n − rang) / (n − 1)

« meilleur 1 % de sa catégorie » veut dire la même chose pour un village et pour une métropole.

6.4 Ex æquo

Deux communes au même score partagent le même rang (« 1, 2, 2, 4 »). Les départager par l'ordre alphabétique ou par celui du fichier publierait un ordre indéfendable.

Dans le classement personnalisé, où les scores tiennent sur un octet et où les égalités sont massives, les ex æquo sont départagés d'abord par la couverture (à score égal, la commune mesurée sur le plus de critères passe devant — c'est une mesure de confiance, pas de qualité), puis par un mélange de bits du code INSEE. Ce dernier n'affirme rien : il évite seulement que l'ordre du fichier fasse sortir cinq communes consécutives du même département, ce qui, lui, affirmait quelque chose de faux.


7. Les lettres A–E

A : relatif ≥ 80   ·   B : ≥ 60   ·   C : ≥ 40   ·   D : ≥ 20   ·   E : < 20

⚠️ L'entrée est une position relative, jamais la note brute. La note est une moyenne géométrique : sa distribution se masse vers le bas, et appliquer ces bornes à la note brute ferait tomber la commune médiane de France en D. Une lettre doit dire « où je me situe », pas « quel nombre j'ai obtenu ».

Les lettres comparent une commune à celles de sa strate. Une commune sans pair de taille comparable dans son assiette régionale ne reçoit pas 50 par défaut : elle bascule sur l'assiette nationale, et l'assiette utilisée est affichée. Renvoyer 50 faisait apparaître neuf critères à 50 et cinq thèmes en C sur une ville-centre — une fausse moyenne, indiscernable d'une vraie.


8. Les refus (« rédhibitoires »)

Six refus peuvent exclure une commune : zone inondable (TRI), site Seveso à moins de 2 km, radon classe 3, argiles en aléa fort, recul du trait de côte, couloir aérien (PEB).

Règle centrale : on n'exclut jamais sur une donnée absente. Un refus ne s'applique qu'aux communes pour lesquelles l'information existe. Chaque refus affiche le nombre de communes qu'il écarterait avant le clic, et disparaît quand ce nombre est nul : un filtre inerte qui reste à l'écran laisse croire qu'il agit.


9. Questions ouvertes

Un standard crédible documente ses limites.


10. Implémentation de référence et versionnage

L'implémentation de référence est le code lui-même :

SectionFichier
§2 normalisationpipeline/src/boussole_pipeline/normalize.py
§3 agrégationweb/src/engine/score.tsmatchScore, lnScore
§4 poids ROCweb/src/engine/weighting.tsrocWeights, rocWeightsModules
§5 filtresweb/src/engine/score.tsscoreBudget, scoreCommute
§6 note globalepipeline/src/boussole_pipeline/global_score.py
§6.3 rang relatifpipeline/scripts/build_global_scores.py
§7 lettresglobal_score.pygrade_of
§8 refusweb/src/engine/dealbreakers.ts
§1.3 garde-fousweb/src/engine/guardrails.test.ts

Vecteurs dorés : web/src/engine/golden/vecteurs.json, exécutés par web/src/engine/golden/golden.test.ts (TypeScript) et pipeline/tests/test_golden.py (Python).

Tolérance : 1e-6 en valeur absolue sur une échelle 0–100. Une moyenne géométrique passe par une somme de logarithmes : l'ordre d'accumulation change le dernier chiffre en virgule flottante et l'exponentielle l'amplifie. Écart mesuré entre deux implémentations correctes de cette même spec : 5,4·10⁻⁷. Exiger l'égalité au bit ferait échouer des réimplémentations justes.

Versionnage : toute évolution du moteur qui change un vecteur doré est un changement de méthode. Elle exige un incrément de version et une note de changement — pas une correction discrète du fichier de vecteurs.


Licence

Cette spécification est publiée sous Creative Commons Attribution 4.0 (CC BY 4.0) : vous pouvez la reprendre, l'adapter et l'implémenter, y compris commercialement, à condition de citer Icioola.

Les données relèvent d'une licence distincte : ODbL 1.0 (voir /donnees).