2. Codif regex
Rôle de l’étape
codif-regex applique une liste de règles d’expressions régulières au libellé normalisé. C’est un premier filtre rapide et déterministe pour les libellés standardisés : lignes techniques (carte bancaire, prélèvement, remise…) et quelques catégories génériques (« courses », « restaurant »…).
Une règle qui matche → le code est attribué immédiatement, méthode
"REGEX", et la ligne n’est plus traitée par les modèles.Une ligne sans match → transmise telle quelle à LCS, RAG et TTC.
Code :
regex-codif/— entréeregex-codif/src/main.pyRègles :
regex-codif/config/rules.yaml
Entrées
Les chemins dépendent du mode (sélectionné par --input-file, cf. Généralités) :
| Variable config | Mode | Chemin S3 |
|---|---|---|
paths.test_set |
évaluation | …/{run}/preprocessing/raw_test.parquet |
paths.train_set |
évaluation | …/{run}/preprocessing/raw_train.parquet |
paths.test_set_prod |
production | …/{run}/preprocessing/observations.parquet (lignes à coder) |
paths.train_set_prod |
production | …/{run}/preprocessing/annotations_full.parquet (KB complète) |
C’est aussi ici qu’a lieu l’échantillonnage centralisé du pipeline : si sample-annotations (éval) ou sample-observations (prod) est renseigné, le jeu à coder est échantillonné une seule fois (random_state=42) et tous les classifieurs aval héritent des mêmes lignes via raw_test_without_regex.parquet.
Traitement
Les règles
Chaque règle associe un motif (sur le libellé) à un code COICOP, dans rules.yaml :
rules:
- pattern: "^\\s*boulangerie\\s*$" # libellé = "boulangerie"
code: "01.1.1.3"
- pattern: "^restaurant$"
code: "11.1.1"
- pattern: "\\b(divers\\s+)?courses?\\b" # contient "course(s)" / "divers courses"
code: "98.1"
- pattern: "^carte bancaire$"
code: "98.3"
- pattern: "^prelevement$"
code: "98.4"
- pattern: "^illisible$"
code: "98.4"
- pattern: "\\bcantine\\b"
code: "11.1.2"Le fichier contient 43 règles. Quelques codes spéciaux apparaissent : "Reprise manuelle" (à recoder à la main) et les codes techniques 98.x / 99.x.
Le cas « Reprise manuelle »
Deux des 43 règles ne renvoient pas un code COICOP mais le littéral "Reprise manuelle" : des libellés reconnus comme réels mais impossibles à coder automatiquement de façon fiable. Ce littéral se comporte ensuite comme n’importe quel code regex :
- la ligne est considérée codée (elle ne passe pas par les modèles ni par le LLM) ;
- la valeur traverse le pipeline telle quelle jusqu’à la colonne
predicted_codedu fichier livré, avecprediction_source = "regex"(voir le tableau des valeurs possibles) ; - côté aval, il faut donc filtrer
predicted_code == "Reprise manuelle"pour constituer la pile de recodage humain.
Application
apply_regex() (regex-codif/src/data/apply_regex.py) applique les règles sur la colonne s_pr_product (normalisation forte), puis sépare le jeu en deux :
df["predict_code"] = apply_regex_rules(df["s_pr_product"], rules)
df_regex_predicted = df[df["predict_code"].notna()] # matché → prédiction finale
df_regex_predicted["method"] = "REGEX"
df_without_regex = df[df["predict_code"].isna()].drop(columns="predict_code") # → modèlesLa plupart des règles sont ancrées (^…$) : elles n’attrapent que des libellés exacts. Un libellé comme Ticket CB (normalisé ticket cb) ne correspond ni à ^carte bancaire$ ni à ^cb$ — il n’est donc pas codé par regex et part vers les modèles. C’est attendu : le regex ne traite que les cas non ambigus, le reste relève des modèles + arbitrage LLM.
Exemple sur le fil rouge
s_pr_product |
Règle déclenchée | predict_code |
Suite |
|---|---|---|---|
illisible |
^illisible$ |
98.4 |
prédiction finale (si non retiré au preprocessing) |
ticket cb |
(aucune) | — | → LCS / RAG / TTC |
bagu tradition u ble bretagne |
(aucune) | — | → LCS / RAG / TTC |
max garden flowers balle surprise |
(aucune) | — | → LCS / RAG / TTC |
Sorties
{run} = …/workflow_runs/{run_date}/{run_id}/codif-regex
| Fichier | Contenu |
|---|---|
{run}/REGEX_pred.parquet |
lignes codées par regex (predict_code, method="REGEX") |
{run}/raw_test_without_regex.parquet |
lignes non codées → entrée des modèles |
{run}/raw_train_without_regex.parquet |
idem côté train / suggester |
{run}/error_regex_pred.parquet |
erreurs (si vérité terrain disponible) |
➡️ Étape suivante : 3. Prune — troncature & élagage