1. Preprocessing
Rôle de l’étape
preprocessing construit, à partir des sources brutes, tous les jeux de données normalisés dont le pipeline a besoin en aval — quel que soit le mode d’exécution. Elle enchaîne deux pipelines indépendants :
- Pipeline annotations (toujours exécuté) — consolide les données annotées (BdF 2024 + historique 2017 + suggester), standardise les libellés, agrège le budget et produit un fichier complet + un split train / test.
- Pipeline observations (uniquement si
--input-fileest fourni) — prépare les libellés à coder (sans vérité terrain) issus du fichier d’entrée.
Avant, l’étape avait deux chemins mutuellement exclusifs : en mode prédiction elle écrasait raw_test.parquet avec les observations et laissait raw_train.parquet vide. Désormais raw_train/raw_test désignent toujours le split train/test des annotations, et les observations à coder vont dans un fichier dédié observations.parquet. Ainsi, en prod comme en évaluation, toutes les données utiles sont disponibles.
- Code :
preprocessing/— entréepreprocessing/main.py - Commande (évaluation) :
python main.py --run-id=<id> --run-date=YYYY-MM-DD - Commande (prédiction) :
python main.py --run-id=<id> --run-date=YYYY-MM-DD --input-file=<s3> --text-column=NAT_DEP --shop-column=MAG_DEP --budget-column=MONT_DEP
Entrées
| Source | Chemin |
|---|---|
| Annotations COPAIN | data/output-annotation/** (parquet partitionné) |
| Annotations 2024 (CSV) | data/codification-manuelle-anterieure/annotations_test_2024.csv |
| Annotations historiques 2017 | data/codification-manuelle-anterieure/annotations_BDF_2017.csv |
| Pool « suggester » | data/input-annotation/liste_produits_fr_copain.csv |
| Table enseigne → type | data/input-annotation/liste_magasins.csv |
| Produits non codables | data/codification-manuelle-anterieure/produit_non_annotable.csv |
| Fichier à coder (prédiction) | s3://…/workflow_inputs/BDF_data_tickets_appli_20260320_a_codif.csv |
Traitement
Briques partagées
Les deux pipelines réutilisent la même standardisation des libellés (preprocessing/main.py) :
merge_shop_types— normaliseshoppuis le joint àliste_magasins.csvpour récupérershop_type_code/shop_type_name(ex. supermarché, restauration rapide…).normalize_products— calcule les deux variantes de libellé, retire les produits non codables, attribue un identifiant uniqueid(UUID).
Deux niveaux de normalisation
À partir de raw_product, deux colonnes sont calculées :
l_pr_product(normalisation légère,normalize_text) : espaces multiples réduits, ligatures (œ→oe), accents supprimés (décomposition NFKD + filtrage ASCII), passage en minuscules. Utilisée par les modèles (RAG, TTC).s_pr_product(normalisation forte,preprocess_text) : repart del_pr_productpuis retire le bruit, tokenise, supprime les stopwords, etc. Utilisée par les regex.
df["l_pr_product"] = normalize_text(df["raw_product"])
df["s_pr_product"] = df["l_pr_product"].copy()
df = preprocess_text(df, "s_pr_product", stopwords)Pipeline annotations
build_annotations consolide COPAIN + 2024 + suggester, ajoute l’historique 2017, applique les briques partagées, exporte des contrôles qualité (qa/), puis :
- agrège le budget séparément pour les données 2024 et l’historique (
aggregate_budget, dédoublonnage + somme) ; - découpe les données 2024 en train / test (
train_test_split, 50/50,random_state=42) ; l’historique 2017 (+ suggester) rejoint le train ; - le fichier complet est l’union train ∪ test.
Pipeline observations (prédiction)
build_observations ne s’exécute que si --input-file est fourni. Les colonnes d’entrée sont d’abord renommées vers les noms canoniques :
column_mapping = {
args.text_column: "raw_product", # NAT_DEP → raw_product
args.shop_column: "shop", # MAG_DEP → shop
args.budget_column: "budget", # MONT_DEP → budget
args.annee_column: "annee",
args.source_column: "source",
}Comme il n’y a pas de vérité terrain, les colonnes manquantes reçoivent des valeurs par défaut pour que les étapes suivantes ne plantent pas :
prediction_defaults = {
"annee": pd.NA, "code": pd.NA, "coicop": pd.NA,
"shop": pd.NA, "shop_type_name": pd.NA, "budget": pd.NA,
"n_obs": 1, "source": "prediction",
}_source_input_file conserve le chemin du fichier d’origine (réutilisé par final-output pour nommer le résultat).
Exemple sur le fil rouge
raw_product (=NAT_DEP) |
l_pr_product |
s_pr_product |
shop |
|---|---|---|---|
Bagu. Tradition U Blé Bretagne |
bagu. tradition u ble bretagne |
bagu tradition u ble bretagne |
u |
Ticket CB |
ticket cb |
ticket cb |
station netto |
Max garden flowers balle surprise |
max garden flowers balle surprise |
max garden flowers balle surprise |
action |
Illisible |
illisible |
illisible |
— |
Si Illisible figure dans la liste des produits non codables (produit_non_annotable.csv), la ligne est retirée à cet endroit (df.loc[~df["raw_product"].isin(uncodable_products)]). Sinon, elle poursuit et sera captée plus loin par la règle regex ^illisible$ → 98.4.
Sorties
{run} = s3://projet-budget-famille/data/workflow_runs/{run_date}/{run_id}/preprocessing
| Fichier | Éval | Prédiction | Contenu |
|---|---|---|---|
{run}/annotations_full.parquet |
✅ | ✅ | annotations consolidées complètes (= raw_train ∪ raw_test), budget agrégé |
{run}/raw_train.parquet |
✅ | ✅ | split train des annotations (2024-train + tout 2017 + suggester) |
{run}/raw_test.parquet |
✅ | ✅ | split test des annotations (2024-test) |
{run}/suggester.parquet |
✅ | ✅ | suggester préprocessé (source="suggester"), lu directement par codif-lcs |
{run}/observations.parquet |
— | ✅ | lignes à coder (id, raw_product, l_pr_product, s_pr_product, shop, shop_type_name, budget, code(NA)…, _source_input_file, source="prediction") |
{run}/qa/*.parquet |
✅ | ✅ | contrôles qualité (doublons suggester, produits multi-codes) |
En mode production, les étapes aval travaillent sur observations.parquet (codif-regex via test_set_prod, decide-coicop --extra-columns-file, final-output) ; en mode évaluation, elles travaillent sur raw_test.parquet. Voir Généralités — modes prod / éval.
➡️ Étape suivante : 2. Codif regex