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ée R/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é.

NoteD’où vient le pool suggester ?

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)} \]

\(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