Skip to content

Radar de risque impayé — mise en situation

Le module 3 a construit un moteur de classification from scratch. Ce module utilise scikit-learn pour un cas complet et réaliste : un pipeline de bout en bout, du feature engineering à l’évaluation critique des résultats.

Une entreprise B2B facture ses clients à crédit. Le service recouvrement veut savoir, avant l’échéance, quelles factures risquent d’être payées en retard, pour prioriser ses relances. On dispose de l’historique de facturation : client, secteur d’activité, montant, délai de paiement observé.

import numpy as np
import pandas as pd
rng = np.random.default_rng(7)
secteurs = ["Industrie", "Commerce", "Services", "BTP"]
taux_retard_secteur = {"Industrie": 0.15, "Commerce": 0.20, "Services": 0.10, "BTP": 0.40}
# ... génération de ~1300 factures pour 60 clients, avec un taux de retard qui
# dépend du secteur (le BTP paie structurellement plus en retard)
Nombre total de factures: 1289
taux de retard global: 0.231

Environ 23 % des factures sont payées en retard — un problème modérément déséquilibré, ce qui aura son importance plus loin dans ce module.

Feature engineering temporel, sans fuite d’information

Section titled “Feature engineering temporel, sans fuite d’information”

La feature la plus utile est la forme récente d’un client : son taux de retard sur ses dernières factures. La calculer correctement demande une règle stricte : ne jamais utiliser d’information postérieure à la facture qu’on décrit.

from collections import defaultdict, deque
WINDOW = 5
historique_client = defaultdict(lambda: deque(maxlen=WINDOW))
taux_retard_recent = []
for row in factures.itertuples():
hist = historique_client[row.client]
if len(hist) == 0:
taux_retard_recent.append(np.nan)
else:
taux_retard_recent.append(np.mean([h[0] for h in hist]))
# Mise a jour de l'historique APRES calcul de la feature
hist.append((row.paye_en_retard, row.montant))
factures["taux_retard_recent"] = taux_retard_recent
factures = factures.dropna(subset=["taux_retard_recent"])

L’ordre des deux opérations est crucial : on lit d’abord l’historique accumulé jusque-là (hist), et seulement après on y ajoute la facture courante. Inverser ces deux lignes ferait fuiter dans la feature une information que le modèle n’aurait jamais en pratique au moment de la prédiction — un modèle qui semblerait excellent en test, mais inutilisable en production.

Lignes apres feature engineering: 1229

60 lignes sont écartées : ce sont les toutes premières factures de chaque client, pour lesquelles aucun historique n’existe encore.

taux_secteur_observe = factures.groupby("secteur")["taux_retard_recent"].transform("mean")
factures["ecart_taux_secteur"] = factures["taux_retard_recent"] - taux_secteur_observe

Cette feature positionne chaque facture par rapport à la moyenne de son secteur — un client « pire que la moyenne de son secteur » n’a pas le même profil de risque qu’un client « dans la moyenne », même à taux de retard brut identique.

👉 Découper les données par client, pas par ligne


Junior TSAFACK – 12/09/2026