Segmentation de clientèle avec RFM

Mar 04 2023
Sur un problème commercial réel
Tout d'abord, qu'est-ce que le RFM ? RFM est un indicateur clé composé de trois mesures pour comprendre les relations actuelles des clients avec les entreprises. L'analyse RFM est une technique largement utilisée, basée sur des données et basée sur des règles, utilisée pour la segmentation de la clientèle avec des mesures de récence, de fréquence et monétaires en utilisant les données client.

Tout d'abord, qu'est-ce que le RFM ?

RFM est un indicateur clé composé de trois mesures pour comprendre les relations actuelles des clients avec les entreprises. L'analyse RFM est une technique largement utilisée, basée sur des données et basée sur des règles, utilisée pour la segmentation de la clientèle avec des mesures de récence , de fréquence et monétaires en utilisant les données client.

L'analyse RFM est effectuée pour mieux répondre aux besoins des différents types de clients séparément. Les clients sont regroupés en fonction de leurs habitudes d'achat afin que des stratégies commerciales uniques puissent être développées pour ces groupes. Les traitements individuels peuvent empêcher le désabonnement des clients et garantir que les clients fidèles le restent.

Trois facteurs clés :

La récence fait référence à la durée de la dernière interaction avec le client. La plus petite valeur de récence correspond au dernier achat ; donc plus c'est petit mieux c'est. Les clients plus récents sont plus précieux.

Récence = Date de l'analyse — Date du dernier achat

La fréquence fait référence à la fréquence à laquelle un client effectue un achat. Les clients fréquents sont plus précieux.

Monétaire fait référence à la valeur financière que chaque client a dépensée pour cette entreprise.

Comment comparer ces métriques ?

Ces métriques sont converties en scores afin qu'elles soient du même type et puissent être comparables entre elles. Cette conversion peut également être appelée standardisation. Les scores provenant des métriques sont attachés les uns aux autres et forment le score RFM pour chaque client individuel. Les clients sont principalement classés en fonction de la valeur qu'ils apportent à l'entreprise de 1 à 5.

Les scores RFM peuvent être sur une large plage, c'est pourquoi une méthode est appliquée sur les scores RFM pour former des segments afin que ces segments aient des informations significatives.

Segmentation par scores RFM :

Tableau 1 : Modèle RFM

La segmentation est généralement effectuée à l'aide d'un graphique 2D présenté dans le tableau 1 . De cette façon, les scores RFM ont un sens. RFM Analysis peut aider les entreprises à connaître leurs clients et à améliorer leurs stratégies marketing en conséquence.

Comme on le voit, le score monétaire n'est pas sur le graphique car dans l'analyse CRM, la fréquence et la récence sont plus importantes que le monétaire. Quand on y pense, l'analyse monétaire d'un client avec peu de transactions n'est pas si importante par rapport aux clients fréquents. Bien que la valeur monétaire ne soit pas explicitement représentée dans le graphique, elle reste un élément important de l'analyse RFM et est utilisée pour formuler des hypothèses.

Résoudre un problème métier via RFM :

Voyons comment ces concepts sont appliqués sur un ensemble de données du monde réel, quelles informations peuvent être extraites des données. Vous pouvez accéder au code complet dans mon profil GitHub pour une meilleure démonstration.

Lors d'une analyse de données, certaines étapes doivent être suivies.

1.Problème commercial

Une société de commerce électronique basée au Royaume-Uni souhaite diviser ses clients en segments et définir des stratégies marketing en fonction de ces segments. Un ensemble de données composé des ventes entre le 01/12/2009 et le 09/12/2011 existe.

2.Comprendre les données

L'ensemble de données comprend 8 variables, à savoir la facture, le code de stock, la description, la quantité, la date de facturation, le prix, l'ID client et le pays. Les factures qui commencent par "C" sont pour les transactions annulées. Pour comprendre à quoi nous sommes confrontés, nous devons examiner les données. L'analyse sera faite pour les années 2010-2011, mais elle peut être utilisée aussi pour les années 2009-2010 puisque le processus sera généralisé à la fin.

Réglages principaux (importation des bibliothèques et définition des préférences d'affichage des données) :

import datetime as dt
import pandas as pd
pd.set_option('display.max_columns', None) #to see all the columns
pd.set_option('display.float_format', lambda x: '%.3f' % x) #to have 3 decimal points

df_ = pd.read_excel("excel-file-destination.xlsx", sheet_name="Year 2010-2011")
df = df_.copy() #It takes time to read the file,thus a copy is taken after reading the file
                #so we do not have to read the file and have to wait repeatedly thoughout the analysis

df.head() #unless specified otherwise, returns the first 5 rows of the dataset
df.shape  #returns the number of the ows and columns
df.isnull().sum() #returns the number of null values in every column

      
                

Un grand ensemble de données en cause avec 541910 lignes et 8 colonnes. Mais faut-il considérer toutes les données pour l'analyse ?

Nous avons des valeurs manquantes dans la plupart des variables Customer ID et Description. Cela signifie que nous devrons supprimer les lignes d'ID client manquantes puisque l'analyse est spécifique au client, les données manquantes ne donnent aucune information.

Répondons à quelques questions :

# Number of unique products
df["Description"].nunique()

# Number of products appearing in invoices()
df["Description"].value_counts().head()

# Most ordered product (including quantities)
df.groupby("Description").agg({"Quantity": "sum"}).sort_values("Quantity", ascending=False).head()

# Number of unique invoices
df["Invoice"].nunique()

# Add a new column for TotalPrice
df["TotalPrice"] = df["Quantity"] * df["Price"]

# Find the total cost in every invoice
df.groupby("Invoice").agg({"TotalPrice": "sum"}).head()
# needed for monetary metric

      
                

La préparation des données joue un rôle important dans l'analyse. Les données doivent être manipulées de manière à pouvoir faire des déductions. Les problèmes sur cet ensemble de données et les solutions sont expliqués ci-dessous sur le code.

df.shape # To observe the reducing number of rows as we make adjustments
df.isnull().sum()  # Observed problem: Missing Customer ID data, needed to be erased
df.describe().T  # Observed problem: Negative Quantitity values, no meaning, needed to be erased

df = df[(df['Quantity'] > 0)] # To solve Negative Quantitity values problem

df.dropna(inplace=True)
# To solve Missing Customer ID data problem
# Inplace makes the changes on dataset immediately without a need for assigning

df["Invoice"] = df["Invoice"].astype(str)
df = df[~df["Invoice"].str.contains("C", na=False)]
# Cancelled invoices do not need to be in the analysis, so they are erased.
# "~" means except this:

      
                

4.Calcul des métriques RFM

Jusqu'à présent, nous préparions essentiellement les données afin de pouvoir effectuer une analyse RFM dessus. Commençons maintenant l'analyse par le calcul des métriques. Puisque nous ne vivons plus en 2010-2011, nous devons établir une date à laquelle l'analyse est effectuée. À cette fin, trouvons la dernière date et décidons d'un jour proche pour effectuer l'analyse.

Ensuite, les opérations sont effectuées sur les colonnes InvoiceDate, Invoice et TotalPrice et regroupées par ID client pour obtenir une trame de données RFM contenant respectivement les mesures de récence, de fréquence et monétaires.

df["InvoiceDate"].max()  # latest date on data

today_date = dt.datetime(2011, 12, 11)
# added 2 days to last day to form today's date
# utilized datetime library so that today_date's datatype is datetime

type(today_date)

rfm = df.groupby('Customer ID').agg({'InvoiceDate': lambda InvoiceDate: (today_date - InvoiceDate.max()).days,
                                     'Invoice': lambda Invoice: Invoice.nunique(),
                                     'TotalPrice': lambda TotalPrice: TotalPrice.sum()})

# In order, Recency, Frequency, Monetary are found.
# Recency is the difference in the today's date and last purchase on Customer ID level.
# Frequency is the number of unique invoices that belongs to the same customer.
# Monetary is summation of total prices of the same customer, so we know how much each customer brings to the company.

rfm.head()
# check the data
rfm.columns = ['recency', 'frequency', 'monetary']
# change the column names

rfm.describe().T # to see if there is an unsual situation

rfm = rfm[rfm["monetary"] > 0]
# min monetary = 0 is unusual, so they are excluded.

rfm.shape

      
                

5.Calcul des scores RFM :

Lors du calcul des scores RFM, la première chose à considérer est que les scores de récence sont attribués à l'envers par rapport à la fréquence et à la valeur monétaire en raison de leur signification.

rfm["recency_score"] = pd.qcut(rfm['recency'], 5, labels=[5, 4, 3, 2, 1])
# recency metrics divided 5 equal parts acoording to their sizes and
# labelled such a way that greatest recency got 1, the smallest recency got 5.

rfm["frequency_score"] = pd.qcut(rfm['frequency'].rank(method="first"), 5, labels=[1, 2, 3, 4, 5])
# the problem in frequency is some numbers are too repetitive that
# qcut can not label the same frequency number diffently
# rank method solves this problem by assigning the first comer number to first label.

rfm["monetary_score"] = pd.qcut(rfm['monetary'], 5, labels=[1, 2, 3, 4, 5])

rfm["RFM_SCORE"] = (rfm['recency_score'].astype(str) +
                    rfm['frequency_score'].astype(str))
# by RFM definition, string is created with recency and frequency score
# and formed final RFM Score
# monetary score is necessary for observation, but it is not used in forming RFM Score

rfm.describe().T
# newly created scores did not apper with describe function
# since they are strings and not integers

rfm[rfm["RFM_SCORE"] == "55"]
# champions

rfm[rfm["RFM_SCORE"] == "11"]
# hibernating

      
                

Afin d'augmenter la lisibilité du tableau, nous allons ajouter ce que chaque segment signifie pour nous selon le tableau 1 .

RegEx (expressions régulières) contient des correspondances conformes à un certain modèle dans une série. C'est la partie que nous extrayons des informations à partir des données.

# regex
# RFM Naming (Pattern Matching)
seg_map = {
    r'[1-2][1-2]': 'hibernating',
    r'[1-2][3-4]': 'at_Risk',
    r'[1-2]5': 'cant_loose',
    r'3[1-2]': 'about_to_sleep',
    r'33': 'need_attention',
    r'[3-4][4-5]': 'loyal_customers',
    r'41': 'promising',
    r'51': 'new_customers',
    r'[4-5][2-3]': 'potential_loyalists',
    r'5[4-5]': 'champions'
}
#  Basically the R-F table is coded using regex.

rfm['segment'] = rfm['RFM_SCORE'].replace(seg_map, regex=True)
segment_analysis = rfm[["segment", "recency", "frequency", "monetary"]].groupby("segment").agg(["mean", "count"])


rfm[rfm["segment"] == "cant_loose"].head()
cant_loose_customers = rfm[rfm["segment"] == "cant_loose"].index
#  A list of customers which the company should be more careful with is formed.
#  This way, every segment of customers is accesible. 

new_df = pd.DataFrame()
#  Empty dataframe is formed

new_df["new_customer_id"] = rfm[rfm["segment"] == "new_customers"].index
new_df["new_customer_id"] = new_df["new_customer_id"].astype(int)
#  To get rid of the decimals

new_df.to_csv("new_customers.csv")
rfm.to_csv("rfm.csv")
#  Segment information is extracted into an excel file.

      
                

Selon les principes de programmation de base tels que DRY (Don't Repeat Yourself), la fonctionnalisation de l'ensemble de ce processus est indispensable. Ainsi, nous pouvons utiliser cette fonction, d'où l'analyse RFM, sur différentes trames de données à plusieurs reprises.

Version propre de toutes les données ci-dessus écrites en fonction ci-dessous au nom de create_rfm avec l'option de création d'un fichier csv.

def create_rfm(dataframe, csv=False):

PS Merci beaucoup d'avoir consacré votre temps à lire tout cela. Je partage au fur et à mesure que j'apprends, donc de nouveaux articles liés à la science des données arriveront chaque semaine. J'espère que ce message vous aidera aussi. N'hésitez pas à laisser un commentaire :) Bonne chance dans votre propre voyage en science des données.

Connectez-vous avec moi sur LinkedIn

Connectez-vous avec moi sur Medium

Référence:

  • Vidéos de conférence Miuul