【Python】RFM模型实现
1 RFM模型
RFM模型:根据用户历史行为数据,结合业务理解,实现用户分层分类,助力用户的精准营销,是衡量客户价值和客户创利能力的重要工具和手段
- Recency 最近一次消费
- Frequency 消费频次
- Money 消费金额
客户标签 | 客户标签 | 运营方向 | 客户状态 | R | F | M |
---|
重要价值用户 | VIP客户 | 保持现状 | 最近交易时间近、交易频率和交易金额高,“两高一近” | 1 | 1 | 1 | 重要发展用户 | 频次深耕客户 | 提升频次 | 最近交易时间近,交易金额高,但交易次数少;不太活跃,忠诚度不高,需要通过相关激励,提高其购买频率。 | 1 | 0 | 1 | 重要保持用户 | 流失预警VIP客户 | 用户回流 | 交易金额和交易频次都很高,但最近一次交易时间远,很长时间没来的忠实客户,需要主动和客户互动,及时唤回。 | 0 | 1 | 1 | 重要挽留用户 | 高消费换回客户 | 重点召回 | 交易金额高,但最近交易时间远、交易频次低;消费力较高,是潜在的价值客户,需要重点维持。 | 0 | 0 | 1 | 一般价值用户 | 消费潜力客户 | 刺激消费 | 最近交易时间近、交易频次也高,唯独交易金额小,属于低客单价群体。分为两种情况,低价高利润产品可适当维持及发展;低价低利润不需要额外大量投资预算维持。 | 1 | 1 | 0 | 一般发展用户 | 新客户 | 挖掘需求 | 最近交易时间近,但交易频次和交易金额小,说明属于意向用户,有推广价值,以此提高交易频次和交易金额。 | 1 | 0 | 0 | 一般保持用户 | 一般客户 | 流失召回 | 交易次数多,但是贡献不大,一般维持即可。 | 0 | 1 | 0 | 一般挽留用户 | 流失客户 | 可放弃治疗 | 最近交易时间远、交易频次和交易金额也都很小,贡献度最小,如果不需要额外的运营预算和精力,也可适当进行维护。 | 0 | 0 | 0 |
2 Python实现
2.1 数据结果
# -*- coding: utf-8 -*-
# In[0] 导入库
import numpy as np
import pandas as pd
import datetime as dt
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# In[1] 数据处理
df = pd.read_csv('F://Retail_Data_Transactions.csv')
df["trans_date"] = pd.to_datetime(df["trans_date"])
print(df['trans_date'].min(), df['trans_date'].max())
R_today = dt.datetime(2015,3,17)
df['R_diff'] = (R_today - df['trans_date']).dt.days
R = df.groupby(by=['customer_id'])['R_diff'].agg([('R_diff','min')])
F = df.groupby(by=['customer_id'])['customer_id'].agg([('F_fre','count')])
M = df.groupby(by=['customer_id'])['tran_amount'].agg([('M_sum',sum)])
# In[2.1] 直接加权划分, 权重:R0.3,F0.4,M0.3
RFM = R.join(F).join(M)
RFM['r_score'] = pd.cut(RFM['R_diff'], 5, labels=[5, 4, 3, 2, 1])
RFM['f_score'] = pd.cut(RFM['F_fre'], 5, labels=[1, 2, 3, 4, 5])
RFM['m_score'] = pd.cut(RFM['M_sum'], 5, labels=[1, 2, 3, 4, 5])
RFM['r_s']=pd.factorize(RFM['r_score'])[0]
RFM['f_s']=pd.factorize(RFM['f_score'])[0]
RFM['m_s']=pd.factorize(RFM['m_score'])[0]
RFM["RFMsum"] = RFM['r_s']*0.3+RFM['f_s']*0.4+RFM['m_s']*0.3
labels = ['一般挽留用户','一般发展客户','一般保持客户','一般价值客户','重要挽留客户','重要发展客户','重要保持客户','高价值客户']
RFM['labels_1'] = pd.cut(RFM["RFMsum"], bins=8, labels=labels, include_lowest=True)
# In[2.2] RFM直接组合
dftmp = RFM.copy()
dftmp['r']= (dftmp['R_diff']<dftmp['R_diff'].mean())*1
dftmp['f']= (dftmp['F_fre']>dftmp['F_fre'].mean())*1
dftmp['m']= (dftmp['M_sum']>dftmp['M_sum'].mean())*1
dftmp['r'] = dftmp['r'].astype('string')
dftmp['f'] = dftmp['f'].astype('string')
dftmp['m'] = dftmp['m'].astype('string')
RFM['rfm'] = dftmp['r'].str.cat(dftmp['f']).str.cat(dftmp['m'])
RFM['lables'] = RFM['rfm'].apply(lambda x :
'重要价值用户(VIP)' if x=='111'
else '重要发展用户(频次深耕)' if x=='101'
else '重要保持用户(流失预警)' if x=='011'
else '重要挽留用户(重点召回)' if x=='001'
else '一般价值用户(刺激消费)' if x=='110'
else '一般发展用户(挖掘需求)' if x=='100'
else '一般保持用户(流失召回)' if x=='010'
else '一般挽留用户(流失放弃)' if x=='000'
else x)
2.2 图像展示
# In[3] 气泡图,8个类别显示不明显
categories = np.unique(RFM['lables'])
colors = [plt.cm.tab10(i / float(len(categories) - 1)) for i in range(len(categories))]
fig = plt.figure(figsize=(10, 6), dpi=120, facecolor='w', edgecolor='k')
for i, category in enumerate(categories):
plt.scatter('F_fre', 'M_sum', data=RFM.loc[RFM.lables == category, :]
, s=RFM.loc[RFM.lables == category, 'R_diff']
, c=np.array(colors[i]).reshape(1, -1)
, edgecolors=np.array(colors[i]).reshape(1, -1)
, label=str(category)
, alpha=0.7
, linewidths=.5)
plt.gca().set(xlim=(0, 40), ylim=(0, 3000),xlabel='F', ylabel='M')
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.title('客户RFM分类模型图', fontsize=18)
plt.legend(loc='best',fontsize=10,frameon=True,markerscale=0.5)
plt.show()
# In[4] 3D散点图
import matplotlib as mpl
ax = plt.subplot(projection = '3d')
ax.set_title('客户RFM分类模型图', fontsize=18)
for i, category in enumerate(categories):
ax.scatter('R_diff','F_fre', 'M_sum', data=RFM.loc[RFM.lables == category, :]
, s=RFM.loc[RFM.lables == category, 'R_diff']
, c=np.array(colors[i]).reshape(1, -1)
, edgecolors=np.array(colors[i]).reshape(1, -1)
, label=str(category)
, alpha=0.7
, linewidths=.5)
ax.set_xlabel('R')
ax.set_ylabel('F')
ax.set_zlabel('M')
legend_lines = [mpl.lines.Line2D([0], [0], linestyle="none", marker='o', c=colors[y]) for y in range(len(categories))]
ax.legend(legend_lines, categories, numpoints=1, title='客户RFM分类模型图')
plt.show()
# In[5] 直方图
data = RFM.groupby('lables')['rfm'].count().sort_values(ascending = False)
plt.figure(figsize=(8,6))
data.plot(kind='bar', width=0.6)
plt.ylabel(u'客户数量')
p = 1.0*data.cumsum()/data.sum()
p.plot(color = 'r', secondary_y = True, style = '-o',linewidth = 2)
for i in range(len(p)):
plt.annotate(format(p[i], '.2%'),xy = (i, p[i]), xytext=(i*1.005, p[i]*1.005))
plt.ylabel(u'客户数量(比例)')
plt.show()
|