Architecture & Documentation — Pipeline de codification regex
Enquête Budget de Famille — Documentation technique
Présentation du projet
Ce pipeline assigne automatiquement un code COICOP à chaque libellé de dépense de l’Enquête Budget de Famille (BdF) à l’aide d’expressions régulières déterministes. Il constitue la première étape de la chaîne de codification : les libellés reconnus par les règles regex sont directement codifiés ; les autres sont transmis aux modèles de machine learning en aval.
Objectifs
| Objectif | Description |
|---|---|
| Précision ciblée | Identifier avec certitude les libellés stéréotypés (libellés génériques, cas techniques) avant tout traitement ML |
| Réductibilité | Alléger la charge des modèles aval en traitant les cas simples en amont |
| Traçabilité | Maintenir un jeu de règles explicites, versionnées et auditables |
| Évolutivité | Permettre l’ajout ou la suppression de règles sans retraîner de modèle |
Nomenclature COICOP
La COICOP (Classification of Individual Consumption According to Purpose) est la nomenclature internationale de référence pour classifier les dépenses des ménages. Dans le cadre BdF, elle est complétée par des codes techniques (préfixe 98) pour les cas particuliers (libellés vides, saisies illisibles, remises…) et des codes hors champ (préfixe 99) pour les opérations non consommatoires.
Architecture technique
Structure du projet
regex_codif/
├── config/
│ ├── config.yaml # Chemins S3 et paramètres d'export
│ └── rules.yaml # Règles regex de codification COICOP
├── src/
│ ├── __init__.py
│ ├── main.py # Point d'entrée du pipeline
│ └── utils/
│ ├── __init__.py
│ ├── load_config.py # Chargement du fichier de configuration
│ ├── load_rules.py # Chargement et application des règles regex
│ ├── logging.py # Configuration du logger
│ └── init_duckdb.py # Initialisation DuckDB avec secrets S3
├── index.qmd # Documentation et architecture (ce document)
├── resultats.qmd # Analyse des résultats
├── _quarto.yml # Configuration du site Quarto
└── rapport_style.css # Feuille de style CSS
Documentation des modules Python
src/main.py — Point d’entrée du pipeline
Orchestre l’ensemble du pipeline dans l’ordre suivant :
- Configuration — charge
config.yamlviaload_config()et initialise le logger - DuckDB — ouvre une connexion en mémoire avec les secrets S3 via
init_duckdb() - Règles — charge
rules.yamlviaload_regex_rules() - Données — lit le jeu de test Parquet depuis S3 via une requête DuckDB
- Classification — applique les règles via
apply_regex_rules()sur la colonnes_pr_product - Split — sépare les lignes classifiées (
predict_codenon nul) des non classifiées - Métriques — calcule l’accuracy globale et par code, journalise les erreurs
- Export — exporte les trois fichiers de sortie vers S3 (si
S3.export: true)
Le flag S3.export dans config.yaml permet de désactiver l’export S3 pour les exécutions de test locales sans modifier le code.
src/utils/load_config.py — Chargement de la configuration
def load_config(config_path="config/config.yaml") -> dict:
"""Charge le fichier de configuration YAML et retourne un dictionnaire."""Lecture simple d’un fichier YAML via yaml.safe_load(). Retourne un dictionnaire structuré avec les clés paths et S3.
src/utils/load_rules.py — Chargement et application des règles
Contient deux fonctions :
def load_regex_rules(yaml_path: str) -> list[dict]:
"""Charge les règles depuis rules.yaml.
Retourne une liste de dicts {"pattern": str, "code": str}."""def apply_regex_rules(series: pd.Series, rules: list[dict]) -> pd.Series:
"""Applique les règles sur une Series de libellés.
Mécanisme :
- Pré-compilation de tous les patterns avec re.IGNORECASE
- Pour chaque libellé : itération ordonnée, premier match gagne
- Retourne None si aucune règle ne correspond
"""L’ordre des règles est critique. Les règles sont appliquées séquentiellement ; la première règle qui correspond détermine le code prédit. Les règles les plus spécifiques (avec ancres ^…$) doivent impérativement précéder les règles génériques (sous-chaîne libre).
src/utils/init_duckdb.py — Initialisation DuckDB
def init_duckdb(config: dict) -> duckdb.DuckDBPyConnection:
"""Crée une connexion DuckDB en mémoire et configure les secrets S3.
Les credentials sont lus depuis les variables d'environnement :
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_S3_ENDPOINT
"""La connexion DuckDB est utilisée uniquement pour lire le fichier Parquet depuis S3. Le traitement des données est ensuite effectué avec Pandas.
src/utils/logging.py — Configuration du logger
def setup_logging() -> logging.Logger:
"""Configure et retourne un logger standard Python.
Niveau INFO, format : [timestamp] [level] message."""Configuration
config/config.yaml
paths:
# Jeu de test annoté manuellement (vérité terrain)
test_set: "s3://bucket/path/raw_test.parquet"
# Prédicitions des libellés classifiés par regex
output_pred: "s3://bucket/path/raw_test_REGEX_pred.parquet"
# Erreurs de prédiction (code prédit ≠ code vrai)
error_pred: "s3://bucket/path/error_regex_pred.parquet"
# Libellés non classifiés (à transmettre aux modèles ML)
output_test_set: "s3://bucket/path/raw_test_without_regex.parquet"
S3:
BUCKET: "nom-du-bucket"
export: true # false pour désactiver l'export S3 (mode test)config/rules.yaml
Chaque règle est un objet YAML avec deux champs :
rules:
- pattern: "expression régulière" # Motif regex (syntaxe Python re)
code: "XX.X.X" # Code COICOP à assigner si match
# Exemple : libellé exact "restaurant"
- pattern: "^\\s*restaurant\\s*$"
code: "11.1.1"
# Exemple : sous-chaîne "drive" entourée de mots
- pattern: "\\bdrive\\b"
code: "98.1"Les patterns sont appliqués avec re.IGNORECASE (insensible à la casse) et re.search() (recherche de sous-chaîne). Les ancres ^ et $ permettent de contraindre la correspondance à l’intégralité du libellé.