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()
../_images/a4420663b8d570b9f8fde9afe1cf19d3628a42de539a8dd942403917bb88ca91.png ../_images/8c715b13d7d15d2a643909fa6ce4ebbc294d93bf8d7badcc64c167e07079c96b.png ../_images/535de86f5578b9a8f8f8e93fbac957ff028d8062b0b9ad5ef4111fa322dc69af.png ../_images/d7e25d0f614f95836ff97fd14131866fa0d8abff743395cf2f7dd3c2b3c5f3a4.png ../_images/1962a9c5878fc313f2f486d6f0fe3db9dd317775135dcb9e868ba35ec0147dfc.png ../_images/1db649327d8ab339b89a3707b7ca02594ea917abdcd9bce3b7e0d9131eaf4dff.png ../_images/b3cdd71b526534d585888a1fe1b8c47dd9ad986474a33bd828488f62e99df100.png ../_images/f09bcf7f6328c2ebe33a6c03cd4dd36e40ba1f945c3bd8853b32e2ebdb9c053c.png ../_images/834ab3b303618dfcf5636b566ba0bff2f44e99ac3f58bc8d5749af724a4a7e88.png ../_images/5208f2e656014e607799215f3fb5406534444553b9f5f70f3fab6117479e86b1.png ../_images/6fd80a734c216eec5d45c400ad2a53243038faa7be1a5262ea926b741f7a1140.png ../_images/4b451cf067d583f9911de1f0f221afa1131ab2a118d5d7300452ebbd7bfb75ef.png ../_images/df7937f1384a757814e6f17606000f453843ac10b5c285196ed5f87d3549abe3.png ../_images/364ede4adb90c15ebced1a388e51597f21192a9b4f441c1da4bbcb4d8c7c399a.png
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: >
../_images/6a4f7afdc786a01739cf72624c1d3a468b6a0eb73328cc66ae0adcf35b89f505.png
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'>
../_images/0abc969af95203fc6dc328baf91c12f99dd86bd4cf2fda9708a8de199dcc5827.png

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()
../_images/be36facc46a5a5993f2b511a4c6ad30d716e69ff2215c5dd80bb738c058b6cdb.png ../_images/16b511cba4c55998153079e46317ce39507067e54a6513582def8df3901515c5.png ../_images/b52d307836ad947c7043f926e7d212bc5d385909720f2622b8ef712c9ca607dd.png ../_images/ad048c78e1d5657e055b74557c0f4621ebf5a69c80c06cc4226051a2495749e2.png ../_images/d72662bc7a5822e747b0b43d2979842d05f4c639f0f4ec03e4013bf2b0074148.png ../_images/a31387369caf8af72cc4cb8e9082ce59e45b95e42c10badc2140b2f139639ec0.png ../_images/9458bd0507b21a371daeb35544ee80ddd174743ed0193732b9994bc461a35f1e.png ../_images/aa80bbbc8035b4da130f6575057b0f1a9a7f3213f7eabca17c5a708a57fd3581.png ../_images/7830914d50e7e9da587bff1a7e80881b929b230b495f389b1721659fda99e8a2.png ../_images/957fd9017af55717a36f0ea0a64417ac4d55aa975ee5b500e7a80264d8112858.png ../_images/938920be3c5f8218a06535169921b3c27162f45f4a90886abb409af11cbaf767.png
1df[
2    df[var_categoriques].columns[df[var_categoriques].nunique() > 3]
3].value_counts().plot(kind="bar")
<Axes: xlabel='Education,Marital_Status'>
../_images/af4ae6daade0b0c9257015048d84e6d9c8473a09ffaeea96cc504cec8f725002.png
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'>
../_images/4d32c3f37e6894197bb25594444b755036cb3281ac18b94c93717514fc85fe11.png

Nous observons l’absence de valeurs manquantes parmi les variables catégoriques.

Dates#

1## todo
1df["Dt_Customer"].hist(bins=50)
<Axes: >
../_images/4ab58672cd43e54cbca0bb6892af53366f0f18ad1ffd3e9e20c79a3101d5fb79.png

Sauvegarde du Dataframe#

1df.to_csv(f"{data_folder}/data.csv")