RFM 是個簡易分析客戶價值的模型,在電商零售上常常使用。RFM 透過客戶近期的購買行為(Recency)、購買頻率(Frequency)、購買花費(Monetary) 三項指標評估客戶的價值狀況。
模型意義
- 最近一次消費(Recency):
上一次消費時間越近的顧客應該是比較好的顧客,對提供即時的商品或是服務也最有可能會有反應。 - 消費頻率(Frequency):
也就是常客,也可以說是客戶對品牌的忠誠度,頻率高的優於頻率低的客戶。 - 消費金額(Monetary):
客戶消費力,依循 Pareto’s Law (八二法則),80%的銷售額來自20%的客戶,高消費金額優於低消費金額。
長跪客戶分群意義
Recency 消費時間 | Frequency 頻率 | Monetary 金額 | 客戶分群 |
---|
近 | 高 | 高 | 高價值,VIP 客戶 |
遠 | 高 | 高 | 高價值,流失客戶 |
近 | 低 | 高 | 高價值,新客戶 |
遠 | 低 | 高 | 高價值,淺在客戶 |
近 | 高 | 低 | 一般價值,VIP 客戶 |
遠 | 高 | 低 | 一般價值,流失客戶 |
近 | 低 | 低 | 一般價值,新客戶 |
遠 | 低 | 低 | 一般價值,淺在客戶 |
以 R 與 F 分析圖劃分

Python 程式碼
使用 kaggle 的資料做練習
Data Prepare
資料清理,排除缺失值與負值,並計算出收益。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from datetime import datetime
df = pd.read_csv('data.csv', encoding = "ISO-8859-1")
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate']) df_new = df.dropna() df_new = df_new[df_new.Quantity > 0] df_new = df_new[df_new.UnitPrice > 0]
df['Revenue'] = df['Quantity'] * df['UnitPrice'] df['CustomerID'] = df['CustomerID'].astype('int64')
|

RFM Table
統計個人消費行為,得到至今最近一次消費的天數、消費次數、消費總金額
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| NOW = datetime(2011,12,10) rfm_df = df.groupby('CustomerID').agg({ 'InvoiceDate': lambda x: (NOW - x.max()).days, 'InvoiceNo': lambda x: x.nunique(), 'Revenue': lambda x: x.sum() }).reset_index()
rfm_df.rename( columns={'InvoiceDate': 'recency', 'InvoiceNo': 'frequency', 'Revenue': 'monetary'}, inplace=True ) rfm_df['user'] = 1
|

RFM Model
使用中位數(median)將數據分為兩級,也可以使用 33 與 66 分位(quantile)分三級
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
def get_level(col_val, level: list): for idx, val in enumerate(reversed(level)): if col_val >= val: return f'≥{int(val)}'
recency_level = [0, rfm_df['recency'].median()] recency_label = [f'≥{int(i)}' for i in recency_level] rfm_df['recency_label'] = rfm_df['recency'].apply( lambda x: get_level(x, recency_level) )
frequency_level = [0, rfm_df['frequency'].median()] frequency_label = [f'≥{int(i)}' for i in frequency_level] rfm_df['frequency_label'] = rfm_df['frequency'].apply( lambda x: get_level(x, frequency_level) )
monetary_level = [0, rfm_df['monetary'].median()] monetary_label = [f'≥{int(i)}' for i in monetary_level] rfm_df['monetary_label'] = rfm_df['monetary'].apply( lambda x: get_level(x, monetary_level) )
|

RFM Draw
利用 seaborn 繪圖,分成四個區塊為各個客戶群,裡面兩條長條圖分別為高消費力與低消費力的人數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| g = sns.FacetGrid( rfm_df, col="recency_label", row="frequency_label" , col_order=recency_label, row_order=frequency_label[::-1], margin_titles=True )
g = g.map_dataframe( sns.barplot, x='monetary_label', y='user', order=monetary_label, estimator=sum, palette = sns.color_palette("muted") )
g = g.set_axis_labels('Recency','Frequency').add_legend()
|

找出 VIP 客戶群
最後可以根據條件對不同客戶群分別做行銷
1 2 3 4 5 6
| vip = rfm_df[ (rfm_df['frequency_label'] == frequency_label[-1]) & (rfm_df['recency_label'] == recency_label[-1]) & (rfm_df['monetary_label'] == monetary_label[-1]) ] vip.head(10)
|
