Python RFM用户分析模型(个人笔记)

关注微信公共号:小程在线

关注CSDN博客:程志伟的博客

 

Python 3.7.6 (default, Jan  8 2020, 20:23:39) [MSC v.1916 64 bit (AMD64)]
Type "copyright", "credits" or "license" for more information.

IPython 7.12.0 -- An enhanced Interactive Python.

• R, Rencency,即每个客户有多少天没回购了,可以理解为最近一次购买到现在
隔了多少天。
• F, Frequency,是每个客户购买了多少次。
• M, Monetary,代表每个客户平均购买金额,这里也可以是累计购买金额。

 

01 数据概览

import pandas as pd
df = pd.read_excel(r'F:\Python\用实战玩转Pandas数据分析\07 RFM建模实战\RFM\PYTHON-RFM实战数据.xlsx')
df.head()
Out[1]: 
   品牌名称        买家昵称                付款日期               订单状态  ...  邮费   省份   城市 购买数量
0  小程在线        叫我李2 2019-01-01 00:17:59               交易成功  ...   6   上海  上海市    1
1  小程在线    0cyb1992 2019-01-01 00:59:54               交易成功  ...   0  广东省  广州市    1
2  小程在线        萝污萌莉 2019-01-01 07:48:48               交易成功  ...   8  山东省  东营市    1
3  小程在线  atblovemyy 2019-01-01 09:15:49  付款以后用户退款成功,交易自动关闭  ...   0  江苏省  镇江市    1
4  小程在线        小星期鱼 2019-01-01 09:59:33  付款以后用户退款成功,交易自动关闭  ...   0   上海  上海市    1

[5 rows x 9 columns]

 

我们发现在订单状态中,除了交易成功的,还有用户退款导致交易关闭的,那还包括其他状态吗?
df['订单状态'].unique()
Out[2]: array(['交易成功', '付款以后用户退款成功,交易自动关闭'], dtype=object)

 

只有这两种状态,其中退款订单对于我们模型价值不大,需要在后续清洗中剔除。
接着再观察数据的类型和缺失情况:

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 28833 entries, 0 to 28832
Data columns (total 9 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   品牌名称    28833 non-null  object        
 1   买家昵称    28833 non-null  object        
 2   付款日期    28833 non-null  datetime64[ns]
 3   订单状态    28833 non-null  object        
 4   实付金额    28833 non-null  int64         
 5   邮费      28833 non-null  int64         
 6   省份      28833 non-null  object        
 7   城市      28832 non-null  object        
 8   购买数量    28833 non-null  int64         
dtypes: datetime64[ns](1), int64(3), object(5)
memory usage: 2.0+ MB

订单一共 28833 行,没有任何缺失值, Nice!类型方面,付款日期是时间格式,
实付金额、邮费和购买数量是数值型,其他均为字符串类型。

 


02 数据清洗


剔除退款

df = df.loc[df['订单状态'] == '交易成功',:]
print('剔除退款后还剩:%d行' % len(df))
剔除退款后还剩:27793行

 

关键字段提取
剔除之后,觉得我们订单的字段还是有点多,而 RFM 模型只需要买家昵称,付款时间和实付金额这 3 个关键字段,所以提取之:
df = df[['买家昵称','付款日期','实付金额']]
df.head()
Out[5]: 
       买家昵称                付款日期  实付金额
0      叫我李2 2019-01-01 00:17:59   186
1  0cyb1992 2019-01-01 00:59:54   145
2      萝污萌莉 2019-01-01 07:48:48   194
5       重碎叠 2019-01-01 10:00:07   197
6  iho_jann 2019-01-01 10:00:08   168


pd.to_datetime('2019-11-11') > pd.to_datetime('2019-1-1')
Out[7]: True

 

要拿到所有用户最近一次付款时间,只需要按买家昵称分组,再选取付款日期的最大值即可:
r = df.groupby('买家昵称')['付款日期'].max().reset_index()
r.head()
Out[8]: 
         买家昵称                    付款日期
0   .blue_ram 2019-02-04 17:49:34.000
1  .christiny 2019-01-29 14:17:15.000
2     .willn1 2019-01-11 03:46:18.000
3        .托托m 2019-01-11 02:26:33.000
4       0000妮 2019-06-28 16:53:26.458

 

为了得到最终的 R 值,用今天减去每位用户最近一次付款时间,就得到 R 值了,这份订单是 7 月 1 日生成的,所以这里我们把“2019-7-1”当作“今天”:

r['R'] = (pd.to_datetime('2019-7-1') - r['付款日期']).dt.days
r = r[['买家昵称','R']]
r.head(8)
Out[9]: 
         买家昵称    R
0   .blue_ram  146
1  .christiny  152
2     .willn1  170
3        .托托m  170
4       0000妮    2
5    0009797王  127
6     000xyx0   73
7   000米粒儿米粒0  148

 

搞定 F 值

df['日期标签'] = df['付款日期'].astype(str).str[:10]

dup_f = df.groupby(['买家昵称','日期标签'])['付款日期'].count().reset_index()

f = dup_f.groupby('买家昵称')['付款日期'].count().reset_index()
f.columns = ['买家昵称','F']
f.head()
Out[12]: 
         买家昵称  F
0   .blue_ram  1
1  .christiny  1
2     .willn1  1
3        .托托m  1
4       0000妮  1

 

 

# M 值构造

我们只需要得到每个用户总金额,再用总金额除以购买频次,就能拿到用户平均支付金额:

sum_m = df.groupby('买家昵称')['实付金额'].sum().reset_index()
sum_m.columns = ['买家昵称','总支付金额']
com_m = pd.merge(sum_m,f,left_on = '买家昵称',right_on = '买家昵称',how = 'inner')

#计算用户平均支付金额
com_m['M'] = com_m['总支付金额'] / com_m['F']
com_m.head()
Out[13]: 
         买家昵称  总支付金额  F      M
0   .blue_ram     49  1   49.0
1  .christiny    183  1  183.0
2     .willn1     34  1   34.0
3        .托托m     37  1   37.0
4       0000妮    164  1  164.0

 

三个指标合并:

rfm = pd.merge(r,com_m,left_on = '买家昵称',right_on = '买家昵称',how = 'inner')
rfm = rfm[['买家昵称','R','F','M']]
rfm.head()

 

03 维度打分


维度确认的核心是分值确定,按照设定的标准,我们给每个消费者的 R/F/M 值打分,分值的大小取决于我们的偏好, 即我们越喜欢的行为,打的分数就越高:
• 以 R 值为例, R 代表了用户有多少天没来下单,这个值越大,用户流失的可能性越大,我们当然不希望用户流失,所以 R 越大,分值越小。
• F 值代表了用户购买频次, M 值则是用户平均支付金额,这两个指标是越大越好,即数值越大,得分越高。
 

04 分值计算
 

rfm['R-SCORE'] = pd.cut(rfm['R'],bins = [0,30,60,90,120,1000000],labels = [5,4,3,2,1],right = False).astype(float)
rfm.head()
Out[15]: 
         买家昵称    R  F      M  R-SCORE
0   .blue_ram  146  1   49.0      1.0
1  .christiny  152  1  183.0      1.0
2     .willn1  170  1   34.0      1.0
3        .托托m  170  1   37.0      1.0
4       0000妮    2  1  164.0      5.0

rfm['F-SCORE'] = pd.cut(rfm['F'],bins = [1,2,3,4,5,1000000],labels = [1,2,3,4,5],right = False).astype(float)
rfm['M-SCORE'] = pd.cut(rfm['M'],bins = [0,50,100,150,200,10000000],labels = [1,2,3,4,5],right = False).astype(float)
rfm.head()

 

每个客户和平均值对比后的 R、 F、 M,只有 0 和 1(0 表示小于平均值, 1 表示大于平均值)两种结果,整体组合下来共有 8 个分组,是比较合理的一个情况。我们来判断用户的每个分值是否大于平均值:

rfm['R是否大于均值'] = (rfm['R-SCORE'] > rfm['R-SCORE'].mean()) * 1
rfm['F是否大于均值'] = (rfm['F-SCORE'] > rfm['F-SCORE'].mean()) * 1
rfm['M是否大于均值'] = (rfm['M-SCORE'] > rfm['M-SCORE'].mean()) * 1
rfm.head()
Out[17]: 
         买家昵称    R  F      M  ...  M-SCORE  R是否大于均值  F是否大于均值  M是否大于均值
0   .blue_ram  146  1   49.0  ...      1.0        0        0        0
1  .christiny  152  1  183.0  ...      4.0        0        0        1
2     .willn1  170  1   34.0  ...      1.0        0        0        0
3        .托托m  170  1   37.0  ...      1.0        0        0        0
4       0000妮    2  1  164.0  ...      4.0        1        0        1

[5 rows x 10 columns]

 

05 客户分层
 

先引入一个人群数值的辅助列,把之前判断的 R\F\M 是否大于均值的三个值给串联起来:
rfm['人群数值'] = (rfm['R是否大于均值'] * 100) + (rfm['F是否大于均值'] * 10) + (rfm['M是否大于均值'] * 1)
rfm.head()

 

 

人群数值是数值类型,所以位于前面的 0 就自动略过, 比如 1 代表着“001”的高消费唤回客户人群, 10 对应着“010”的一般客户。
为了得到最终人群标签,再定义一个判断函数,通过判断人群数值的值,来返回对应的分类标签:

def transform_label(x):
    if x == 111:
        label = '重要价值客户'
    elif x == 110:
        label = '消费潜力客户'
    elif x == 101:
        label = '频次深耕客户'
    elif x == 100:
        label = '新客户'
    elif x == 11:
        label = '重要价值流失预警客户'
    elif x == 10:
        label = '一般客户'
    elif x == 1:
        label = '高消费唤回客户'
    elif x == 0:
        label = '流失客户'
    return label

 

最后把标签分类函数应用到人群数值列:

rfm['人群类型'] = rfm['人群数值'].apply(transform_label)
rfm.head()

 

客户分类工作的完成,宣告着 RFM 模型建模的结束,每一位客户都有了属于自己的 RFM 标签。
 

 

RFM 模型结果分析
 

各类用户占比情况:

count = rfm['人群类型'].value_counts().reset_index()
count.columns = ['客户类型','人数']
count['人数占比'] = count['人数'] / count['人数'].sum()
count

 

不同类型客户消费金额贡献占比:
rfm['购买总金额'] = rfm['F'] * rfm['M']
mon = rfm.groupby('人群类型')['购买总金额'].sum().reset_index()
mon.columns = ['客户类型','消费金额']
mon['金额占比'] = mon['消费金额'] / mon['消费金额'].sum()
mon

 

客户类型

result = pd.merge(count,mon,left_on = '客户类型',right_on = '客户类型')
result
Out[23]: 
         客户类型    人数      人数占比       消费金额      金额占比
0     高消费唤回客户  7338  0.288670  1338153.0  0.381098
1        流失客户  6680  0.262785   444617.0  0.126624
2      频次深耕客户  5427  0.213493   981893.0  0.279638
3         新客户  4224  0.166168   270869.0  0.077142
4      重要价值客户   756  0.029740   269230.0  0.076675
5      消费潜力客户   450  0.017703    64075.0  0.018248
6  重要价值流失预警客户   360  0.014162   116665.0  0.033226
7        一般客户   185  0.007278    25803.0  0.007349

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值