RFM Model โดยใช้ Python

RFM Model โดยใช้ Python
Photo by Lisa Fotios: https://www.pexels.com/photo/man-coding-on-pc-16129728/

RFM ย่อจาก Recency, Frequency, Monetary ใช้เพื่อแบ่ง Segment ลูกค้า ถือเป็น Behavioral Segmentation มี 3 Features ที่สำคัญ คือ

  1. Recency - จำนวนวัน ตั้งแต่การซื้อครั้งสุดท้าย
  2. Frequency - จำนวน Transactions ในช่วงเวลาที่กำหนด
  3. Monetary - จำนวนเงินที่ใช้ ในช่วงเวลาที่กำหนด

สามารถทำการ Group (Aggregate) Features เหล่านี้โดย

  1. Percentiles หรือ Quartiles
  2. Pareto Rule - 80/20
  3. มุมมองทางธุรกิจ
Photo by Thirdman: https://www.pexels.com/photo/woman-online-shopping-6238100/

ลงมือทำ

  • ในที่นี้ จะใช้ E-Commerce Dataset จาก https://www.kaggle.com/fabiendaniel/customer-segmentation
  • เรียกใช้ Libraries ที่จำเป็น จากนั้น Read Dataset และ แปลง Column “InvoiceDate” ให้อยู่ในรูปแบบ DateTime
# Import libraries
import pandas as pd
from datetime import timedelta
import matplotlib.pyplot as plt
import seaborn as sns
import squarify

# Read dataset
online = pd.read_csv('../data.csv', encoding = "ISO-8859-1")

# Convert InvoiceDate from object into datetime
online['InvoiceDate'] = pd.to_datetime(online['InvoiceDate'])
  • ขั้นแรก ต้องทำการจัดเรียงลูกค้าตาม ค่า Recency, Frequency, Monetary
  • ทำการคำนวณ Total Sum = Quantity x Unit Price
  • ในการคำนวณ Recency จะใช้ ค่าวันสุดท้าย + 1 ของ Invoice Date ลบ กับ วันที่ลูกค้าซื้อครั้งสุดท้าย
  • จากนั้น ทำการ Group by “Customer ID” และ เก็บไว้ใน data_process
# Create TotalSum column
online['TotalSum'] = online['Quantity'] * online['UnitPrice']

# Create snapshot date
snapshot_date = online['InvoiceDate'].max() + timedelta(days=1)

print(snapshot_date)

# Group by CustomerID
data_process = online.groupby(['CustomerID']).agg({
        'InvoiceDate': lambda x: (snapshot_date - x.max()).days,
        'InvoiceNo': 'count',
        'TotalSum': 'sum'})

# Change column name      
data_process.rename(columns={'InvoiceDate': 'Recency',
                         'InvoiceNo': 'Frequency',
                         'TotalSum': 'MonetaryValue'}, inplace=True)

# Print top 5 rows & shape
print(data_process.head())
print('{:,} rows; {:,} columns'
      .format(data_process.shape[0], data_process.shape[1]))
  • ดูผลลัพธ์ที่ได้ มีลูกค้า 4,372 คน แสดงผลด้วยค่า Recency, Frequency, Monetary

Output:
CustomerID Recency Frequency MonetaryValue
12346.0 326 2 0.00
12347.0 2 182 4310.00
12348.0 75 31 1797.24
12349.0 19 73 1757.55
12350.0 310 17 334.40
4,372 rows; 3 columns

  • แสดงการ Plot Distribution ของ RFM
plt.figure(figsize=(12,10))
plt.subplot(3, 1, 1); sns.distplot(data_process['Recency'])
plt.subplot(3, 1, 2); sns.distplot(data_process['Frequency'])
plt.subplot(3, 1, 3); sns.distplot(data_process['MonetaryValue'])
plt.show()
Reference - https://towardsdatascience.com/recency-frequency-monetary-model-with-python-and-how-sephora-uses-it-to-optimize-their-google-d6a0707c5f17
  • จากการ Plot Distribution ทำให้รู้ว่า ข้อมูลมีการ Skewed ไปมากเพียงใด ในขั้นต่อไปเราจะจัดกลุ่มข้อมูลนี้ โดยใช้ Quartiles
  • อย่างไรก็ตาม หากใช้ Algorithm อย่างเช่น K-Means ต้องแน่ใจว่าข้อมูลถูก Scaled ให้อยู่ที่ Center (พิจารณา Mean, Standard Deviation)
  • ทำการให้คะแนน โดยแบ่งเป็น 4 กลุ่ม R, F, M โดย R ที่มีค่าน้อยจะได้คะแนนสูง (เพิ่งซื้อไม่นาน)
# Create labels for R, F, M
r_labels = range(4, 0, -1)
f_labels = range(1, 5)
m_labels = range(1, 5)

# Assign these labels to 4 equal percentile groups 
r_groups = pd.qcut(data_process['Recency'], q=4, labels=r_labels)
f_groups = pd.qcut(data_process['Frequency'], q=4, labels=f_labels)
m_groups = pd.qcut(data_process['MonetaryValue'], q=4, labels=m_labels)

# Create new columns R, F, M
data_process = data_process.assign(R = r_groups.values, 
                                   F = f_groups.values, 
                                   M = m_groups.values)
data_process.head()

# Create RFM dataframe
rfm = data_process
rfm.head()
  • การหาผลรวมของคะแนน เป็นวิธีการที่ตรงไปตรงมาอันหนึ่ง โดยรวมให้เป็นคะแนนเดียว และ กำหนด RFM Levels ในแต่ละช่วงคะแนน
# Calculate RFM_Score
rfm['RFM_Score'] = rfm[['R','F','M']].sum(axis=1)
print(rfm['RFM_Score'].head())

Output:
CustomerID
12346.0 3.0
12347.0 12.0
12348.0 8.0
12349.0 10.0
12350.0 4.0
Name: RFM_Score, dtype: float64

  • สามารถกำหนดได้ว่า แต่ละช่วงคะแนน จะให้ชื่อ Segments ว่าอะไร อาจให้ผู้เชี่ยวชาญในธุรกิจช่วยแนะนำ ในที่นี้ จะแสดงตัวอย่างแบบหนึ่ง
# Define RFM level 
def rfm_level(df):
    if df['RFM_Score'] >= 9:
        return 'Can\'t Loose Them'
    elif ((df['RFM_Score'] >= 8) and (df['RFM_Score'] < 9)):
        return 'Champions'
    elif ((df['RFM_Score'] >= 7) and (df['RFM_Score'] < 8)):
        return 'Loyal'
    elif ((df['RFM_Score'] >= 6) and (df['RFM_Score'] < 7)):
        return 'Potential'
    elif ((df['RFM_Score'] >= 5) and (df['RFM_Score'] < 6)):
        return 'Promising'
    elif ((df['RFM_Score'] >= 4) and (df['RFM_Score'] < 5)):
        return 'Needs Attention'
    else:
        return 'Require Activation'
        
# Create a new variable RFM_Level
rfm['RFM_Level'] = rfm.apply(rfm_level, axis=1)

# Print the top 5 rows 
rfm.head()
  • ทำการหาค่า Mean และ Count เพื่อวิเคราะห์จำนวนลูกค้าแต่ละ Segment
# Calculate average values for each RFM_Level, and return a size of each segment 
rfm_level_agg = rfm.groupby('RFM_Level').agg({
    'Recency': 'mean',
    'Frequency': 'mean',
    'MonetaryValue': ['mean', 'count']
}).round(1)

# Pring the aggregate data
print(rfm_level_agg)
  • จากที่นี่ เราจะเห็นได้ว่าลูกค้าส่วนใหญ่ (~ 60%) ของเราอยู่ในระดับ RFM สูงสุด ร้านค้าจะต้องพยายามรักษาความภักดีนี้ไว้ แต่อีก 40% ที่เหลือ อาจจะต้องมีการกระตุ้นบ้าง
  1. Potential - มีศักยภาพสูงในการเข้าสู่กลุ่มลูกค้า Loyal อาจลองแจกของสมนาคุณในการซื้อครั้งต่อไป เพื่อแสดงว่าเราให้คุณค่ากับพวกเขา
  2. Promising - มีแนวโน้มดี ทั้งปริมาณและมูลค่าของการซื้อ แต่เป็นเวลานานแล้วซื้อครั้งสุดท้ายจากเรา อาจ Target ด้วย Items ที่อยากได้ และ เสนอส่วนลดให้ในเวลาจำกัด
  3. Need Attention - ทำการซื้อครั้งแรก แต่ยังไม่มีการซื้ออีกเลย เป็นประสบการณ์ที่ไม่ดีของลูกค้าหรือไม่ ? หรือความเหมาะสมของผลิตภัณฑ์? อาจสร้างการรับรู้ถึง Brands ให้มากขึ้น
  4. Required Activation - กลุ่มที่แย่ที่สุด อาจใช้ผลิตภัณฑ์ของคู่แข่งอยู่ จะต้องใช้กลยุทธ์ที่แตกต่างออกไปเพื่อดึงกลับมา
  • ทำการแสดงผล โดยใช้ Squarify Library
# Change column name
rfm_level_agg.columns = rfm_level_agg.columns.droplevel()
rfm_level_agg.columns =['RecencyMean','FrequencyMean','MonetaryMean', 
                         'Count']

# Plot using Squarify
fig = plt.gcf()
ax = fig.add_subplot()
fig.set_size_inches(16, 9)
squarify.plot(sizes=rfm_level_agg['Count'], 
              label=['Can\'t Loose Them',
                     'Champions',
                     'Loyal',
                     'Needs Attention',
                     'Potential', 
                     'Promising', 
                     'Require Activation'], alpha=.6 )
plt.title("RFM Segments",fontsize=18,fontweight="bold")
plt.axis('off')
plt.show()
Reference - https://towardsdatascience.com/recency-frequency-monetary-model-with-python-and-how-sephora-uses-it-to-optimize-their-google-d6a0707c5f17

Tips (by Admin)

  • การสร้าง RFM Model จำเป็นต้องกำหนดช่วงเวลา เพื่อไม่ให้เกิด Bias สำหรับลูกค้าใหม่หรือเก่า
  • ในตัวอย่างแสดงการคำนวณ Score โดยใช้ Quartiles อาจใช้วิธีการอื่น เช่น Percentiles
  • ในตัวอย่างให้ Weight ของ R,F,M เท่าๆ กัน (ไม่มีการกำหนด Weight) หากธุรกิจสนใจในเรื่องใดเรื่องหนึ่งมากกว่า เช่น F (Frequency) อาจพิจารณาให้ Weight ที่มากกว่าได้

******

ข้อมูลอ้างอิง - https://towardsdatascience.com/recency-frequency-monetary-model-with-python-and-how-sephora-uses-it-to-optimize-their-google-d6a0707c5f17