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 :

  1. 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.
  2. Pipeline observations (uniquement si --input-file est fourni) — prépare les libellés à coder (sans vérité terrain) issus du fichier d’entrée.
Note

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ée preprocessing/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 — normalise shop puis le joint à liste_magasins.csv pour récupérer shop_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 unique id (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 de l_pr_product puis 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_trainraw_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)
Note

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