Librairies

library(tidyverse)
library(scales)
library(lubridate)
library(knitr)
library(zoo)
source("C:/Users/teore/OneDrive/OC_DataAnalyst/P6/euro.R")

1 Analyse de conformité des données

1.1 Chargement

customers <- read_csv("customers.csv", col_types = c('cfd'))
products <- read_csv('products.csv', col_types = c('cdf'))
transactions <- read_csv('transactions.csv')

Problèmes de décodage dans le fichier transactions. Il s’agit de lignes de test avec id T_0 donc on peut laisser ces valeurs nulles ou bien les retirer.

glimpse(products)
## Rows: 3,287
## Columns: 3
## $ id_prod <chr> "0_1421", "0_1368", "0_731", "1_587", "0_1507", "0_1163", "1_4~
## $ price   <dbl> 19.99, 5.13, 17.99, 4.99, 3.99, 9.99, 36.99, 34.99, 16.99, 18.~
## $ categ   <fct> 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,~
glimpse(transactions)
## Rows: 679,532
## Columns: 4
## $ id_prod    <chr> "0_1518", "1_251", "0_1277", "2_209", "0_1509", "0_1418", "~
## $ date       <dttm> 2022-05-20 13:21:29, 2022-02-02 07:55:19, 2022-06-18 15:44~
## $ session_id <chr> "s_211425", "s_158752", "s_225667", "s_52962", "s_325227", ~
## $ client_id  <chr> "c_103", "c_8534", "c_6714", "c_6941", "c_4232", "c_1478", ~

1.2 Valeurs manquantes

transactions %>% filter(is.na(date)) %>% unique()
## # A tibble: 2 x 4
##   id_prod date                session_id client_id
##   <chr>   <dttm>              <chr>      <chr>    
## 1 T_0     NA                  s_0        ct_0     
## 2 T_0     NA                  s_0        ct_1
#les lignes dont la date a été traduite par des valeurs nulles sont que de tests; on pourra retirer le même id sur le fichier produits
transactions <- na.omit(transactions)
products[!products$id_prod == 'T_0',] -> products

1.3 Test unicité

sum(duplicated(products$id_prod))
## [1] 0

Comparaison entre les id des deux dataframes. Une valeur de product_id du fichier transactions n’est pas présente dans products. 21 références du fichier products ne paraissent pas dans transactions.

diffprodtr <- setdiff(products$id_prod, transactions$id_prod)
difftrprod <- setdiff(transactions$id_prod, products$id_prod)

transactions[transactions$id_prod == '0_2245',]
## # A tibble: 221 x 4
##    id_prod date                session_id client_id
##    <chr>   <dttm>              <chr>      <chr>    
##  1 0_2245  2022-09-23 07:22:38 s_272266   c_4746   
##  2 0_2245  2022-07-23 09:24:14 s_242482   c_6713   
##  3 0_2245  2022-12-03 03:26:35 s_306338   c_5108   
##  4 0_2245  2021-08-16 11:33:25 s_76493    c_1391   
##  5 0_2245  2022-07-16 05:53:01 s_239078   c_7954   
##  6 0_2245  2023-01-21 18:39:25 s_330241   c_6268   
##  7 0_2245  2022-11-20 20:21:06 s_300389   c_8524   
##  8 0_2245  2021-10-20 13:11:05 s_107564   c_1746   
##  9 0_2245  2021-04-17 16:43:16 s_21906    c_7808   
## 10 0_2245  2022-02-28 18:08:49 s_172304   c_4964   
## # ... with 211 more rows
products[products$id_prod %in% diffprodtr,]
## # A tibble: 21 x 3
##    id_prod price categ
##    <chr>   <dbl> <fct>
##  1 0_1016  35.1  0    
##  2 0_1780   1.67 0    
##  3 0_1062  20.1  0    
##  4 0_1119   2.99 0    
##  5 0_1014   1.15 0    
##  6 1_0     31.8  1    
##  7 0_1318  20.9  0    
##  8 0_1800  22.0  0    
##  9 0_1645   2.99 0    
## 10 0_322    2.99 0    
## # ... with 11 more rows
pdtr <- merge(products, transactions, on='product_id')
head(pdtr)
##   id_prod price categ                date session_id client_id
## 1     0_0  3.75     0 2022-11-05 12:19:02   s_293040    c_6343
## 2     0_0  3.75     0 2021-09-07 21:38:41    s_86520    c_7587
## 3     0_0  3.75     0 2021-05-08 12:18:32    s_31643    c_2204
## 4     0_0  3.75     0 2022-08-23 12:05:45   s_257317    c_5104
## 5     0_0  3.75     0 2021-05-08 05:55:57    s_31508    c_7219
## 6     0_0  3.75     0 2022-11-23 19:17:58   s_301733    c_7532
glimpse(pdtr)
## Rows: 679,111
## Columns: 6
## $ id_prod    <chr> "0_0", "0_0", "0_0", "0_0", "0_0", "0_0", "0_0", "0_0", "0_~
## $ price      <dbl> 3.75, 3.75, 3.75, 3.75, 3.75, 3.75, 3.75, 3.75, 3.75, 3.75,~
## $ categ      <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,~
## $ date       <dttm> 2022-11-05 12:19:02, 2021-09-07 21:38:41, 2021-05-08 12:18~
## $ session_id <chr> "s_293040", "s_86520", "s_31643", "s_257317", "s_31508", "s~
## $ client_id  <chr> "c_6343", "c_7587", "c_2204", "c_5104", "c_7219", "c_7532",~

2 Présentation du chiffre d’affaires global

#création des intervalles par semaine et mois
pdtr$week <- as.Date(cut(pdtr$date,breaks = "week"))
pdtr$month <- as.Date(cut(pdtr$date,breaks = "month"))
ca_m <- pdtr %>% group_by(month) %>% summarise(CA = sum(price)) # CA par mois
ca_h <- pdtr %>% group_by(week) %>% summarise(CA = sum(price)) # CA par semaine
ca_j <- pdtr %>% group_by(day = as.Date(date)) %>% summarise(CA = sum(price)) # CA par jour 
ca_m %>% summarise(max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA mensuel global")
CA mensuel global
max(CA) min(CA) mean(CA) median(CA) sd(CA)
535.572€ 320.799€ 493.905€ 501.566€ 41.208,74€
summarise(ca_h, max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA hebdomadaire global")
CA hebdomadaire global
max(CA) min(CA) mean(CA) median(CA) sd(CA)
136.415€ 37.275,96€ 112.893€ 114.583€ 12.674,93€
summarise(ca_j, max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA journalier global")
CA journalier global
max(CA) min(CA) mean(CA) median(CA) sd(CA)
20.487,68€ 8.076,04€ 16.237,98€ 16.223,30€ 1.847,91€
sd <- sd(ca_m$CA)
ggplot(data = ca_m,aes(month, CA)) +
  geom_col(fill = "steelblue") + geom_hline(yintercept = mean(ca_m$CA), colour ='red') +
  scale_x_date(date_labels = "%b-%Y" ,breaks = "2 month") + 
  theme(axis.text.x = element_text(angle = 90)) + 
  labs(x = '', y = 'Totale des ventes', title = "Chiffre d'affaires mensuel") +
  scale_y_continuous(labels = euro, n.breaks = 6)

#adding rolling average
ca_m %>% mutate(rolavr = rollmean(x = CA, k = 3, align = "center", na.pad = TRUE)) -> ca_m
ca_h %>% mutate(rolavr = rollmean(x = CA, k = 4, align = "center", na.pad = TRUE)) -> ca_h
ca_h[ca_h$week == '2023-02-13',]$rolavr = NA
# graph by week:
colors <- c("Total" = "steelblue", "Moyenne Mensuelle" = "black", "Moyenne Mobile" = "red")
ggplot(data = pdtr,
  aes(week, price)) +
  stat_summary(aes(color='Total'), fun = sum, 
    geom = "line", size = 2) + geom_line(data = ca_h, aes(x=week, y=rolavr,color='Moyenne Mobile'),show.legend = TRUE, size=1) + geom_hline(aes(color="Moyenne Mensuelle"), yintercept = mean(ca_h$CA),linetype='dotted',show.legend = TRUE) + scale_x_date(labels = date_format('%b-%Y'), breaks = "3 month", limits = c(min(pdtr$week), max(pdtr$week) -7)) +
    labs(title="Evolution du chiffre d'affaires hebdomadaire", y="Total hebdomadaire",x="")  + scale_y_continuous(labels=euro) + scale_color_manual(values =colors) +theme_minimal() + theme(axis.text.x = element_text(angle = 90), legend.title = element_blank()) 

2.1 Chiffre d’affaires par categorie et par produit

2.1.1 Repartition du prix des produits

On analysera les prix listés dans le fichier products pour avoir un coup d’œil sur les prix et leur indicateurs.

products %>% group_by(categ) %>% summarise(min(price), max(price), mean(price), median(price), sd(price))
## # A tibble: 3 x 6
##   categ `min(price)` `max(price)` `mean(price)` `median(price)` `sd(price)`
##   <fct>        <dbl>        <dbl>         <dbl>           <dbl>       <dbl>
## 1 0             0.62         41.0          11.7            10.3        7.57
## 2 1             2            81.0          25.5            23.0       15.4 
## 3 2            31.0         300           108.            102.        49.6
products %>% ggplot(aes(categ, price)) + geom_boxplot(aes(fill=categ)) + scale_y_continuous(labels=euro, n.breaks=8) + colScale + labs(x = "Categorie", y = "Prix")

On remarque la présence des trois tranches des prix bien demarquées et que la catégorie “2” se distingue nettement de deux autres avec une médiane de ~100€. Regardons maintenant le chiffre global par catégorie et le top produits:

ventes_tot_cat <-  pdtr %>% group_by(categ) %>% summarise(total = sum(price))
ventes_tot_prod <- pdtr %>% group_by(id_prod, categ) %>% summarise(n_ventes = n(), total = sum(price)) %>% arrange(desc(total))
ventes_tot_cat %>% mutate(total = euro(total)) %>% kable(caption = 'Ventes totales par categorie')
Ventes totales par categorie
categ total
0 4.419.731€
1 4.653.723€
2 2.780.275€
head(ventes_tot_prod,10) %>% mutate(total = euro(total)) %>% kable(align = c('lccr'), caption='Top 10 produits')
Top 10 produits
id_prod categ n_ventes total
2_159 2 650 94.893,50€
2_135 2 1005 69.334,95€
2_112 2 968 65.407,76€
2_102 2 1027 60.736,78€
2_209 2 814 56.971,86€
1_395 1 1875 54.356,25€
1_369 1 2252 54.025,48€
2_110 2 865 53.846,25€
2_39 2 915 53.060,85€
2_166 2 228 52.449,12€
tail(ventes_tot_prod,10) %>% mutate(total = euro(total)) %>% kable(align = c('lccr'), caption='Le 10 produits moins vendus')
Le 10 produits moins vendus
id_prod categ n_ventes total
0_1840 0 2 2,56€
0_898 0 2 2,54€
0_1498 0 1 2,48€
0_1728 0 1 2,27€
0_1601 0 1 1,99€
0_541 0 1 1,99€
0_807 0 1 1,99€
0_1653 0 2 1,98€
0_1284 0 1 1,38€
0_1539 0 1 0,99€
ggplot(ventes_tot_cat %>% ungroup(), aes(x=categ, y=total, fill= categ)) + geom_col(color="white", position = 'dodge') + scale_y_continuous(labels = euro) +
  labs(title = "Chiffre d'affaires par categorie", x="Categorie", y="Total")  + colScale 

top20r <- ventes_tot_prod %>% ungroup() %>%  slice_max(total, n = 20, with_ties = FALSE)
top20nv <- ventes_tot_prod %>% ungroup() %>%  slice_max(n_ventes, n = 20, with_ties = FALSE)
#distribution n ventes?

ggplot(top20r, aes(x = fct_reorder(id_prod, total, .desc = TRUE), y= total, fill= categ)) + geom_col() +  labs(title='Top 20 produits par revenu', x='Id Produit', y='Total ventes') + scale_y_continuous(labels=euro, n.breaks = 7) + colScale + theme(axis.text.x = element_text(angle = 90)) 

ggplot(top20nv, aes(x = fct_reorder(id_prod, n_ventes, .desc = TRUE), y= n_ventes, fill= categ)) + geom_col() +  labs(title='Top 20 produits le plus vendus', x='Id Produit', y='Total ventes') +  colScale +  theme(axis.text.x = element_text(angle = 90)) 

La catégorie 1 c’est celle qui apporte le plus de revenus mais elle ne dépasse pas largement la catégorie 0. Le top produits sont de catégorie 2 et 1 (la catégorie qui a le prix moyen plus fort) et on ne retrouve pas de produits cat. 0, qui pourtant dépasse la 2 en CA totale. Entre le produits le plus vendu on retrouve plusieurs références de cat1 qui sont aussi en tête de liste pour revenus.

2.2 Analyse des clients

Nous allons joindre le fichier pdtr créé précedemment au fichier customers qui contient en outre que l’identifiant du client, son genre et son année de naissance.

str(customers)
## spec_tbl_df [8,623 x 3] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ client_id: chr [1:8623] "c_4410" "c_7839" "c_1699" "c_5961" ...
##  $ sex      : Factor w/ 2 levels "f","m": 1 1 1 1 2 2 1 1 2 2 ...
##  $ birth    : num [1:8623] 1967 1975 1984 1962 1943 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   client_id = col_character(),
##   ..   sex = col_factor(levels = NULL, ordered = FALSE, include_na = FALSE),
##   ..   birth = col_double()
##   .. )
sum(duplicated(customers$client_id)) # les id clients sont uniques
## [1] 0
all(is.element(pdtr$client_id, customers$client_id)) # toutes les id client du fichier pdtr sont inclues dans customers
## [1] TRUE
setdiff(customers$client_id, pdtr$client_id) # quelques id ne sont pas références dans transactions, on peut les ignorer
##  [1] "c_8253" "c_3789" "c_4406" "ct_0"   "c_2706" "c_3443" "c_4447" "c_3017"
##  [9] "c_4086" "c_6930" "c_4358" "c_8381" "c_1223" "c_6862" "c_5245" "c_5223"
## [17] "c_6735" "c_862"  "c_7584" "c_90"   "c_587"  "ct_1"   "c_3526"
pdtr_cust <- merge(pdtr, customers, on = 'client_id')
str(pdtr_cust)
## 'data.frame':    679111 obs. of  10 variables:
##  $ client_id : chr  "c_1" "c_1" "c_1" "c_1" ...
##  $ id_prod   : chr  "0_1448" "1_713" "0_1429" "0_1475" ...
##  $ price     : num  18.94 33.99 7.99 11.99 8.99 ...
##  $ categ     : Factor w/ 3 levels "0","1","2": 1 2 1 1 1 2 1 2 1 1 ...
##  $ date      : POSIXct, format: "2021-07-26 17:37:29" "2021-11-15 20:40:00" ...
##  $ session_id: chr  "s_67467" "s_120172" "s_105105" "s_230001" ...
##  $ week      : Date, format: "2021-07-26" "2021-11-15" ...
##  $ month     : Date, format: "2021-07-01" "2021-11-01" ...
##  $ sex       : Factor w/ 2 levels "f","m": 2 2 2 2 2 2 2 2 2 2 ...
##  $ birth     : num  1955 1955 1955 1955 1955 ...
summary(pdtr_cust)
##   client_id           id_prod              price        categ     
##  Length:679111      Length:679111      Min.   :  0.62   0:415459  
##  Class :character   Class :character   1st Qu.:  8.87   1:227169  
##  Mode  :character   Mode  :character   Median : 13.99   2: 36483  
##                                        Mean   : 17.45             
##                                        3rd Qu.: 18.99             
##                                        Max.   :300.00             
##       date                      session_id             week           
##  Min.   :2021-03-01 00:01:07   Length:679111      Min.   :2021-03-01  
##  1st Qu.:2021-09-08 09:15:03   Class :character   1st Qu.:2021-09-06  
##  Median :2022-03-03 07:40:49   Mode  :character   Median :2022-02-28  
##  Mean   :2022-03-03 15:15:06                      Mean   :2022-02-28  
##  3rd Qu.:2022-08-31 00:01:46                      3rd Qu.:2022-08-29  
##  Max.   :2023-02-28 23:58:30                      Max.   :2023-02-27  
##      month            sex            birth     
##  Min.   :2021-03-01   f:338285   Min.   :1929  
##  1st Qu.:2021-09-01   m:340826   1st Qu.:1970  
##  Median :2022-03-01              Median :1980  
##  Mean   :2022-02-16              Mean   :1978  
##  3rd Qu.:2022-08-01              3rd Qu.:1987  
##  Max.   :2023-02-01              Max.   :2004

Analysons maintenant le chiffre d’affaire par client, pour mettre en évidence les clients principaux.

achatsxclient <- pdtr_cust %>% group_by(client_id) %>% summarise(tot_achats = sum(price)) %>% arrange(desc(tot_achats))
achatsxclient %>% head(10) %>% mutate(tot_achats = euro(tot_achats)) %>% kable()
client_id tot_achats
c_1609 324.033€
c_4958 289.760€
c_6714 153.599€
c_3454 113.638€
c_3263 5.277€
c_1570 5.272€
c_2899 5.214€
c_2140 5.209€
c_7319 5.156€
c_7959 5.085€
achatsxclient %>% ggplot(aes(tot_achats)) + geom_boxplot() + scale_x_continuous(label=euro) + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank())

2.2.1 Repartition du chiffre d’affaire cumulatif (courbe de Lorenz)

Le quatre clients en tête de liste constituent le ~ 0.05 % des clients totaux et il génèrent a peut près le 7.5% du chiffre d’affaires totale:

## % du CA totale des clients principaux: 0.07432518
## coefficient gini: 0.4463865

On peut donc supposer que les premiers quatre clients, vu leur total d’achats soient des business. Pour étudier les clients “normaux” on verra donc de le mettre a coté.

business_id <- achatsxclient[1:4, "client_id"]$client_id
clients <- pdtr_cust %>% filter(!client_id %in% business_id)
b_to_b <- pdtr_cust[pdtr_cust$client_id %in% business_id,]

2.2.2 Repartition des clients par tranche d’age

Examinons maintenant la distribution des age des clients.

# binning by birth year
clients$agegroup <-cut(clients$birth, 5, dig.lab = 10)

n_age <- clients %>% group_by(birth,sex) %>% distinct(client_id) %>% count() %>% arrange(desc(n))
n_clients <- clients %>% group_by(agegroup, sex) %>% distinct(client_id,sex) %>% count() %>% arrange(desc(n))

clients %>% distinct(client_id,birth) %>% summarise(min(birth), max(birth), mean(birth), median(birth), IQR(birth), sd(birth))
##   min(birth) max(birth) mean(birth) median(birth) IQR(birth) sd(birth)
## 1       1929       2004     1978.26          1979         26  16.91054
clients %>% group_by(client_id) %>% distinct(client_id,sex,birth) %>%  ggplot(aes(x = sex, y = birth, fill=sex)) + geom_boxplot() + labs(x="Sexe", y="Année de naissance") 

 n_age %>% ggplot(aes(birth, n)) + geom_col(aes(fill=sex), color="white") + scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) + labs(title="Nombre des clients par age et sexe") 

   n_clients %>% ggplot(aes(agegroup, n)) + geom_col(aes(fill=sex), color="white") +  scale_y_continuous(breaks = scales::pretty_breaks(n = 10)) + labs(x="Tranche d'age", y="Nombre de personnes") 

La répartition par sexe des clients est a peu près égale, avec une prévalence des générations over 1974. L’année de naissance plus présent est le 2004 avec ~430 membres.

2.3 Chiffre d’affaires par tranche d’age et categorie

clients %>%  group_by(agegroup) %>% summarise(total_ventes = sum(price))%>% arrange(desc(total_ventes)) %>% mutate(total_ventes = euro(total_ventes))
## # A tibble: 5 x 2
##   agegroup        total_ventes
##   <fct>           <chr>       
## 1 (1974,1989]     4.058.100€  
## 2 (1989,2004.075] 3.466.544€  
## 3 (1959,1974]     2.202.166€  
## 4 (1944,1959]     1.013.221€  
## 5 (1928.925,1944] 232.666€
clients %>%  ggplot(aes(x=agegroup,fill=categ)) + geom_bar(aes(weight = price), color='grey') + scale_y_continuous(labels=euro) + colScale + labs(x='Année de naissance', y='Ventes totales') 

clients %>%  ggplot(aes(x=categ,fill=categ)) + geom_bar(aes(weight = price), color='grey') +  facet_grid(.~ agegroup) +   scale_y_continuous(labels=euro) + colScale + labs(x='', y='Ventes totales')

C’est évidente la préférence de la tranche (1989, 2005) pour le produits de catégorie deux, catégorie sur laquelle ils ont presque l’exclusivité et engendre le plus gros revenus (toute tranche d’age confondue). En revanche, la tranche d’age qui contribue le plus au chiffre d’affaire c’est 1974-1989 avec un CA totale de 4M €.

#création du df stats_clients
achats_clients_cat <- clients %>% 
  group_by(client_id) %>% 
  count(categ) %>% 
  pivot_wider(names_from = categ, values_from = n, names_glue = "cat_{categ}", values_fill = 0)

achats_clients <- clients %>% 
  group_by(client_id,agegroup,sex, birth) %>% 
  summarise(tot_achats = sum(price), achats_mois = n()/24) 

achats_session <- clients %>% group_by(client_id,session_id) %>% summarise(tot_session = sum(price)) %>% group_by(client_id) %>% summarise(panier_moyen = median(tot_session))

stats_clients <- merge(achats_clients, achats_session )
stats_clients <- merge(stats_clients, achats_clients_cat)
head(stats_clients)
##   client_id        agegroup sex birth tot_achats achats_mois panier_moyen cat_0
## 1       c_1     (1944,1959]   m  1955     558.18   1.6250000       11.990    30
## 2      c_10     (1944,1959]   m  1956    1353.60   2.4166667       29.005    20
## 3     c_100 (1989,2004.075]   m  1992     254.85   0.3333333       15.990     2
## 4    c_1000     (1959,1974]   f  1966    2261.89   5.2083333       22.990    43
## 5    c_1001     (1974,1989]   m  1982    1812.86   4.2500000       33.610    70
## 6    c_1002     (1974,1989]   f  1982     415.78   1.5000000       20.980    30
##   cat_1 cat_2
## 1     8     1
## 2    34     4
## 3     5     1
## 4    82     0
## 5    31     1
## 6     6     0

2.3.1 Repartition du CA par tranche d’age et par sexe

achats_clients %>% group_by(agegroup) %>% summarise(tot_age = sum(tot_achats)) %>% mutate(pourcentage = tot_age / sum(tot_age) * 100) -> ca_age
ca_age %>%  ggplot(aes(x= fct_reorder(agegroup, pourcentage), y=pourcentage, fill=agegroup)) + geom_bar(stat='identity', color="black", position = "dodge")  + scale_y_continuous(labels=euro) + geom_text(aes(y = pourcentage, label = percent(pourcentage/100)), size=5, nudge_y = 1.5) + theme_void() + labs(title="Pourcentage du CA total par tranche d'age")

psex <- clients %>% group_by(sex) %>% summarise(total = sum(price))%>% mutate(p = total/sum(total))   
psex %>% ggplot(aes(x=sex, y=total, fill=sex)) + geom_col() +   scale_y_continuous(labels = euro) + geom_text(data=psex, aes(label = percent(p))) + labs(title = "Repartition par genre du chiffre d'affaires total")

2.3.2 Test de difference entre deux moyennes

Nous allons nous poser la question suivante juste a but didactique. Il faudra remarquer que plusieurs tests statistiques ont un sens seulement si appliqués sur des échantillons de la population (test de comparaison de la variance, t-test, ANOVA). Ici nous travaillons sur la totalité de la population, donc ces paramétres nous les connaissons déjà.

  • Est-ce qu’il existe une différence significative entre les totaux d’achats des clients des deux sexes ?

    Conditions pour un t-test

    1)les deux échantillons doivent être indépendants et normalement distribués.

    2)Ça sera alors possible de tester l’égalité de la variance et successivement l’égalité des moyennes.

stats_clients %>% group_by(sex) %>% summarise(mean(tot_achats), median(tot_achats)) %>% kable()
sex mean(tot_achats) median(tot_achats)
f 1274.248 1029.56
m 1278.925 1025.90
achats_clients %>% ggplot(aes(x=sex, y=tot_achats, fill=sex)) + geom_boxplot()+ stat_summary(fun=mean) + scale_y_continuous(label=euro, n.breaks = 10) + theme_minimal()

achats_clients %>% ggplot(aes(x=tot_achats, fill=sex)) +  geom_histogram(aes(y=..density..), color="white") + geom_density(alpha=0.5) + facet_wrap(~sex) +scale_y_continuous(labels = scales::percent) + scale_x_continuous(label=euro)

op = par(mfrow=c(1,2))
qqnorm(subset(achats_clients, sex=='m')$tot_achats)
qqline(subset(achats_clients, sex=='m')$tot_achats)
qqnorm(subset(achats_clients, sex=='f')$tot_achats)
qqline(subset(achats_clients, sex=='f')$tot_achats)

par(op)

#les deux distributions ne remplissent les conditions de normalité. On procédera a un test non-paramétrique, qui dans ce cas testera l'hypothese d'egalité des medianes des deux groupes:

wilcox.test(achats_clients$tot_achats[achats_clients$sex=='m'],achats_clients$tot_achats[achats_clients$sex=='f'])
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  achats_clients$tot_achats[achats_clients$sex == "m"] and achats_clients$tot_achats[achats_clients$sex == "f"]
## W = 9218727, p-value = 0.9898
## alternative hypothesis: true location shift is not equal to 0

On ne rejettera donc pas H0 muhommes == mufemmes et on ne concluera que le sexe n’as pas d’influence sur la somme des achats d’un client.


2.3.3 CA Business to Client

#statistiques b to c
clients %>% group_by(month) %>% summarise(CA = sum(price)) %>% summarise(max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA mensuel")
CA mensuel
max(CA) min(CA) mean(CA) median(CA) sd(CA)
492.927€ 296.728€ 457.196€ 464.510€ 38.382,21€
clients %>% group_by(week) %>% summarise(CA = sum(price)) %>% summarise( max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA hebdomadaire")
CA hebdomadaire
max(CA) min(CA) mean(CA) median(CA) sd(CA)
124.078€ 34.644,42€ 104.502€ 105.832€ 11.727,85€
clients %>% group_by(day = as.Date(date)) %>% summarise(CA = sum(price)) %>% summarise(max(CA), min(CA), mean(CA), median(CA), sd(CA)) %>% mutate(across(.fns=euro)) %>% kable(caption = "CA journalier")
CA journalier
max(CA) min(CA) mean(CA) median(CA) sd(CA)
19.155,90€ 7.396,45€ 15.031,09€ 15.054,34€ 1.715,58€
ca_mois_c <- clients %>% group_by(month) %>% summarise(CA = sum(price)) %>% mutate(rolavr = rollmean(CA,3, na.pad = TRUE))

ca_week_c <- clients %>% group_by(week) %>% summarise(CA = sum(price)) %>% mutate(rolavr = rollmean(CA,3, na.pad = TRUE))

ggplot(data = clients,
  aes(month, price)) +
  stat_summary(aes(fill=agegroup),fun = sum, 
    geom = "bar", position="stack")  + geom_line(data = ca_mois_c, aes(x = month, y=rolavr), color="red",size=1) +
  scale_x_date(labels = date_format("%b-%Y"), limits = c(min(clients$week), max(clients$week) - 7)) + labs(title="Evolution du chiffre d'affaires hebdomadaire BtoC par tranche d'age", y="",x="")  + scale_y_continuous(labels=euro) +  theme(axis.text.x = element_text(angle = 90)) + geom_vline(data = ca_j, aes(xintercept = as.Date('2021-10-01')), linetype='dotted')

ggplot(data = clients,
  aes(week, price)) +
  stat_summary(aes(fill=categ),geom='bar',fun = sum,size=1,position ='stack')  +  geom_line(data = ca_week_c, aes(x = week, y=rolavr), color="red",size=1) +
   colScale +
  scale_x_date(labels = date_format("%b-%Y"),  breaks = "3 months") + labs(title="Evolution du chiffre d'affaire BtoC hebdomadaire par categorie", y="",x="")  + scale_y_continuous(labels=euro) + theme(axis.text.x = element_text(angle = 90)) + geom_vline(data = ca_j, aes(xintercept = as.Date('2021-10-01')), linetype='dotted')

# B to B
ca_week_btb <- b_to_b %>% group_by(week) %>% summarise(CA = sum(price)) %>% mutate(rolavr = rollmean(CA,3, na.pad = TRUE))

ggplot(data = b_to_b,
  aes(week, price)) +
  stat_summary(aes(fill=categ),geom='bar',fun = sum,position ='stack')  +  geom_line(data = ca_week_btb, aes(x = week, y=rolavr), color="red", size=1) +
   colScale +
  scale_x_date(labels = date_format("%b-%Y"),  breaks = "3 month") + labs(title="Evolution du chiffre d'affaires hebdomadaire BtoB par categorie", y="",x="")  + scale_y_continuous(labels=euro) + theme(axis.text.x = element_text(angle = 90)) + geom_vline(data = ca_j, aes(xintercept = as.Date('2021-10-01')), linetype='dotted')

On remarque une evidente chute du CA alentour Oct. 2, avec une baisse considérable des ventes de cat 1. En effet aucun produit de catégorie 1 a été vendu pendant le mois d’octobre 2021.

3 Correlations

3.1 Genre client/ categorie de livres achetés

Pour ce genre de corrélation entre variables qualitatives nous irons utiliser un test chi2:

library(broom)

sc <- table(clients$sex, clients$categ)
chi.sc <- chisq.test(sc)
print(chi.sc)
## 
##  Pearson's Chi-squared test
## 
## data:  sc
## X-squared = 20.213, df = 2, p-value = 4.08e-05
augment(chi.sc)
## # A tibble: 6 x 9
##   Var1  Var2  .observed  .prop .row.prop .col.prop .expected .resid .std.resid
##   <fct> <fct>     <int>  <dbl>     <dbl>     <dbl>     <dbl>  <dbl>      <dbl>
## 1 f     0        200793 0.317     0.610      0.518   201518. -1.62       -3.75
## 2 m     0        186488 0.295     0.615      0.482   185763.  1.68        3.75
## 3 f     1        111331 0.176     0.338      0.524   110493.  2.52        4.46
## 4 m     1        101017 0.160     0.333      0.476   101855. -2.62       -4.46
## 5 f     2         16980 0.0268    0.0516     0.517    17092. -0.858      -1.27
## 6 m     2         15868 0.0251    0.0523     0.483    15756.  0.894       1.27
ggplot(data = augment(chi.sc), aes(x=Var1, y=Var2)) + 
  geom_tile(aes(fill=.resid)) + 
  geom_text(aes(label=round(.resid,2))) + 
              labs(x="Sexe", y="Categorie") + scale_fill_gradient2() + theme_minimal() +theme(legend.position = "none")

Le test permet de rejeter H0 (sexe et catégorie d’achats sont indépendants): il y a donc, à un niveau alpha de 0.05, une corrélation entre le deux variables. Les différence plus accentués sont remarquables pour les achats de catégorie 1.

3.2 Relation entre la variable age,agegroup et autre variables

3.2.1 Difference entre plusieurs moyennes

Encore une fois, nous aborderons l’analyse suivante à but démonstratif, car on sait déjà qu’il existe une différence entre le moyennes des groupes étudiés.

Pour utiliser une ANOVA pour étudier la relation entre tranche d’age et montant des totale des achats on doit vérifier les conditions:

1) Les mesures sont indépendantes

2) Les échantillons ont des variances égales

3) Au moins 20 individus par échantillon, ou normalité des populations de chaque échantillon supposée ou vérifiée
stats_clients %>% group_by(agegroup) %>%summarise(total = sum(tot_achats), var=var(tot_achats), mean=mean(tot_achats)) %>% kable()
agegroup total var mean
(1928.925,1944] 232666.2 401305.2 891.4413
(1944,1959] 1013221.5 426121.1 911.9905
(1959,1974] 2202166.1 685199.4 1076.8538
(1974,1989] 4058100.5 1171571.2 1548.8933
(1989,2004.075] 3466543.9 899671.0 1354.6479
stats_clients %>% group_by(agegroup) %>% ggplot(aes(agegroup, tot_achats, fill= agegroup)) +geom_boxplot() + stat_summary(fun=mean) + scale_y_continuous(label=euro)

ggplot(stats_clients, aes(sample=tot_achats, group=agegroup)) +geom_qq() + geom_qq_line() + facet_wrap(~agegroup)

#ratio max var  / min var >> 2

Même en acceptant la première condition d’indépendance, on remarquera que les distributions des totaux par catégorie d’age ne remplissent pas la condition de normalité et le ratio entre les variances max/min est supérieur à 2.Nous allons nous servir donc du test non paramétrique de Kruskal-Wallis avant d’étudier la relation entre age considérée comme variable continue et total d’achats.

kruskal.test(stats_clients$tot_achats, stats_clients$agegroup)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  stats_clients$tot_achats and stats_clients$agegroup
## Kruskal-Wallis chi-squared = 481.41, df = 4, p-value < 2.2e-16

H0(égalité des moyennes entre groupes) est facilement rejeté.

D’après le résultat du test de Kruskal-Wallis, nous savons qu’il existe une différence significative entre les groupes, mais nous ne savons pas quelles paires de groupes sont différentes.

Il est possible d’utiliser la fonction pairwise.wilcox.test() pour calculer des comparaisons par paires entre les niveaux de groupe avec des corrections pour les tests multiples.

pairwise.wilcox.test(stats_clients$tot_achats, stats_clients$agegroup)
## 
##  Pairwise comparisons using Wilcoxon rank sum test with continuity correction 
## 
## data:  stats_clients$tot_achats and stats_clients$agegroup 
## 
##                 (1928.925,1944] (1944,1959] (1959,1974] (1974,1989]
## (1944,1959]     0.7840          -           -           -          
## (1959,1974]     0.0085          2.0e-05     -           -          
## (1974,1989]     < 2e-16         < 2e-16     < 2e-16     -          
## (1989,2004.075] 4.2e-14         < 2e-16     < 2e-16     4.9e-08    
## 
## P value adjustment method: holm

Les moyennes de chaque groupe sont toutes significativement différentes l’une de l’autre exceptée la différence entre 1928-1944 et 1944-1959. La tranche d’age est corrélée a donc une corrélation avec la somme totale des achats pour la plupart des catégories.


Nous allons résumer les statistiques demandées sur un tableau ou les clients sont regroupé par années de naissance:

final_stats <- stats_clients %>% group_by(birth) %>% summarise(moy_achats_tot = mean(tot_achats), moy_achats_mois = mean(achats_mois), panier_moyen = median(panier_moyen))

stats_clients %>% group_by(agegroup) %>% summarise(moy_achats_tot = mean(tot_achats), moy_achats_mois = mean(achats_mois), panier_moyen = median(panier_moyen)) %>% kable()
agegroup moy_achats_tot moy_achats_mois panier_moyen
(1928.925,1944] 891.4413 2.208972 18.99
(1944,1959] 911.9905 2.269802 18.91
(1959,1974] 1076.8538 2.895640 19.99
(1974,1989] 1548.8933 4.876256 27.94
(1989,2004.075] 1354.6479 1.780985 55.76

Ensuite nous allons étudier à l’aide de la regression lineaire la correlation entre l’année de naissance et les autre variables.

3.3 Total des achats par client en relation à l’année de naissance

stats_clients %>% group_by(agegroup) %>% summarise(moy_tot_achats = mean(tot_achats), sd_tot_achats = sd(tot_achats))
## # A tibble: 5 x 3
##   agegroup        moy_tot_achats sd_tot_achats
##   <fct>                    <dbl>         <dbl>
## 1 (1928.925,1944]           891.          633.
## 2 (1944,1959]               912.          653.
## 3 (1959,1974]              1077.          828.
## 4 (1974,1989]              1549.         1082.
## 5 (1989,2004.075]          1355.          949.
ggplot(stats_clients, aes(agegroup, tot_achats, fill = agegroup)) + geom_boxplot(show.legend = FALSE) + stat_summary(fun =mean, show.legend = FALSE) + scale_y_continuous(label=euro, n.breaks = 6)

stats_clients %>% ggplot(aes(birth, tot_achats, color=agegroup)) + geom_jitter(alpha=0.5) + scale_y_continuous(label=euro)

final_stats %>% ggplot(aes(birth, moy_achats_tot))  + geom_point() + geom_smooth(method = "lm", se=FALSE) + scale_y_continuous(label=euro)

cor.test(~ tot_achats + birth, data=stats_clients)
## 
##  Pearson's product-moment correlation
## 
## data:  tot_achats and birth
## t = 17.782, df = 8594, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.1679045 0.2086854
## sample estimates:
##       cor 
## 0.1883762
lr.agetot <- lm(tot_achats ~ birth, data=stats_clients)
lr.agemoytot <- lm(moy_achats_tot ~ birth, data=final_stats)
summary(lr.agetot)
## 
## Call:
## lm(formula = tot_achats ~ birth, data = stats_clients)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1538.9  -700.7  -235.6   500.3  3987.3 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.975e+04  1.183e+03  -16.70   <2e-16 ***
## birth        1.063e+01  5.978e-01   17.78   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 937.2 on 8594 degrees of freedom
## Multiple R-squared:  0.03549,    Adjusted R-squared:  0.03537 
## F-statistic: 316.2 on 1 and 8594 DF,  p-value: < 2.2e-16
summary(lr.agemoytot)
## 
## Call:
## lm(formula = moy_achats_tot ~ birth, data = final_stats)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -316.4 -150.5  -61.2  156.4  421.8 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -19623.05    2084.98  -9.412 2.79e-14 ***
## birth           10.56       1.06   9.961 2.61e-15 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 202.8 on 74 degrees of freedom
## Multiple R-squared:  0.5728, Adjusted R-squared:  0.567 
## F-statistic: 99.21 on 1 and 74 DF,  p-value: 2.611e-15

IL y a bien une corrélation significative, même si faible (r=0.19) entre tot_achats et birth. Si nous travaillons avec tous les données, un modèle linéaire est très faiblement significatif et permet d’expliquer seulement le 3% de variabilité de la variable dépendante. En revanche si on travaille avec la moyenne par groupe le modèle est beaucoup plus intéressant avec un R2 de 0.567.

3.4 Panier moyen par client en relation à l’année de naissance

stats_clients %>% group_by(agegroup) %>% summarise(p_moyen = mean(panier_moyen), sd_p_m = sd(panier_moyen))
## # A tibble: 5 x 3
##   agegroup        p_moyen sd_p_m
##   <fct>             <dbl>  <dbl>
## 1 (1928.925,1944]    21.3   9.98
## 2 (1944,1959]        21.3   9.46
## 3 (1959,1974]        23.1  10.8 
## 4 (1974,1989]        28.8   7.83
## 5 (1989,2004.075]    57.0  26.4
ggplot(stats_clients, aes(agegroup, panier_moyen, fill = agegroup)) + geom_boxplot(show.legend = FALSE) + stat_summary(fun =mean, show.legend = FALSE)

stats_clients %>% ggplot(aes(birth, panier_moyen, color=agegroup)) + geom_jitter(alpha=0.3) + scale_y_continuous(label=euro)

final_stats %>% ggplot(aes(birth, panier_moyen)) + geom_point() + geom_smooth(method='lm',se=FALSE) + geom_smooth(se=FALSE, linetype='dotted',color = 'orange') + scale_y_continuous(label=euro)

cor.test(stats_clients$birth, stats_clients$panier_moyen)
## 
##  Pearson's product-moment correlation
## 
## data:  stats_clients$birth and stats_clients$panier_moyen
## t = 67.42, df = 8594, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5741645 0.6018228
## sample estimates:
##       cor 
## 0.5881656
lr.pm <- lm(panier_moyen ~ birth, data=stats_clients)
summary(lr.pm)
## 
## Call:
## lm(formula = panier_moyen ~ birth, data = stats_clients)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -46.806 -10.307  -3.432   6.339 273.398 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.485e+03  2.254e+01  -65.88   <2e-16 ***
## birth        7.682e-01  1.139e-02   67.42   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 17.86 on 8594 degrees of freedom
## Multiple R-squared:  0.3459, Adjusted R-squared:  0.3459 
## F-statistic:  4545 on 1 and 8594 DF,  p-value: < 2.2e-16

Ici la relation est bien significative avec un r = 0.59 et un modèle linéaire est possible. Même si un modèle simple n’explique pas beaucoup de la variance, il est un point de départ.

3.5 Année de naissance et frequence des achats

stats_clients %>% group_by(agegroup) %>%summarise(n_achats_mois=mean(achats_mois), sd_achats_mois= sd(achats_mois))
## # A tibble: 5 x 3
##   agegroup        n_achats_mois sd_achats_mois
##   <fct>                   <dbl>          <dbl>
## 1 (1928.925,1944]          2.21           1.53
## 2 (1944,1959]              2.27           1.59
## 3 (1959,1974]              2.90           2.40
## 4 (1974,1989]              4.88           3.46
## 5 (1989,2004.075]          1.78           1.82
ggplot(stats_clients, aes(agegroup, achats_mois, fill = agegroup)) + geom_boxplot(show.legend = FALSE) + stat_summary(fun =mean, show.legend = FALSE)

stats_clients %>% ggplot(aes(birth, achats_mois, color=agegroup)) + geom_jitter(alpha=0.5) 

final_stats %>% ggplot(aes(birth, moy_achats_mois)) + geom_point() + geom_smooth(method='lm') + geom_smooth(se=FALSE, color='orange', linetype='dotted')

cor.test(~ achats_mois + birth, data=stats_clients)
## 
##  Pearson's product-moment correlation
## 
## data:  achats_mois and birth
## t = -2.8239, df = 8594, p-value = 0.004756
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.05155423 -0.00931282
## sample estimates:
##         cor 
## -0.03044712
lr.achm <- lm(achats_mois ~ birth, data=stats_clients)
summary(lr.achm)
## 
## Call:
## lm(formula = achats_mois ~ birth, data = stats_clients)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -3.2128 -1.9150 -0.9193  0.9087 13.7602 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 13.134927   3.565862   3.684 0.000231 ***
## birth       -0.005090   0.001802  -2.824 0.004756 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.826 on 8594 degrees of freedom
## Multiple R-squared:  0.000927,   Adjusted R-squared:  0.0008108 
## F-statistic: 7.974 on 1 and 8594 DF,  p-value: 0.004756

Ici on remarque bien une evidente relation non lineaire(mais significative) entre les deux variables et en effet comme souvent dans ces cas le coefficient r est proche de 0.

On resumera ces relations à l’aide d’un pairplot:

library(GGally)
ggpairs(stats_clients, aes(color=agegroup,alpha=0.5), columns = c(4:7) , progress = FALSE) +theme_minimal()

3.6 Relation entre tranche d’age et categories achetés

Pour terminer nous allons étudier la relation entre ces deux variable, pour vérifier la leur dépendance éventuelle.

stats_clients %>% group_by(agegroup) %>% summarise(across(cat_0:cat_2, sum)) %>% pivot_longer(cols=cat_0:cat_2,names_to = 'categorie', values_to = 'nombre_achats') %>% ggplot() + geom_tile(aes(agegroup,categorie, fill=nombre_achats)) + scale_fill_viridis_c()

tac <- table(clients$agegroup, clients$categ)
chitac <- chisq.test(tac)
chitac
## 
##  Pearson's Chi-squared test
## 
## data:  tac
## X-squared = 174517, df = 8, p-value < 2.2e-16
ggplot(data = augment(chitac), aes(x=Var1, y=Var2)) + 
  geom_tile(aes(fill=.resid)) + 
  geom_text(aes(label=round(.resid,2))) + 
              labs(x="Tranche d'age", y="Categorie") + scale_fill_gradient2() + theme_minimal() +theme(legend.position = "none")

Avec une p-value < 2.2e-16 on rejette l’hypothèse nulle d’indépendance. On pourra remarquer principalement la forte valeur du résidu en catégorie 2 pour la tranche d’age 1989-2004.


3.7 Probabilité conditionnelle

Probabilité qu’un client achète la référence 0_525 sachant qu’il a acheté la référence 2_159

La probabilité conditionnelle à calculer est la suivante : P(0_525 | 2_159) = P(0_525) ^ P(2_159) / P(2_159)

getproba <- function(subset) {mean(subset)}
conditional <- function(proposition, given) {getproba(proposition[given])}

pdtr_cust %>% group_by(client_id) %>% summarise(ref1 = if ('0_525' %in% id_prod) 1 else 0, ref2 = if ('2_159' %in% id_prod) 1 else 0 ) -> cl_ref

sub1 <- cl_ref$ref1 == 1
sub2 <- cl_ref$ref2 ==1
conditional(sub1, sub2)
## [1] 0.8660377
#ou alternativement
getproba(sub1 & sub2) / getproba(sub2)
## [1] 0.8660377

Le 86 % de clients qui ont acheté la référence 2_159 ont aussi acheté la référence 0_525; a noter aussi que tous les clients qui ont acheté la première réferénce ont aussi acheté la seconde.

4 Conclusions

Pour résumer:

final_stats %>% mutate(agegroup = cut(birth, 5)) %>% ggpairs(columns = c(1:4), aes(fill=agegroup, color=agegroup),progress = FALSE) + theme_light() + labs(title ='Correlations sur les données agrégés')

#donnés non agrégés
cor(stats_clients$birth, stats_clients[c(5:10)])
##      tot_achats achats_mois panier_moyen       cat_0      cat_1     cat_2
## [1,]  0.1883762 -0.03044712    0.5881656 -0.00264917 -0.2885691 0.5155918
#données agrégés
cor(final_stats$birth, final_stats[-1])
##      moy_achats_tot moy_achats_mois panier_moyen
## [1,]      0.7568219       0.2196597    0.7772562
  • tot_achats : bien que faiblement cette variable est bien corrélée à l’âge du client; cette relation est bien plus évidente quand on traite avec la moyenne. La catégorie qui tend à dépenser le plus en moyenne c’est les 1974-1989, suivi des générations plus jeunes.

  • panier moyen : cette corrélation est la plus forte entre celles enquêtées avec une tendance non linéaire très marqué. Danse ce cas on remarque bien la relation positive entre catégorie d’âge et panier moyen, ce dérnièr (en moyenne) tend à s’agrandir en baissant l’âge du client.

  • achats mois : encore une fois nous trouvons une relation significative, cette fois fortement non linéaire entre l’âge et la fréquence d’achats, avec des groupes bien démarqués. Les acheteurs plus fréquents ce sont ceux de la tranche 1974-1989, suivis de générations plus anciennes et en dernière la génération 1989-2004, qui en revanche démontre le panier moyen plus importante (et cela s’explique avec la préférence pour les produits de catégorie 2, bien plus chères des autres).

Nous avons aussi rémarqué une relation, bien que faible, entre sexe et catégorie de produits achetés et entre tranche d’âge et catégorie produit, bien marqué en particulier pour la jeune génération et la catégorie 2.