Librairie Mathématique & Statistique

La Librairie Mathématique & Statistique est une boite à outils pour les plugins : elle fournit les fonctions de statistique descriptive, de discrétisation, de géométrie et de manipulation de matrices dont un plugin a besoin pour analyser des données quantitatives — typiquement pour les cartographier.

Elle ne rend aucun service visible à l’utilisateur d’un site : ni page dans l’espace privé, ni saisie, ni squelette, ni balise, ni filtre. On l’inclut, on l’appelle. Elle n’a par ailleurs aucune dépendance, ni à un plugin ni à une librairie externe : les algorithmes qui reposaient sur une librairie tierce — la discrétisation de Jenks notamment — ont été réécrits dans le plugin.

Son périmètre est celui du calcul : elle reçoit des nombres, elle rend des nombres. Tout ce qui relève de la provenance des données, de leur signification ou de leur restitution reste à la charge du plugin appelant.

Concepts

La série

La série est l’objet central : un tableau PHP de valeurs numériques représentant une variable quantitative observée sur un ensemble d’individus.

$serie = ['ain' => 12.4, 'aisne' => 8.1, 'allier' => 15.9];

Les clés portent l’identité des individus et les valeurs, l’observation. La librairie ne s’intéresse qu’aux valeurs pour ses calculs, mais restitue les clés d’origine dans tout résultat qui reste indexé sur les individus — la série discrétisée, la série transformée, la série scindée. Un plugin peut donc rapprocher le résultat de ses propres données sans tenir de table de correspondance.

Seules les séries quantitatives sont traitées : la librairie n’a rien à offrir sur des variables qualitatives ou ordinales.

La classe et la discrétisation

Discrétiser, c’est répartir les valeurs continues d’une série en un petit nombre d’intervalles appelés classes, afin de rendre la série lisible — typiquement pour la représenter sur une carte choroplèthe.

Une classe n’est pas seulement un intervalle : c’est un objet décrit par ses bornes et par ce que la série y dépose.

IndexContenu
binf, bsup Bornes de l’intervalle
centre Valeur centrale de la classe
effectif, frequence Nombre d’éléments et fréquence
moyenne Moyenne des valeurs de la classe
serie_min, serie_max Plus petite et plus grande valeur effectivement présentes
etendue, etendue_ponderee Étendue brute et étendue pondérée par la moyenne
serie Les éléments de la série tombant dans la classe, clés conservées

Les bornes décrivent l’intervalle théorique, serie_min et serie_max ce que la série y dépose réellement : sur une classe vide les premières existent, les secondes non.

Le sens des intervalles n’est pas uniforme : pour toutes les méthodes sauf celle de Jenks, ils sont fermés à gauche et ouverts à droite ; pour Jenks, c’est l’inverse. Cette exception tient à la définition même de la méthode, et un plugin qui compare des discrétisations doit en tenir compte.

La transformation

Une série très dissymétrique se discrétise mal : les classes obtenues sont soit vides, soit surchargées. La transformation applique une fonction mathématique à chaque valeur pour normaliser la distribution avant de la découper. Elle est décrite par un tableau à trois index : le jeton de la fonction — pow, sqrt, log, log10, exp ou exp_base —, son paramètre éventuel, et un indicateur d’inversion.

La transformation est réversible : chaque fonction a sa réciproque, ce qui permet de rendre à l’appelant des bornes de classes exprimées dans l’unité d’origine plutôt que dans l’espace transformé. C’est indispensable à l’affichage d’une légende.

Une transformation qui sort du domaine de définition — logarithme d’une valeur négative, inversion d’un zéro — rejette la série entière plutôt que de produire silencieusement des valeurs non finies.

Le point et la matrice

Le point est un tableau de coordonnées reprises par leur rang : ['latitude' => 45.7, 'longitude' => 4.8] et [45.7, 4.8] sont équivalents pourvu que l’ordre soit respecté.

La matrice est un tableau de lignes, chaque ligne étant un tableau de valeurs numériques. Ses clés sont elles aussi quelconques et peuvent identifier des unités spatiales ; les calculs reprennent les valeurs par leur rang et restituent les identifiants d’origine.

Les conventions de la librairie

Ces quatre conventions valent pour toutes les fonctions, dans tous les domaines. Elles forment le contrat que le plugin appelant doit connaitre.

Aucune exception, une valeur de repli

Aucune fonction ne lève d’exception. Une opération impossible renvoie null quand elle devait rendre un nombre, un tableau vide quand elle devait rendre un tableau. L’appelant teste la valeur de retour, il n’a pas à encadrer chaque appel d’un bloc try.

La nuance propre à cette librairie est que null ne signifie pas toujours une erreur. Sur une série vide, il signale bien un appel sans objet. Mais serie_asymetrie_yule() rend null sur une série dont les quartiles extrêmes se confondent : le calcul est alors mathématiquement indéfini, ce qui est une information sur les données et non une faute de l’appelant.

Corollaire pratique : le test doit porter sur null explicitement. 0.0 est un résultat parfaitement valide qu’un test de vérité rejetterait à tort.

Le type des valeurs n’est pas contrôlé

Les fonctions déclarent leurs arguments typés — array $serie, int $nb_classes — mais ne vérifient pas le type des valeurs contenues dans les tableaux. Une série comportant une chaine non numérique lève une erreur d’arithmétique PHP.

C’est un choix assumé : contrôler chaque valeur de chaque série à chaque appel coûterait cher sur des séries de plusieurs milliers d’éléments, pour un contrôle que l’appelant est mieux placé pour faire une fois, au moment où il constitue ses données. La fonction serie_transtyper() est fournie pour cela — les valeurs lues en base arrivant sous forme de chaines.

L’exception est géographique. Les fonctions sphériques normalisent leurs points par sphere_point_normaliser(), qui réindexe les coordonnées, les transtype et vérifie les bornes. La raison est que des bornes fausses — une latitude de 200 degrés — produiraient un résultat numériquement valide mais géographiquement absurde, qu’aucun test ultérieur ne détecterait.

Les clés sont quelconques, le rang fait foi

Séries, points et matrices ont des clés libres. Les calculs reprennent les valeurs par leur rang, jamais par leur clé, et les fonctions restituent les clés d’origine dans les résultats qui restent indexés sur les individus.

Conséquence pratique : deux séries que l’on veut apparier — une série de valeurs et une série de poids, dans serie_moyenne_ponderee() — doivent être dans le même ordre, et non porter les mêmes clés.

Aucun état, aucune configuration

Toutes les fonctions sont pures : même entrée, même sortie, aucun effet de bord, aucune écriture, aucune lecture de configuration ni de meta. La librairie ne s’installe pas, ne se configure pas et ne laisse aucune trace.

Deux constantes seulement sont définies, et uniquement si elles ne le sont pas déjà, ce qui permet à un site de les redéfinir dans ses options : _EZMATH_RAYON_TERRE_KM et _EZMATH_TAI_METHODE.

Les cinq domaines

Chaque fichier est autonome : un plugin n’inclut que celui dont il a besoin.

FichierDomaine
inc/ezmath_serie.php Transformation, transtypage et scission d’une série
inc/ezmath_statistique.php Position, dispersion, forme, autocorrélation
inc/ezmath_discretisation.php Nombre de classes, découpage en classes, indices de qualité
inc/ezmath_geometrie.php Distances euclidienne et géodésiques, pondération spatiale
inc/ezmath_matrice.php Produit, transposition, dimensions

La distinction entre les deux premiers tient à ce qu’ils rendent : les opérations reçoivent une série et en rendent une autre, les statistiques reçoivent une série et rendent un nombre.

Statistique descriptive

FamilleFonctions
Position serie_modes, serie_moyenne et ses variantes géométrique, harmonique et pondérée, serie_mediane, serie_centile, serie_quantiles, serie_quartiles
Dispersion serie_etendue, serie_ecart_interquartile, serie_variance, serie_ecart_type, serie_moment
Forme serie_asymetrie_fisher, _yule, _pearson, serie_kurtosis_pearson, _fisher
Autocorrélation serie_autocorrelation_geary

serie_statistiques() calcule l’ensemble en un appel. La correction de Bessel — division par n-1 plutôt que par n — est un argument optionnel de la variance et de l’écart-type, désactivée par défaut : c’est à l’appelant de savoir s’il estime une population ou un échantillon.

Discrétisation

C’est le domaine le plus fourni, et le cœur de l’usage cartographique.

Combien de classes ? Cinq indices classiques répondent à partir de la taille et de la distribution de la série — serie_indice_huntsberger, _brooks, _yule, _scott, _diaconis. Ils suggèrent ; serie_nb_classes_constructibles() dit en revanche combien de classes sont réellement constructibles sur la série fournie.

Quelle méthode ? Neuf sont disponibles, en deux familles : les mathématiques — égales étendues, progressions arithmétique et géométrique — et les statistiques — quantiles, moyenne et écart-type avec ses variantes, moyennes emboitées, Jenks.

serie_discretisation() est le point d’entrée : il aiguille vers la méthode demandée, applique la transformation éventuelle et rétablit les bornes dans l’unité d’origine. Il rend la série discrétisée, la liste des classes, et un indicateur signalant qu’une classe est restée vide. Les fonctions serie_classes_* restent appelables directement pour qui veut un découpage sans le reste.

La discrétisation est-elle bonne ? Cinq indices de qualité — TAI, I, redondance, D et C de Geary — mesurent l’homogénéité du résultat et permettent de comparer objectivement deux méthodes sur une même série. Ils se calculent séparément, l’indice de Geary réclamant une matrice de pondération spatiale que la librairie ne peut pas construire puisqu’elle ignore tout des unités observées.

Géométrie et matrices

Trois fonctions calculent une distance géodésique entre deux points d’une sphère, exprimés en degrés décimaux, et rendent des kilomètres : la formule d’Haversine, stable sur les petites distances ; la loi sphérique des cosinus ; et une approximation plane, rapide et valable sur de courtes distances. point_distance_euclidienne() est d’une autre nature : elle opère dans un espace à n dimensions sans rapport avec la géographie.

sphere_ponderation_spatiale() produit, à partir d’une liste de coordonnées indexées par unité spatiale, la matrice des poids que réclame l’indice de Geary : chaque unité est pondérée par l’inverse de sa distance aux autres. Son second argument est le nom d’une fonction de distance — c’est le seul point d’extension de la librairie, un plugin pouvant y passer la sienne.

Le domaine matriciel se limite à trois fonctions — produit, transposition, dimensions — qui existent pour servir les autres domaines, principalement l’autocorrélation spatiale. Ce n’est pas une bibliothèque d’algèbre linéaire et elle n’a pas vocation à le devenir.

Mise en œuvre dans un plugin utilisateur

Il n’y a rien à configurer, rien à déclarer, aucun service à écrire. On déclare la dépendance dans son paquet.xml, on inclut le fichier du domaine voulu, on appelle.

include_spip('inc/ezmath_statistique');

$moyenne = serie_moyenne($serie);
$stats   = serie_statistiques($serie, true);   // avec correction de Bessel

Le cas d’usage le plus courant est le choix d’une discrétisation, et la librairie donne de quoi le fonder plutôt que de trancher à la place de l’appelant.

include_spip('inc/ezmath_discretisation');

// Combien de classes ? Les indices suggèrent, la série contraint.
$suggere  = serie_indice_huntsberger($serie);
$possible = serie_nb_classes_constructibles($serie, $suggere);

// Comparer deux méthodes sur la même série
$quantile = serie_discretisation($serie, 'quantile', $possible);
$jenks    = serie_discretisation($serie, 'jenks', $possible);

// L’indice TAI est le plus élevé pour la discrétisation la plus homogène
$tai_quantile = discretisation_indice_tai($serie, $quantile['classes']);
$tai_jenks    = discretisation_indice_tai($serie, $jenks['classes']);

Si la série est très dissymétrique, une transformation appliquée avant le découpage donne des classes plus équilibrées, les bornes rendues restant exprimées dans l’unité d’origine.

Sur la validation des calculs

Une librairie de calcul pose une question que les autres plugins ne posent pas : comment sait-on que les formules sont justes ? Un test qui vérifie qu’une fonction rend ce qu’elle rendait hier ne dit rien de sa justesse.

La suite de tests du plugin — près de trois mille assertions, exécutables sans installation de SPIP par un simple php tests/executer.php — répond sur deux plans.

Les valeurs de référence sont soit calculées à la main et explicitées en commentaire, soit issues du logiciel R, auquel cas la commande correspondante est citée. Aucune valeur n’a été relevée depuis la sortie du code lui-même.

Au-delà des valeurs ponctuelles, la suite vérifie surtout des invariants, qui sont ce qui compte réellement pour une carte : toute valeur de la série tombe dans exactement une classe — aucun territoire ne peut rester sans couleur ; les bornes couvrent exactement le domaine de la série, sont croissantes, jointives et finies ; la somme des fréquences vaut 1 ; une série non discrétisable est refusée proprement plutôt que de produire des classes fantaisistes. Ces invariants sont éprouvés sur un jeu de séries couvrant les cas dégénérés comme les profils réels.

Résumé pour le développement d’un plugin utilisateur

Trois lignes suffisent dans l’immense majorité des cas : déclarer la dépendance, inclure le fichier du domaine, appeler la fonction. La librairie n’expose aucun mécanisme d’extension et n’en a pas besoin.

Les deux points d’attention sont le contrat de retour — null ou tableau vide, jamais d’exception, et un test qui porte explicitement sur null — et le fait que le type des valeurs n’est pas contrôlé, ce dont serie_transtyper() permet de se prémunir.

La version 1.0.0 est la première version publiée. Le plugin existait depuis aout 2024 sans avoir jamais été étiqueté ; cette version marque le passage de son API sous contrat, à l’issue d’une revue de conception complète qui a notamment unifié les règles de nommage de ses fonctions.

Pour aller plus loin, le document de conception, plus détaillé que cette présentation, est disponible dans le dépôt : https://git.spip.net/spip-contrib-e....

L’historique des versions est consigné dans le journal des modifications : https://git.spip.net/spip-contrib-e....

Le plugin Territoires Data l’utilise pour discrétiser et cartographier ses séries de données territoriales.

N’hésitez donc à partager vos retours.

Discussion

No discussion

Add a comment

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Who are you?
[Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom