import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.patches import Rectangle
import scipy.stats as st
import datetime as dt
from math import *
from statsmodels.formula.api import ols
import re
import warnings
warnings.simplefilter("ignore")
def regress_lin(x,y):
"""Renvoie la régression linéaire des deux variables quantitatives renseignées"""
slope, intercept, r_value, p_value, std_er = st.linregress(x,y)
return plt.plot(x, (slope * x + intercept), c='r')
def Ranking(df, column_critere, column_result):
"""
---
Utilisation pour la segmentation RFM
---
Système de ranking par rapport à 5 quintiles [0:1]
df = le dataframe sur lequel l'on travail
column_critere = colonne sur laquelle on se base pour la comparaison
column_result = colonne sur laquelle on vient afficher le rang
"""
df[column_result] = 0
q = 0
rank = 1
for quintile in range(5):
q_next = q + 0.2
date = np.quantile(df[column_critere], q)
date_next = np.quantile(df[column_critere], q_next)
df.loc[(df[column_critere] >= date) & (df[column_critere] <= date_next),column_result] = rank
rank += 1
q += 0.2
#Import des informations clients
df_customer = pd.read_csv("BDD/customers.csv", sep=";")
#Import des informations produits
df_product = pd.read_csv("BDD/products.csv", sep=";")
#Import des informations de ventes
df_transaction = pd.read_csv("BDD/Transactions.csv", sep=";")
df_customer.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8621 entries, 0 to 8620 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 client_id 8621 non-null object 1 sex 8621 non-null object 2 birth 8621 non-null int64 dtypes: int64(1), object(2) memory usage: 202.2+ KB
#Création d'une colonne age afin de simplifier les analyses
df_customer["age"] = 2023 - df_customer["birth"]
#Suppression de la colonne de naissance devenue inutile
df_customer.drop(columns={"birth"}, inplace=True)
#Vérification des doublons du la clé primaire
df_customer["client_id"].duplicated().sum()
0
Il y a 8621 clients enregistrés
#Combien de sexes différents ?
df_customer["sex"].unique()
array(['f', 'm'], dtype=object)
Sexes représentés : Femme ("f") & Homme ("m")
df_customer.head()
client_id | sex | age | |
---|---|---|---|
0 | c_4410 | f | 56 |
1 | c_7839 | f | 48 |
2 | c_1699 | f | 39 |
3 | c_5961 | f | 61 |
4 | c_5320 | m | 80 |
df_product.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3286 entries, 0 to 3285 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 3286 non-null object 1 price 3286 non-null float64 2 categ 3286 non-null int64 dtypes: float64(1), int64(1), object(1) memory usage: 77.1+ KB
#Vérification des doublons des références produits
df_product["id_prod"].duplicated().sum()
0
Il y a 3286 produits différents
#Quelle est l'échelle du prix
df_product["price"].describe().round(2)
count 3286.00 mean 21.86 std 29.85 min 0.62 25% 6.99 50% 13.08 75% 22.99 max 300.00 Name: price, dtype: float64
plt.title("Distribution des prix")
plt.hist(df_product["price"]);
plt.xlabel("Prix unitaire")
Text(0.5, 0, 'Prix unitaire')
#Combien et quelles sont les catégories
df_product["categ"].unique()
array([0, 1, 2], dtype=int64)
Il y a 3 catégories: 0 , 1 et 2
df_transaction.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 687534 entries, 0 to 687533 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 687534 non-null object 1 date 687534 non-null object 2 session_id 687534 non-null object 3 client_id 687534 non-null object dtypes: object(4) memory usage: 21.0+ MB
Premières notifications:
#Correction des dates à 24h -> 00h et + 1j
for date in df_transaction["date"]:
if (date[11] == "2") & (date[12] == "4"):
date_split = re.split("[-: ]", date) #divise la chaine de caractères par les "-", ":", " "
date_split[3] = "00" #changement des 24h en 00h
#Incrémentation d'un jour
date_split[2] = int(date_split[2]) + 1
if date_split[2] < 10 :
date_split[2] = "0" + str(date_split[2])
else: date_split[2] = str(date_split[2])
jour = ("-").join(date_split[:3])
heure = (":").join(date_split[3:])
date_split = jour + " " + heure
df_transaction.loc[df_transaction["date"] == date,["date"]] = date_split
#Correction de la variable date au bon format
df_transaction["date"] = pd.to_datetime(df_transaction["date"])
#Récupération des données necessaire de la date (tout sauf les milisecondes)
df_transaction["date"] = pd.to_datetime(df_transaction["date"].dt.strftime("%Y-%m-%d %H:%M:%S"))
df_transaction.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 687534 entries, 0 to 687533 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 687534 non-null object 1 date 687534 non-null datetime64[ns] 2 session_id 687534 non-null object 3 client_id 687534 non-null object dtypes: datetime64[ns](1), object(3) memory usage: 21.0+ MB
#Création de la colonne mensuelle
df_transaction["monthly"] = pd.to_datetime(df_transaction["date"].dt.strftime('%Y-%m'))
#Nombre de clients uniques représentés dans ce fichier
df_transaction["client_id"].unique().__len__()
8600
#Nombre de produits uniques représentés dans ce fichier
df_transaction["id_prod"].unique().__len__()
3265
#Nombre de sessions uniques représentés dans ce fichier
df_transaction["session_id"].unique().__len__()
345505
Il y a beaucoup plus de transactions que de clients. Est-ce que la variable "session_id" correspond à plusieurs achats d'un seul client?
#Tri des sessions
df_transaction.sort_values(by="session_id", ascending=True).head(10)
id_prod | date | session_id | client_id | monthly | |
---|---|---|---|---|---|
0 | 0_1259 | 2021-03-01 00:01:07 | s_1 | c_329 | 2021-03-01 |
10 | 1_635 | 2021-03-01 00:10:33 | s_10 | c_2218 | 2021-03-01 |
205 | 0_1451 | 2021-03-01 04:43:58 | s_100 | c_3854 | 2021-03-01 |
181 | 0_1030 | 2021-03-01 04:12:43 | s_100 | c_3854 | 2021-03-01 |
1989 | 0_1590 | 2021-03-03 02:49:03 | s_1000 | c_1014 | 2021-03-01 |
2021 | 0_1449 | 2021-03-03 03:18:58 | s_1000 | c_1014 | 2021-03-01 |
2029 | 0_1438 | 2021-03-03 03:25:12 | s_1000 | c_1014 | 2021-03-01 |
1984 | 0_1625 | 2021-03-03 02:38:09 | s_1000 | c_1014 | 2021-03-01 |
20203 | 1_395 | 2021-03-22 17:46:05 | s_10000 | c_476 | 2021-03-01 |
20211 | 0_1324 | 2021-03-22 17:58:20 | s_10000 | c_476 | 2021-03-01 |
Un n° de session possède bien exclusivement qu'un n° de client.
df_customer.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8621 entries, 0 to 8620 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 client_id 8621 non-null object 1 sex 8621 non-null object 2 age 8621 non-null int64 dtypes: int64(1), object(2) memory usage: 202.2+ KB
df_product.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3286 entries, 0 to 3285 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 3286 non-null object 1 price 3286 non-null float64 2 categ 3286 non-null int64 dtypes: float64(1), int64(1), object(1) memory usage: 77.1+ KB
df_transaction.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 687534 entries, 0 to 687533 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 687534 non-null object 1 date 687534 non-null datetime64[ns] 2 session_id 687534 non-null object 3 client_id 687534 non-null object 4 monthly 687534 non-null datetime64[ns] dtypes: datetime64[ns](2), object(3) memory usage: 26.2+ MB
#Inner join entre les transactions et les produits
df_temp = df_transaction.merge(df_product, on="id_prod")
#Récupération des données clients avec un inner join entre le future dataframe complet et les données clients
df_complet = df_temp.merge(df_customer, on="client_id")
df_complet.head()
id_prod | date | session_id | client_id | monthly | price | categ | sex | age | |
---|---|---|---|---|---|---|---|---|---|
0 | 0_1259 | 2021-03-01 00:01:07 | s_1 | c_329 | 2021-03-01 | 11.99 | 0 | f | 56 |
1 | 0_1259 | 2022-10-01 00:01:07 | s_275943 | c_329 | 2022-10-01 | 11.99 | 0 | f | 56 |
2 | 0_1259 | 2022-12-01 00:01:07 | s_305291 | c_329 | 2022-12-01 | 11.99 | 0 | f | 56 |
3 | 0_1259 | 2023-01-01 00:01:07 | s_320153 | c_329 | 2023-01-01 | 11.99 | 0 | f | 56 |
4 | 1_397 | 2021-11-23 18:21:56 | s_123998 | c_329 | 2021-11-01 | 18.99 | 1 | f | 56 |
#Arrangement des colonnes
df_complet = df_complet[["date", "monthly", "session_id", "id_prod", "price", "categ", "client_id", "age", "sex"]]
df_complet.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 687534 entries, 0 to 687533 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 687534 non-null datetime64[ns] 1 monthly 687534 non-null datetime64[ns] 2 session_id 687534 non-null object 3 id_prod 687534 non-null object 4 price 687534 non-null float64 5 categ 687534 non-null int64 6 client_id 687534 non-null object 7 age 687534 non-null int64 8 sex 687534 non-null object dtypes: datetime64[ns](2), float64(1), int64(2), object(4) memory usage: 47.2+ MB
df_complet.describe()
date | monthly | price | categ | age | |
---|---|---|---|---|---|
count | 687534 | 687534 | 687534.000000 | 687534.000000 | 687534.000000 |
mean | 2022-03-01 21:24:01.353685504 | 2022-02-14 16:12:28.721081344 | 17.493918 | 0.448789 | 45.182609 |
min | 2021-03-01 00:01:07 | 2021-03-01 00:00:00 | 0.620000 | 0.000000 | 19.000000 |
25% | 2021-09-10 10:35:20 | 2021-09-01 00:00:00 | 8.990000 | 0.000000 | 36.000000 |
50% | 2022-02-27 06:50:25 | 2022-02-01 00:00:00 | 13.990000 | 0.000000 | 43.000000 |
75% | 2022-08-28 22:16:49 | 2022-08-01 00:00:00 | 19.080000 | 1.000000 | 53.000000 |
max | 2023-02-28 23:58:30 | 2023-02-01 00:00:00 | 300.000000 | 2.000000 | 94.000000 |
std | NaN | NaN | 18.238337 | 0.594563 | 13.607935 |
#Calcul du C.A total
ca_total = df_complet["price"].sum().round(0)
ca_total
12027663.0
Chiffre d'affaires total : 12'027'663 €
#Création d'un tabeau avec les achats réalisés par chaque client
df_totalca_client = df_complet.groupby("client_id")["price"].agg(total_client="sum").reset_index()
#Tri des paniers dans l'ordre décroissant
df_totalca_client.sort_values(by="total_client", ascending=False, inplace=True)
df_totalca_client = df_totalca_client.reset_index()
plt.title("Panier total par client")
plt.boxplot(df_totalca_client["total_client"]);
plt.ylabel("Panier (en €)")
Text(0, 0.5, 'Panier (en €)')
df_totalca_client.head(6)
index | client_id | total_client | |
---|---|---|---|
0 | 677 | c_1609 | 326039.89 |
1 | 4388 | c_4958 | 290227.03 |
2 | 6337 | c_6714 | 153918.60 |
3 | 2724 | c_3454 | 114110.57 |
4 | 634 | c_1570 | 5285.82 |
5 | 2513 | c_3263 | 5276.87 |
Il semblerai qu'il y ai plusieurs clients avec un panier total largement supérieur aux autres. Utiliser l'Inter Quartile Range pour cibler ces clients semblent inutiles car les 4 premiers se détachent très clairement.
#Récupération de la liste des clients B2B
list_client_btob = df_totalca_client.iloc[0:4]["client_id"]
list_client_btob
0 c_1609 1 c_4958 2 c_6714 3 c_3454 Name: client_id, dtype: object
#Création d'un data frame pour les B2B (professionnels)
df_btob = df_complet.loc[df_complet["client_id"].isin(list_client_btob)]
df_btob.info()
<class 'pandas.core.frame.DataFrame'> Index: 46800 entries, 84 to 103787 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 46800 non-null datetime64[ns] 1 monthly 46800 non-null datetime64[ns] 2 session_id 46800 non-null object 3 id_prod 46800 non-null object 4 price 46800 non-null float64 5 categ 46800 non-null int64 6 client_id 46800 non-null object 7 age 46800 non-null int64 8 sex 46800 non-null object dtypes: datetime64[ns](2), float64(1), int64(2), object(4) memory usage: 3.6+ MB
#CA total client BtoB
ca_btob = df_btob["price"].sum()
ca_btob
884296.0900000001
#Nombre total de livre
qte_livre_btob = df_btob["price"].count()
qte_livre_btob
46800
prix_moy = round(ca_btob / qte_livre_btob, 2)
prix_moy
18.9
#distribution des prix
sns.histplot(data=df_btob, x="price", kde=True)
<Axes: xlabel='price', ylabel='Count'>
df_btob.describe()
date | monthly | price | categ | age | |
---|---|---|---|---|---|
count | 46800 | 46800 | 46800.000000 | 46800.000000 | 46800.000000 |
mean | 2022-03-02 17:45:08.661089536 | 2022-02-15 13:11:08.307692288 | 18.895216 | 0.475577 | 44.835321 |
min | 2021-03-01 00:07:04 | 2021-03-01 00:00:00 | 0.620000 | 0.000000 | 24.000000 |
25% | 2021-09-09 10:13:16 | 2021-09-01 00:00:00 | 8.990000 | 0.000000 | 43.000000 |
50% | 2022-03-01 16:13:04.500000 | 2022-03-01 00:00:00 | 13.990000 | 0.000000 | 43.000000 |
75% | 2022-08-29 16:19:28 | 2022-08-01 00:00:00 | 19.530000 | 1.000000 | 54.000000 |
max | 2023-02-28 23:16:12 | 2023-02-01 00:00:00 | 300.000000 | 2.000000 | 55.000000 |
std | NaN | NaN | 21.144730 | 0.636203 | 9.098456 |
#Création d'un data frame représentant les clients B2C (particuliers)
df_btoc = df_complet.set_index("client_id").drop(list_client_btob).reset_index()
df_btoc.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 640734 entries, 0 to 640733 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 client_id 640734 non-null object 1 date 640734 non-null datetime64[ns] 2 monthly 640734 non-null datetime64[ns] 3 session_id 640734 non-null object 4 id_prod 640734 non-null object 5 price 640734 non-null float64 6 categ 640734 non-null int64 7 age 640734 non-null int64 8 sex 640734 non-null object dtypes: datetime64[ns](2), float64(1), int64(2), object(4) memory usage: 44.0+ MB
#Visualisation du CA B2C
df_btoc.set_index("date")["price"].resample("m").sum().plot(label="Chiffre d'affaires");
#Visualisation de la moyenne mobile par semestre
df_btoc.set_index("monthly")["price"].resample("m").sum().rolling(6).mean().plot(label="Moyenne mobile (semestre)");
plt.title("Évolution du chiffre d'affaires");
plt.xlabel("Date")
plt.ylabel("Chiffre d'affaires (en €)")
plt.legend()
plt.grid(which="both")
#Détermination exacte du CA pour le dernier mois
df_temp = df_btoc.groupby("monthly")["price"].agg(ca_total="sum").reset_index()
ca_total_dernier_mois = df_temp.loc[df_temp["monthly"] == df_temp["monthly"].max()]["ca_total"].item()
ca_total_dernier_mois
421485.44
#Détermination exacte du CA pour fevrier n-1
df_temp = df_btoc.groupby("monthly")["price"].agg(ca_total="sum").reset_index()
ca_total_dernier_mois_n1 = df_temp.loc[df_temp["monthly"] == "2022-02-01"]["ca_total"].item()
ca_total_dernier_mois_n1
492927.13
Conclusions :
df_temp = df_btoc.groupby(["monthly", "client_id"]).agg(nb_livres=("id_prod","count")).reset_index()
df_temp = df_temp.groupby("monthly").agg(nb_clients=("nb_livres","count")).reset_index()
df_temp.set_index("monthly")["nb_clients"].plot(kind="line")
plt.grid(which="both")
nb_client_derniermois = df_temp.loc[df_temp["monthly"] == "2023-02-01"]["nb_clients"].item()
nb_client_derniermois
5583
nb_client_derniermois_n1 = df_temp.loc[df_temp["monthly"] == "2022-02-01"]["nb_clients"].item()
nb_client_derniermois_n1
5725
df_time = df_btoc
#Création de la colonne des jours
df_time["jour"] = df_time['date'].dt.strftime("%A")
df_time.head()
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | |
---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 00:01:07 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday |
1 | c_329 | 2022-10-01 00:01:07 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday |
2 | c_329 | 2022-12-01 00:01:07 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday |
3 | c_329 | 2023-01-01 00:01:07 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday |
4 | c_329 | 2021-11-23 18:21:56 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday |
df_temp = df_time.groupby("jour").agg(total_livre=("price", "count")).reset_index()
df_temp
jour | total_livre | |
---|---|---|
0 | Friday | 90370 |
1 | Monday | 92212 |
2 | Saturday | 90776 |
3 | Sunday | 92116 |
4 | Thursday | 90838 |
5 | Tuesday | 92669 |
6 | Wednesday | 91753 |
sns.barplot(data=df_temp, x="jour", y="total_livre")
<Axes: xlabel='jour', ylabel='total_livre'>
Conclusion :
df_time["heure"] = pd.to_timedelta(df_transaction['date'].dt.strftime("%H:%M:%S"))
heures_vente = []
for date in df_time["date"]:
date_splited = re.split("[: ]", str(date))
heures = date_splited[1]
heures_vente.append(heures)
df_time["heure"] = heures_vente
df_time
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | heure | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 00:01:07 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday | 00 |
1 | c_329 | 2022-10-01 00:01:07 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday | 00 |
2 | c_329 | 2022-12-01 00:01:07 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday | 00 |
3 | c_329 | 2023-01-01 00:01:07 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday | 00 |
4 | c_329 | 2021-11-23 18:21:56 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday | 18 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
640729 | c_7739 | 2022-08-28 16:51:07 | 2022-08-01 | s_259828 | 2_163 | 68.99 | 2 | 26 | m | Sunday | 16 |
640730 | c_7739 | 2022-10-28 16:51:07 | 2022-10-01 | s_289331 | 2_163 | 68.99 | 2 | 26 | m | Friday | 16 |
640731 | c_712 | 2021-12-18 20:54:25 | 2021-12-01 | s_136405 | 1_64 | 19.81 | 1 | 56 | f | Saturday | 20 |
640732 | c_712 | 2022-10-18 20:54:25 | 2022-10-01 | s_284469 | 1_64 | 19.81 | 1 | 56 | f | Tuesday | 20 |
640733 | c_4478 | 2021-09-15 19:42:08 | 2021-09-01 | s_90430 | 0_1692 | 13.36 | 0 | 53 | f | Wednesday | 19 |
640734 rows × 11 columns
#Création d'un dataframe pour récupérer le nombre de livres par heures et par mois
df_temp = df_time.groupby(["monthly", "heure"]).agg(nb_livres=("price","count")).reset_index()
#Création d'un dataframe récupérant le nombre de livre moyen par heures
df_temp = df_temp.groupby("heure").agg(nbmoy_livre=("nb_livres","mean")).reset_index()
df_temp.head()
heure | nbmoy_livre | |
---|---|---|
0 | 00 | 1108.625000 |
1 | 01 | 1111.583333 |
2 | 02 | 1120.250000 |
3 | 03 | 1095.541667 |
4 | 04 | 1093.583333 |
xmax = df_temp.loc[df_temp["nbmoy_livre"] == df_temp["nbmoy_livre"].max()].index.item()
ymax = df_temp["nbmoy_livre"].max()
xmin = df_temp.loc[df_temp["nbmoy_livre"] == df_temp["nbmoy_livre"].min()].index.item()
ymin = df_temp["nbmoy_livre"].min()
sns.lineplot(data=df_temp)
plt.title("Évolution du nombre moyen de livres vendus par heure sur une journée")
plt.grid(which="both")
plt.ylabel("Nombre moyen de livres vendus")
plt.xlabel("Heures de la journée")
plt.text(xmax+1, ymax-2, "Maximum : {}\n{}h00".format(int(ymax), xmax))
plt.text(xmin+1, ymin, "Minimum : {}\n{}h00".format(int(ymin), xmin))
plt.plot(xmax,ymax,'go')
plt.plot(xmin,ymin,'ro')
plt.legend()
plt.show()
Conclusion :
ex: session_id(s_158089) -> 2022-02-01 00:18:14 & 2022-01-31 23:55:49
#Création du data frame avec le CA par session
df_session = df_btoc.groupby(["monthly", "session_id"])["price"].agg(total_ca="sum").reset_index()
#Affichage des sessions à cheval sur 2 mois
#df_session[df_session.duplicated(subset="session_id")]
#Visualisation de l'évolution du panier moyen
df_session.set_index("monthly")["total_ca"].resample("m").mean().plot(label="Panier moyen")
#Visualisation de la moyenne mobile en semestre
df_session.set_index("monthly")["total_ca"].resample("m").mean().rolling(6).mean().plot(label="Moyenne mobile (semestre)")
plt.title("Évolution du panier moyen par session")
plt.xlabel("Date")
plt.ylabel("Panier moyen (en €)")
plt.legend()
plt.grid()
panier_moy_derniermois = df_session.loc[df_session["monthly"] == "2023-02-01"]["total_ca"].mean().item()
panier_moy_derniermois
34.61324135665599
panier_moy_derniermois_n1 = df_session.loc[df_session["monthly"] == "2022-02-01"]["total_ca"].mean().item()
panier_moy_derniermois_n1
36.90403009657857
Conclusion :
#Création du data frame avec le nombre de session par mois
df_nbsession = df_btoc.groupby(["monthly", "session_id"])["price"].sum().reset_index()
df_nbsession = df_nbsession.groupby("monthly").agg(nb_session=("session_id","count")).reset_index()
#Visualisation de l'évolution du nombre de session par mois
df_nbsession.set_index("monthly")["nb_session"].plot(label="Session")
#Visualisation de la moyenne mobile semestrielle
df_nbsession.set_index("monthly")["nb_session"].rolling(6).mean().plot(label="Moyenne mobile (semestre)")
plt.title("Évolution du nombre de sessions")
plt.xlabel("Date")
plt.ylabel("Nombre de sessions")
plt.legend()
plt.grid()
nbsession_derniermois = df_nbsession.loc[df_nbsession["monthly"] == "2023-02-01"]["nb_session"].item()
nbsession_derniermois
12177
nbsession_derniermois_n1 = df_nbsession.loc[df_nbsession["monthly"] == "2022-02-01"]["nb_session"].item()
nbsession_derniermois_n1
13357
Conclusion :
df_btoc.set_index("monthly")["price"].resample("m").mean().plot()
df_btoc.set_index("monthly")["price"].resample("m").mean().rolling(6).mean().plot()
<Axes: xlabel='monthly'>
df_prix_moy = df_btoc.groupby(["monthly"]).agg(prix_moy=("price","mean")).reset_index()
df_prix_moy
monthly | prix_moy | |
---|---|---|
0 | 2021-03-01 | 16.734293 |
1 | 2021-04-01 | 16.597577 |
2 | 2021-05-01 | 17.314535 |
3 | 2021-06-01 | 17.856944 |
4 | 2021-07-01 | 19.397320 |
5 | 2021-08-01 | 18.618339 |
6 | 2021-09-01 | 15.203287 |
7 | 2021-10-01 | 16.508220 |
8 | 2021-11-01 | 18.165283 |
9 | 2021-12-01 | 16.145505 |
10 | 2022-01-01 | 17.829373 |
11 | 2022-02-01 | 17.879761 |
12 | 2022-03-01 | 17.216505 |
13 | 2022-04-01 | 17.822833 |
14 | 2022-05-01 | 17.147356 |
15 | 2022-06-01 | 17.270697 |
16 | 2022-07-01 | 17.722813 |
17 | 2022-08-01 | 17.640754 |
18 | 2022-09-01 | 17.399315 |
19 | 2022-10-01 | 17.428574 |
20 | 2022-11-01 | 17.243644 |
21 | 2022-12-01 | 17.741687 |
22 | 2023-01-01 | 17.820219 |
23 | 2023-02-01 | 17.729586 |
prixmoy_derniermois = df_prix_moy.loc[df_prix_moy["monthly"] == "2023-02-01"]["prix_moy"].item()
prixmoy_derniermois
17.729585664409203
prixmoy_derniermois_n1 = df_prix_moy.loc[df_prix_moy["monthly"] == "2022-02-01"]["prix_moy"].item()
prixmoy_derniermois_n1
17.879760963400923
#Création du dataframe aggrégé sur les produits
df_temp = df_btoc.groupby("id_prod")["price"].agg(total_book="count", total_ca="sum").reset_index()
df_bestseller = df_temp.sort_values("total_book", ascending=False)
df_bestseller.head(5)
id_prod | total_book | total_ca | |
---|---|---|---|
2589 | 1_369 | 2205 | 52897.95 |
2642 | 1_417 | 2123 | 44561.77 |
2639 | 1_414 | 2114 | 50376.62 |
2731 | 1_498 | 2042 | 47721.54 |
2651 | 1_425 | 2026 | 34421.74 |
reference_bestseller = df_bestseller.iloc[0:10,0].tolist()
reference_bestseller
['1_369', '1_417', '1_414', '1_498', '1_425', '1_413', '1_412', '1_407', '1_396', '1_403']
df_rentable = df_temp.sort_values("total_ca", ascending=False)
df_rentable.head(10)
id_prod | total_book | total_ca | |
---|---|---|---|
3093 | 2_159 | 624 | 91097.76 |
3067 | 2_135 | 920 | 63470.80 |
3043 | 2_112 | 870 | 58785.90 |
3032 | 2_102 | 941 | 55650.74 |
2589 | 1_369 | 2205 | 52897.95 |
2617 | 1_395 | 1800 | 52182.00 |
3149 | 2_209 | 725 | 50742.75 |
2639 | 1_414 | 2114 | 50376.62 |
2605 | 1_383 | 1716 | 49746.84 |
3101 | 2_166 | 210 | 48308.40 |
reference_rentable = df_rentable.iloc[0:10,0].tolist()
reference_rentable
['2_159', '2_135', '2_112', '2_102', '1_369', '1_395', '2_209', '1_414', '1_383', '2_166']
df_flop = df_temp.sort_values(["total_book", "total_ca"], ascending=True)
df_flop.head(5)
id_prod | total_book | total_ca | |
---|---|---|---|
209 | 0_1191 | 1 | 0.99 |
594 | 0_1539 | 1 | 0.99 |
312 | 0_1284 | 1 | 1.38 |
799 | 0_1726 | 1 | 1.57 |
664 | 0_1601 | 1 | 1.99 |
df_inutile = df_temp.sort_values("total_ca", ascending=True)
df_inutile.head(5)
id_prod | total_book | total_ca | |
---|---|---|---|
594 | 0_1539 | 1 | 0.99 |
209 | 0_1191 | 1 | 0.99 |
312 | 0_1284 | 1 | 1.38 |
799 | 0_1726 | 1 | 1.57 |
718 | 0_1653 | 2 | 1.98 |
#Récupération des références vendues sur toute la période
prod_fulltime = df_btoc["id_prod"].unique().tolist()
#Récupération des références vendues sur seulement la denrièere année
prod_last_annee = df_btoc.loc[df_btoc["monthly"] >= "2022-02-01"]["id_prod"].unique().tolist()
ventes_nulles = list(set(prod_fulltime) - set(prod_last_annee))
len(ventes_nulles)
43
#Création du dataframe des références invendues sur la dernière année
df_ss_vente = df_product.loc[df_product["id_prod"].isin(ventes_nulles)]
df_ss_vente.head()
id_prod | price | categ | |
---|---|---|---|
114 | 0_1595 | 2.99 | 0 |
274 | 1_470 | 5.41 | 1 |
643 | 0_2263 | 32.99 | 0 |
674 | 0_1902 | 2.28 | 0 |
790 | 0_680 | 7.43 | 0 |
plt.subplot(121)
sns.histplot(data=df_ss_vente["categ"], kde=True)
plt.subplot(122)
sns.boxplot(data=df_ss_vente, x="categ", y="price")
<Axes: xlabel='categ', ylabel='price'>
df_ss_vente_T = df_ss_vente.pivot_table(index="id_prod", values="price", columns="categ").reset_index()
df_ss_vente_T.describe()
categ | 0 | 1 | 2 |
---|---|---|---|
count | 36.000000 | 4.000000 | 3.000000 |
mean | 11.247222 | 30.795000 | 138.816667 |
std | 11.409900 | 30.070196 | 19.774545 |
min | 0.990000 | 5.410000 | 115.990000 |
25% | 1.990000 | 6.692500 | 132.865000 |
50% | 2.990000 | 25.055000 | 149.740000 |
75% | 21.940000 | 49.157500 | 150.230000 |
max | 32.990000 | 67.660000 | 150.720000 |
df_temp_btob = df_btob.loc[(df_btob["monthly"] >= "2022-02-01") & (df_btob["id_prod"].isin(ventes_nulles))]
df_temp = df_temp_btob.groupby(["id_prod","categ", "price"]).agg(ca_total=("price","sum"), nb_book=("price","count"))
df_temp
ca_total | nb_book | |||
---|---|---|---|---|
id_prod | categ | price | ||
0_1191 | 0 | 0.99 | 0.99 | 1 |
0_1314 | 0 | 20.63 | 61.89 | 3 |
0_1726 | 0 | 1.57 | 1.57 | 1 |
0_1820 | 0 | 22.30 | 66.90 | 3 |
0_419 | 0 | 20.99 | 41.98 | 2 |
0_680 | 0 | 7.43 | 37.15 | 5 |
1_470 | 1 | 5.41 | 5.41 | 1 |
df_temp["ca_total"].sum()
215.89000000000001
chiffre_affaires = df_rentable['total_ca'].values
n = len(chiffre_affaires)
lorenz = np.cumsum(np.sort(chiffre_affaires)) / chiffre_affaires.sum() #calcul le ratio cumulé du CA des différents produits
pareto = np.quantile(lorenz, 0.8)
lorenz = np.append([0],lorenz) # La courbe de Lorenz commence à 0
#Il y a un segment de taille n pour chaque individu, plus 1 segment supplémentaire d'ordonnée 0. Le premier segment commence à 0-1/n, et le dernier termine à 1+1/n
xaxis = np.linspace(0-1/n,1+1/n,n+1)
plt.plot(xaxis, lorenz, label="Courbe de Lorenz")
plt.plot([0,1], c="r", linestyle="--", label="Bissectrice")
rect = Rectangle((0,0), 0.8,pareto, linewidth=1, edgecolor='r', facecolor='none', linestyle=":", label="Pareto")
plt.gca().add_patch(rect)
# Remplir la région entre la courbe de Lorenz et la bissectrice
plt.fill_between(xaxis, xaxis, lorenz, alpha=0.2, label='Inégalité')
plt.title("Courbe de Lorenz sur les prix produits")
plt.xlabel("Proportion produit")
plt.ylabel("Proportion CA")
plt.legend()
plt.grid()
plt.show()
pareto.round(2)
0.22
Loi de Pareto (20/80) largement respectée : Il y a 20% des références qui représentent 78% du C.A (1-0.22)
AUC = (lorenz.sum() -lorenz[-1]/2 -lorenz[0]/2)/n # Surface sous la courbe de Lorenz. Le premier segment (lorenz[0]) est à moitié en dessous de 0, on le coupe donc en 2, on fait de même pour le dernier segment lorenz[-1] qui est à moitié au dessus de 1.
S = 0.5 - AUC # surface entre la première bissectrice et le courbe de Lorenz
gini = 2*S
gini
0.7430432755072107
Option 1 :
#Détermine l'index qui vient au 1 quintile (df déjà trié par CA décroissant)
seuil = int(np.quantile(df_rentable.index, 0.2))
#Récupération des références avec le plus grand CA
references_pareto = df_rentable.iloc[0:seuil]
references_pareto.info()
<class 'pandas.core.frame.DataFrame'> Index: 652 entries, 3093 to 21 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_prod 652 non-null object 1 total_book 652 non-null int64 2 total_ca 652 non-null float64 dtypes: float64(1), int64(1), object(1) memory usage: 20.4+ KB
#Vérification des 20/80
ratio_ca_test = round(references_pareto["total_ca"].sum() / df_rentable["total_ca"].sum() * 100, 2)
ratio_ca_test
78.21
20% des références représentent bien 78.21% du chiffre d'affaires -> loi de Pareto 20/80 OK
Option 2 : (logique à revoir, ça ne fonctionne pas)
#Création des colonnes de proportions pour le nombre de livres et le C.A
df_rentable["proportion_CA"] = (df_rentable["total_ca"] / df_rentable["total_ca"].sum())*100
df_rentable["proportion_book"] = (df_rentable["total_book"] / df_rentable["total_book"].sum())*100
df_rentable.head()
id_prod | total_book | total_ca | proportion_CA | proportion_book | |
---|---|---|---|---|---|
3093 | 2_159 | 624 | 91097.76 | 0.817507 | 0.097388 |
3067 | 2_135 | 920 | 63470.80 | 0.569584 | 0.143585 |
3043 | 2_112 | 870 | 58785.90 | 0.527542 | 0.135782 |
3032 | 2_102 | 941 | 55650.74 | 0.499407 | 0.146863 |
2589 | 1_369 | 2205 | 52897.95 | 0.474703 | 0.344137 |
#Tri dans l'ordre décroissant de la proportion C.A
df_rentable.sort_values("proportion_CA", ascending=False, inplace=True)
#Création d'une colonne avec le cumul de proportion du nombre de livres
df_rentable["cum_book_proportion"] = np.cumsum(df_rentable["proportion_book"])
#Création d'une colonne avec le cumul de proportion de C.A
df_rentable["cum_CA_proportion"] = np.cumsum(df_rentable["proportion_CA"])
df_rentable["difference"] = df_rentable["cum_CA_proportion"] - df_rentable["cum_book_proportion"]
df_rentable = df_rentable.reset_index()
#Récupération du seuil où l'écart en la proportion du nombre de livres et le prop du C.A est le plus grand.
df_rentable.loc[df_rentable["difference"] == df_rentable["difference"].max()]
index | id_prod | total_book | total_ca | proportion_CA | proportion_book | cum_book_proportion | cum_CA_proportion | difference | |
---|---|---|---|---|---|---|---|---|---|
243 | 601 | 0_1545 | 673 | 12780.27 | 0.114689 | 0.105036 | 33.903461 | 51.413055 | 17.509594 |
df_rentable
index | id_prod | total_book | total_ca | proportion_CA | proportion_book | cum_book_proportion | cum_CA_proportion | difference | |
---|---|---|---|---|---|---|---|---|---|
0 | 3093 | 2_159 | 624 | 91097.76 | 0.817507 | 0.097388 | 0.097388 | 0.817507 | 7.201183e-01 |
1 | 3067 | 2_135 | 920 | 63470.80 | 0.569584 | 0.143585 | 0.240974 | 1.387090 | 1.146117e+00 |
2 | 3043 | 2_112 | 870 | 58785.90 | 0.527542 | 0.135782 | 0.376755 | 1.914632 | 1.537876e+00 |
3 | 3032 | 2_102 | 941 | 55650.74 | 0.499407 | 0.146863 | 0.523618 | 2.414039 | 1.890421e+00 |
4 | 2589 | 1_369 | 2205 | 52897.95 | 0.474703 | 0.344137 | 0.867755 | 2.888742 | 2.020987e+00 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3257 | 718 | 0_1653 | 2 | 1.98 | 0.000018 | 0.000312 | 99.999376 | 99.999956 | 5.800425e-04 |
3258 | 799 | 0_1726 | 1 | 1.57 | 0.000014 | 0.000156 | 99.999532 | 99.999970 | 4.380606e-04 |
3259 | 312 | 0_1284 | 1 | 1.38 | 0.000012 | 0.000156 | 99.999688 | 99.999982 | 2.943736e-04 |
3260 | 209 | 0_1191 | 1 | 0.99 | 0.000009 | 0.000156 | 99.999844 | 99.999991 | 1.471868e-04 |
3261 | 594 | 0_1539 | 1 | 0.99 | 0.000009 | 0.000156 | 100.000000 | 100.000000 | -9.947598e-13 |
3262 rows × 9 columns
À voir, ça ne joue pas.
df_btoc.describe().round(2)
date | monthly | price | categ | age | |
---|---|---|---|---|---|
count | 640734 | 640734 | 640734.00 | 640734.00 | 640734.00 |
mean | 2022-03-01 19:54:49.819372544 | 2022-02-14 14:40:32.677522688 | 17.39 | 0.45 | 45.21 |
min | 2021-03-01 00:01:07 | 2021-03-01 00:00:00 | 0.62 | 0.00 | 19.00 |
25% | 2021-09-10 12:57:20.750000128 | 2021-09-01 00:00:00 | 8.99 | 0.00 | 36.00 |
50% | 2022-02-27 03:35:19 | 2022-02-01 00:00:00 | 13.99 | 0.00 | 44.00 |
75% | 2022-08-28 20:52:43.750000128 | 2022-08-01 00:00:00 | 19.04 | 1.00 | 52.00 |
max | 2023-02-28 23:58:30 | 2023-02-01 00:00:00 | 300.00 | 2.00 | 94.00 |
std | NaN | NaN | 18.00 | 0.59 | 13.88 |
df_btoc["price"].mode()
0 15.99 Name: price, dtype: float64
df_btoc["price"].hist(bins=10)
plt.xlim(left=0, right=100)
(0.0, 100.0)
Tranche de prix la plus vendue est de 0.62€ : ~ 30€ == cat 1?
#Prix le plus vendu
mode_cat0 = df_btoc.loc[df_btoc["categ"] == 0]["price"].mode()
mode_cat0
0 4.99 Name: price, dtype: float64
#Prix le plus vendu
mode_cat1 = df_btoc.loc[df_btoc["categ"] == 1]["price"].mode()
mode_cat1
0 15.99 Name: price, dtype: float64
#Prix le plus vendu
mode_cat2 = df_btoc.loc[df_btoc["categ"] == 2]["price"].mode()
mode_cat2
0 68.99 Name: price, dtype: float64
first_date = df_btoc["date"].min().strftime("%m-%y")
last_date = df_btoc["date"].max().strftime("%m-%y")
df_proportion = df_btoc.groupby("categ").agg(ca_total = ("price","sum")).reset_index()
df_proportion
categ | ca_total | |
---|---|---|
0 | 0 | 4119200.69 |
1 | 1 | 4520101.86 |
2 | 2 | 2504064.46 |
plt.pie(data=df_proportion, x="ca_total", labels="ca_total", labeldistance=0.4, autopct='%1.1f%%');
plt.title("Proportion de CA des catégories")
plt.legend(df_proportion.index, title="Catégorie", bbox_to_anchor=(1.2, 1), loc='upper right');
plt.xlabel("Du {} au {}".format(first_date, last_date));
Conclusion: Autant de CA pour les catégories 0 et 1. Deux fois moins de CA est représenté par la catégorie 2 (la plus cher)
df_btoc.head()
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | heure | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 00:01:07 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday | 00 |
1 | c_329 | 2022-10-01 00:01:07 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday | 00 |
2 | c_329 | 2022-12-01 00:01:07 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday | 00 |
3 | c_329 | 2023-01-01 00:01:07 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday | 00 |
4 | c_329 | 2021-11-23 18:21:56 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday | 18 |
df_temp = df_btoc.groupby(["monthly","categ"]).agg(total_ca=("price","sum")).reset_index()
df_temp = df_temp.pivot(index="monthly", columns="categ", values="total_ca")
df_temp.head()
categ | 0 | 1 | 2 |
---|---|---|---|
monthly | |||
2021-03-01 | 180637.95 | 174569.68 | 90711.08 |
2021-04-01 | 191458.18 | 145272.82 | 102606.85 |
2021-05-01 | 182350.51 | 155309.63 | 117227.32 |
2021-06-01 | 156621.73 | 177840.33 | 112640.11 |
2021-07-01 | 135456.27 | 176647.61 | 135489.27 |
df_temp.plot(kind="bar", stacked=True)
plt.title("Évolution du chiffre d'affaires des 3 catégories de livres")
plt.legend(bbox_to_anchor=(1.2, 1), loc='upper right', borderaxespad=0, title="Catégorie")
plt.xlabel("Date")
plt.ylabel("Chiffre d'affaires (en €)")
Text(0, 0.5, "Chiffre d'affaires (en €)")
df_temp = df_temp.reset_index()
df_temp.set_index("monthly").resample("m").sum().rolling(6).mean().plot()
plt.title("Évolution du chiffre d'affaires des 3 catégories de livres (MA sur semestre)")
plt.legend(bbox_to_anchor=(1.2, 0.8), loc='upper right', borderaxespad=0, title="Catégorie")
plt.grid(which="both")
plt.xlabel("Date")
plt.xlim(left=np.quantile(df_temp.monthly,0.2))#afin d'afficher seulement la portion utile
plt.ylabel("Chiffre d'affaires (en €)")
Text(0, 0.5, "Chiffre d'affaires (en €)")
Conclusion: ventes de la catégorie 0 stablent, cependant quand il y a plus de C.A dans la catégorie 1, le CA de la catégorie 2 chute.
df_produit_unique = df_btoc[["id_prod","price", "categ"]].drop_duplicates(subset="id_prod")
df_produit_unique.head()
id_prod | price | categ | |
---|---|---|---|
0 | 0_1259 | 11.99 | 0 |
4 | 1_397 | 18.99 | 1 |
6 | 0_1140 | 3.73 | 0 |
8 | 1_451 | 20.99 | 1 |
9 | 1_378 | 26.61 | 1 |
sns.boxplot(data=df_produit_unique, x="categ", y="price")
plt.grid()
df_T_produit = df_produit_unique.pivot(index="id_prod", columns="categ", values="price")
df_T_produit.describe().round(2)
categ | 0 | 1 | 2 |
---|---|---|---|
count | 2290.00 | 737.00 | 235.00 |
mean | 11.71 | 25.50 | 107.49 |
std | 7.53 | 15.44 | 49.31 |
min | 0.62 | 2.00 | 30.99 |
25% | 5.64 | 13.35 | 70.46 |
50% | 10.30 | 22.99 | 100.99 |
75% | 16.46 | 33.99 | 133.55 |
max | 40.99 | 80.99 | 300.00 |
#Création du dataframe
df_prix_moy = df_btoc.groupby("categ").agg(prix_moyen=("price","mean")).reset_index()
df_prix_moy.head()
categ | prix_moyen | |
---|---|---|
0 | 0 | 10.636207 |
1 | 1 | 20.489571 |
2 | 2 | 76.231870 |
#Création du dataframe
df_evoprix_moy = df_btoc.groupby(["monthly","categ"]).agg(prix_moyen=("price","mean")).reset_index()
df_evoprix_moy.head()
monthly | categ | prix_moyen | |
---|---|---|---|
0 | 2021-03-01 | 0 | 10.678526 |
1 | 2021-03-01 | 1 | 20.436628 |
2 | 2021-03-01 | 2 | 76.291909 |
3 | 2021-04-01 | 0 | 10.610040 |
4 | 2021-04-01 | 1 | 20.573973 |
#Transposition du tableau
df_evoprix_moy = df_evoprix_moy.pivot(index="monthly", columns="categ", values="prix_moyen")
df_evoprix_moy.plot(kind="bar", stacked=True)
<Axes: xlabel='monthly'>
df_evoprix_moy.describe()
categ | 0 | 1 | 2 |
---|---|---|---|
count | 24.000000 | 24.000000 | 24.000000 |
mean | 10.636844 | 20.491227 | 76.280268 |
std | 0.033196 | 0.065956 | 1.155821 |
min | 10.560430 | 20.371095 | 73.811916 |
25% | 10.611399 | 20.453718 | 75.385607 |
50% | 10.641847 | 20.468322 | 76.371703 |
75% | 10.661510 | 20.522093 | 77.011563 |
max | 10.693232 | 20.614729 | 78.308163 |
Conclusion : peu de variation de prix moyen sur les différentes catégories. Seule la catégorie 2 (la plus cher) varie de 4.5€
df_CA_client = df_btoc.groupby("client_id").agg(CA_total=("price","sum")).reset_index()
df_CA_client.head()
client_id | CA_total | |
---|---|---|
0 | c_1 | 629.02 |
1 | c_10 | 1353.60 |
2 | c_100 | 254.85 |
3 | c_1000 | 2291.88 |
4 | c_1001 | 1823.85 |
chiffre_affaires = df_CA_client['CA_total'].values
n = len(chiffre_affaires)
lorenz = np.cumsum(np.sort(chiffre_affaires)) / chiffre_affaires.sum()
pareto = np.quantile(lorenz, 0.8)
lorenz = np.append([0],lorenz) # La courbe de Lorenz commence à 0
#Il y a un segment de taille n pour chaque individu, plus 1 segment supplémentaire d'ordonnée 0. Le premier segment commence à 0-1/n, et le dernier termine à 1+1/n
xaxis = np.linspace(0-1/n,1+1/n,n+1)
plt.plot(xaxis, lorenz, label="Courbe de Lorenz")
plt.plot([0,1], c="r", linestyle="--", label="Bissectrice")
rect = Rectangle((0,0), 0.8,pareto, linewidth=1, edgecolor='r', facecolor='none', linestyle=":", label="Pareto")
plt.gca().add_patch(rect)
plt.title("Courbe de Lorenz sur le CA réalisé par les clients")
plt.xlabel("Proportion client")
plt.ylabel("Proportion CA")
plt.legend()
plt.grid()
plt.show()
pareto.round(2)
0.56
Loi de Paréto (20/80) non-respectée : 20% des clients représentent 44% du CA (1 - 0.56)
Ou lire : 80% des clients représentent 56% de CA
#Création du dataframe
df_temp = df_btoc.groupby(["client_id","session_id","sex"]).agg(CA_total=("price","sum"), book_session=("session_id", "count")).reset_index()
df_temp.head()
client_id | session_id | sex | CA_total | book_session | |
---|---|---|---|---|---|
0 | c_1 | s_101417 | m | 15.87 | 1 |
1 | c_1 | s_105105 | m | 62.96 | 4 |
2 | c_1 | s_114737 | m | 92.62 | 5 |
3 | c_1 | s_120172 | m | 44.29 | 2 |
4 | c_1 | s_134971 | m | 10.30 | 1 |
df_parite = df_temp.groupby(["client_id","sex"]).agg(ca_total=("CA_total","sum"), nb_session=("book_session","count"), livre_total=("book_session","sum")).reset_index()
df_parite.head()
client_id | sex | ca_total | nb_session | livre_total | |
---|---|---|---|---|---|
0 | c_1 | m | 629.02 | 34 | 43 |
1 | c_10 | m | 1353.60 | 34 | 58 |
2 | c_100 | m | 254.85 | 5 | 8 |
3 | c_1000 | f | 2291.88 | 94 | 126 |
4 | c_1001 | m | 1823.85 | 47 | 103 |
#Transposition des données sur le sexe
df_pariteT = df_parite.groupby("sex").agg(ca_total=("ca_total","sum"), session_total=("nb_session","sum"), livre_total=("livre_total","sum"), population=("client_id","count")).reset_index()
df_pariteT
sex | ca_total | session_total | livre_total | population | |
---|---|---|---|---|---|
0 | f | 5796925.08 | 168546 | 333494 | 4478 |
1 | m | 5346441.93 | 153920 | 307240 | 4118 |
#Affichage de la proportion homme/femme
explode = (0, 0.1)
plt.pie(data=df_pariteT, x="population", labels="population",
autopct='%1.1f%%',labeldistance=0.3, explode=explode, shadow=True,
colors=["pink","cornflowerblue"], textprops={'fontsize': 14});
plt.title("Proportion de clients homme/femme")
plt.legend(bbox_to_anchor=(1.2, 1), loc='upper right', borderaxespad=0, title="Sexe", labels=df_pariteT["sex"])
<matplotlib.legend.Legend at 0x2158fdcb790>
#Transposition des données pour un graphique plus lisible
df_test = df_pariteT.set_index("sex").T
df_test
sex | f | m |
---|---|---|
ca_total | 5796925.08 | 5346441.93 |
session_total | 168546.00 | 153920.00 |
livre_total | 333494.00 | 307240.00 |
population | 4478.00 | 4118.00 |
#Normalisation pour afficher un graphique avec les mêmes echelles de données
df_norm = df_test
df_norm["f"] = df_test["f"] / (df_test["f"]+df_test["m"])
df_norm["m"]= 1 - df_test["f"]
df_norm
sex | f | m |
---|---|---|
ca_total | 0.520213 | 0.479787 |
session_total | 0.522678 | 0.477322 |
livre_total | 0.520487 | 0.479513 |
population | 0.520940 | 0.479060 |
df_norm.plot(kind="bar", color=["pink","cornflowerblue"])
plt.legend(bbox_to_anchor=(1.2, 1), loc='upper right', borderaxespad=0, title="Sexe")
plt.grid(which="both")
df_temp = df_btoc.groupby(["client_id", "sex", "session_id"]).agg(ca_session=("price","sum")).reset_index()
df_sexe = df_temp.groupby(["client_id", "sex"]).agg(panier_moyen=("ca_session","mean")).reset_index()
df_sexe.head()
client_id | sex | panier_moyen | |
---|---|---|---|
0 | c_1 | m | 18.500588 |
1 | c_10 | m | 39.811765 |
2 | c_100 | m | 50.970000 |
3 | c_1000 | f | 24.381702 |
4 | c_1001 | m | 38.805319 |
sns.boxplot(data=df_sexe, x="sex", y="panier_moyen", showfliers=False)
<Axes: xlabel='sex', ylabel='panier_moyen'>
df_sexe_T = df_sexe.pivot_table(index="client_id",columns="sex", values="panier_moyen")
df_sexe_T.describe()
sex | f | m |
---|---|---|
count | 4478.000000 | 4118.000000 |
mean | 40.176306 | 40.649086 |
std | 22.741304 | 22.788894 |
min | 5.570000 | 4.150000 |
25% | 25.467425 | 25.986893 |
50% | 32.282341 | 32.418056 |
75% | 47.822981 | 48.731896 |
max | 259.422500 | 241.160000 |
df_btoc["age"].hist()
plt.title("Nombre de livres achetés selon l'age");
plt.xlabel("Age de {} à {} ans".format(df_btoc["age"].min(), df_btoc["age"].max()))
plt.ylabel("Nombre totaux de livres achetés")
Text(0, 0.5, 'Nombre totaux de livres achetés')
Ce sont les 35-45 ans qui achètent le plus de livres
sns.scatterplot(data=df_btoc[["price", "age"]], x="age", y="price")
<Axes: xlabel='age', ylabel='price'>
Ce sont essentiellement des jeunes de 19 à 30 ans qui achètes les livres les plus chers (entre 80€ et 250€) Par ailleur, les livres à 150€ / unité semblent satisfaire presque tous les ages
#Création d'un dataframe avec les clients aggrégés
df_temp = df_btoc.groupby("client_id").agg(age=("age","mean"), CA_total=("price","sum")).reset_index()
df_temp["age"].hist()
plt.title("Nombre de clients selon l'age");
plt.ylabel("Nombre de clients")
plt.xlabel("Age de {} à {} ans".format(df_btoc["age"].min(), df_btoc["age"].max()))
Text(0.5, 0, 'Age de 19 à 94 ans')
#Quel est l'age le plus récurrent
df_temp["age"].mode()
0 19.0 Name: age, dtype: float64
df_temp.describe().round(0)
age | CA_total | |
---|---|---|
count | 8596.0 | 8596.0 |
mean | 45.0 | 1296.0 |
std | 17.0 | 958.0 |
min | 19.0 | 6.0 |
25% | 31.0 | 563.0 |
50% | 44.0 | 1046.0 |
75% | 57.0 | 1796.0 |
max | 94.0 | 5286.0 |
Écart type de 17 ans
df_temp["tranche_age"] = 0
#Calcul du nombre de tranche d'age necessaire (arrondi à l'entier superieur)
nb_tranche = ceil((df_temp["age"].max() - df_temp["age"].min()) / df_temp["age"].std())
nb_tranche
5
taille_tranche = ceil((df_temp["age"].max() - df_temp["age"].min()) / nb_tranche)
taille_tranche
15
Désignation des tranches d'age (15 ans)
#Désignation des tranches d'ages
age_mini = df_temp["age"].min()
for tranche in range(nb_tranche):
clients_index = np.where(
(df_temp["age"] >= age_mini)
&(df_temp["age"] <= (age_mini + taille_tranche)))
df_temp["tranche_age"].iloc[clients_index] = tranche + 1
age_mini = age_mini + taille_tranche + 1
df_temp.head()
client_id | age | CA_total | tranche_age | |
---|---|---|---|---|
0 | c_1 | 68.0 | 629.02 | 4 |
1 | c_10 | 67.0 | 1353.60 | 4 |
2 | c_100 | 31.0 | 254.85 | 1 |
3 | c_1000 | 57.0 | 2291.88 | 3 |
4 | c_1001 | 41.0 | 1823.85 | 2 |
#Dataframe utilisé dans les analyses pour Julies (plus bas)
df_tranches_ageCA = df_temp
#Synthèse du nombre de client par tranche d'age
df_tranche = df_temp.groupby("tranche_age")["client_id"].count()
df_tranche
tranche_age 1 2707 2 2783 3 2040 4 921 5 145 Name: client_id, dtype: int64
#Vérification du bon nombre de clients total
df_tranche.sum()
8596
df_tranche.plot(kind="bar")
plt.title("Nombre de clients par tranche d'age")
plt.ylabel("Nombre de clients")
plt.xlabel("Tranche d'age\nDe {} à {} ans".format(int(df_temp["age"].min()),int(df_temp["age"].max())));
#Création d'un data frame destiné aux classements
df_rfm = df_btoc
df_rfm["date"] = pd.to_datetime(df_rfm["date"].dt.strftime("%Y-%m-%d"))
df_rfm
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | heure | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday | 00 |
1 | c_329 | 2022-10-01 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday | 00 |
2 | c_329 | 2022-12-01 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday | 00 |
3 | c_329 | 2023-01-01 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday | 00 |
4 | c_329 | 2021-11-23 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday | 18 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
640729 | c_7739 | 2022-08-28 | 2022-08-01 | s_259828 | 2_163 | 68.99 | 2 | 26 | m | Sunday | 16 |
640730 | c_7739 | 2022-10-28 | 2022-10-01 | s_289331 | 2_163 | 68.99 | 2 | 26 | m | Friday | 16 |
640731 | c_712 | 2021-12-18 | 2021-12-01 | s_136405 | 1_64 | 19.81 | 1 | 56 | f | Saturday | 20 |
640732 | c_712 | 2022-10-18 | 2022-10-01 | s_284469 | 1_64 | 19.81 | 1 | 56 | f | Tuesday | 20 |
640733 | c_4478 | 2021-09-15 | 2021-09-01 | s_90430 | 0_1692 | 13.36 | 0 | 53 | f | Wednesday | 19 |
640734 rows × 11 columns
#Regroupement par session
df_rfm = df_rfm.groupby(["date","client_id","session_id"])["price"].agg(CA_session="sum").reset_index()
df_rfm
date | client_id | session_id | CA_session | |
---|---|---|---|---|
0 | 2021-03-01 | c_1031 | s_348 | 27.99 |
1 | 2021-03-01 | c_1052 | s_429 | 247.22 |
2 | 2021-03-01 | c_1074 | s_360 | 29.21 |
3 | 2021-03-01 | c_110 | s_244 | 21.23 |
4 | 2021-03-01 | c_1112 | s_54 | 31.31 |
... | ... | ... | ... | ... |
323945 | 2023-02-28 | c_945 | s_348033 | 105.98 |
323946 | 2023-02-28 | c_951 | s_348377 | 9.99 |
323947 | 2023-02-28 | c_973 | s_348099 | 74.66 |
323948 | 2023-02-28 | c_990 | s_348255 | 34.73 |
323949 | 2023-02-28 | c_990 | s_348299 | 7.99 |
323950 rows × 4 columns
#Récupération des sessions les plus récentes par client
df_recent = df_rfm.groupby("client_id")["date"].max().reset_index()
df_recent
--------------------------------------------------------------------------- NameError Traceback (most recent call last) j:\Mon Drive\Professionnel\OC\OC - Projet 6\Grenier_Yohan_1_notebook_092023.ipynb Cellule 242 line 2 <a href='vscode-notebook-cell:/j%3A/Mon%20Drive/Professionnel/OC/OC%20-%20Projet%206/Grenier_Yohan_1_notebook_092023.ipynb#Y463sZmlsZQ%3D%3D?line=0'>1</a> #Récupération des sessions les plus récentes par client ----> <a href='vscode-notebook-cell:/j%3A/Mon%20Drive/Professionnel/OC/OC%20-%20Projet%206/Grenier_Yohan_1_notebook_092023.ipynb#Y463sZmlsZQ%3D%3D?line=1'>2</a> df_recent = df_rfm.groupby("client_id")["date"].max().reset_index() <a href='vscode-notebook-cell:/j%3A/Mon%20Drive/Professionnel/OC/OC%20-%20Projet%206/Grenier_Yohan_1_notebook_092023.ipynb#Y463sZmlsZQ%3D%3D?line=3'>4</a> df_recent NameError: name 'df_rfm' is not defined
#Tri dans l'ordre du plus récent au moins récent
df_recent.sort_values("date", ascending=False, inplace=True)
df_recent
client_id | date | |
---|---|---|
2713 | c_3445 | 2023-02-28 |
6034 | c_6444 | 2023-02-28 |
1532 | c_238 | 2023-02-28 |
1529 | c_2377 | 2023-02-28 |
2942 | c_3653 | 2023-02-28 |
... | ... | ... |
2371 | c_3136 | 2021-05-08 |
693 | c_1624 | 2021-04-27 |
4084 | c_4686 | 2021-04-07 |
5825 | c_6256 | 2021-04-02 |
5865 | c_6292 | 2021-03-09 |
8596 rows × 2 columns
Ranking(df_recent,"date","R")
df_recent
client_id | date | R | |
---|---|---|---|
2713 | c_3445 | 2023-02-28 | 5 |
6034 | c_6444 | 2023-02-28 | 5 |
1532 | c_238 | 2023-02-28 | 5 |
1529 | c_2377 | 2023-02-28 | 5 |
2942 | c_3653 | 2023-02-28 | 5 |
... | ... | ... | ... |
2371 | c_3136 | 2021-05-08 | 1 |
693 | c_1624 | 2021-04-27 | 1 |
4084 | c_4686 | 2021-04-07 | 1 |
5825 | c_6256 | 2021-04-02 | 1 |
5865 | c_6292 | 2021-03-09 | 1 |
8596 rows × 3 columns
df_recent["R"].unique()
array([5, 4, 3, 2, 1], dtype=int64)
#Récupération des sessions les plus fréquentes par client
df_frequence = df_rfm.groupby("client_id")["session_id"].agg(total_session = "count").reset_index()
df_frequence.head()
client_id | total_session | |
---|---|---|
0 | c_1 | 34 |
1 | c_10 | 34 |
2 | c_100 | 5 |
3 | c_1000 | 95 |
4 | c_1001 | 47 |
#Tri dans l'ordre du plus de sessions au moins
df_frequence.sort_values("total_session", ascending=False, inplace=True)
df_frequence.head()
client_id | total_session | |
---|---|---|
8340 | c_8526 | 167 |
707 | c_1637 | 166 |
1405 | c_2265 | 166 |
8323 | c_8510 | 165 |
6306 | c_669 | 164 |
#Création du classement par fréquence
Ranking(df_frequence, "total_session", "F")
df_frequence
client_id | total_session | F | |
---|---|---|---|
8340 | c_8526 | 167 | 5 |
707 | c_1637 | 166 | 5 |
1405 | c_2265 | 166 | 5 |
8323 | c_8510 | 165 | 5 |
6306 | c_669 | 164 | 5 |
... | ... | ... | ... |
1433 | c_2290 | 1 | 1 |
5825 | c_6256 | 1 | 1 |
4042 | c_4648 | 1 | 1 |
2583 | c_3327 | 1 | 1 |
8147 | c_8351 | 1 | 1 |
8596 rows × 3 columns
#Récupération du classement de chiffre d'affaires par client
df_montant = df_rfm.groupby("client_id")["CA_session"].agg(totalCA_client = "sum").reset_index()
df_montant.head()
client_id | totalCA_client | |
---|---|---|
0 | c_1 | 629.02 |
1 | c_10 | 1353.60 |
2 | c_100 | 254.85 |
3 | c_1000 | 2291.88 |
4 | c_1001 | 1823.85 |
#Tri dans l'ordre du plus grand chiffre d'affaires au moins
df_montant.sort_values("totalCA_client", ascending=False, inplace=True)
df_montant.head()
client_id | totalCA_client | |
---|---|---|
634 | c_1570 | 5285.82 |
2512 | c_3263 | 5276.87 |
1267 | c_2140 | 5260.18 |
2107 | c_2899 | 5214.05 |
7002 | c_7319 | 5155.77 |
#Création du classement par CA client
Ranking(df_montant, "totalCA_client", "M")
df_montant
client_id | totalCA_client | M | |
---|---|---|---|
634 | c_1570 | 5285.82 | 5 |
2512 | c_3263 | 5276.87 | 5 |
1267 | c_2140 | 5260.18 | 5 |
2107 | c_2899 | 5214.05 | 5 |
7002 | c_7319 | 5155.77 | 5 |
... | ... | ... | ... |
3853 | c_4478 | 13.36 | 1 |
4042 | c_4648 | 11.20 | 1 |
7885 | c_8114 | 9.98 | 1 |
7914 | c_8140 | 8.30 | 1 |
8147 | c_8351 | 6.31 | 1 |
8596 rows × 3 columns
#Merge des 3 dataframe R(récence), F(Fréquence), M(Montant)
df_RFM = pd.merge(df_recent, df_frequence, on="client_id")
df_RFM = pd.merge(df_RFM, df_montant, on="client_id")
df_RFM
client_id | date | R | total_session | F | totalCA_client | M | |
---|---|---|---|---|---|---|---|
0 | c_3445 | 2023-02-28 | 5 | 54 | 4 | 1978.80 | 4 |
1 | c_6444 | 2023-02-28 | 5 | 99 | 5 | 3193.62 | 5 |
2 | c_238 | 2023-02-28 | 5 | 84 | 5 | 2665.22 | 5 |
3 | c_2377 | 2023-02-28 | 5 | 71 | 5 | 1402.86 | 4 |
4 | c_3653 | 2023-02-28 | 5 | 61 | 5 | 1597.55 | 4 |
... | ... | ... | ... | ... | ... | ... | ... |
8591 | c_3136 | 2021-05-08 | 1 | 2 | 1 | 176.13 | 1 |
8592 | c_1624 | 2021-04-27 | 1 | 1 | 1 | 28.54 | 1 |
8593 | c_4686 | 2021-04-07 | 1 | 2 | 1 | 64.58 | 1 |
8594 | c_6256 | 2021-04-02 | 1 | 1 | 1 | 23.26 | 1 |
8595 | c_6292 | 2021-03-09 | 1 | 1 | 1 | 24.24 | 1 |
8596 rows × 7 columns
#Création de la colonne pour le code RFM
df_RFM["RFM"] = df_RFM["R"].map(str) + df_RFM["F"].map(str) + df_RFM["M"].map(str)
#Tri dans l'ordre du top clients
df_RFM.sort_values("RFM", ascending=False, inplace=True)
df_RFM
client_id | date | R | total_session | F | totalCA_client | M | RFM | |
---|---|---|---|---|---|---|---|---|
1769 | c_4973 | 2023-02-24 | 5 | 109 | 5 | 3503.94 | 5 | 555 |
549 | c_247 | 2023-02-27 | 5 | 90 | 5 | 2597.22 | 5 | 555 |
531 | c_7403 | 2023-02-27 | 5 | 123 | 5 | 2592.73 | 5 | 555 |
1332 | c_6635 | 2023-02-25 | 5 | 112 | 5 | 3704.54 | 5 | 555 |
533 | c_7418 | 2023-02-27 | 5 | 74 | 5 | 2792.71 | 5 | 555 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
7893 | c_2417 | 2022-11-08 | 1 | 12 | 1 | 196.10 | 1 | 111 |
7895 | c_3439 | 2022-11-08 | 1 | 5 | 1 | 102.27 | 1 | 111 |
7897 | c_4392 | 2022-11-08 | 1 | 10 | 1 | 317.32 | 1 | 111 |
7898 | c_3024 | 2022-11-08 | 1 | 4 | 1 | 179.96 | 1 | 111 |
8595 | c_6292 | 2021-03-09 | 1 | 1 | 1 | 24.24 | 1 | 111 |
8596 rows × 8 columns
#Conversion de la colonne RFM en INT
df_RFM["RFM"] = pd.to_numeric(df_RFM["RFM"],downcast="integer")
#Création des 11 catégories
noms_segments = {"1. Meilleur" : [555, 554, 544, 545, 454, 455, 445],
"2. Fidèle" : [543, 444, 435, 355, 354, 345, 344, 335],
"3. Potentiellement fidèle": [553, 551, 552, 541, 542, 533, 532, 531, 452, 451, 442, 441, 431, 453, 433, 432, 423, 353, 352, 351, 342, 341, 333, 323],
"4. Nouveau client" : [512, 511, 422, 421, 412, 411, 311],
"5. Prometteur" : [525, 524, 523, 522, 521, 515, 514, 513, 425,424, 413,414,415, 315, 314, 313],
"6. Standard" : [535, 534, 443, 434, 343, 334, 325, 324],
"7. Besoin d'attention" : [331, 321, 312, 221, 213, 231, 241, 251],
"8. À ne pas perdre" : [255, 254, 245, 244, 253, 252, 243, 242, 235, 234, 225, 224, 153, 152, 145, 143, 142, 135, 134, 133, 125, 124],
"9. À risque" : [155, 154, 144, 214,215,115, 114, 113],
"10. En perdition" : [332, 322, 233, 232, 223, 222, 132, 123, 122, 212, 211],
"11. Perdu" : [111, 112, 121, 131, 141, 151]}
#Création de la colonne catégorie client
df_RFM["client_categ"] = "Non renseigné"
#Assignation des segments aux clients
for client in df_RFM["client_id"]:
rfm_client = df_RFM.loc[df_RFM["client_id"] == client,"RFM"].item()
for segment in noms_segments.items():
if rfm_client in segment[1]:
df_RFM.loc[df_RFM["client_id"] == client,"client_categ"] = segment[0]
df_RFM
client_id | date | R | total_session | F | totalCA_client | M | RFM | client_categ | |
---|---|---|---|---|---|---|---|---|---|
1769 | c_4973 | 2023-02-24 | 5 | 109 | 5 | 3503.94 | 5 | 555 | 1. Meilleur |
549 | c_247 | 2023-02-27 | 5 | 90 | 5 | 2597.22 | 5 | 555 | 1. Meilleur |
531 | c_7403 | 2023-02-27 | 5 | 123 | 5 | 2592.73 | 5 | 555 | 1. Meilleur |
1332 | c_6635 | 2023-02-25 | 5 | 112 | 5 | 3704.54 | 5 | 555 | 1. Meilleur |
533 | c_7418 | 2023-02-27 | 5 | 74 | 5 | 2792.71 | 5 | 555 | 1. Meilleur |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
7893 | c_2417 | 2022-11-08 | 1 | 12 | 1 | 196.10 | 1 | 111 | 11. Perdu |
7895 | c_3439 | 2022-11-08 | 1 | 5 | 1 | 102.27 | 1 | 111 | 11. Perdu |
7897 | c_4392 | 2022-11-08 | 1 | 10 | 1 | 317.32 | 1 | 111 | 11. Perdu |
7898 | c_3024 | 2022-11-08 | 1 | 4 | 1 | 179.96 | 1 | 111 | 11. Perdu |
8595 | c_6292 | 2021-03-09 | 1 | 1 | 1 | 24.24 | 1 | 111 | 11. Perdu |
8596 rows × 9 columns
sns.pairplot(df_RFM[["client_id", "total_session", "totalCA_client", "client_categ"]], hue="client_categ",)
<seaborn.axisgrid.PairGrid at 0x215910e5510>
df_RFM_synthese = df_RFM.groupby("client_categ").agg(
CA_total = ("totalCA_client","sum"),
session_total = ("total_session","sum"),
client_total = ("client_id","count")).reset_index()
df_RFM_synthese.sort_values("CA_total", ascending=False, inplace=True)
df_RFM_synthese
client_categ | CA_total | session_total | client_total | |
---|---|---|---|---|
0 | 1. Meilleur | 4250233.37 | 138072 | 1671 |
3 | 2. Fidèle | 1890145.39 | 55199 | 980 |
9 | 8. À ne pas perdre | 1336501.61 | 31184 | 810 |
1 | 10. En perdition | 1055245.36 | 28207 | 1525 |
7 | 6. Standard | 858618.51 | 21989 | 620 |
4 | 3. Potentiellement fidèle | 741971.28 | 23807 | 832 |
2 | 11. Perdu | 340986.04 | 9010 | 1062 |
6 | 5. Prometteur | 251936.16 | 4395 | 261 |
5 | 4. Nouveau client | 164546.84 | 4934 | 414 |
8 | 7. Besoin d'attention | 141262.03 | 5230 | 329 |
10 | 9. À risque | 111920.42 | 1923 | 92 |
#Première visualisation
genre_categ = df_btoc.pivot_table(index='sex', columns='categ', values='id_prod', aggfunc=len).reset_index()
genre_categ.plot(kind='bar', x='sex');
#Création du tableau de contingence
X = "categ"
Y = "sex"
df_cont = df_btoc[[X,Y]].pivot_table(index=X, columns=Y, aggfunc=len, margins=True, margins_name="total")
df_cont
sex | f | m | total |
---|---|---|---|
categ | |||
0 | 200793 | 186488 | 387281 |
1 | 115721 | 104884 | 220605 |
2 | 16980 | 15868 | 32848 |
total | 333494 | 307240 | 640734 |
#Création du dataframe de comparaison (dataframe théorique)
tx = df_cont.loc[:,["total"]] #récupère la colonne total
ty = df_cont.loc[["total"],:] #récupère la ligne total
n = len(df_btoc) #récupère le total général (toutes les lignes)
indep = tx.dot(ty) / n #produit matriciel en la colonne total et ligne total
#Calcul des écarts entre les 2 dataframe (réel et théorique)
measure = (df_cont-indep)**2/indep #(Effectifs observés − Effectifs théoriques)² / Effectifs théoriques -> calcul des écarts de chaque cellules
xi_n = measure.sum().sum() #Valeur du Chi2 (somme de tous les écarts)
table = measure/xi_n #En divisant tous les écarts par leur somme totale -> tout est entre 0:1
table
sex | f | m | total |
---|---|---|---|
categ | |||
0 | 0.133794 | 0.145227 | 0.0 |
1 | 0.310415 | 0.336940 | 0.0 |
2 | 0.035303 | 0.038320 | 0.0 |
total | 0.000000 | 0.000000 | 0.0 |
#Représentation graphique des différences
sns.heatmap(table.iloc[:-1,:-1],annot=table.iloc[:-1,:-1]) #Heatmap sans la ligne "total" et la colonne "total"
plt.title("Tableau de contingence des écarts\nEffectifs observés - Effectifs théoriques");
#Copie du dataframe sans la colonnes "total" et la ligne ""total" pour calculer la p-value
df_cont_sstotal = df_cont.drop(index="total",columns={"total"})
df_cont_sstotal
sex | f | m |
---|---|---|
categ | ||
0 | 200793 | 186488 |
1 | 115721 | 104884 |
2 | 16980 | 15868 |
#Calcul de la p-value
st_chi2, st_p, st_dof, st_exp = st.chi2_contingency(df_cont_sstotal)
st_p
1.1955928116587024e-05
Conclusion :
n = df_cont_sstotal.sum().sum()
min_dim = min(df_cont_sstotal.shape)-1
coef_cramerv = np.sqrt(st_chi2/(n*min_dim))
coef_cramerv
0.005948029928802536
Conclusion :
#Création des variables necessaire à l'affichage de la regression linaire - Méthode des moindres carrés (OLS)
slope, intercept, r_value, p_value, std_er = st.linregress(df_tranches_ageCA["age"],df_tranches_ageCA["CA_total"])
def predict(x):
return slope * x + intercept
#Affichage graphique
sns.scatterplot(data=df_tranches_ageCA, x="age", y="CA_total")
regress_lin(df_tranches_ageCA["age"],df_tranches_ageCA["CA_total"])
plt.legend(["individu","OLS"])
plt.show()
Correlation légère et négative à première vue -> le CA_total maximum semble diminuer avec l'age
df_tranches_ageCA.head()
client_id | age | CA_total | tranche_age | |
---|---|---|---|---|
0 | c_1 | 68.0 | 629.02 | 4 |
1 | c_10 | 67.0 | 1353.60 | 4 |
2 | c_100 | 31.0 | 254.85 | 1 |
3 | c_1000 | 57.0 | 2291.88 | 3 |
4 | c_1001 | 41.0 | 1823.85 | 2 |
df_tranches_ageCA["tranche_age"].unique()
array([4, 1, 3, 2, 5], dtype=int64)
#Récupération des ages concernés par les tranches pour plus de lisibilité
tranches = []
for t in sorted(df_tranches_ageCA["tranche_age"].unique()):
age_mini = int(df_tranches_ageCA.loc[df_tranches_ageCA["tranche_age"] == t, "age"].min())
age_max = int(df_tranches_ageCA.loc[df_tranches_ageCA["tranche_age"] == t, "age"].max())
tranches.append(str(age_mini)+"-"+str(age_max))
tranches
['19-34', '35-50', '51-66', '67-82', '83-94']
# Assignation des tranches d'ages
for t in sorted(df_tranches_ageCA["tranche_age"].unique()):
df_tranches_ageCA.loc[df_tranches_ageCA["tranche_age"] == t, "tranche_age"] = tranches[t-1]
df_tranches_ageCA.sort_values("tranche_age", ascending=True, inplace=True)
#Création d'un dictionnaire du nombre d'effectif par tranche
groupes = []
for tranche in sorted(df_tranches_ageCA["tranche_age"].unique()):
nb_clients = df_tranches_ageCA.loc[df_tranches_ageCA["tranche_age"] == tranche]["tranche_age"].count()
groupes.append(nb_clients)
#Représentation graphique
sns.boxplot(df_tranches_ageCA, x="tranche_age", y="CA_total")
i=0
for g in groupes:
plt.text(i,0,"(n={})".format(g), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
À prendre note :
sns.histplot(data=df_tranches_ageCA["age"], kde=True)
<Axes: xlabel='age', ylabel='Count'>
sns.histplot(data=df_tranches_ageCA["CA_total"], kde=True)
<Axes: xlabel='CA_total', ylabel='Count'>
Les 2 variables ne suivent visuellement pas de loi normale
Vérifions à l'aide du test de Shapiro (quantité d'individu de taille moyenne):
Test de Shapiro-Wilk sur chacune des 2 variables :
for var in [df_tranches_ageCA["age"], df_tranches_ageCA["CA_total"]]:
shapiro_value, p_value = st.shapiro(var)
print("La p-value est de la variable {} est de : {} ".format(var.name, p_value))
La p-value est de la variable age est de : 4.628808323714737e-39 La p-value est de la variable CA_total est de : 0.0
Conclusion :
Conditions de validité :
Hypothèses p-value:
Risque accepté : 5%
Intensité et sens de corrélation indiqué par le coefficient spearman_r
À nuancer avec le graphique de début
spearman_r, p_value = st.spearmanr(df_tranches_ageCA["age"],df_tranches_ageCA["CA_total"])
covar = np.cov(df_tranches_ageCA["age"],-df_tranches_ageCA["CA_total"],ddof=0)
print("Le coefficient de Spearman est de {}".format(round(spearman_r, 3)))
print("La p-value est de {}".format(p_value))
Le coefficient de Spearman est de -0.185 La p-value est de 1.0211908275536399e-66
Force de la corrélation:
Conclusion :
#Création du dataframe sur le nombre d'achat par client
df_temp = df_btoc.groupby(["client_id", "age","session_id"]).agg(livres_session=("session_id","count")).reset_index()
df_age_freq = df_temp.groupby(["client_id", "age"]).agg(session_total=("livres_session","count")).reset_index()
df_age_freq.head()
client_id | age | session_total | |
---|---|---|---|
0 | c_1 | 68 | 34 |
1 | c_10 | 67 | 34 |
2 | c_100 | 31 | 5 |
3 | c_1000 | 57 | 94 |
4 | c_1001 | 41 | 47 |
df_verif = df_btoc.groupby(["client_id","session_id"]).agg(session_total=("session_id","count")).reset_index()
df_verif.loc[df_verif["client_id"]=="c_100"]
client_id | session_id | session_total | |
---|---|---|---|
68 | c_100 | s_23060 | 1 |
69 | c_100 | s_240961 | 1 |
70 | c_100 | s_270695 | 1 |
71 | c_100 | s_53051 | 2 |
72 | c_100 | s_73929 | 3 |
#Affichage de la fréquence d'achat global
nb_clients = df_age_freq["client_id"].count()
nb_session = df_age_freq["session_total"].sum()
freq_globale = round(nb_session/nb_clients, 2) #similaire à df_age_freq["session_total"].mean()
freq_globale
37.51
Un client fait en moyenne 37.5 commandes
#Création de la colonne de fréquence -> nombre de session / nombre total de session
df_age_freq["freq_client"] = df_age_freq["session_total"]/df_age_freq["session_total"].sum()
#Récupération des tranches d'ages
df_age_freq = df_age_freq.merge(df_tranches_ageCA[["client_id", "tranche_age"]], on="client_id")
df_age_freq.head()
client_id | age | session_total | freq_client | tranche_age | |
---|---|---|---|---|---|
0 | c_1 | 68 | 34 | 0.000105 | 67-82 |
1 | c_10 | 67 | 34 | 0.000105 | 67-82 |
2 | c_100 | 31 | 5 | 0.000016 | 19-34 |
3 | c_1000 | 57 | 94 | 0.000292 | 51-66 |
4 | c_1001 | 41 | 47 | 0.000146 | 35-50 |
#Création d'un dictionnaire du nombre d'effectifs par tranche
groupes = []
for tranche in sorted(df_age_freq["tranche_age"].unique()):
nb_clients = df_age_freq.loc[df_age_freq["tranche_age"] == tranche]["tranche_age"].count()
groupes.append(nb_clients)
data = df_age_freq.sort_values("tranche_age", ascending=True)
x = "tranche_age"
y = "freq_client"
sns.boxplot(data=data , x="tranche_age", y="freq_client")
i=0
for g in groupes:
plt.text(i,0,"(n={})".format(g), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
Nous savons d'avance que la variable age ne suit pas une loi normale
spearman_r, p_value = st.spearmanr(df_age_freq["age"], df_age_freq["freq_client"])
print("Le coefficient de Spearman est de {}".format(round(spearman_r, 3)))
print("La p-value est de {}".format(p_value))
Le coefficient de Spearman est de 0.212 La p-value est de 6.629168433162815e-88
Force de la corrélation:
Conclusion préliminaire:
Ce résultat est étonnant au vu du boxplot au dessus, vérifions en groupant les ages avec leur moyenne de fréquence :
#Création du data frame necessaire
df_test= df_age_freq.groupby("age")["freq_client"].mean().reset_index()
df_test.head()
age | freq_client | |
---|---|---|
0 | 19 | 0.000062 |
1 | 20 | 0.000056 |
2 | 21 | 0.000057 |
3 | 22 | 0.000062 |
4 | 23 | 0.000056 |
#Création des variables necessaire à l'affichage de la regression linaire - Méthode des moindres carrés (OLS)
slope, intercept, r_value, p_value, std_er = st.linregress(df_test["age"],df_test["freq_client"])
def predict(x):
return slope * x + intercept
sns.scatterplot(data=df_test, x="age", y="freq_client")
regress_lin(df_test["age"],df_test["freq_client"])
plt.legend(["individu","OLS"])
<matplotlib.legend.Legend at 0x21595e98710>
En effet, le résultat confirme l'hypothèse vu sur le boxplot : il semble y avoir une corrélation négative à partir de 32 ans environs et faussée par le premier groupe Vérifion en isolant les 2 groupes (19-à définir | à définir-94ans):
#Determinons l'age bascule (autour des 32 ans) où toutes les premières valeurs sont supérieures à la moyenne des valeurss (choisi arbitrairement)
agemini_groupe_valide = df_test.loc[df_test["freq_client"] > df_test["freq_client"].mean()]["age"].min()
#Création du nouveau dataframe avec le bon groupe d'individu
df_groupe_valide = df_age_freq.loc[df_age_freq["age"] >= agemini_groupe_valide]
groupes = []
for tranche in sorted(df_groupe_valide["tranche_age"].unique()):
nb_clients = df_groupe_valide.loc[df_groupe_valide["tranche_age"] == tranche]["tranche_age"].count()
groupes.append(nb_clients)
sns.boxplot(data=df_groupe_valide.sort_values("age", ascending=True), x="tranche_age", y="freq_client")
i=0
for g in groupes:
plt.text(i,0,"(n={})".format(g), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
sns.scatterplot(data=df_groupe_valide, x="age", y="freq_client")
regress_lin(df_groupe_valide["age"],df_groupe_valide["freq_client"])
plt.show()
spearman_r, p_value = st.spearmanr(df_groupe_valide["age"], df_groupe_valide["freq_client"])
print("Le coefficient de Spearman est de {} sur les individus de {} à {} ans".format(round(spearman_r, 3), agemini_groupe_valide,df_groupe_valide["age"].max() ))
print("La p-value est de {}".format(p_value))
Le coefficient de Spearman est de -0.148 sur les individus de 32 à 94 ans La p-value est de 3.6670140717793306e-32
Conclusion finale:
Sur les 32-94 ans
#Création d'un premier dataframe avec les paniers pour chaque client
df_panier = df_btoc.groupby(["client_id", "age", "session_id"]).agg(CA_session=("price","sum")).reset_index()
df_panier.head()
client_id | age | session_id | CA_session | |
---|---|---|---|---|
0 | c_1 | 68 | s_101417 | 15.87 |
1 | c_1 | 68 | s_105105 | 62.96 |
2 | c_1 | 68 | s_114737 | 92.62 |
3 | c_1 | 68 | s_120172 | 44.29 |
4 | c_1 | 68 | s_134971 | 10.30 |
#Création du dataframe aggrégé sur les clients
df_panier_moy = df_panier.groupby(["client_id", "age"]).agg(nb_paniers=("session_id","count"), CA_total_paniers=("CA_session","sum")).reset_index()
df_panier_moy.head()
client_id | age | nb_paniers | CA_total_paniers | |
---|---|---|---|---|
0 | c_1 | 68 | 34 | 629.02 |
1 | c_10 | 67 | 34 | 1353.60 |
2 | c_100 | 31 | 5 | 254.85 |
3 | c_1000 | 57 | 94 | 2291.88 |
4 | c_1001 | 41 | 47 | 1823.85 |
#Création de la colonne avec le panier moyen (CA / nb de panier)
df_panier_moy["panier_moyen"] = df_panier_moy["CA_total_paniers"] / df_panier_moy["nb_paniers"]
#Rapprochement des tranches d'ages
df_panier_moy = df_panier_moy.merge(df_tranches_ageCA[["client_id", "tranche_age"]], on="client_id")
df_panier_moy.head()
client_id | age | nb_paniers | CA_total_paniers | panier_moyen | tranche_age | |
---|---|---|---|---|---|---|
0 | c_1 | 68 | 34 | 629.02 | 18.500588 | 67-82 |
1 | c_10 | 67 | 34 | 1353.60 | 39.811765 | 67-82 |
2 | c_100 | 31 | 5 | 254.85 | 50.970000 | 19-34 |
3 | c_1000 | 57 | 94 | 2291.88 | 24.381702 | 51-66 |
4 | c_1001 | 41 | 47 | 1823.85 | 38.805319 | 35-50 |
sns.scatterplot(data=df_panier_moy.sort_values("age",ascending=True), x="age", y="panier_moyen", hue="tranche_age")
regress_lin(df_panier_moy["age"], df_panier_moy["panier_moyen"])
[<matplotlib.lines.Line2D at 0x2159720d750>]
Premier aperçu: les 19-32 ans on un panier moyen nettement supérieur aux autres tranches d'ages ce qui a un impact sur la corrélation qui sera potentiellement fortement négative
sns.boxplot(df_panier_moy.sort_values("age",ascending=True), x="tranche_age", y="panier_moyen", showmeans=True, showfliers = False )
i=0
for g in groupes:
plt.text(i,5,"(n={})".format(g), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
spearman_r, p_value= st.spearmanr(df_panier_moy["age"],df_panier_moy["panier_moyen"])
print("Le coefficient de spearman est de {}".format(round(spearman_r, 3)))
print("La p-value est de {}".format(p_value))
Le coefficient de spearman est de -0.701 La p-value est de 0.0
Conclusion :
Force de la corrélation:
#Création du df concerné
df_groupe_valide = df_panier_moy.loc[df_panier_moy["age"] >= agemini_groupe_valide]
df_groupe_valide.min()
client_id c_1 age 32 nb_paniers 1 CA_total_paniers 6.31 panier_moyen 4.15 tranche_age 19-34 dtype: object
sns.scatterplot(data=df_groupe_valide.sort_values("age",ascending=True), x="age", y="panier_moyen", hue="tranche_age")
regress_lin(df_groupe_valide["age"], df_groupe_valide["panier_moyen"])
[<matplotlib.lines.Line2D at 0x215953f6d90>]
spearman_r, p_value= st.spearmanr(df_groupe_valide["age"],df_groupe_valide["panier_moyen"])
print("Le coefficient de Pearson est de {}".format(round(spearman_r, 3)))
print("La p-value est de {}".format(p_value))
Le coefficient de Pearson est de -0.402 La p-value est de 1.2270547101185366e-244
Conclusion finale :
Sur les 32-94 ans
#Création du dataframe concerné
x = "categ"
y = "age"
df_age_cat = df_btoc[[x,y]]
sns.boxplot(df_age_cat, x="categ", y="age", showfliers=True, showmeans=True)
i=0
for cat in sorted(df_age_cat["categ"].unique()):
n_cat = len(df_age_cat.loc[df_age_cat["categ"] == cat])
plt.text(i,0,"(n={})".format(n_cat), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
Les 3 catégories semblent assez différentes, même si l'ordre de grandeur de ces écarts n'est pas très grand, la question est de savoir si ces écarts sont significatifs ou pas.
C'est l'ANOVA qui nous permettra de répondre à cette question.
len(df_age_cat)
640734
Je vais outre-passer le test de Shapiro pour 2 raisons:
#Création des variables par categorie
cat_0 = df_age_cat.loc[df_age_cat["categ"] == 0]["age"]
cat_1 = df_age_cat.loc[df_age_cat["categ"] == 1]["age"]
cat_2 = df_age_cat.loc[df_age_cat["categ"] == 2]["age"]
#ANOVA test
st.f_oneway(cat_0, cat_1, cat_2)
F_onewayResult(statistic=39705.51969324281, pvalue=0.0)
Conclusion :
Il y a bien une indépendance entre les catégories 0, 1 et 2
sns.boxplot(df_age_cat, x="categ", y="age", showfliers=False, showmeans=True)
i=0
for cat in sorted(df_age_cat["categ"].unique()):
n_cat = len(df_age_cat.loc[df_age_cat["categ"] == cat])
plt.text(i,0,"(n={})".format(n_cat), horizontalalignment='center', verticalalignment='top')
i+=1
plt.show()
#Création d'un dataframe pour vérifier si les variances sont égales
df_age_cat.groupby("categ")['age'].agg('var')
categ 0 132.620783 1 250.414686 2 98.007304 Name: age, dtype: float64
Vérification si les variances sont significativement différentes avec le test de Levene (distribution non Gaussienne) :
cat_0 = df_btoc["age"][df_btoc["categ"] == 0]
cat_1 = df_btoc["age"][df_btoc["categ"] == 1]
cat_2 = df_btoc["age"][df_btoc["categ"] == 2]
st.levene(cat_0, cat_1, cat_2)
LeveneResult(statistic=24651.780367284304, pvalue=0.0)
Conclusion :
st.kruskal(cat_0, cat_1, cat_2)
KruskalResult(statistic=71359.73412120914, pvalue=0.0)
Conclusion :
#Création d'un dataframe pour visualiser des informations sur powerpoint -> proportion catégorie par tranche d'age
df_temp = df_btoc.merge(df_age_freq[["client_id","tranche_age"]], on="client_id")
df_temp.head()
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | heure | tranche_age | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday | 00 | 51-66 |
1 | c_329 | 2022-10-01 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday | 00 | 51-66 |
2 | c_329 | 2022-12-01 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday | 00 | 51-66 |
3 | c_329 | 2023-01-01 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday | 00 | 51-66 |
4 | c_329 | 2021-11-23 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday | 18 | 51-66 |
df_tempT = df_temp.pivot_table(index="tranche_age", values="price", aggfunc="count", columns="categ").reset_index()
df_tempT.head()
categ | tranche_age | 0 | 1 | 2 |
---|---|---|---|---|
0 | 19-34 | 55809 | 42164 | 30100 |
1 | 35-50 | 248250 | 80318 | 1190 |
2 | 51-66 | 58422 | 65062 | 1037 |
3 | 67-82 | 21550 | 28838 | 441 |
4 | 83-94 | 3250 | 4223 | 80 |
df_tranches_ageCA
client_id | age | CA_total | tranche_age | |
---|---|---|---|---|
6086 | c_6491 | 27.0 | 302.71 | 19-34 |
2295 | c_3068 | 33.0 | 664.12 | 19-34 |
7664 | c_7916 | 19.0 | 487.36 | 19-34 |
4675 | c_5218 | 26.0 | 684.68 | 19-34 |
4674 | c_5217 | 19.0 | 2039.50 | 19-34 |
... | ... | ... | ... | ... |
5285 | c_577 | 94.0 | 1829.96 | 83-94 |
7173 | c_7473 | 87.0 | 1434.75 | 83-94 |
5306 | c_5789 | 84.0 | 1151.39 | 83-94 |
401 | c_1360 | 89.0 | 1643.92 | 83-94 |
8159 | c_8362 | 94.0 | 842.87 | 83-94 |
8596 rows × 4 columns
#Top CA
df_temp = df_tranches_ageCA.groupby("age").agg(panier_moyen=("CA_total","mean"),nb_client=("client_id","count")).sort_values("panier_moyen", ascending=False)
df_temp.head()
panier_moyen | nb_client | |
---|---|---|
age | ||
39.0 | 1769.851677 | 155 |
40.0 | 1765.272469 | 162 |
38.0 | 1705.251739 | 115 |
45.0 | 1655.286131 | 199 |
49.0 | 1633.616625 | 160 |
sns.scatterplot(df_temp, x="age", y="panier_moyen", hue="nb_client", size="nb_client", palette="dark:red")
plt.ylabel("Panier moyen (en €)")
plt.title("Panier moyen selon l'age")
plt.legend(title="Nombre de clients")
plt.grid(which="both")
#Flop CA
df_tranches_ageCA.groupby("age").agg(CA_total=("CA_total","sum")).sort_values("CA_total", ascending=True).head()
CA_total | |
---|---|
age | |
92.0 | 2815.45 |
94.0 | 3249.84 |
93.0 | 4251.22 |
88.0 | 4945.03 |
91.0 | 5059.18 |
df_tranches_ageCA.groupby("tranche_age").agg(CA_total=("CA_total","sum"))
CA_total | |
---|---|
tranche_age | |
19-34 | 3732990.03 |
35-50 | 4386638.92 |
51-66 | 2041793.00 |
67-82 | 854416.21 |
83-94 | 127528.85 |
#Création d'un dataframe pour visualiser des informations sur powerpoint -> proportion catégorie par tranche d'age
df_temp = df_btoc.merge(df_age_freq[["client_id","tranche_age"]], on="client_id")
df_temp.head()
client_id | date | monthly | session_id | id_prod | price | categ | age | sex | jour | heure | tranche_age | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | c_329 | 2021-03-01 | 2021-03-01 | s_1 | 0_1259 | 11.99 | 0 | 56 | f | Monday | 00 | 51-66 |
1 | c_329 | 2022-10-01 | 2022-10-01 | s_275943 | 0_1259 | 11.99 | 0 | 56 | f | Saturday | 00 | 51-66 |
2 | c_329 | 2022-12-01 | 2022-12-01 | s_305291 | 0_1259 | 11.99 | 0 | 56 | f | Thursday | 00 | 51-66 |
3 | c_329 | 2023-01-01 | 2023-01-01 | s_320153 | 0_1259 | 11.99 | 0 | 56 | f | Sunday | 00 | 51-66 |
4 | c_329 | 2021-11-23 | 2021-11-01 | s_123998 | 1_397 | 18.99 | 1 | 56 | f | Tuesday | 18 | 51-66 |
df_temp = df_temp.groupby("age").agg(nb_book=("price","count"))
df_temp.sort_values("nb_book",ascending=False).head()
nb_book | |
---|---|
age | |
35 | 25245 |
44 | 25101 |
45 | 24905 |
37 | 23693 |
43 | 22116 |
df_temp.sort_values("nb_book",ascending=True).head()
nb_book | |
---|---|
age | |
92 | 170 |
94 | 202 |
93 | 238 |
88 | 278 |
91 | 312 |
sns.pairplot(data=df_btob)
<seaborn.axisgrid.PairGrid at 0x215952dc450>
df_btob.head()
date | monthly | session_id | id_prod | price | categ | client_id | age | sex | |
---|---|---|---|---|---|---|---|---|---|
84 | 2021-03-04 07:26:01 | 2021-03-01 | s_1519 | 0_1259 | 11.99 | 0 | c_1609 | 43 | m |
85 | 2021-06-24 09:40:37 | 2021-06-01 | s_53058 | 0_1259 | 11.99 | 0 | c_1609 | 43 | m |
86 | 2021-06-24 14:40:37 | 2021-06-01 | s_53165 | 0_1259 | 11.99 | 0 | c_1609 | 43 | m |
87 | 2021-09-26 21:30:08 | 2021-09-01 | s_96014 | 0_1259 | 11.99 | 0 | c_1609 | 43 | m |
88 | 2021-10-16 03:22:15 | 2021-10-01 | s_105418 | 0_1259 | 11.99 | 0 | c_1609 | 43 | m |
df_btob.set_index("date")["price"].resample("m").sum().plot(kind="line")
plt.grid(which="both")
df_custom_btob = df_btob.groupby(["client_id", "session_id", "age"]).agg(
CA_panier=("price","sum"), nb_book=("price","count")).reset_index()
df_custom_btob.head()
client_id | session_id | age | CA_panier | nb_book | |
---|---|---|---|---|---|
0 | c_1609 | s_10008 | 43 | 9.04 | 1 |
1 | c_1609 | s_100109 | 43 | 23.37 | 2 |
2 | c_1609 | s_100157 | 43 | 15.99 | 1 |
3 | c_1609 | s_100170 | 43 | 9.80 | 1 |
4 | c_1609 | s_100243 | 43 | 48.28 | 4 |
df_custom_btob = df_custom_btob.groupby(["client_id", "age"]).agg(
nb_session=("session_id","count"),
CA_total=("CA_panier","sum"),
panier_moyen=("CA_panier","mean"),
total_book=("nb_book","sum"),
nb_book_mean=("nb_book","mean")).reset_index()
df_custom_btob.head()
client_id | age | nb_session | CA_total | panier_moyen | total_book | nb_book_mean | |
---|---|---|---|---|---|---|---|
0 | c_1609 | 43 | 10997 | 326039.89 | 29.648076 | 25586 | 2.326635 |
1 | c_3454 | 54 | 5571 | 114110.57 | 20.482960 | 6793 | 1.219350 |
2 | c_4958 | 24 | 3851 | 290227.03 | 75.364069 | 5222 | 1.356011 |
3 | c_6714 | 55 | 2620 | 153918.60 | 58.747557 | 9199 | 3.511069 |
#Références particulières?
df_btob.groupby("id_prod").agg(
nb_book=("price","count"),
CA_total=("price","sum")).sort_values("CA_total",ascending=False).head(5)
nb_book | CA_total | |
---|---|---|
id_prod | ||
2_112 | 98 | 6621.86 |
2_209 | 89 | 6229.11 |
2_110 | 96 | 5976.00 |
2_135 | 85 | 5864.15 |
2_39 | 96 | 5567.04 |
#Références particulières?
df_btob.groupby("id_prod").agg(
nb_book=("price","count"),
CA_total=("price","sum")).sort_values("nb_book",ascending=False).head(5)
nb_book | CA_total | |
---|---|---|
id_prod | ||
1_403 | 166 | 2986.34 |
1_498 | 160 | 3739.20 |
1_395 | 153 | 4435.47 |
1_417 | 146 | 3064.54 |
1_400 | 141 | 2340.60 |
df_temp = df_btob.groupby("categ").agg(ca_total=("price","sum"), nb_livre=("price","count"), prix_moy_livre=("price","mean"))
df_temp
ca_total | nb_livre | prix_moy_livre | |
---|---|---|---|
categ | |||
0 | 300530.28 | 28178 | 10.665423 |
1 | 307555.25 | 14987 | 20.521469 |
2 | 276210.56 | 3635 | 75.986399 |
df_temp["ca_total"] = df_temp["ca_total"].round(0)
plt.subplot(121)
plt.pie(x=df_temp["ca_total"], autopct="%.2f", pctdistance=0.5, colors=["black", "grey","red"]);
plt.subplot(122)
plt.pie(x=df_temp["nb_livre"], autopct="%.2f", pctdistance=0.5,colors=["black", "grey","red"]);
plt.legend(["0","1","2"], bbox_to_anchor=(1.05, 1))
<matplotlib.legend.Legend at 0x21597885010>