Segmentation comportementale des clients à partir de leurs données transactionnelles, basée sur une approche RFM + K-Means.
Le module construit les indicateurs Recency, Frequency et Monetary, applique des transformations reproductibles, entraîne un modèle de clustering K-Means et attribue ensuite chaque client à un segment comportemental.
L'architecture sépare l'entraînement du modèle et le scoring afin de garantir que les mêmes paramètres sont utilisés lors des différentes exécutions.
- Feature engineering RFM (
rfm.py) — construit les indicateurs Recency, Frequency et Monetary à partir de l'historique des ventes. - Entraînement (
train_segmentation.py) — calcule les bornes de quantiles, ajuste les transformations, entraîne K-Means et sauvegarde les artefacts du modèle. - Scoring (
predict_segmentation.py) — recharge les artefacts entraînés et les applique aux clients sans recalculer les paramètres.
L'architecture suit le même principe que le module Churn : une étape qui apprend les paramètres et les sauvegarde, une étape séparée qui les réutilise tels quels. Le détail des deux flux est donné plus bas dans "Principe entraînement vs scoring".
| Fichier | Rôle |
|---|---|
rfm.py |
Fonctions réutilisables pour construire les données RFM |
train_segmentation.py |
Pipeline d'entraînement et de persistance des artefacts |
predict_segmentation.py |
Pipeline de scoring utilisant les artefacts sauvegardés |
Ce module utilise uv pour la gestion de l'environnement Python.
Depuis la racine du projet :
uv syncCette commande crée l'environnement virtuel et installe les dépendances définies dans pyproject.toml / uv.lock.
La segmentation repose sur les trois indicateurs RFM :
| Indicateur | Signification | Interprétation |
|---|---|---|
| Recency | Nombre de jours depuis le dernier achat | Plus faible = client plus récemment actif |
| Frequency | Nombre d'achats effectués | Plus élevé = client plus régulier |
| Monetary | Montant total dépensé | Plus élevé = client à plus forte valeur |
Les valeurs RFM sont transformées en scores à l'aide de bornes de quantiles calculées pendant l'entraînement. Les bornes calculées sont sauvegardées afin d'être réutilisées exactement de la même manière pendant le scoring.
Les bornes de quantiles sont calculées directement sur les valeurs RFM originales. Elles ne sont pas recalculées pendant le scoring.
Cette distinction est importante : si les bornes étaient recalculées sur une population différente, la définition des scores RFM pourrait changer d'une exécution à l'autre.
Les doublons sont gérés avec duplicates="drop" afin d'éviter qu'un grand nombre de valeurs identiques, notamment pour Frequency, empêche la construction des intervalles.
Une transformation logarithmique est appliquée à Recency. Cette transformation vise à réduire l'influence des valeurs extrêmes et à obtenir une distribution plus adaptée à la phase de clustering.
La liste des colonnes concernées est sauvegardée dans log_columns.joblib afin que le scoring applique exactement les mêmes transformations que l'entraînement.
Les variables utilisées par K-Means sont standardisées avec un StandardScaler.
Le scaler est ajusté uniquement pendant l'entraînement puis sauvegardé dans :
models/segmentation/scaler.joblib
Lors du scoring, le même scaler est rechargé et utilisé avec transform().
Le modèle retenu utilise :
K = 3
Le choix du nombre de clusters est basé sur l'analyse de plusieurs métriques de clustering :
- Silhouette Score
- Calinski-Harabasz Index
- Davies-Bouldin Index
Le nombre de clusters retenu est ensuite utilisé pour entraîner le modèle K-Means final.
Le modèle entraîné est sauvegardé dans :
models/segmentation/kmeans_model.joblib
K-Means produit initialement des clusters numériques. Les clusters sont ensuite interprétés à partir de leurs caractéristiques RFM afin de leur attribuer une signification métier.
Les segments actuellement utilisés sont :
| Segment | Interprétation |
|---|---|
| VIP / Champions | Clients présentant une forte valeur et/ou une forte activité |
| Clients fidèles | Clients réguliers présentant une bonne activité |
| Occasionnels | Clients ayant une activité intermédiaire |
| À risque | Clients présentant des signes d'inactivité ou de baisse d'activité |
| Perdus | Clients présentant une forte inactivité |
| Nouveaux / à activer | Clients avec peu d'historique nécessitant une activation |
⚠️ Note : K-Means ne connaît pas directement ces notions métier. Il identifie uniquement des groupes de clients ayant des caractéristiques similaires. Les noms des segments constituent donc une interprétation métier des clusters et doivent être validés en analysant les statistiques RFM de chaque groupe.
Sur le jeu de données synthétique actuel de 1 000 clients, le scoring produit la répartition suivante :
| Segment | Nombre de clients | Pourcentage |
|---|---|---|
| Perdus | 365 | 36,5 % |
| Occasionnels | 257 | 25,7 % |
| VIP / Champions | 246 | 24,6 % |
| Clients fidèles | 118 | 11,8 % |
| À risque | 13 | 1,3 % |
| Nouveaux / à activer | 1 | 0,1 % |
| Total | 1000 | 100 % |
Cette distribution décrit uniquement le jeu de données synthétique actuellement utilisé. Elle ne doit pas être interprétée comme une représentation générale d'une population réelle de clients.
Les données utilisées par le module sont générées dans :
data/generated/
| Fichier | Colonnes requises |
|---|---|
data/generated/sales_data.csv |
Customer_ID, Date, Quantity, Sale_Price |
data/generated/customers_data.csv |
Customer_ID et informations client disponibles |
Les indicateurs RFM sont calculés à partir de l'historique des ventes. La date de référence utilisée pour calculer Recency doit rester cohérente entre l'entraînement et le scoring.
Le scoring produit les résultats dans :
outputs/segmentation/
Les sorties actuelles comprennent notamment :
| Fichier | Description |
|---|---|
segmentation_clients.csv |
Segmentation finale attribuée à chaque client |
profil_segments.csv |
Profil RFM des différents segments |
churn_par_segment.png |
Analyse du churn par segment |
heatmap_rfm.png |
Visualisation des caractéristiques RFM |
segments_final.png |
Visualisation finale des segments |
Une ligne de segmentation_clients.csv correspond à un client.
Les artefacts entraînés sont stockés dans :
models/segmentation/
| Fichier | Contenu |
|---|---|
scaler.joblib |
StandardScaler ajusté sur les données d'entraînement |
kmeans_model.joblib |
Modèle K-Means entraîné |
log_columns.joblib |
Colonnes auxquelles appliquer la transformation logarithmique |
quantile_edges.joblib |
Bornes de quantiles utilisées pour transformer les valeurs RFM |
Contrairement à une approche où les modèles sont régénérés après chaque clone du repository, les artefacts entraînés sont versionnés dans Git. L'objectif est de permettre aux autres membres du projet d'utiliser directement le modèle existant sans devoir relancer l'entraînement.
Après un clone du repository, les fichiers nécessaires au scoring sont donc déjà disponibles dans :
models/segmentation/
Les quatre artefacts doivent cependant rester synchronisés. Il ne faut pas remplacer ou régénérer un seul fichier indépendamment des autres.
Toute modification concernant :
- le calcul RFM ;
- les transformations ;
- les bornes de quantiles ;
- le scaler ;
- le nombre de clusters ;
- le modèle K-Means ;
doit être suivie d'un nouvel entraînement complet afin de régénérer l'ensemble des artefacts.
Depuis la racine du projet :
uv run python src/segmentation/train_segmentation.pyCette étape :
- charge les données ;
- construit les données RFM ;
- calcule les bornes de quantiles ;
- définit les transformations ;
- ajuste le
StandardScaler; - entraîne K-Means ;
- sauvegarde les quatre artefacts dans
models/segmentation/.
uv run python src/segmentation/predict_segmentation.pyCette étape :
- charge les données à scorer ;
- reconstruit les données RFM ;
- recharge les bornes de quantiles ;
- applique les transformations sauvegardées ;
- recharge le scaler ;
- recharge le modèle K-Means ;
- attribue un segment à chaque client ;
- sauvegarde les résultats dans
outputs/segmentation/.
⚠️ predict_segmentation.pynécessite que les quatre artefacts présents dansmodels/segmentation/existent et correspondent au pipeline utilisé.
Le module sépare volontairement les opérations qui apprennent des paramètres de celles qui appliquent des paramètres existants.
Données
│
▼
Calcul RFM
│
▼
Bornes de quantiles ─────────┐
│ │
▼ │
Transformation log │
│ │
▼ │
StandardScaler ──────────────┤
│ │
▼ │
K-Means ─────────────────────┤
│
▼
Artefacts persistés
Nouvelles données
│
▼
Calcul RFM
│
▼
Bornes sauvegardées
│
▼
Transformation log
│
▼
Scaler sauvegardé
│
▼
K-Means sauvegardé
│
▼
Segmentation
Le scoring ne doit donc jamais réapprendre les paramètres du modèle. Cela permet de conserver une définition stable des segments lorsque la population à scorer change.
Une première version du module recalculait entièrement :
- les bornes de quantiles ;
- le scaler ;
- le clustering K-Means ;
à chaque exécution.
Cette approche pouvait provoquer une incohérence. Par exemple, si la population de clients changeait, les bornes de qcut pouvaient changer et les centroïdes K-Means pouvaient être différents. Un même client pouvait alors recevoir un segment différent alors que son propre comportement n'avait pas changé.
Le module a donc été refactorisé selon le même principe que le module Churn : une étape d'entraînement qui apprend les paramètres et les sauvegarde, une étape de scoring qui les réutilise sans les recalculer (voir "Principe entraînement vs scoring" ci-dessus pour le détail des deux flux).
Un second problème a été identifié pendant l'implémentation : les bornes de quantiles étaient initialement calculées sur le rang des valeurs (.rank(), utile pour gérer les ex-æquo) plutôt que sur les valeurs RFM brutes. Le scoring appliquait ensuite ces bornes directement aux valeurs brutes — un décalage d'échelle qui faisait tomber la plupart des clients hors des tranches attendues. Corrigé en calculant les bornes directement sur les valeurs RFM, avec duplicates="drop" pour gérer les cas où trop de valeurs identiques (notamment Frequency) empêchent 5 tranches distinctes. Détail complet : voir docs/segmentation.md.
Une vérification manuelle a été effectuée (voir notebooks/segmentation_validation.ipynb et l'entrée correspondante dans docs/segmentation.md), en deux temps :
- Inspection du seul client classé "Nouveaux / à activer" (Customer_ID 2634) — profil confirmé cohérent avec la règle de
label_from_rfm(achat récent, mais peu fréquent et peu dépensier). Pas un bug, juste un segment naturellement rare dans ce dataset. - Taux de churn par segment, pour vérifier que les noms attribués aux clusters correspondent à un vrai comportement :
| Segment | Taux de churn | Clients |
|---|---|---|
| Perdus | 100% | 365 |
| À risque | 76.9% | 13 |
| Occasionnels | 12.8% | 257 |
| VIP / Champions | 0% | 246 |
| Clients fidèles | 0% | 118 |
| Nouveaux / à activer | 0% | 1 |
La cohérence est quasi parfaite avec les noms des segments — mais cette cohérence doit être nuancée, voir "Limites connues" ci-dessous : elle ne constitue pas une preuve indépendante de la qualité du clustering.
- Données synthétiques : Les données utilisées actuellement sont synthétiques. Les résultats obtenus ne permettent donc pas de conclure que la segmentation aura la même pertinence sur de véritables données clients.
-
RFM et Churn partagent la même origine, pas deux signaux indépendants : dans
generate_data.py,_Behaviordétermine à la fois la cibleChurnet les leviers qui construisent le RFM (nombre d'achats, quantité, accès aux produits premium — voirdocs/churn.md). La cohérence quasi parfaite entre segments et taux de churn (100%/0%) reflète donc en grande partie cette origine commune, pas une validation indépendante que la segmentation "prédit" le churn. À présenter comme telle dans le rapport final (M9), pas comme une performance généralisable. -
Choix de K : K-Means nécessite de choisir le nombre de clusters. Le modèle actuel utilise
$K = 3$ , déterminé à partir de plusieurs métriques de clustering. Le détail des scores obtenus pour les différentes valeurs de K doit être conservé dans la documentation du projet afin de justifier ce choix dans le rapport final. - Sensibilité aux données : Les résultats d'un clustering dépendent des données utilisées lors de l'entraînement. Si les données deviennent très différentes du dataset actuel, une réévaluation du modèle et de ses segments peut être nécessaire.
- Absence de prédiction temporelle : La segmentation décrit le comportement observé sur la période disponible. Elle ne prédit pas directement l'évolution future du client. Pour estimer le risque de départ futur, le module Churn est utilisé en complément.
La segmentation et le modèle Churn répondent à deux questions différentes.
Quel type de comportement présente actuellement ce client ?
Client ──► RFM ──► K-Means ──► Segment
Quelle est la probabilité que ce client soit associé au churn ?
Client ──► Features ──► Logistic Regression ──► churn_probability
Les deux informations sont complémentaires. Par exemple :
VIP / Champions
+
forte probabilité de churn
│
▼
Client à forte valeur présentant un risque élevé
│
▼
Priorité élevée pour une action de rétention
La segmentation fournit donc une vue comportementale du client, tandis que le modèle Churn fournit une estimation du risque de départ. Ces deux résultats peuvent ensuite être exploités par le module marketing pour définir et prioriser les actions commerciales.