Analyse exploratoire#
Analyse et segmentation de clientèle d’un magasin avec campagnes de marketing Jules EXBRAYAT & Abdenour MADANI
Import des outils / jeu de données#
1import matplotlib.pyplot as plt
2import pandas as pd
3import seaborn as sns
4
5from src.config import data_folder
6from src.constants import var_categoriques_original as var_categoriques
7from src.constants import var_numeriques
8from src.utils import init_notebook
1init_notebook()
1df = pd.read_csv(
2 f"{data_folder}/marketing_campaign.csv",
3 sep="\t",
4 index_col="ID",
5 parse_dates=True,
6)
Présentation#
Problématique#
Dans le rôle d’analystes de données en mission pour un magasin de produits alimentaires, nous chercherons à :
Réaliser une segmentation de la clientèle de l’entreprise (clustering),
Prédire l’efficacité d’une future campagne de marketing (classification binaire)
Tableau. Liste de nos objectifs
Mission |
Type de tâche |
|
---|---|---|
Objectif 1 |
Segmentation de la clientèle |
Clustering non supervisé |
Objectif 2 |
Prédiction d’acceptation à une campagne marketing |
Classification binaire |
Tableau. Plan de la présentation
| :— | :— |
| Exploration des données | Présentation des variables
Première visualisation des variables |
| Pré-traitement des données | Nettoyage des données
Création de variables (Feature Engineering) |
| Visualisation des données | Visualisation en fonction de la variable cible |
| Analyse factorielle | ACP
AFC
ACM |
| ANOVA | Vérification des hypothèses
Test d’ANOVA |
| Segmentation de clientèle | Comparaison de différents algorithmes de clusters
Visualisation des clusters
Description des profils “type” de clients |
| Prédiction d’acceptation
de campagne marketing | Comparaison de différents modèles de classification
Équilibrage des classes
Diagnostic du meilleur modèle
Conclusion sur l’efficacité de prédiction |
Jeu de données#
1## todo : ajouter une description / une partie pour parler du jeu de données
2## "Ce jeu de données contient le profil de plusieurs clients d'une enseigne de grande distribution" \
3## "Il contient les données de juillet 2012 à juillet 2014" \
4## "Nous disposons des ventes en Vin, etc"
Présentation des variables#
1df.head()
Year_Birth | Education | Marital_Status | Income | Kidhome | Teenhome | Dt_Customer | Recency | MntWines | MntFruits | ... | NumWebVisitsMonth | AcceptedCmp3 | AcceptedCmp4 | AcceptedCmp5 | AcceptedCmp1 | AcceptedCmp2 | Complain | Z_CostContact | Z_Revenue | Response | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ID | |||||||||||||||||||||
5524 | 1957 | Graduation | Single | 58138.0 | 0 | 0 | 04-09-2012 | 58 | 635 | 88 | ... | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 11 | 1 |
2174 | 1954 | Graduation | Single | 46344.0 | 1 | 1 | 08-03-2014 | 38 | 11 | 1 | ... | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 11 | 0 |
4141 | 1965 | Graduation | Together | 71613.0 | 0 | 0 | 21-08-2013 | 26 | 426 | 49 | ... | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 11 | 0 |
6182 | 1984 | Graduation | Together | 26646.0 | 1 | 0 | 10-02-2014 | 26 | 11 | 4 | ... | 6 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 11 | 0 |
5324 | 1981 | PhD | Married | 58293.0 | 1 | 0 | 19-01-2014 | 94 | 173 | 43 | ... | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 11 | 0 |
5 rows × 28 columns
1print(f"Il y a {df.shape[1]} variables et {df.shape[0]} individus.")
Il y a 28 variables et 2240 individus.
Variables#
ID: identifiant du client
Year_Birth: numérique, année de naissance du client
Education: qualitative, niveau d’éducation
Marital_Status: qualitative, statut marital
Income: numérique, revenu annuel en $
Kidhome: numérique, nombre d’enfants en bas-âge
Teenhome: numérique, nombre d’enfants adolescents
Dt_Customer: date, date à laquelle le client s’est inscrit
Recency: numérique, nombre de jours depuis le dernier achat
Complain: catégorique, est-ce que le client s’est plaint les 2 dernières années (0 ou 1)
Products#
MntWines: numérique, argent dépensé les 2 dernières années en vin
MntFruits: numérique, argent dépensé les 2 dernières années en fruits
MntMeatProducts: numérique, argent dépensé les 2 dernières années en viande
MntFishProducts: numérique, argent dépensé les 2 dernières années en poisson
MntSweetProducts: numérique, argent dépensé les 2 dernières années en bonbons / gâteaux
MntGoldProds: numérique, argent dépensé les 2 dernières années en bijoux / or
Promotion#
NumDealsPurchases: numérique, nombre d’achats effectués avec une promotion
AcceptedCmp1: catégorique, le client a-t-il acheté durant la campagne promotionnelle numéro 1 (1 s’il a acheté, 0 sinon)
AcceptedCmp2: pareil pour la campagne numéro 2
AcceptedCmp3: pareil pour la campagne numéro 3
AcceptedCmp4: pareil pour la campagne numéro 4
AcceptedCmp5: pareil pour la campagne numéro 5
Response: catégorique, 1 si le client a acheté durant la dernière campagne, 0 sinon (potentielle variable à prédire)
Place#
NumWebPurchases: numérique, nombre d’achats effectués sur le site Internet
NumCatalogPurchases: numérique, nombre d’achats effectués via le catalogue
NumStorePurchases: numérique, nombre d’achats effectués en magasin
NumWebVisitsMonth: numérique, nombre de visites sur le site Internet le dernier mois
Nous séparons les variables numériques des variables catégoriques pour plus de commodités.
Nous convertissons les variables catégoriques en type category
. (Nous les convertissons au préalable en type string
car cela facilite l’affichage de la légende avec Matplotlib et Seaborn)
1df[var_categoriques] = df[var_categoriques].astype(str).astype("category")
1## todo: convertir en int la variable Income (qui est float)
Nous convertissons les variables au format date.
1df["Dt_Customer"] = pd.to_datetime(df["Dt_Customer"], format="%d-%m-%Y")
1df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2240 entries, 5524 to 9405
Data columns (total 28 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Year_Birth 2240 non-null int64
1 Education 2240 non-null category
2 Marital_Status 2240 non-null category
3 Income 2216 non-null float64
4 Kidhome 2240 non-null category
5 Teenhome 2240 non-null category
6 Dt_Customer 2240 non-null datetime64[ns]
7 Recency 2240 non-null int64
8 MntWines 2240 non-null int64
9 MntFruits 2240 non-null int64
10 MntMeatProducts 2240 non-null int64
11 MntFishProducts 2240 non-null int64
12 MntSweetProducts 2240 non-null int64
13 MntGoldProds 2240 non-null int64
14 NumDealsPurchases 2240 non-null int64
15 NumWebPurchases 2240 non-null int64
16 NumCatalogPurchases 2240 non-null int64
17 NumStorePurchases 2240 non-null int64
18 NumWebVisitsMonth 2240 non-null int64
19 AcceptedCmp3 2240 non-null category
20 AcceptedCmp4 2240 non-null category
21 AcceptedCmp5 2240 non-null category
22 AcceptedCmp1 2240 non-null category
23 AcceptedCmp2 2240 non-null category
24 Complain 2240 non-null category
25 Z_CostContact 2240 non-null int64
26 Z_Revenue 2240 non-null int64
27 Response 2240 non-null category
dtypes: category(11), datetime64[ns](1), float64(1), int64(15)
memory usage: 340.7 KB
Nous avons 11 variables catégoriques, 16 variables quantitatives (dont 15 entières) ainsi qu’une variable de type date.
Découverte des données#
Analyse univariée#
1df[var_numeriques].describe()
Year_Birth | Income | Recency | MntWines | MntFruits | MntMeatProducts | MntFishProducts | MntSweetProducts | MntGoldProds | NumDealsPurchases | NumWebPurchases | NumCatalogPurchases | NumStorePurchases | NumWebVisitsMonth | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 2240.000000 | 2216.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 | 2240.000000 |
mean | 1968.805804 | 52247.251354 | 49.109375 | 303.935714 | 26.302232 | 166.950000 | 37.525446 | 27.062946 | 44.021875 | 2.325000 | 4.084821 | 2.662054 | 5.790179 | 5.316518 |
std | 11.984069 | 25173.076661 | 28.962453 | 336.597393 | 39.773434 | 225.715373 | 54.628979 | 41.280498 | 52.167439 | 1.932238 | 2.778714 | 2.923101 | 3.250958 | 2.426645 |
min | 1893.000000 | 1730.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 1959.000000 | 35303.000000 | 24.000000 | 23.750000 | 1.000000 | 16.000000 | 3.000000 | 1.000000 | 9.000000 | 1.000000 | 2.000000 | 0.000000 | 3.000000 | 3.000000 |
50% | 1970.000000 | 51381.500000 | 49.000000 | 173.500000 | 8.000000 | 67.000000 | 12.000000 | 8.000000 | 24.000000 | 2.000000 | 4.000000 | 2.000000 | 5.000000 | 6.000000 |
75% | 1977.000000 | 68522.000000 | 74.000000 | 504.250000 | 33.000000 | 232.000000 | 50.000000 | 33.000000 | 56.000000 | 3.000000 | 6.000000 | 4.000000 | 8.000000 | 7.000000 |
max | 1996.000000 | 666666.000000 | 99.000000 | 1493.000000 | 199.000000 | 1725.000000 | 259.000000 | 263.000000 | 362.000000 | 15.000000 | 27.000000 | 28.000000 | 13.000000 | 20.000000 |
1df[var_categoriques].describe()
Education | Marital_Status | Kidhome | Teenhome | Complain | AcceptedCmp1 | AcceptedCmp2 | AcceptedCmp3 | AcceptedCmp4 | AcceptedCmp5 | Response | |
---|---|---|---|---|---|---|---|---|---|---|---|
count | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 | 2240 |
unique | 5 | 8 | 3 | 3 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
top | Graduation | Married | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
freq | 1127 | 864 | 1293 | 1158 | 2219 | 2096 | 2210 | 2077 | 2073 | 2077 | 1906 |
1## todo: commenter
Visualisation#
Variables numériques#
1for var in var_numeriques:
2 _, ax = plt.subplots(1, 2, figsize=(8, 2))
3 sns.boxplot(df[var], width=0.25, ax=ax[0])
4 sns.histplot(df[var], kde=True, ax=ax[1])
5 plt.show()
1## todo: commenter les distributions et boxplots
1plt.figure(figsize=(8, 8))
2sns.heatmap(
3 df[var_numeriques].corr()[df[var_numeriques].corr().abs() > 0.5],
4 annot=True,
5 cmap="BrBG",
6 linewidths=0.5,
7 vmax=1,
8 vmin=-1,
9)
<Axes: >
1## todo: commenter la matrice de corrélation
1_, ax = plt.subplots(1, 2, figsize=(15, 4))
2
3ax[0].set_title("Nombre de valeurs présentes")
4df.notna().sum()[var_numeriques].plot.barh(ax=ax[0])
5
6ax[1].set_title("Valeurs manquantes")
7sns.heatmap(df[var_numeriques].isna(), cbar=False, ax=ax[1])
<Axes: title={'center': 'Valeurs manquantes'}, ylabel='ID'>
Nous observons qu’il n’y a quasiment pas de valeurs manquantes parmi les variables numériques.
Variables catégoriques#
1for var in var_categoriques:
2 if df[var].nunique() > 3:
3 sns.histplot(y=df[var])
4 else:
5 plt.figure(figsize=(4, 2))
6 sns.histplot(df[var], shrink=0.3)
7 plt.show()
1df[
2 df[var_categoriques].columns[df[var_categoriques].nunique() > 3]
3].value_counts().plot(kind="bar")
<Axes: xlabel='Education,Marital_Status'>
1## todo: commenter
Valeurs manquantes#
1_, ax = plt.subplots(1, 2, figsize=(15, 4))
2
3ax[0].set_title("Nombre de valeurs présentes")
4df.notna().sum()[var_categoriques].plot.barh(ax=ax[0])
5
6ax[1].set_title("Valeurs manquantes")
7sns.heatmap(df[var_categoriques].isna(), cbar=False, ax=ax[1])
<Axes: title={'center': 'Valeurs manquantes'}, ylabel='ID'>
Nous observons l’absence de valeurs manquantes parmi les variables catégoriques.
Dates#
1## todo
1df["Dt_Customer"].hist(bins=50)
<Axes: >
Sauvegarde du Dataframe#
1df.to_csv(f"{data_folder}/data.csv")