Notebook 2 Analyse des données¶
Importation du Notebook 1
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
donnees2 = pd.read_csv('donnees2.csv')
donnees2
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 13:21:29.043970 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 |
| 1 | 1_251 | 2022-02-02 07:55:19.149409 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 |
| 2 | 0_1277 | 2022-06-18 15:44:33.155329 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 |
| 3 | 2_209 | 2021-06-24 04:19:29.835891 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 |
| 4 | 0_1509 | 2023-01-11 08:22:08.194479 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 13:05:06.246925 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 |
| 679328 | 1_639 | 2022-03-19 16:03:23.429229 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 |
| 679329 | 0_1425 | 2022-12-20 04:33:37.584749 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 |
| 679330 | 0_1994 | 2021-07-16 20:36:35.350579 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 |
| 679331 | 1_523 | 2022-09-28 01:12:01.973763 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 |
679332 rows × 9 columns
I- Questions Antoine (Analyse des différents indicateurs de vente)¶
a. différents indicateurs et graphiques autour du chiffre d'affaires¶
Describe de la variable "price"¶
Max, min, moyenne, median, ecart-type
donnees2['price'].describe(include='all')
count 679332.000000 mean 17.452345 std 18.326510 min 0.620000 25% 8.870000 50% 13.990000 75% 18.990000 max 300.000000 Name: price, dtype: float64
Variance
donnees2['price'].var()
335.86097957484003
Describe de la variable "price" par categorie¶
Max, min, moyenne, median, ecart-type
donnees2.groupby(by='categ')['price'].describe()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| categ | ||||||||
| 0.0 | 415680.0 | 10.637843 | 4.932238 | 0.62 | 6.29 | 9.99 | 14.45 | 40.99 |
| 1.0 | 227169.0 | 20.485730 | 7.584894 | 2.00 | 15.81 | 19.08 | 24.98 | 80.99 |
| 2.0 | 36483.0 | 76.207412 | 39.749015 | 30.99 | 53.99 | 62.83 | 73.72 | 300.00 |
Variance
donnees2[['price','categ']].groupby('categ').var()
| price | |
|---|---|
| categ | |
| 0.0 | 24.326974 |
| 1.0 | 57.530610 |
| 2.0 | 1579.984161 |
sns.boxplot(x='categ', y='price',data=donnees2)
plt.title("Boxplot sur la variable prix par rapport à la catégorie", pad=20)
plt.show()
La catégorie 2 est la plus chère
Describe de la variable price par tranche d'âge¶
Définition des tranches d'âge
donnees2['age'].min()
19
donnees2['age'].max()
94
donnees2['age_cat']= pd.cut(donnees2['age'],
bins= [18,30,40,50,60,70,80,90,100],
labels= [']18-30]',']30-40]', ']40-50]',']50-60]',']60-70]',']70-80]',']80-90]',']90-100]'])
donnees2
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 13:21:29.043970 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] |
| 1 | 1_251 | 2022-02-02 07:55:19.149409 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] |
| 2 | 0_1277 | 2022-06-18 15:44:33.155329 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] |
| 3 | 2_209 | 2021-06-24 04:19:29.835891 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] |
| 4 | 0_1509 | 2023-01-11 08:22:08.194479 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 13:05:06.246925 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] |
| 679328 | 1_639 | 2022-03-19 16:03:23.429229 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] |
| 679329 | 0_1425 | 2022-12-20 04:33:37.584749 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] |
| 679330 | 0_1994 | 2021-07-16 20:36:35.350579 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] |
| 679331 | 1_523 | 2022-09-28 01:12:01.973763 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] |
679332 rows × 10 columns
donnees2[donnees2['age']==30]
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | |
|---|---|---|---|---|---|---|---|---|---|---|
| 302 | 1_652 | 2021-06-13 02:38:07.929888 | s_47897 | c_5739 | 27.52 | 1.0 | m | 1993 | 30 | ]18-30] |
| 430 | 0_1347 | 2021-11-11 07:16:04.592923 | s_117901 | c_5365 | 5.99 | 0.0 | m | 1993 | 30 | ]18-30] |
| 454 | 2_46 | 2021-08-25 07:32:34.090052 | s_80370 | c_3176 | 41.31 | 2.0 | m | 1993 | 30 | ]18-30] |
| 600 | 1_376 | 2022-04-29 05:52:28.866283 | s_200947 | c_2503 | 17.49 | 1.0 | m | 1993 | 30 | ]18-30] |
| 642 | 1_277 | 2022-02-05 22:20:29.834375 | s_160542 | c_3641 | 25.99 | 1.0 | m | 1993 | 30 | ]18-30] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 678554 | 0_1298 | 2021-03-11 14:56:01.898237 | s_4873 | c_1559 | 18.32 | 0.0 | m | 1993 | 30 | ]18-30] |
| 678717 | 1_369 | 2022-12-31 13:27:45.093038 | s_319955 | c_1018 | 23.99 | 1.0 | f | 1993 | 30 | ]18-30] |
| 678907 | 2_185 | 2022-06-21 10:29:57.071599 | s_227005 | c_2527 | 43.99 | 2.0 | f | 1993 | 30 | ]18-30] |
| 679015 | 0_1600 | 2021-10-14 01:11:32.206966 | s_104428 | c_4653 | 13.23 | 0.0 | m | 1993 | 30 | ]18-30] |
| 679206 | 1_644 | 2021-12-25 02:21:15.709734 | s_139569 | c_6113 | 25.79 | 1.0 | f | 1993 | 30 | ]18-30] |
4553 rows × 10 columns
Describe
donnees2.groupby(by='age_cat')['price'].describe()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| age_cat | ||||||||
| ]18-30] | 74041.0 | 41.394067 | 39.076017 | 0.62 | 14.99 | 25.99 | 57.99 | 300.00 |
| ]30-40] | 181444.0 | 14.068553 | 11.755676 | 0.62 | 7.99 | 11.99 | 17.29 | 247.22 |
| ]40-50] | 227765.0 | 13.195886 | 8.821735 | 0.62 | 7.45 | 11.99 | 16.99 | 300.00 |
| ]50-60] | 105921.0 | 16.258741 | 10.970972 | 0.66 | 9.99 | 14.99 | 19.99 | 247.22 |
| ]60-70] | 55952.0 | 16.793220 | 11.322644 | 0.62 | 10.71 | 15.99 | 20.99 | 236.99 |
| ]70-80] | 22526.0 | 16.632380 | 10.025043 | 0.99 | 10.71 | 15.99 | 20.99 | 158.17 |
| ]80-90] | 10775.0 | 16.830731 | 10.559323 | 0.62 | 10.73 | 15.99 | 20.99 | 225.17 |
| ]90-100] | 908.0 | 16.623744 | 9.372838 | 2.35 | 10.79 | 15.99 | 20.99 | 145.99 |
variance
donnees2[['price','age_cat']].groupby('age_cat').var()
| price | |
|---|---|
| age_cat | |
| ]18-30] | 1526.935136 |
| ]30-40] | 138.195924 |
| ]40-50] | 77.823003 |
| ]50-60] | 120.362217 |
| ]60-70] | 128.202261 |
| ]70-80] | 100.501486 |
| ]80-90] | 111.499296 |
| ]90-100] | 87.850089 |
sns.boxplot(x='age_cat', y='price',data=donnees2,showmeans=True)
plt.title("Boxplot sur la variable price par rapport à age_cat", pad=20)
plt.show()
La categorie 18-30 dépense le plus. On voit que leur médiane est de loin supérieur à celle des autres categories
Evolution du chiffre d'affaire (par jour puis par mois)¶
Convertion date en format yyyy-mm-dd
donnees2.dtypes
id_prod object date object session_id object client_id object price float64 categ float64 sex object birth int64 age int64 age_cat category dtype: object
donnees2['date']=pd.to_datetime(donnees2['date'],format ='%Y-%m-%d')
donnees2['date']=donnees2['date'].dt.strftime('%Y-%m-%d')
donnees2['date']=pd.to_datetime(donnees2['date'],format ='%Y-%m-%d')
donnees2
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] |
| 1 | 1_251 | 2022-02-02 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] |
| 2 | 0_1277 | 2022-06-18 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] |
| 3 | 2_209 | 2021-06-24 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] |
| 4 | 0_1509 | 2023-01-11 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] |
| 679328 | 1_639 | 2022-03-19 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] |
| 679329 | 0_1425 | 2022-12-20 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] |
| 679330 | 0_1994 | 2021-07-16 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] |
| 679331 | 1_523 | 2022-09-28 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] |
679332 rows × 10 columns
Courbe évolution ca par jour
donnees_jour_ca = donnees2.groupby([donnees2['date'].dt.date])['price'].sum()
#donnees_jour_ca['date']= donnees2.groupby([donnees2['date'].dt.date])['price'].sum()[0]
#donnees_jour_ca['price']= donnees2.groupby([donnees2['date'].dt.date])['price'].sum()[1]
donnees_jour_ca
date
2021-03-01 16575.21
2021-03-02 15496.44
2021-03-03 15198.69
2021-03-04 15196.07
2021-03-05 17471.37
...
2023-02-24 15207.89
2023-02-25 15761.25
2023-02-26 16304.72
2023-02-27 19170.81
2023-02-28 18105.15
Name: price, Length: 730, dtype: float64
donnees_jour_ca.info()
<class 'pandas.core.series.Series'> Index: 730 entries, 2021-03-01 to 2023-02-28 Series name: price Non-Null Count Dtype -------------- ----- 730 non-null float64 dtypes: float64(1) memory usage: 27.6+ KB
donnees3= donnees_jour_ca.reset_index(name='price')
donnees3['date']=pd.to_datetime(donnees2['date'],format ='%Y-%m-%d')
donnees3
| date | price | |
|---|---|---|
| 0 | 2022-05-20 | 16575.21 |
| 1 | 2022-02-02 | 15496.44 |
| 2 | 2022-06-18 | 15198.69 |
| 3 | 2021-06-24 | 15196.07 |
| 4 | 2023-01-11 | 17471.37 |
| ... | ... | ... |
| 725 | 2022-10-11 | 15207.89 |
| 726 | 2022-03-07 | 15761.25 |
| 727 | 2022-06-14 | 16304.72 |
| 728 | 2022-01-12 | 19170.81 |
| 729 | 2022-11-03 | 18105.15 |
730 rows × 2 columns
donnees3.dtypes
date datetime64[ns] price float64 dtype: object
df = donnees3.sort_values('date')
df
| date | price | |
|---|---|---|
| 112 | 2021-03-01 | 15619.79 |
| 404 | 2021-03-02 | 15009.22 |
| 420 | 2021-03-03 | 17298.11 |
| 702 | 2021-03-05 | 16718.43 |
| 149 | 2021-03-07 | 15848.94 |
| ... | ... | ... |
| 456 | 2023-02-26 | 15244.74 |
| 44 | 2023-02-26 | 15956.47 |
| 507 | 2023-02-27 | 17017.96 |
| 636 | 2023-02-28 | 15794.71 |
| 364 | 2023-02-28 | 18868.26 |
730 rows × 2 columns
plt.figure(figsize=(18,8))
plt.plot(df['date'],df['price'],marker='o', linestyle='--', color='blue')
plt.title("Evolution du chiffre d'affaire par jour", fontname='Arial',fontsize=30)
plt.xlabel("jour", fontname='Arial', fontsize=18)
plt.ylabel("chiffre d'affaire produit en milliers d'euros", fontname='Arial', fontsize=18)
plt.show()
evolution du ca par mois
df1= donnees2.set_index(donnees2.date)
df1['date']= pd.to_datetime(df1['date'])
df1
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | |
|---|---|---|---|---|---|---|---|---|---|---|
| date | ||||||||||
| 2022-05-20 | 0_1518 | 2022-05-20 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] |
| 2022-02-02 | 1_251 | 2022-02-02 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] |
| 2022-06-18 | 0_1277 | 2022-06-18 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] |
| 2021-06-24 | 2_209 | 2021-06-24 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] |
| 2023-01-11 | 0_1509 | 2023-01-11 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-01-15 | 0_1551 | 2022-01-15 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] |
| 2022-03-19 | 1_639 | 2022-03-19 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] |
| 2022-12-20 | 0_1425 | 2022-12-20 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] |
| 2021-07-16 | 0_1994 | 2021-07-16 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] |
| 2022-09-28 | 1_523 | 2022-09-28 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] |
679332 rows × 10 columns
donnees4= df1.groupby(pd.Grouper(freq="M"))["price"].sum().reset_index()
donnees4
| date | price | |
|---|---|---|
| 0 | 2021-03-31 | 482530.52 |
| 1 | 2021-04-30 | 476249.16 |
| 2 | 2021-05-31 | 493023.39 |
| 3 | 2021-06-30 | 484158.49 |
| 4 | 2021-07-31 | 482875.36 |
| 5 | 2021-08-31 | 482374.70 |
| 6 | 2021-09-30 | 507360.56 |
| 7 | 2021-10-31 | 320868.67 |
| 8 | 2021-11-30 | 516267.63 |
| 9 | 2021-12-31 | 525987.21 |
| 10 | 2022-01-31 | 525388.94 |
| 11 | 2022-02-28 | 535681.39 |
| 12 | 2022-03-31 | 515566.42 |
| 13 | 2022-04-30 | 493138.80 |
| 14 | 2022-05-31 | 517292.44 |
| 15 | 2022-06-30 | 496086.05 |
| 16 | 2022-07-31 | 510903.00 |
| 17 | 2022-08-31 | 506547.19 |
| 18 | 2022-09-30 | 494204.44 |
| 19 | 2022-10-31 | 508017.67 |
| 20 | 2022-11-30 | 496774.83 |
| 21 | 2022-12-31 | 510279.44 |
| 22 | 2023-01-31 | 517610.48 |
| 23 | 2023-02-28 | 456749.69 |
plt.figure(figsize=(18,8))
plt.plot(donnees4['date'],donnees4['price'],marker='o', linestyle='--', color='purple')
plt.title("Evolution du chiffre d'affaire par mois", fontname='Arial',fontsize=30)
plt.xlabel("mois", fontname='Arial', fontsize=18)
plt.ylabel("chiffre d'affaire produit en milliers d'euros", fontname='Arial', fontsize=18)
plt.show()
chute du CA au mois de nov 2021. Debut de chute en mars 2023
Graphique avec la moyenne mobile de la variable price¶
price = df1['price']
price
date
2022-05-20 4.18
2022-02-02 15.99
2022-06-18 7.99
2021-06-24 69.99
2023-01-11 4.99
...
2022-01-15 12.99
2022-03-19 10.99
2022-12-20 12.99
2021-07-16 4.98
2022-09-28 23.99
Name: price, Length: 679332, dtype: float64
affichage de l'evolution du prix par jour puis par semaine puis par mois
fig, ax = plt.subplots(3, figsize=(18,7))
price_j = price.resample('D').sum()
price_j.plot(ax=ax[0],legend=False)
ax[0].set_ylabel('Décompte journalier des prix')
price_w = price.resample('W').sum()
price_w.plot(ax=ax[1], legend=False)
ax[1].set_ylabel('Décompte hebdo des prix');
price_m = price.resample('M').sum()
price_m.plot(ax=ax[2], legend=False)
ax[2].set_ylabel('Décompte mois des prix');
confirme le commentaire precedent
decomposition en moyenne mobile de la variable price permettant d'évaluer la tendance globale
plt.figure(figsize=(20,12))
plt.plot(price_j)
plt.plot(price_j.rolling(30, center=True).mean()) #moyen sur 30
plt.show()
b. zoom sur les références¶
faire un zoom sur les références, pour voir un peu les tops et les flops, la répartition par catégorie, etc.
Produits les plus vendus
top_produits = donnees2.groupby('id_prod')['session_id'].count().reset_index(name='count').sort_values(['count'], ascending=False)
top_produits
| id_prod | count | |
|---|---|---|
| 2592 | 1_369 | 2252 |
| 2645 | 1_417 | 2189 |
| 2642 | 1_414 | 2180 |
| 2734 | 1_498 | 2128 |
| 2654 | 1_425 | 2096 |
| ... | ... | ... |
| 313 | 0_1284 | 1 |
| 1793 | 0_549 | 1 |
| 549 | 0_1498 | 1 |
| 1785 | 0_541 | 1 |
| 2167 | 0_886 | 1 |
3266 rows × 2 columns
Produit les moins vendus
flops_produits = donnees2.groupby('id_prod')['session_id'].count().reset_index(name='count').sort_values(['count'], ascending=True)
flops_produits
| id_prod | count | |
|---|---|---|
| 1793 | 0_549 | 1 |
| 1327 | 0_2201 | 1 |
| 3176 | 2_23 | 1 |
| 313 | 0_1284 | 1 |
| 752 | 0_1683 | 1 |
| ... | ... | ... |
| 2654 | 1_425 | 2096 |
| 2734 | 1_498 | 2128 |
| 2642 | 1_414 | 2180 |
| 2645 | 1_417 | 2189 |
| 2592 | 1_369 | 2252 |
3266 rows × 2 columns
par catégorie
data_categ = donnees2.groupby('categ').agg({"id_prod" :[np.size], "price" :[np.sum]}).reset_index()
data_categ['prop_CA']= round((data_categ['price', 'sum']/data_categ['price', 'sum'].sum())*100,2)
data_categ['prop_size']= round((data_categ['id_prod', 'size']/data_categ['id_prod', 'size'].sum())*100,2)
data_categ
| categ | id_prod | price | prop_CA | prop_size | |
|---|---|---|---|---|---|
| size | sum | ||||
| 0 | 0.0 | 415680 | 4421938.76 | 37.30 | 61.19 |
| 1 | 1.0 | 227169 | 4653722.69 | 39.25 | 33.44 |
| 2 | 2.0 | 36483 | 2780275.02 | 23.45 | 5.37 |
On voit que le CA le plus elevee provient de la categorie 1. tandis que le nombre de vente le plus elevee provient de la categorie 0. Tres peu de vente de la catégorie 2 bien que le CA n'est pas négligeable.
Poid des catégories en nb de vente (volume)¶
categ = data_categ['categ']
id_size_cat = data_categ['id_prod','size']
plt.figure(figsize=(12,5))
plt.bar(categ ,id_size_cat, width = 0.5, color = 'seagreen', edgecolor = 'white')
plt.title('Volume des ventes par catégorie', fontsize=15)
plt.xlabel('Category', fontsize=10)
plt.ylabel('Quantity', fontsize=10)
plt.show()
# Proportion des catégories en volume
labels_categ = data_categ['categ']
colors3 = ['seagreen', 'darkgreen', 'teal']
plt.figure(figsize=(12,5))
plt.pie(data_categ['prop_size'], labels=labels_categ, autopct='%1.1f%%', colors=colors3)
plt.title('Proportion des ventes par catégorie', fontsize=15)
plt.legend()
plt.show()
#Distribution des prix par categories
plt.figure(figsize=(12,5))
sns.boxplot(
data=donnees2, y='categ', x='price',
showmeans=True, showfliers=False, orient='h', palette='Greens')
plt.title('Distribution des prix par catégorie', fontsize=15)
plt.show()
les produits les moins chers proviennent de la catégories 0 . Et les plus chers de la categorie 2.
c. Courbe de Lorenz¶
j’aimerais avoir quelques informations sur les profils de nos clients, et également la répartition du chiffre d'affaires entre eux, via une courbe de Lorenz.
Je calcule le CA par client
ca_client = donnees2.groupby('client_id')['price'].sum()
df_ca_client = ca_client.reset_index(name='price')
df_ca_client.sort_values(['price'], ascending=False)
| client_id | price | |
|---|---|---|
| 677 | c_1609 | 324033.35 |
| 4388 | c_4958 | 289760.34 |
| 6337 | c_6714 | 153658.86 |
| 2724 | c_3454 | 113667.90 |
| 2513 | c_3263 | 5276.87 |
| ... | ... | ... |
| 4044 | c_4648 | 11.20 |
| 1556 | c_240 | 11.06 |
| 7889 | c_8114 | 9.98 |
| 7918 | c_8140 | 8.30 |
| 8151 | c_8351 | 6.31 |
8600 rows × 2 columns
J'affiche le résultat via la Courbe de lorenz
globale = df_ca_client['price']
lorenz = np.cumsum(np.sort(globale)) / globale.sum()
lorenz = np.append([0], lorenz) # La courbe de Lorenz commence à 0
y = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
x = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
plt.plot(x,y, label ="Droite d'équirépartition")
plt.plot(np.linspace(0,1, len(lorenz)), lorenz, drawstyle='steps-post', label = 'Courbe de Lorenz')
plt.legend()
plt.show()
La zone entre la droite d'équirépartition et la courbe de Lorenz représente l'inégalité dans la distribution. On peut donc voir qu'ici il y a une distribution non équitable du CA entre les clients. Notons: 50% du CA est généré par 80% des clients
courbe de lorenz sur les references (=id prod)
ca_prod=donnees2.groupby('id_prod')['price'].sum()
df_ca_prod = ca_prod.reset_index(name='price')
df_ca_prod.sort_values(['price'], ascending=False)
| id_prod | price | |
|---|---|---|
| 3097 | 2_159 | 94893.50 |
| 3071 | 2_135 | 69334.95 |
| 3046 | 2_112 | 65407.76 |
| 3035 | 2_102 | 60736.78 |
| 3153 | 2_209 | 56971.86 |
| ... | ... | ... |
| 665 | 0_1601 | 1.99 |
| 2080 | 0_807 | 1.99 |
| 719 | 0_1653 | 1.98 |
| 313 | 0_1284 | 1.38 |
| 595 | 0_1539 | 0.99 |
3266 rows × 2 columns
globale = df_ca_prod['price']
lorenz = np.cumsum(np.sort(globale)) / globale.sum()
lorenz = np.append([0], lorenz) # La courbe de Lorenz commence à 0
y = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
x = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
plt.plot(x,y, label ="Droite d'équirépartition")
plt.plot(np.linspace(0,1, len(lorenz)), lorenz, drawstyle='steps-post', label = 'Courbe de Lorenz')
plt.legend()
plt.show()
plus forte inégalité. Distribution non équitable des produits par rapport au CA. Il y a 50% des produits qui représente 90% du CA
d. Information profil client¶
donnees2
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] |
| 1 | 1_251 | 2022-02-02 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] |
| 2 | 0_1277 | 2022-06-18 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] |
| 3 | 2_209 | 2021-06-24 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] |
| 4 | 0_1509 | 2023-01-11 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] |
| 679328 | 1_639 | 2022-03-19 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] |
| 679329 | 0_1425 | 2022-12-20 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] |
| 679330 | 0_1994 | 2021-07-16 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] |
| 679331 | 1_523 | 2022-09-28 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] |
679332 rows × 10 columns
client_classe_dage= donnees2.groupby('age_cat')['price'].sum()
client_classe_dage = client_classe_dage.reset_index(name='price')
client_classe_dage['prop_CA']= round((client_classe_dage['price']/client_classe_dage['price'].sum())*100,2)
client_classe_dage
| age_cat | price | prop_CA | |
|---|---|---|---|
| 0 | ]18-30] | 3064858.12 | 25.85 |
| 1 | ]30-40] | 2552654.54 | 21.53 |
| 2 | ]40-50] | 3005561.01 | 25.35 |
| 3 | ]50-60] | 1722142.06 | 14.53 |
| 4 | ]60-70] | 939614.26 | 7.93 |
| 5 | ]70-80] | 374660.99 | 3.16 |
| 6 | ]80-90] | 181351.13 | 1.53 |
| 7 | ]90-100] | 15094.36 | 0.13 |
labels_categ = client_classe_dage['prop_CA']
plt.figure(figsize=(12,13))
plt.pie(client_classe_dage['prop_CA'], labels=client_classe_dage['age_cat'],autopct='%1.1f%%')
plt.title('Proportion du CA par classe dage', fontsize=15)
plt.legend()
plt.show()
Classe d'age qui a le plus gros CA : 18-30ans
proportion CA par rapport au genre
client_genre= donnees2.groupby('sex')['price'].sum()
client_genre = client_genre.reset_index(name='price')
client_genre['prop_CA']= round((client_genre['price']/client_genre['price'].sum())*100,2)
client_genre
| sex | price | prop_CA | |
|---|---|---|---|
| 0 | f | 5860851.96 | 49.43 |
| 1 | m | 5995084.51 | 50.57 |
labels_categ = client_genre['prop_CA']
plt.figure(figsize=(12,13))
plt.pie(client_genre['prop_CA'], labels=client_genre['sex'],autopct='%1.1f%%')
plt.title('Proportion du CA par rapport au genre', fontsize=15)
plt.legend()
plt.show()
Le CA n'est pas impacté selon le genre
II- Questions de Julie (Analyse ciblée sur les clients)¶
Le lien entre le genre d’un client et les catégories des livres achetés¶
sns.countplot(data=donnees2,x='categ',hue='sex')
plt.title('Le lien entre le genre d’un client et les catégories des livres achetés')
plt.show()
TabContingence = pd.crosstab(donnees2['categ'], donnees2['sex'], margins=False)
print('Table de contingence :')
print(TabContingence)
Table de contingence : sex f m categ 0.0 206220 209460 1.0 114899 112270 2.0 17283 19200
Les categories sont independantes du genre ? (h0) On procède à un test d'independance entre les 2 variables qualitatives
stats.chi2_contingency(TabContingence)
(147.00253568681114,
1.1989607410166063e-32,
2,
array([[207066.56444861, 208613.43555139],
[113161.81769444, 114007.18230556],
[ 18173.61785695, 18309.38214305]]))
pvalue=stats.chi2_contingency(TabContingence)[1]
pvalue
1.1989607410166063e-32
pvalue est inferieur à 0.5 donc H0 est fausse . Les categories sont dependantes du genre.
Le lien entre l’âge des clients et le montant total des achats¶
client_classe_dage
| age_cat | price | prop_CA | |
|---|---|---|---|
| 0 | ]18-30] | 3064858.12 | 25.85 |
| 1 | ]30-40] | 2552654.54 | 21.53 |
| 2 | ]40-50] | 3005561.01 | 25.35 |
| 3 | ]50-60] | 1722142.06 | 14.53 |
| 4 | ]60-70] | 939614.26 | 7.93 |
| 5 | ]70-80] | 374660.99 | 3.16 |
| 6 | ]80-90] | 181351.13 | 1.53 |
| 7 | ]90-100] | 15094.36 | 0.13 |
fig, ax = plt.subplots()
plt.figure(figsize=(20,15))
#bar_labels = ['red', 'blue', '_red', 'orange']
#bar_colors = ['tab:red', 'tab:blue', 'tab:red', 'tab:orange']
ax.bar(client_classe_dage['age_cat'], client_classe_dage['price'], color= ['green','yellow','red','blue','orange','pink','purple','grey'])
ax.set_ylabel('montant total')
ax.set_title('Montant total pour chaque catégorie dage')
plt.show()
<Figure size 2000x1500 with 0 Axes>
la classe ]18-30] apporte le plus gros CA vu que le montant total d'achat pour cette categorie est la plus haute
client_ca=donnees2.pivot_table(
index='client_id', values='price',
aggfunc='sum').reset_index().rename(
columns={'price': 'montant_achats'})
client_ca
| client_id | montant_achats | |
|---|---|---|
| 0 | c_1 | 558.18 |
| 1 | c_10 | 1353.60 |
| 2 | c_100 | 254.85 |
| 3 | c_1000 | 2261.89 |
| 4 | c_1001 | 1812.86 |
| ... | ... | ... |
| 8595 | c_995 | 189.41 |
| 8596 | c_996 | 1625.58 |
| 8597 | c_997 | 1490.01 |
| 8598 | c_998 | 2779.88 |
| 8599 | c_999 | 701.40 |
8600 rows × 2 columns
df2 = donnees2.merge(client_ca,on='client_id', how='left')
df2.sample(3)
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat | montant_achats | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 353043 | 0_2038 | 2022-11-09 | s_295002 | c_605 | 4.39 | 0.0 | m | 1991 | 32 | ]30-40] | 3971.93 |
| 152375 | 0_999 | 2021-09-16 | s_90932 | c_1611 | 9.99 | 0.0 | m | 1981 | 42 | ]40-50] | 2758.27 |
| 163708 | 0_1328 | 2021-04-30 | s_28047 | c_6881 | 8.52 | 0.0 | m | 1980 | 43 | ]40-50] | 3088.84 |
plt.figure(figsize=(8, 4))
sns.boxplot(data=df2, y='age_cat', x='montant_achats', showfliers=False)
plt.title('Montant d\'achat par age')
plt.show()
On déduis que la classe ]40-50] est celle qui contient les clients qui dépensent le plus cher. Bien que les clients agés entre ]40-50] ne soient pas aussi nombreux que la classe ]18-30]
plt.figure(figsize=(8, 4))
sns.scatterplot(data=df2.sample(200), x='age', y='montant_achats', hue='age_cat')
plt.ylim([0, 5000])
plt.title('Distribution des prix par classe d\'âge')
plt.show()
H0: L'age n'est pas corrélé au montant d'achat Ha: Les 2 variables sont corrélées
stats.pearsonr(df2['age'], df2['montant_achats'])
PearsonRResult(statistic=-0.04999539338547815, pvalue=0.0)
Une negative correlation indique 2 variables sont dans opposite directions. La valeur de p est inférieure à 0,05, on rejete l'hypothèse nulle. l’âge des clients et le montant total des achats sont corrélées.
Le lien entre l’âge des clients et la fréquence d’achat¶
client_frq=donnees2.pivot_table(
index='client_id', values='session_id',
aggfunc='count').reset_index().rename(
columns={'session_id': 'freq_achats'})
client_frq
| client_id | freq_achats | |
|---|---|---|
| 0 | c_1 | 39 |
| 1 | c_10 | 58 |
| 2 | c_100 | 8 |
| 3 | c_1000 | 125 |
| 4 | c_1001 | 102 |
| ... | ... | ... |
| 8595 | c_995 | 14 |
| 8596 | c_996 | 95 |
| 8597 | c_997 | 59 |
| 8598 | c_998 | 53 |
| 8599 | c_999 | 46 |
8600 rows × 2 columns
client_age= donnees2[['client_id','age_cat']]
client_age
| client_id | age_cat | |
|---|---|---|
| 0 | c_103 | ]30-40] |
| 1 | c_8534 | ]30-40] |
| 2 | c_6714 | ]50-60] |
| 3 | c_6941 | ]18-30] |
| 4 | c_4232 | ]40-50] |
| ... | ... | ... |
| 679327 | c_8489 | ]70-80] |
| 679328 | c_4370 | ]40-50] |
| 679329 | c_304 | ]30-40] |
| 679330 | c_2227 | ]30-40] |
| 679331 | c_3873 | ]18-30] |
679332 rows × 2 columns
client_age.value_counts(subset=['client_id'])
client_id
c_1609 25488
c_6714 9187
c_3454 6773
c_4958 5195
c_3263 403
...
c_240 1
c_5962 1
c_8351 1
c_4478 1
c_6292 1
Length: 8600, dtype: int64
client_age.drop_duplicates(subset=['client_id'],inplace=True)
client_age
C:\Users\User\AppData\Local\Temp\ipykernel_18096\1785311510.py:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy client_age.drop_duplicates(subset=['client_id'],inplace=True)
| client_id | age_cat | |
|---|---|---|
| 0 | c_103 | ]30-40] |
| 1 | c_8534 | ]30-40] |
| 2 | c_6714 | ]50-60] |
| 3 | c_6941 | ]18-30] |
| 4 | c_4232 | ]40-50] |
| ... | ... | ... |
| 542530 | c_5401 | ]30-40] |
| 566887 | c_3615 | ]18-30] |
| 567963 | c_8114 | ]60-70] |
| 577591 | c_240 | ]18-30] |
| 600335 | c_305 | ]18-30] |
8600 rows × 2 columns
frq_age= client_frq.merge(client_age,on='client_id', how='outer')
frq_age
| client_id | freq_achats | age_cat | |
|---|---|---|---|
| 0 | c_1 | 39 | ]60-70] |
| 1 | c_10 | 58 | ]60-70] |
| 2 | c_100 | 8 | ]30-40] |
| 3 | c_1000 | 125 | ]50-60] |
| 4 | c_1001 | 102 | ]40-50] |
| ... | ... | ... | ... |
| 8595 | c_995 | 14 | ]60-70] |
| 8596 | c_996 | 95 | ]50-60] |
| 8597 | c_997 | 59 | ]18-30] |
| 8598 | c_998 | 53 | ]18-30] |
| 8599 | c_999 | 46 | ]50-60] |
8600 rows × 3 columns
df3 = df2.merge(frq_age,on='client_id', how='left')
df3
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat_x | montant_achats | freq_achats | age_cat_y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] | 2288.49 | 195 | ]30-40] |
| 1 | 1_251 | 2022-02-02 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] | 3498.14 | 247 | ]30-40] |
| 2 | 0_1277 | 2022-06-18 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] | 153658.86 | 9187 | ]50-60] |
| 3 | 2_209 | 2021-06-24 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] | 886.50 | 17 | ]18-30] |
| 4 | 0_1509 | 2023-01-11 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] | 2381.56 | 168 | ]40-50] |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] | 1088.84 | 73 | ]70-80] |
| 679328 | 1_639 | 2022-03-19 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] | 1186.77 | 84 | ]40-50] |
| 679329 | 0_1425 | 2022-12-20 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] | 1692.89 | 117 | ]30-40] |
| 679330 | 0_1994 | 2021-07-16 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] | 3056.29 | 234 | ]30-40] |
| 679331 | 1_523 | 2022-09-28 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] | 2207.57 | 86 | ]18-30] |
679332 rows × 13 columns
plt.figure(figsize=(8, 4))
sns.boxplot(data=frq_age, y='age_cat', x='freq_achats', showfliers=False)
plt.title('Fréquence achat par age')
plt.show()
Plus forte frequence d'achat : ]40-50] Plus faible frequence d'achat : ]18-30] donc si on prend un client de cette catégorie, il ne revient pas souvent malgré que cette catégorie est la plus présente (en nombre)
plt.figure(figsize=(8, 4))
sns.scatterplot(data=frq_age.sample(150), x='age_cat', y='freq_achats', hue='age_cat')
plt.ylim([0, 1000])
plt.title('Distribution Fréquence d\'achat par classe d\'âge')
plt.show()
Test de Pearson. H0: L'age n'est pas corrélé au fréquence d'achat Ha: Les 2 variables sont corrélées
stats.pearsonr(df3['age'], df3['freq_achats'])
PearsonRResult(statistic=-0.01860895810424756, pvalue=4.189586899241133e-53)
La valeur de pvalue < 0,05, onrejete l'hypothèse nulle. l’âge des clients et fréquence d'achats sont corrélés.
Le lien entre l’âge des clients et la taille du panier moyen¶
average basket= diviser CA par le nombre de commandes
df3['avg_b']=round (df3['montant_achats']/df3['freq_achats'],2)
df3
| id_prod | date | session_id | client_id | price | categ | sex | birth | age | age_cat_x | montant_achats | freq_achats | age_cat_y | avg_b | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0_1518 | 2022-05-20 | s_211425 | c_103 | 4.18 | 0.0 | f | 1986 | 37 | ]30-40] | 2288.49 | 195 | ]30-40] | 11.74 |
| 1 | 1_251 | 2022-02-02 | s_158752 | c_8534 | 15.99 | 1.0 | m | 1988 | 35 | ]30-40] | 3498.14 | 247 | ]30-40] | 14.16 |
| 2 | 0_1277 | 2022-06-18 | s_225667 | c_6714 | 7.99 | 0.0 | f | 1968 | 55 | ]50-60] | 153658.86 | 9187 | ]50-60] | 16.73 |
| 3 | 2_209 | 2021-06-24 | s_52962 | c_6941 | 69.99 | 2.0 | m | 2000 | 23 | ]18-30] | 886.50 | 17 | ]18-30] | 52.15 |
| 4 | 0_1509 | 2023-01-11 | s_325227 | c_4232 | 4.99 | 0.0 | m | 1980 | 43 | ]40-50] | 2381.56 | 168 | ]40-50] | 14.18 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 679327 | 0_1551 | 2022-01-15 | s_150195 | c_8489 | 12.99 | 0.0 | f | 1951 | 72 | ]70-80] | 1088.84 | 73 | ]70-80] | 14.92 |
| 679328 | 1_639 | 2022-03-19 | s_181434 | c_4370 | 10.99 | 1.0 | f | 1977 | 46 | ]40-50] | 1186.77 | 84 | ]40-50] | 14.13 |
| 679329 | 0_1425 | 2022-12-20 | s_314704 | c_304 | 12.99 | 0.0 | f | 1988 | 35 | ]30-40] | 1692.89 | 117 | ]30-40] | 14.47 |
| 679330 | 0_1994 | 2021-07-16 | s_63204 | c_2227 | 4.98 | 0.0 | m | 1986 | 37 | ]30-40] | 3056.29 | 234 | ]30-40] | 13.06 |
| 679331 | 1_523 | 2022-09-28 | s_274568 | c_3873 | 23.99 | 1.0 | m | 1995 | 28 | ]18-30] | 2207.57 | 86 | ]18-30] | 25.67 |
679332 rows × 14 columns
plt.figure(figsize=(8, 4))
sns.boxplot(data=df3, y='age_cat_y', x='avg_b', showfliers=False)
plt.title('Panier moyen par age')
plt.show()
Plus gros panier moyen : ]18-30]
plt.figure(figsize=(8, 4))
sns.scatterplot(data=df3.sample(150), x='age', y='avg_b', hue='age_cat_y')
plt.title('Distribution panier moyen par âge')
plt.show()
h0: L'age n'est pas corrélé au panier moyen Ha: Les 2 variables sont corrélées
stats.pearsonr(df3['age'], df3['avg_b'])
PearsonRResult(statistic=-0.3754538317993842, pvalue=0.0)
stats pearson négative, p_value < 0.05, l'age et panier moyen sont corrélés.
le lien entre l’âge des clients et les catégories des livres achetés¶
L'analyse de la variance est utile pour vérifier la corrélation entre une qualitative et une quantitative.
plt.figure(figsize=(8,5))
sns.displot(data=donnees2, x='age', hue='categ', kind='kde', fill=True)
plt.title('Volume des transactions par âge et catégorie')
plt.show()
<Figure size 800x500 with 0 Axes>
Les 3 catégories sont consommées par toutes les classes d'âge. Mais les acheteurs de la catégorie 0 sont principalement les clients 30-50 ans, tandis que la catégorie 2 est consommée quasi exclusivement par les moins de 30 ans
sns.boxplot(data=donnees2, x='age', y='categ', orient='h', showfliers=False, showmeans=True)
plt.title('Distribution des âges par catégorie')
plt.show()
stats.jarque_bera(donnees2['age'])
Jarque_beraResult(statistic=41537.86286418314, pvalue=0.0)
H0: L'age n'est pas corrélé aux catégoriex Ha: Les 2 variables sont corrélées
stats.kruskal(donnees2['age'], donnees2['categ'])
KruskalResult(statistic=1054255.3347514754, pvalue=0.0)
P_value valide la corrélation entre age et catégorie