4. Codif LCS
Rôle de l’étape
codif-lcs code les libellés par similarité de chaîne de caractères, sans modèle d’apprentissage. Elle compare chaque libellé à coder à un pool de référence (le « suggester » : des libellés déjà codés à la main) et reprend le code du libellé de référence le plus proche, au sens de la plus longue sous-chaîne commune (Longest Common Substring).
C’est une approche robuste pour les libellés qui ressemblent fortement à des libellés déjà vus.
- Code :
stats-annotations/(R) — entréeR/main.R - Exécution :
Rscript R/main.R --run-id=<id> --run-date=YYYY-MM-DD
Entrées
| Source | Chemin S3 |
|---|---|
| Jeu à coder | …/{run}/codif-regex/raw_test_without_regex.parquet |
| Pool suggester (mode prédiction) | …/{run}/suggester.parquet (fichier dédié écrit par preprocessing) |
| Pool suggester (mode normal) | …/{run}/codif-regex/raw_train_without_regex.parquet filtré source = 'suggester' |
En mode prédiction, main.R détecte automatiquement le fichier suggester.parquet dédié et l’utilise en priorité.
Le pool provient de la table COPAIN (liste_produits_fr_copain.csv, dans preprocessing/data/input-annotation/) : des libellés produits codés à la main lors de campagnes antérieures. C’est preprocessing qui l’écrit en suggester.parquet dans le dossier du run. La couverture de LCS dépend directement de la richesse de ce pool : un libellé jamais vu (ou très éloigné de tout libellé du pool) n’aura pas de bon candidat.
Traitement
Étape 0 — égalités strictes
D’abord, les libellés identiques (même s_pr_product) entre le jeu à coder et le pool sont appariés directement : distance = 0, proportion = 1, le code du suggester est repris.
Étape 1 — distance LCS
Pour les libellés restants, on calcule la plus longue sous-chaîne commune (longest common substring, comparaison insensible à la casse) avec chaque libellé du pool, et on en déduit une distance :
\[ \text{distance} = 1 - \frac{\text{longueur de la plus longue sous-chaîne commune}}{\max(n_1, n_2)} \]
où \(n_1\) et \(n_2\) sont les longueurs des deux libellés (stats-annotations/R/fonctions.R, noyau C++ distance_gcd_batch_cpp.cpp).
Exemple : entre bagu tradition u ble bretagne (29 caractères) et baguette tradition (18 caractères), la plus longue sous-chaîne commune est " tradition" (10 caractères, espace compris) ; la distance vaut 1 − 10/29 ≈ 0,66. Deux libellés identiques donnent une distance de 0.
Deux garde-fous s’appliquent dès le calcul :
- les paires de distance > 0,8 sont écartées directement dans le noyau C++ (jamais retournées comme candidates) — un candidat trop éloigné est pire que pas de candidat ;
- le calcul est fait par lots parallélisés (
furrr+ Rcpp), le produit cartésien jeu × pool étant volumineux.
Pour chaque libellé à coder, on retient ensuite le candidat de distance minimale (départage par proportion couverte décroissante) :
extract_res <- results |>
dplyr::arrange(id, distance, dplyr::desc(prop_in_s2)) |>
dplyr::group_by(id) |>
dplyr::slice_head(n = 1) |> # meilleur candidat du suggester
...Les colonnes produites par la comparaison alimentent ensuite l’arbitrage decide-coicop :
| Colonne LCS | Sens | Reçue par decide-coicop |
|---|---|---|
| code du meilleur candidat | code COICOP prédit | lcs_code |
common_substring |
sous-chaîne commune trouvée | lcs_substring |
prop_in_s2 |
part du libellé de référence couverte | lcs_prop |
distance |
distance LCS (0 = identique) | lcs_distance |
Exemple sur le fil rouge
Pour Bagu. Tradition U Blé Bretagne (s_pr_product = "bagu tradition u ble bretagne"), si le pool contient un libellé proche déjà codé (ex. une autre baguette « tradition » codée en 01.1.1.x), LCS retiendra ce code avec une distance faible et une longue sous-chaîne commune ("bagu tradition", "baguette tradition"…). Plus la sous-chaîne commune est longue et la distance faible, plus la prédiction est fiable — un signal que le LLM exploite à l’étape d’arbitrage.
Sorties
{run} = …/workflow_runs/{run_date}/{run_id}/codif-lcs
| Fichier | Contenu |
|---|---|
{run}/raw_test_LCS.parquet |
prédictions LCS (code + sous-chaîne, proportion, distance) |
{run}/analyse_codif_LCS.parquet |
table d’analyse qualité |
{run}/eval/<…>.parquet |
tables d’évaluation (via export_liste_df) |
➡️ Étape suivante : 5. Codif TTC