Fausse carte d'identité > Articles > La moitié de votre liste pondérée n'est pas pondérée

La moitié de votre liste pondérée n'est pas pondérée

Une liste pondérée dans laquelle la moitié des lignes portent le même poids minimal n'est pas une liste pondérée. C'est une liste uniforme dotée d'un préfixe pondéré — et rien dans le fichier, dans le schéma ou dans la suite de tests ne le dit.

Nous en exploitions une. Dans un corpus de prénoms suédois, 997 des 1 865 entrées féminines (53,5 %) et 1 115 des 2 015 entrées masculines (55,3 %) se trouvaient au poids 1. Leurs fréquences réelles n'étaient ni égales ni proches de l'égalité : à l'intérieur de ce bloc aplati, les effectifs réels allaient de 36 à 567 chez les femmes et de 31 à 479 chez les hommes. Un prénom porté par 567 personnes était tiré exactement aussi souvent qu'un prénom porté par 36.

Le fichier était valide. Les poids se sommaient correctement. Les lignes étaient triées par ordre décroissant. Chaque validateur passait avec zéro échec et zéro avertissement. Selon tous les contrôles dont nous disposions, la liste ressemblait à une liste correctement pondérée.

Le mécanisme : un octet

Les poids avaient été quantifiés dans la plage 1–255 afin que chacun tienne dans un octet. La transformation est la plus évidente qui soit :

weight = round(count * CEILING / max_count)
if (weight < 1) weight = 1          // never drop a row entirely

Cela produit un compartiment de largeur max_count / CEILING. Toute entrée plus petite qu'un demi-compartiment s'arrondit à zéro et est ensuite ramenée à 1 ; toute entrée plus petite qu'un compartiment et demi s'arrondit elle aussi à 1. Ainsi, tout ce qui se situe sous 1.5 * max_count / CEILING atterrit sur le plancher, indiscernable.

Dans nos données, le plus grand effectif féminin était 96 428. Un compartiment vaut donc 96 428 / 255 = 378,1, et le seuil d'effondrement est 1,5 × 378,1 = 567,2. Le plus grand effectif réel trouvé au poids 1 était 567. Le mécanisme n'est pas conjecturé ; il reproduit la frontière au chiffre près.

Inverser la correspondance fait clairement apparaître la forme :

Poids stockéLignesEffectifs réels, minEffectifs réels, maxMoyennePrédiction linéaire (w * max / 255)
199736567318378
2269568938727756
31139461 3201 1151 134
4871 3261 6981 4921 513
5401 7052 0701 8701 891
10153 6263 9653 7933 781
50318 76619 05518 92118 907
100137 92237 92237 92237 815
255196 42896 42896 42896 428

Le haut de la liste est sain. Le poids 255 contient exactement un prénom ; le poids 100 en contient un ; le poids 50 en contient trois. La tête est représentée avec une précision pratiquement intégrale. La totalité des dégâts se situe en bas, là où les compartiments sont plus larges que toute la plage restante.

L'ampleur des dégâts est fixée par la seule entrée la plus grande

C'est la partie qui rend ce défaut difficile à anticiper, et elle se généralise à toute quantification à plafond fixe.

Le seuil d'effondrement vaut 1.5 * max / CEILING. Il ne dépend ni du nombre de lignes dont vous disposez, ni du biais de la queue, ni du soin avec lequel vous l'avez construite. Il dépend d'un seul nombre : la plus grande valeur de la liste. L'élément le plus répandu de vos données décide de la part du reste de vos données qui cesse d'exister en tant qu'entité distincte.

Nous l'avons mesuré directement, en écartant les K premières lignes et en requantifiant le reste au même plafond de 255 :

Lignes de tête retiréesNouveau maximumLignes effondrées sur le plancher
096 428997 sur 1 865 (53,5 %)
182 976909 sur 1 864 (48,8 %)
278 280866 sur 1 863 (46,5 %)
550 763525 sur 1 860 (28,2 %)
1037 922220 sur 1 855 (11,9 %)
2030 21914 sur 1 845 (0,8 %)
5018 7666 sur 1 815 (0,3 %)

Vingt lignes sur 1 865 — un pour cent du fichier — sont responsables de l'aplatissement des 53,5 % restants. Si vous quantifiez quoi que ce soit de forme zipfienne — ce qui inclut les noms, les mots, les villes, les produits, les codes d'erreur et à peu près toute table de fréquences que quiconque livre —, telle est votre situation. Le rapport que vous devez représenter est max / min, et pour cette liste il vaut 2 679. Un octet offre 255 niveaux. L'arithmétique était perdue avant même que quiconque ne choisisse la transformation.

L'autre levier est le plafond lui-même, et il est brutalement non linéaire :

PlafondSeuil d'effondrementLignes au plancherPoids distincts produits
159 642,894,2 %12
314 665,988,6 %20
632 295,982,1 %33
1271 138,971,3 %54
255567,253,5 %86
511283,125,5 %127
1 023141,40,4 %181
4 09535,30,0 %357

Passer de 255 à 1 023 — d'un octet à dix bits — fait tomber l'effondrement de 53,5 % à 0,4 %. Le coût du défaut et le coût du correctif sont follement asymétriques, ce qu'il vaut la peine de savoir avant de passer un après-midi à débattre de l'importance de la queue.

Pourquoi rien ne l'a détecté

Voici la moitié inconfortable de l'histoire. Nous avons remesuré la liste après avoir remplacé les poids quantifiés par les effectifs réels, et presque toutes les statistiques de synthèse auxquelles on penserait affirment qu'il ne s'est rien passé :

MesureQuantifiéEffectifs réels
Part de la masse détenue par les 10 premiers prénoms13,05 %13,28 %
Prénoms nécessaires pour atteindre la moitié de la masse totale9289
Nombre effectif d'alternatives (perplexité)542514
Distance de variation totale entre les deux2,45 %

La part du top 10 a bougé de 0,2 point de pourcentage. La perplexité a bougé de 5 %. La distance de variation totale entre la distribution défectueuse et la distribution correcte est inférieure à deux pour cent et demi. Si votre test d'acceptation consiste à demander « la forme agrégée a-t-elle l'air correcte ? », le fichier défectueux le réussit, et il le réussit de façon convaincante.

Il le réussit parce que les mesures agrégées sont dominées par la tête, et la tête n'a jamais été endommagée. La queue détient 8,34 % de la masse de probabilité dans la version défectueuse contre 7,14 % dans la version correcte. Des erreurs confinées à 8 % de la masse ne peuvent pas beaucoup déplacer une statistique de synthèse, quelle que soit leur gravité à l'intérieur de ces 8 %.

Mais regardez ce qui y est faux. Nous avons échantillonné des paires de prénoms au hasard et demandé à quelle fréquence deux prénoms aux fréquences véritablement différentes s'étaient vu attribuer le même poids : 31,1 % de toutes les paires chez les femmes, 33,2 % chez les hommes. Un tiers de la liste a entièrement perdu son ordonnancement. Par prénom, la correction va de ×0,10 à ×1,52 — un ordre de grandeur pour des lignes individuelles, à l'intérieur d'une distribution dont la distance globale à la vérité est de 2,4 %.

C'est là la leçon générale, et elle mérite sa propre ligne : une métrique au niveau de la distribution ne peut pas détecter un défaut confiné à la queue, parce que la queue est par définition la partie qui contribue le moins aux métriques au niveau de la distribution. Si vous n'auditez des données pondérées qu'en agrégé, les défauts de queue sont invisibles par construction — non parce que vous avez été négligent, mais parce que vous avez choisi un instrument qui fait la moyenne précisément sur la région où vit le problème.

Être honnête sur la gravité

Deux éléments tirent dans l'autre sens, et tous deux ont leur place ici.

Premièrement, à l'intérieur du bloc effondré, la distribution réelle était elle-même assez plate. Mesurés en perplexité, les effectifs réels de ces 997 prénoms donnent un nombre effectif de 944 alternatives sur 997 possibles — 94,7 % d'une uniformité parfaite. Aplatir jusqu'à l'exactement plat un bloc déjà presque plat est un crime moindre que ne le laisse entendre l'amplitude de ×15,8. L'amplitude est une affirmation sur les deux lignes extrêmes, non sur le bloc.

Deuxièmement, la quantification a préservé sans faute l'ordre des rangs pour la tête et ne l'a inversé nulle part ; elle n'a créé que des ex æquo. Les ex æquo sont un échec plus faible que les inversions. Une liste qui dit « ces 997 éléments sont également probables » est fausse ; une liste qui dirait « le plus rare est plus probable que le plus courant » serait pire.

Le résumé honnête est donc le suivant : l'erreur agrégée est petite, l'erreur par élément est grande, et laquelle des deux compte dépend entièrement de l'usage auquel la liste est destinée. Pour choisir un prénom plausible à afficher, un décalage distributionnel de 2,4 % n'est rien. Pour tout ce dont la sortie est la probabilité d'un élément individuel — un échantillonneur alimentant une simulation, une répartition A/B pondérée, l'a priori d'un système de recommandation, les poids de shards d'un répartiteur de charge —, le fait qu'un tiers de vos entrées soient ex æquo au plancher est toute l'histoire.

Nous avons corrigé la nôtre parce que le correctif était gratuit : l'ensemble des lignes n'a pas changé, aucune entrée n'a été ajoutée ni supprimée, et les compteurs stockés sont simplement passés en 32 bits. La liste est passée de 86 poids distincts à 1 084.

Les diagnostics

Rien de tout cela n'exige l'accès à la source d'origine. Tout ce qui suit peut être calculé à partir du seul fichier livré, en quelques lignes, et n'importe lequel de ces contrôles aurait détecté notre défaut des années plus tôt.

1. Poids distincts rapportés au nombre de lignes. C'est à lui seul le meilleur signal, et il est presque gratuit.

distinct = count(unique(weights))
ratio    = distinct / rows

Chez nous, cela donnait 86 valeurs distinctes sur 1 865 lignes — 4,6 %. Après le correctif : 1 084 sur 1 865, 58 %. Pour des données de fréquence dérivées d'effectifs, un rapport sain se compte en dizaines de pour cent. Tout ce qui se situe à quelques pour cent seulement signifie que les poids ne sont pas des effectifs ; ce sont des étiquettes.

2. Part des lignes au poids minimal. Une liste zipfienne a une certaine masse au plancher — il existe réellement beaucoup d'éléments rares. Ce qu'elle n'a pas, c'est un plancher qui contient la moitié du fichier.

floor_share = count(w == min(weights)) / rows

Au-dessus de 20 % environ, cela demande une explication. À 53,5 %, la réponse n'est pas « les données sont comme ça ».

3. La plus grande classe d'équivalence. Groupez par poids et prenez le plus gros groupe. Chez nous : 997 lignes partageaient une même valeur, puis 269 la suivante, puis 113. Trois valeurs couvraient 74 % du fichier.

4. Le plafond suspect. Si max(weights) vaut exactement 255, 127, 100, 1 000 ou 65 535, ce nombre n'est pas une mesure. C'est le bord d'un conteneur. C'est le contrôle le moins coûteux de tous, et c'est un diagnostic quasi certain lorsqu'on le combine au (1).

5. La plage dynamique dont vous avez réellement besoin. Calculez max / min sur les effectifs sources, avant quantification. C'est le nombre de niveaux distincts qu'exige un encodage linéaire fidèle. Chez nous, il était de 2 679 contre 255 disponibles. Comparer ces deux nombres prend dix secondes et vous donne la réponse avant que vous n'écriviez l'encodeur.

6. L'amplitude réelle à l'intérieur de la classe plancher. Celui-ci exige la source, et c'est le nombre qui transforme un soupçon en mesure : rattachez les poids livrés aux effectifs d'origine, ne gardez que les lignes au poids minimal et rapportez le max / min de leurs valeurs réelles. Chez nous : ×15,8 et ×15,5. C'est la quantité d'information détruite, exprimée dans les unités qui intéresseront tout le monde.

Une propriété utile de cette liste : les contrôles 1–5 s'exécutent sur l'artefact que vous livrez, sans accès à la source, sans historique et sans contexte. Ce sont ces contrôles-là que vous pouvez lancer en un après-midi sur chaque fichier de données d'un dépôt, et c'est le seul genre de contrôle qui soit réellement exécuté.

Si vous devez quantifier

Parfois l'octet est réellement imposé — un format de transmission qui ne vous appartient pas, une cible embarquée, un type de colonne hérité d'un schéma que vous ne pouvez pas migrer. La transformation, elle, n'est pas pour autant obligée d'être linéaire.

Quantifiez en espace logarithmique. Les fréquences sont multiplicatives ; une échelle linéaire dépense sa résolution là où les données n'en ont aucune. round(k * ln(count)) répartit les niveaux uniformément sur les rapports plutôt que sur les valeurs absolues, et 255 niveaux couvrant un facteur de 2 679 donnent environ 3 % d'erreur relative partout, au lieu d'une précision parfaite en haut et d'un effondrement total en bas.

Ou stockez le rang et gardez les effectifs ailleurs. Si le consommateur n'a besoin que de l'ordre relatif et d'une forme approximative, livrez l'ordre et reconstruisez les poids à partir d'une courbe paramétrique.

Ou découpez le champ. Un octet de mantisse et un quartet d'exposant restent moins coûteux qu'un compteur 32 bits et représentent la plage exactement.

Et quel que soit votre choix, consignez la plage dynamique que vous jetez. Écrivez-la dans l'en-tête du fichier, pas dans un message de commit. « Cette colonne est quantifiée ; la source couvre un facteur de 2 679 et cet encodage résout 255 niveaux » est une phrase qui aurait rendu le défaut auto-déclarant.

L'erreur n'a jamais été « nous avons utilisé un octet ». Elle a été que la compression était avec perte d'une manière que personne n'avait quantifiée, et une transformation avec perte dont la perte n'est pas mesurée est indiscernable d'une transformation correcte — jusqu'au moment exact où quelqu'un rattache la sortie à l'entrée.

La liste de contrôle

  1. Comptez les poids distincts et divisez par le nombre de lignes. En dessous de quelques pour cent, les poids sont des étiquettes, pas des mesures.
  2. Mesurez la part des lignes situées sur le poids minimal. La moitié du fichier au plancher n'est pas une propriété des données.
  3. Cherchez un plafond qui est une frontière de conteneur — 255, 127, 65 535, 100, 1 000. Les mesures ne tombent pas sur ces valeurs.
  4. Calculez max / min sur les effectifs sources avant de choisir un encodage. C'est le nombre de niveaux dont vous avez besoin ; comparez-le au nombre dont vous disposez.
  5. Après quantification, rattachez les données et rapportez l'amplitude réelle à l'intérieur de la classe plancher. C'est le seul nombre qui énonce les dégâts dans les unités qui comptent.
  6. N'acceptez pas la concordance agrégée comme preuve. La part du top 10, l'entropie et la distance de variation totale sont toutes dominées par la tête et se situaient toutes dans le bruit sur un fichier où un tiers des paires avaient perdu leur ordonnancement.
  7. Quantifiez en espace logarithmique si la plage est multiplicative, ce qu'elle est pour tout ce qui a une forme zipfienne.
  8. Écrivez la plage écartée dans l'artefact. Une transformation avec perte non mesurée ressemble exactement à une transformation sans perte.
  9. Rappelez-vous que la tête fixe le plancher. Vingt lignes sur 1 865 ont décidé que 997 autres seraient ex æquo. Rien de tout cela n'est visible dans ces vingt-là.

Le défaut était livré depuis aussi longtemps que le corpus existait. Il a survécu à un validateur qui contrôle le nombre de lignes, l'ordre de tri, l'encodage, les marques d'ordre des octets, les sommes de poids par rapport à une borne 32 bits et la part de concentration du top 10 par rapport à un chiffre de population — 261 fichiers, zéro échec, zéro avertissement. Chacun de ces contrôles était correct, et aucun d'eux ne regardait la seule chose qui clochait : les nombres de la colonne des poids étaient-ils encore des nombres, ou étaient-ils devenus des catégories ?

Pour l'échec jumeau — où un zéro dans les données signifiait deux choses différentes et où aucun validateur structurel ne pouvait les distinguer — voir Quand zéro signifie deux choses différentes. Pour la méthode générale d'audit des données de fréquence face à leur source, voir Comment auditer les données de fréquence des noms. Pour voir à quoi ressemble la tête de ces distributions lorsqu'elle est intacte, voir Courbes de concentration des noms de famille. Pour les contrôles qui passent parce qu'ils n'ont jamais regardé, voir Le contrôle qui devient aveugle exactement là où vous en avez besoin.


Données arrêtées au 2026-07-21

Tous les chiffres ont été mesurés le 21 juillet 2026 sur les fichiers du corpus présents sur le disque et sur une sauvegarde des mêmes fichiers antérieure à la modification, et ils ont été redérivés indépendamment pour cet article plutôt que repris du journal des modifications.

Sources et remarques :

  • Les chiffres suédois sont notre propre calcul. Ils sont dérivés par nos soins des effectifs de prénoms publiés par Statistics Sweden (SCB) ; le matériau traité, les poids et chaque pourcentage de cet article sont les nôtres et non ceux de SCB, et SCB n'est pas la source des chiffres traités. Seuls les effectifs sous-jacents sont attribuables à l'organisme.
  • Liste féminine — 1 865 lignes. Quantifiée : 86 poids distincts, minimum 1, maximum 255, somme 11 954, 997 lignes (53,5 %) au poids 1, part du top 10 13,05 %. Effectifs réels : 1 084 poids distincts, minimum 36, maximum 96 428, somme 4 445 328, part du top 10 13,28 %.
  • Liste masculine — 2 015 lignes. Quantifiée : 100 poids distincts, minimum 1, maximum 255, somme 14 744, 1 115 lignes (55,3 %) au poids 1, part du top 10 13,21 %. Effectifs réels : 1 074 poids distincts, minimum 31, maximum 81 754, somme 4 655 153, part du top 10 13,42 %.
  • Le bloc effondré — pour la liste féminine, les 997 lignes stockées au poids 1 ont des effectifs réels allant de 36 à 567, soit une amplitude de ×15,75 ; quartiles 228 / 291 / 393. Pour la liste masculine, 1 115 lignes allant de 31 à 479, amplitude ×15,45. Le bloc détient 8,34 % de la masse quantifiée contre 7,14 % de la masse réelle (femmes) et 7,56 % contre 6,44 % (hommes).
  • Le mécanisme — reconstruit en rattachant les poids quantifiés aux effectifs réels et en comparant à w * max / 255. L'effectif réel moyen par poids stocké suit la prédiction linéaire à environ 1 % près à chaque niveau à partir de 2, et le maximum observé au poids 1 est 567 contre une frontière d'effondrement prédite de 567,2. L'ensemble des lignes est identique avant et après : 0 ajoutée, 0 supprimée, 0 renommée.
  • Distance distributionnelle — la distance de variation totale entre la distribution quantifiée et la distribution réelle est de 2,446 % (femmes) et 2,252 % (hommes). Plus forte hausse par prénom ×1,52, plus forte baisse ×0,10, dans les deux listes. Perplexité 542 → 514 et 482 → 456. Prénoms nécessaires pour atteindre la moitié de la masse : 92 → 89 et 76 → 74.
  • Paires ex æquo — estimées en échantillonnant 200 000 paires de lignes tirées au hasard à partir d'une graine fixe et en comptant les paires dont les effectifs réels diffèrent mais dont les poids quantifiés sont égaux : 31,1 % (femmes), 33,2 % (hommes).
  • Platitude interne au bloc — la perplexité des effectifs réels des 997 lignes du plancher est de 944, soit 94,7 % des 997 qu'un bloc parfaitement uniforme donnerait ; pour le bloc masculin, 1 057 sur 1 115, soit 94,8 %. C'est le chiffre qui maintient l'amplitude de ×15,8 dans ses justes proportions, et il est rapporté ici parce que l'omettre reviendrait à exagérer le défaut.
  • Sensibilité au plafond et à la tête — les deux tableaux ont été calculés sur les effectifs féminins réels en réappliquant round(count * C / max) avec un plancher de 1, pour chaque plafond C, et en retirant les K premières lignes avant requantification. Plage dynamique de la liste féminine : max / min = 2 678,6 ; max / median = 188.
  • État du validateur — le validateur du corpus rapporte 261 fichiers, 0 échec, 0 avertissement, avant comme après la modification. Il contrôle le nombre de lignes, le tri décroissant, l'UTF-8 sans marque d'ordre des octets, l'absence de retours chariot, les sommes de poids dans une borne 32 bits et la concentration du top 10 par rapport à une part de population. Aucun de ces contrôles n'est capable de détecter le défaut décrit ici, et ce n'est pas une critique du validateur — c'est le propos de l'article.
  • Ce qui a été modifié — la colonne des poids uniquement. Aucune entrée n'a été ajoutée, supprimée ou réordonnée, et aucune autre locale n'a été touchée. Les compteurs stockés sont désormais en 32 bits ; le seuil d'effondrement à cette largeur est inférieur à 1 et aucune ligne ne peut atteindre le plancher par arrondi.

← Articles