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.
Le scénario
Section titled “Le scénario”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é.
Générer les données
Section titled “Générer les données”import numpy as npimport 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: 1289taux de retard global: 0.231Environ 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 = 5historique_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_recentfactures = 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: 122960 lignes sont écartées : ce sont les toutes premières factures de chaque client, pour lesquelles aucun historique n’existe encore.
Une feature différentielle
Section titled “Une feature différentielle”taux_secteur_observe = factures.groupby("secteur")["taux_retard_recent"].transform("mean")factures["ecart_taux_secteur"] = factures["taux_retard_recent"] - taux_secteur_observeCette 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.
Prochaine étape
Section titled “Prochaine étape”👉 Découper les données par client, pas par ligne
Junior TSAFACK – 12/09/2026