Améliorer des données d'autochtonie d'odonates à coups de regex

Sur l'atlas des odonates (libellules et demoiselles), on a une problématique d'incertitude concernant l'autochtonie des espèces sur les mailles où elles ont été observées. Vous n'avez pas forcément besoin de comprendre cette phrase pour lire cet article, tant que vous aimez bien les problématiques de gestion de données et les regex.

L'autochtonie ? Les exuvies ?

OK, petit lexique.

Atlas : un ensemble de cartes représentant la présence ou non d'espèces sur un territoire donnée, généralement par maille.

Maille : une zone, un carré sur la carte. Par exemple en France on utilise souvent des mailles de 10km² tracées avec une projection Lambert93, ce qui donne des mailles identifiables avec un code tel que 10kmL93E024N677.

Autochtonie : une espèce est autochtone sur un lieu si on peut y observer la reproduction, la ponte, la naissance, etc ; en gros, qu'on ne l'a pas observée juste de passage, comme ça peut arriver avec les odonates qui peuvent se balader loin de leur lieu de naissance et de reproduction.

Exuvie : la mue laissée par la larve d'odonate avant qu'elle « émerge » en tant qu'individu adulte. Quelques photos dans cette clé d'identification. Si on trouve une exuvie quelque part, c'est donc qu'il y a bien eu émergence d'adulte, et donc que l'espèce observée est autochtone à cet endroit (Ruffoni, 2018).

Les données

On utilise une extraction OpenObs pour alimenter notre atlas, et ces tables contiennent un champ “Stade de vie” mais celui-ci n'est pas toujours renseigné correctement à cause de l'hétérogénéité des jeux de données et des plateformes de saisie utilisées. Si on regarde bien cependant, l'information peut parfois être retrouvée dans le champ “Commentaires” de l'observation, où l'observateur, parfois le producteur de la donnée, nous gratifie d'informations plus ou moins cryptiques, à propos de l'observation, ou de la météo, ou de son humeur… Dans cet article on va donc se pencher sur ce champ de commentaires et voir comment on peut l'utiliser au mieux pour améliorer les infos d'autochtonie.

Beaucoup des idées ci-dessous proviennent des conseils d'Alexia, notre géomaticienne avec qui j'ai la joie quotidiennement renouvelée de travailler 😌

Les regex ci-dessous sont au format PCRE2 (PHP 7.3 et plus). J'ai remplacé les noms qui apparaissaient dans les commentaires par […].

Pourquoi on ne peut pas juste grep exuvie

On ne peut pas se baser simplement sur la présence du mot « exuvie » en commentaire d'une observation pour pas mal de raisons.

Déjà parce que parfois, c'est écrit « pas d'exuvies ». OK donc on n'a qu'à faire une regex qui enlève « pas [espace] d'exuvies » ; pourquoi pas mais du coup, attention à ne pas ignorer les gros malins qui ont alors commenté « pas mal d'exuvies » ! Il y a aussi quelques commentaires dans d'autres langues, notamment en allemand : « viele exuvien » (plein), « altere Exuvien » (anciennes), « frische Exuvien » (fraîche), « keine Exuvien » (aucune). Exemples relous :

Parfois le commentaire est utilisé pour décrire d'autres éléments de l'observation, qui mentionnent le terme d'exuvie mais sans être en rapport avec la quantité d'exuvies trouvées dans l'observation. Quelques exemples :

À moins de faire une regex particulièrement illisible et constamment incomplète, c'est impossible d'utiliser un modèle de denylist pour vérifier les exuvies, et donc détecter uniquement la présence du mot exuvie n'est pas satisfaisant pour notre besoin ici. On va donc écrire une regex dont on utilisera les matchs pour affirmer la présence d'une exuvie, quitte à avoir des faux négatifs, mais avec presque aucun faux positifs.

Détails, astuces et mauvaises idées

Si on souhaite se restreindre au modèle « [nombre] exuvie », il faut faire attention à ignorer tous les commentaires qui disent gentiment qu'il y a « 0 exuvies » (mais pas ignorer « 10 exuvies » !). Bien entendu c'est parfois écrit en toutes lettres : « Une exuvie », « Trois exuvies », « Quatre exuvies ».

On aimerait bien ignorer les commentaires qui disent « aucune exuvie » ou « pas d'exuvie », mais évidemment des petits malins ont commenté « Pas mal d'exuvies » 😭

On serait tenté de capturer plus de résultat en réduisant la recherche à « exu » car beaucoup de commentaires utilisent « ex » ou « exu » car « exuvie » c'est trop long à écrire j'imagine, mais c'est sans compter la présence de mots comme « exutoire ». On a aussi envie de match les chaînes « exuvies trouvées » ou « exuvies récoltées », mais elles semblent pouvoir apparaître autant dans des affirmations positives que négatives !

Parmi d'autres détails, l'orthographe officielle est « exuvie » mais elle est parfois orthographiée « éxuvie ». Enfin, la gestion de la casse est un problème de tous les instants, et il peut être intéressant de commencer par passer l'entièreté de son jeu de données en minuscules pour simplifier l'écriture des regex ensuite, ou d'ajouter le flag i à ses patterns pour ignorer la casse.

Protocole

Préparation des données

Depuis une extraction d'OpenObs des insectes, préalablement filtrée sur l'ordre Odonata, on extrait les observations dont les commentaires (colonne 80) contiennent le mot « exuvie » avec l'excellent csvgrep :

csvgrep
    -c 80 \
    -r "[eEéÉèÈ]xuvie" \
    extractINPN_insectes_21092023_Odonata.csv \
> /tmp/exuvia.csv

On récupère la liste de ces commentaires dédupliqués et triés pour pouvoir tester nos regex facilement :

… | csvcut -c 80 | sort -u > commentaire_exuvie_uniq.txt

Ce fichier fait 4306 lignes.

Compter les commentaires les plus simples

Si le commentaire commence par « exuvie », sans compter quelques caractères parasites, alors nous n'avons pas trouvé de contre-exemples au fait que l'observation concerne bel et bien une ou plusieurs exuvies.

Regex : /^[\-\*\+]? *[eé]xuvie/i

Rien qu'avec cette regex on a 348 matchs.

Repérer les comptages d'exuvies

À défaut de pouvoir proprement ignorer les commentaires stipulant qu'il n'y a pas d'exuvies parce qu'on ne sait jamais quand un odonatologue décidera d'annoter dans son patois qu'il n'a vu aucune exuvie, on peut essayer de cibler au mieux la présence d'un certain nombre d'exuvie, que ça soit en chiffres ou en toutes lettres.

Pour les chiffres, sont valides les nombres à un chiffre de 1 à 9, ou n'importe quel chiffre à partir de 2 chiffres par nombre. Théoriquement ça fait que « 00 exuvie » match, mais bon… Attention en revanche à la pattern /une exuvie/ car elle match la chaîne « aucune exuvie » 🫠 un petit negative lookbehind est donc de rigueur.

On améliore ses résultats en ignorant certaines qualificatifs comme « ancienne » ou les genres. On détecte les m et f, voire mf qui servent à genrer les nombres, et les x neutres. Comme on est sympa, on match aussi les nombres en toutes lettres (enfin de un à neuf). On trouve également parfois des quantificateurs flous mais utiles, comme « nombreuses exuvies » ou « plusieurs exuvies ».

Regex : /(([0-9]{2,}|[1-9])(x|m|f|mf)?|(?<!auc)une|qq|qlq|quelques|deux|trois|quatre|cinq|six|sept|huit|neuf|nombreuses|plusieurs)([[:blank:]](m[aâ]les?|femelles?|anciennes?|vieilles?))?[[:blank:]]?[eé]xuvie/i

Dans le détail :

/(
    ([0-9]{2,}|[1-9])
    (x|m|f|mf)?
|
    (?<!auc)une
|
    qq|qlq|quelques|deux|trois|quatre|cinq|six|sept|huit|neuf|nombreuses|plusieurs
)
([[:blank:]](m[aâ]les?|femelles?|anciennes?|vieilles?))?
[[:blank:]]?[eé]xuvie
/xi

Tout de suite on passe à 2767 matchs !

En général il semble plus prudent de ne pas supprimer les virgules car on n'est jamais à l'abri d'un naturaliste fou qui aurait écrit « larves 4, exuvies 0 », qu'un filtre trop enthousiaste interprèterait comme « larves 4 exuvies 0 » et bondirait sur « 4 exuvies ». En revanche, s'il n'y a rien après la mention d'exuvie, on peut se permettre de compter dans un match une virgule entre un comptage et le mot exuvie, ce qui permet de match une bonne dizaine de cas supplémentaires.

Regex :

/(([0-9]{2,}|[1-9])(x|m|f|mf)?){1,2},?[[:blank:]]?[eé]xuvies?$/i
                               ^^^^^
                               un ou deux groupes (e.g. 1m, 2f, 3m4f, …)

Quelques cas autres intéressants

D'autres quantificateurs imprécis mais on notera le « d' » avant le mot exuvie.

Regex : /(dizaines?|centaines?)[[:blank:]]d'[eé]xuvie/i

Enfin le mot « présence » est souvent utilisé aussi.

Regex : /pr[eé]sence[[:blank:]](?:d'|de\x20l')[eé]xuvie/i

Ce qui nous fait passer à 2828 matchs.

Regex finale

/(
  ^[\-\*\+]?[[:blank:]]*[eé]xuvie
|
  (([0-9]{2,}|[1-9])(x|m|f|mf)?|(?<!auc)une|qq|qlq|quelques|deux|trois|quatre|cinq|six|sept|huit|neuf|nombreuses|plusieurs)([[:blank:]](m[aâ]les?|femelles?|anciennes?|vieilles?))?[[:blank:]]?[eé]xuvie
|
  (([0-9]{2,}|[1-9])(x|m|f|mf)?){1,2},?[[:blank:]]?[eé]xuvies?$
|
  (dizaines?|centaines?)[[:blank:]]d'[eé]xuvie
|
  pr[eé]sence[[:blank:]](?:d'|de\x20l')[eé]xuvie
)/xi

Au final c'est surtout la deuxième partie, où on cherche « [nombre] exuvie » qui rapporte un maximum d'information, en tout cas sur la diversité des commentaires disponibles car il ne faut pas oublier que les nombres de matchs donnés correspondent à un jeu dé-dupliqué.

Conclusion

Et voila, dans la pratique ça nous a permis d'augmenter significativement le nombre d'observations avec des infos d'autochtonie renseignées :

Comment améliorer la situation ? On voit que certains commentaires essayent de suivre une certaine nomenclature en décrivant les exuvies observées, en ajoutant m et f pour genrer les individus et/ou les exuvies, ce qui donne des entrées comme 2m3f,exuvie pour signaler 2 mâles et 3 femelles. Sont-ce les exuvies qui sont genrées ? Ou des individus observés, auquel on annote qu'il y avait également des exuvies sur le lieu ? Impossible de savoir à moins de contacter l'observateur.

Avec les regex présentées ici, on loupe évidemment une masse de commentaires qui décrivent beaucoup trop de choses, utilisent des abréviations obscures, une syntaxe chaotique, mais vouloir prendre en considération tout ces cas pour un traitement automatisé nécessite un temps que personne n'a ; il y a toujours moyen, théoriquement, d'affiner les regex pour ajouter des cas. Pour les données existantes, la seule solution exhaustive est donc une ✨validation manuelle✨ !

Pour la création de données futures en revanche, la meilleure solution semble être de travailler sur des interfaces qui facilitent au maximum la saisie de données et donner à chaque caractéristique de l'observation des champs clairs, typés et avec des contraintes, pour que l'observateur ait pu tout renseigner aux emplacements adéquats et réserver à la case commentaire sa prose lyrique ou ses souvenirs de pique-nique sur le site de l'observation, mais on n'y est pas encore tout à fait !

Bonus

Des commentaires que j'ai bien aimé même s'ils sont impossible à traiter :