Les prévisions de désabonnement sont devenues essentielles pour toutes les entreprises proposant des abonnements. Elles permettent d’identifier les clients les plus susceptibles de résilier leur abonnement. Ces prévisions aident les fournisseurs à fidéliser leur clientèle et à comprendre les raisons des résiliations. Les entreprises utilisent cette technique pour optimiser leurs actions marketing et commerciales.
Anticiper le taux d’attrition client, c’est-à-dire le moment où les clients cessent de faire affaire avec une entreprise, est crucial pour les entreprises de tous les secteurs. En identifiant les clients susceptibles de se désabonner, les entreprises peuvent prendre des mesures proactives pour les fidéliser, réduisant ainsi les pertes de revenus et préservant une clientèle fidèle.
Le jeu de données utilisé pour notre projet d’apprentissage automatique de classification comprend toutes les données clients pertinentes du secteur des télécommunications. Il provient de Kaggle et plus précisément de la collection d’exemples de données d’IBM. (https://www.kaggle.com/blastchar/telco-customer-churn) Chaque ligne de ces données représente un client, chaque colonne contient les attributs du client. Les données contiennent 7043 lignes (clients) et 21 colonnes (caractéristiques) :
Taux de désabonnement (Churn) = Clients ayant quitté l’entreprise au cours du dernier mois: notre objectif YES ou NO
CustomerID = ID du client
gender = Si le client est un homme ou une femme
SeniorCitizen = Si le client soit un senior ou non
Partner = Si le client a un partenaire ou non
Dependents = Si le client a des personnes à charge ou non
Tenure = Nombre de mois pendant lesquels le client est resté fidèle à l’entreprise
PhoneService = Si le client dispose d’un service téléphonique ou non
MultipleLines = Si le client possède plusieurs lignes ou non
InternetService = Fournisseur d’accès Internet du client, comme le DSL ou la fibre optique
OnlineSecurity = Si le client dispose ou non d’une sécurité en ligne
OnlineBackup = Si le client dispose ou non d’une sauvegarde en ligne
DeviceProtection = Si le client dispose ou non d’une protection pour son appareil
TechSupport = Si le client bénéficie ou non d’une assistance technique
StreamingTV = Si le client possède ou non un abonnement à la télévision en streaming
StreamingMovies = Si le client a accès ou non à des films en streaming
Contract = La durée du contrat du client, par exemple un abonnement mensuel, annuel ou bisannuel
PaperlessBilling = Si le client a opté pour la facturation sans papier ou non
PaymentMethod = Le mode de paiement du client, par exemple : chèque électronique, chèque postal, virement bancaire ou carte de crédit
MonthlyCharges = Le montant facturé au client chaque mois
TotalCharges = Le montant total facturé au client
Dans cette étape, nous allons charger les bibliothèques et les jeux de données nécessaires.
# Chargement des bibliothèques nécessaires
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.3
## Warning: package 'ggplot2' was built under R version 4.5.3
## Warning: package 'tidyr' was built under R version 4.5.3
## Warning: package 'readr' was built under R version 4.5.3
## Warning: package 'purrr' was built under R version 4.5.3
## Warning: package 'forcats' was built under R version 4.5.3
## Warning: package 'lubridate' was built under R version 4.5.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(caret)
## Warning: package 'caret' was built under R version 4.5.3
## Loading required package: lattice
##
## Attaching package: 'caret'
##
## The following object is masked from 'package:purrr':
##
## lift
library(randomForest)
## Warning: package 'randomForest' was built under R version 4.5.3
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
##
## The following object is masked from 'package:dplyr':
##
## combine
##
## The following object is masked from 'package:ggplot2':
##
## margin
library(pROC)
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
##
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
# Chargement du jeu de données
data <- read.csv("Churn_data.csv")
# la structure du jeu de données
str(data)
## 'data.frame': 7043 obs. of 21 variables:
## $ customerID : chr "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
## $ gender : chr "Female" "Male" "Male" "Male" ...
## $ SeniorCitizen : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Partner : chr "Yes" "No" "No" "No" ...
## $ Dependents : chr "No" "No" "No" "No" ...
## $ tenure : int 1 34 2 45 2 8 22 10 28 62 ...
## $ PhoneService : chr "No" "Yes" "Yes" "No" ...
## $ MultipleLines : chr "No phone service" "No" "No" "No phone service" ...
## $ InternetService : chr "DSL" "DSL" "DSL" "DSL" ...
## $ OnlineSecurity : chr "No" "Yes" "Yes" "Yes" ...
## $ OnlineBackup : chr "Yes" "No" "Yes" "No" ...
## $ DeviceProtection: chr "No" "Yes" "No" "Yes" ...
## $ TechSupport : chr "No" "No" "No" "Yes" ...
## $ StreamingTV : chr "No" "No" "No" "No" ...
## $ StreamingMovies : chr "No" "No" "No" "No" ...
## $ Contract : chr "Month-to-month" "One year" "Month-to-month" "One year" ...
## $ PaperlessBilling: chr "Yes" "No" "Yes" "No" ...
## $ PaymentMethod : chr "Electronic check" "Mailed check" "Mailed check" "Bank transfer (automatic)" ...
## $ MonthlyCharges : num 29.9 57 53.9 42.3 70.7 ...
## $ TotalCharges : num 29.9 1889.5 108.2 1840.8 151.7 ...
## $ Churn : chr "No" "No" "Yes" "No" ...
L’AED nous aide à comprendre la relation entre les variables.
# Vérification des valeurs manquantes (NA)
colSums(is.na(data))
## customerID gender SeniorCitizen Partner
## 0 0 0 0
## Dependents tenure PhoneService MultipleLines
## 0 0 0 0
## InternetService OnlineSecurity OnlineBackup DeviceProtection
## 0 0 0 0
## TechSupport StreamingTV StreamingMovies Contract
## 0 0 0 0
## PaperlessBilling PaymentMethod MonthlyCharges TotalCharges
## 0 0 0 11
## Churn
## 0
Nous avons donc un total de 11 valeurs manquantes dans la colonne MonthlyCharges et nous allons supprimer ces valeurs.
Pour visualiser les valeurs manquantes, nous avons utilisé la fonction summary().
La variable TotalCharges contient 11 valeurs manquantes (NA). Nous avons décidé de la supprimer car elle dépend du temps passé par les clients chez Telco.
summary(data) # Résumé des variables contenues dans le jeu de données
## customerID gender SeniorCitizen Partner
## Length:7043 Length:7043 Min. :0.0000 Length:7043
## Class :character Class :character 1st Qu.:0.0000 Class :character
## Mode :character Mode :character Median :0.0000 Mode :character
## Mean :0.1621
## 3rd Qu.:0.0000
## Max. :1.0000
##
## Dependents tenure PhoneService MultipleLines
## Length:7043 Min. : 0.00 Length:7043 Length:7043
## Class :character 1st Qu.: 9.00 Class :character Class :character
## Mode :character Median :29.00 Mode :character Mode :character
## Mean :32.37
## 3rd Qu.:55.00
## Max. :72.00
##
## InternetService OnlineSecurity OnlineBackup DeviceProtection
## Length:7043 Length:7043 Length:7043 Length:7043
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## TechSupport StreamingTV StreamingMovies Contract
## Length:7043 Length:7043 Length:7043 Length:7043
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## PaperlessBilling PaymentMethod MonthlyCharges TotalCharges
## Length:7043 Length:7043 Min. : 18.25 Min. : 18.8
## Class :character Class :character 1st Qu.: 35.50 1st Qu.: 401.4
## Mode :character Mode :character Median : 70.35 Median :1397.5
## Mean : 64.76 Mean :2283.3
## 3rd Qu.: 89.85 3rd Qu.:3794.7
## Max. :118.75 Max. :8684.8
## NA's :11
## Churn
## Length:7043
## Class :character
## Mode :character
##
##
##
##
# Suppression des valeurs manquantes
data<-na.omit(data)
Nous pouvons également comprendre la relation entre le taux de désabonnement, le type de contrat et le mode de paiement, et leur rôle. Cette relation et cette tendance sont mieux comprises lorsqu’on les représente graphiquement à l’aide de ggplot2.
# Création d'une nouvelle caractéristique : frais mensuels moyens (average monthly charges)
trainData <- data %>%
mutate(avgMonthlyCharges = TotalCharges / (tenure + 1))
testData <- data%>%
mutate(avgMonthlyCharges = TotalCharges / (tenure + 1))
# Plot the distribution of tenure by churn
ggplot(trainData, aes(x = tenure, fill = Churn)) +
geom_histogram(binwidth = 1, position = "dodge") +
labs(title = "Distribution of Tenure by Churn",
x = "Tenure (Months)", y = "Count")
Ce graphique illustre la répartition de l’ancienneté (en mois) des clients ayant résilié leur abonnement (Résiliation = Oui) et de ceux qui ne l’ont pas fait (Résiliation = Non). Il permet de mieux comprendre cette répartition, ce qui peut s’avérer utile pour des analyses ultérieures et la modélisation.
Nous créons un histogramme pour visualiser la répartition des frais mensuels pour les clients qui ont résilié leur abonnement et ceux qui ne l’ont pas fait.
# Plot the distribution of monthly charges by churn
ggplot(trainData, aes(x = MonthlyCharges, fill = Churn)) +
geom_histogram(binwidth = 5, position = "dodge") +
labs(title = "Distribution of Monthly Charges by Churn",
x = "Monthly Charges", y = "Count")
En visualisant la répartition des frais mensuels en fonction du taux de désabonnement, les entreprises peuvent obtenir des informations précieuses sur le comportement des clients et prendre des décisions basées sur les données pour atténuer le taux de désabonnement et améliorer les stratégies de fidélisation de la clientèle.
Nous pouvons également comprendre la relation entre le taux de désabonnement, le type de contrat et le mode de paiement, et leur rôle. Cette relation et cette tendance sont mieux comprises lorsqu’on les représente graphiquement à l’aide de ggplot2.
# Plot churn distribution by payment method
ggplot(trainData, aes(x = PaymentMethod, fill = Churn)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent_format()) +
labs(title = "Churn Distribution by Payment Method", x = "Payment Method",
y = "Percentage", fill = "Churn") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Ce graphique à barres nous aide à comprendre que les personnes payant par chèque électronique ont le taux de désabonnement le plus élevé.
Dans cet exemple, nous utiliserons l’algorithme de forêt aléatoire pour construire et entraîner notre modèle. Pour cela, il est nécessaire d’installer le package randomForest dans R. Cet algorithme utilise plusieurs arbres de décision afin d’améliorer les performances globales. Chaque arbre est entraîné sur un échantillon de données d’entraînement. Grâce à cette méthode, la précision est élevée.
# Création d'une nouvelle caractéristique
data$TotalCharges <- as.numeric(as.character(data$TotalCharges))
data <- na.omit(data) # Suppression des lignes avec NA
data$Churn <- as.factor(data$Churn)
data$Churn <- factor(data$Churn, levels = c("No", "Yes"))
# Séparation des données en ensembles d'entrainement et test
set.seed(123)
trainIndex <- createDataPartition(data$Churn, p = 0.8, list = FALSE)
trainData <- data[trainIndex, ]
testData <- data[-trainIndex, ]
# Caractéristique avec dplyr
trainData <- trainData %>%
mutate(avgMonthlyCharges = TotalCharges / (tenure + 1))
testData <- testData %>%
mutate(avgMonthlyCharges = TotalCharges / (tenure + 1))
# Construction du modèle Random Forest
library(randomForest)
set.seed(123)
rfModel <- randomForest(Churn ~ ., data = trainData, ntree = 100, mtry = 3,
importance = TRUE)
# Résumé du modèle
print(rfModel)
##
## Call:
## randomForest(formula = Churn ~ ., data = trainData, ntree = 100, mtry = 3, importance = TRUE)
## Type of random forest: classification
## Number of trees: 100
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 20.42%
## Confusion matrix:
## No Yes class.error
## No 3725 406 0.09828129
## Yes 743 753 0.49665775
La matrice de confusion fournit un résumé détaillé des résultats de la classification, indiquant le nombre de prédictions vraies positives, vraies négatives, fausses positives et fausses négatives.
Le taux d’erreur global OOB est de 20,42 %, ce qui indique que les prédictions du modèle sont incorrectes environ une fois sur cinq.
La matrice de confusion montre que le modèle est plus performant pour prédire « Non » (absence de désabonnement) que « Oui » (désabonnement).
Le taux d’erreur élevé pour la classe « Oui » suggère que le modèle a plus de difficultés à prédire correctement les désabonnements.
Nous allons maintenant vérifier la précision de notre modèle.
# Prévision sur l'ensemble de test
pred_class <- predict(rfModel, newdata = testData)
pred_proba <- predict(rfModel, newdata = testData, type = "prob")[, "Yes"]
# Matrice de Confusion
confusionMatrix <- table(pred = pred_class, Real = testData$Churn)
# Calculer accuracy, precision, and recall
accuracy <- sum(diag(confusionMatrix)) / sum(confusionMatrix)
precision <- confusionMatrix["Yes", "Yes"] / sum(confusionMatrix["Yes", ])
recall <- confusionMatrix["Yes", "Yes"] / sum(confusionMatrix[, "Yes"])
# AUC + Plot ROC
roc_obj <- roc(response = testData$Churn, predictor = pred_proba, levels = c("No", "Yes"))
## Setting direction: controls < cases
auc_value <- auc(roc_obj)
# Affichage des métriques
cat("Accuracy: ", round(accuracy, 3), "\n")
## Accuracy: 0.798
cat("Precision: ", round(precision, 3), "\n")
## Precision: 0.649
cat("Recall: ", round(recall, 3), "\n")
## Recall: 0.52
cat("AUC: ", round(auc_value, 3), "\n")
## AUC: 0.835
La précision est d’environ 80%, ce qui signifie que le modèle prédit correctement le statut de désabonnement environ 80% du temps.
La précision est d’environ 65,2%, ce qui signifie que lorsque le modèle prédit qu’un client va se désabonner, il a raison environ 65,2% du temps.
Le taux de rappel est d’environ 52,3%, ce qui signifie que le modèle identifie correctement 52,3% des clients qui ont effectivement résilié leur abonnement.
L’AUC est de 0,835, ce qui signifie que le modèle a 83,5% de chances de distinguer correctement les clients qui résilient leur abonnement de ceux qui ne le font pas.
# Graphique ROC
plot(roc_obj, col = "red", lwd = 2, main = paste("Courbe ROC - Random Forest | AUC =", round(auc_value, 3)))
abline(a = 0, b = 1, lty = 2, col = "gray")
Sur le jeu de données Telco IBM 7043 clients, le Random Forest atteint AUC 0.835. L’analyse ROC montre que le modele discrimine correctement les clients a risque, principalement ceux en contrat mensuel + faible ancienneté. Ce score permet un ciblage efficace pour la rétention : 75% du churn detecte en contactant 30% de la base client.