声明:本文为学习笔记,侵权删
一、RFM模型
RFM(Recency最近一次消费,Frequency消费频率,Monetary消费金额)
R用户活跃度
F用户忠诚度
M用户重要性
R,F,M都有5个评分,均为1~5分,可简单理解为0~3分均为差,4~5均为好,评分越大越好,越小越差。通过R,F,M三个维度的划分,我们可以将用户分为8种类型,根据不同类别的客户可以制定不同的销售策略。
二、R,F,M的评判标准
大多数时候,我们拿到的数据并不会直白地给出R,F,M的具体数值,更多的是一些实际的数据,我们需要通过这些数据,找到与R,F,M相关的,然后通过计算等等操作,才能得到最终的R,F,M的数值。
以下面这份数据为例(忽略第二行的######,WPS在打开CSV文件的时候的编译错误,原文就是个日期):
这份数据中并没有直接给出R,F,M的数值或评判标准,那么我们首先就要找到决定R,F,M的相关数据。
R,最近一次购买至今间隔的时间,可以通过数据集中最后一次消费的时间和当前时间的差得出(本文中当前时间取2019.12.30)
F,订单数量
M,消费总金额
OK,我们找到了计算,R,F,M的相关数据,那么怎么把这些数据分级,分成1~5 这五个分数呢?
这个问题是主观的,在不同的情况下可能会有不同的分级标准。
我们先来看下R,F,M相关的数据的分布情况:
import pandas as pd
from datetime import datetime
from matplotlib import pyplot as plt
data = pd.read_csv(f"D:\Python Code\dataMining\消费清单.csv")
data["首单日期"] = pd.to_datetime(data["首单日期"])
data["最近一次消费"] = pd.to_datetime(data["最近一次消费"])
#假设当前的日期是2019年12月30日,计算出Recency
#计算最近一次消费到2019.12.30的时间间隔,并在data的最后一列后增加一列Recency
currentDate = datetime(2019,12,30)
data["Recency"] = currentDate - data["最近一次消费"]
#上一行如果直接获取data["Recency"],输出的格式是“xx 天”,是timedelta类型,不便于直接获取天数,因此就需要下一行代码
data["Recency"] = data["Recency"].dt.days
#对于F,M两个指标,F就是订单数量,M就是消费总金额,可以直接得到
#计算出如何定义R,F,M三个标准,即满足什么条件的R,C,F可以描述哪一类的客户
#先通过画图,查看数据的分布,
#R和F的数据量用每个数值在总书据所占的比例表示
R = data["Recency"].groupby(data["Recency"]).count()
R_percent = R/51394 #共有51394个数据
plt.rcParams["font.sans-serif"] = "Arial Unicode MS"
## 以R_percent的index为x轴的值,R_percent的values为y轴的值,
## 即x轴是R的值,y轴是这个R的值数量占所有R的值数量的百分比
plt.bar(R_percent.index, R_percent.values)
plt.title("R")
plt.show()
#
#同样的方法画出F的统计图
F = data["订单数量"].groupby(data["订单数量"]).count()
F_percent = F/51394
plt.rcParams["font.sans-serif"] = "Arial Unicode MS"
plt.bar(F_percent.index, F_percent.values)
plt.title("F")
plt.show()
#
##对于M,因为大多数用户的消费金额都差不多,用上面占比的方法画出来的图不明确
##因此,我们通过分箱的方式,从小到大,这里bins=100,分为100组,画出每组里汉欧消费金额的总个数量
##即,x轴是消费金额,y轴是频数
plt.hist(data["消费总金额"], bins =100)
plt.title("M")
plt.show()
本例中,我希望将这些数据平分,即平均得分为五等分,分别评级为1,2,3,4,5分。但这些数据明显呈现数据值越小,数量越大的情况。通常来说,将数据分成五级,我们用分箱的方法,分箱包括等宽分箱(cut),等深分箱(qcut)。等宽分箱显然不太适合这份数据,一个一个凑箱子的边界太繁琐。因此这里使用等深分箱的方法。
pandas中,等深分箱的结果并不一定是严格意义上的均分。因为有相同数值数据的存在,比如0,1,2,2,3,4,4,4,4,4,5,6,7,8,9这组数字,严格意义上的五等分应该是0,1,2|2,3,4|4,4,4|4,5,6|7,8,9。但这样把两个2分开放在两个箱子,4分开放在了三个箱子,显然是不合理的。因此pandas内部会通过一定的方法,优先将相同的数字分配到同一个箱子中,再考虑是否等分的问题。因此,用qcut算出来的结果并不是严格的等分,会出现一些箱子里的数据多一些,少一些的情况。
#判定R,F,M的评分标准
#最近一次消费距离现在时间越短,R越大
data["R"] = pd.qcut(data["Recency"],q=5,labels=[5,4,3,2,1])
R_count = data["R"].value_counts()
data["F"] = pd.qcut(data["订单数量"],q=5,labels=[1,2,3,4,5])
F_count = data["F"].value_counts()
data["M"] = pd.qcut(data["消费总金额"],q=5,labels=[1,2,3,4,5])
M_count = data["M"].value_counts()
print(R_count)
print(F_count)
print(M_count)
print(data)
可以看到,R,F,M基本上做到了五等分,并且成功得将它们评分为1~5分。
三、用户分类
通过最开始的RFM的定义可以得出,R,F,M三个维度虽然是五级的评分制,但RFM只是将用户分为8大类,也就是说每个维度可以用简单的二分法分为“好”与“不好”两种评价。“好”可以用1表示,“不好”可以用0表示,这样八类用户可以通过三位二进制来描述,RFM=000,001...111。比如000就是低价值用户。
因此,为了方便操作,我们需要将上面得到的R,F,M评分再简化,简化为0,1的二分类,并将R,F,M三个维度简化为一个维度,即上述的三位二进制码的形式。
def bi_classify(x):
if x > 3:
x = 1
else:
x = 0
return x
data["R"] = data["R"].apply(bi_classify)
data["F"] = data["F"].apply(bi_classify)
data["M"] = data["M"].apply(bi_classify)
print(data[["R","F","M"]])
输出:
[51394 rows x 14 columns]
R F M
0 1 1 1
1 1 1 1
2 1 1 1
3 0 1 1
4 1 1 1
... .. .. ..
51389 0 0 0
51390 0 0 0
51391 0 0 0
51392 1 0 0
51393 0 0 0
#将R,F,M三个值维度合成一整个数据,像001,110,101,这样方便判断。
#通过astype函数,转换数据类型
data["RFM"] = data["R"].astype(str) + data["F"].astype(str) + data["M"].astype(str)
print(data["RFM"])
输出:
[51394 rows x 3 columns]
0 111
1 111
2 111
3 011
4 111
...
51389 000
51390 000
51391 000
51392 100
51393 000
def userClassify(x):
if x=="111":
return "高价值用户"
elif x=="101":
return "重点发展用户"
elif x=="011":
return "重点唤回用户"
elif x=="001":
return "重点潜力用户"
elif x=="110":
return "一般潜力用户"
elif x=="100":
return "一般发展用户"
elif x=="010":
return "一般维系用户"
else:
return "低价值用户"
data["用户类别"] = data["RFM"].apply(userClassify)
print(data["用户类别"])
输出:
0 高价值用户
1 高价值用户
2 高价值用户
3 重点唤回用户
4 高价值用户
...
51389 低价值用户
51390 低价值用户
51391 低价值用户
51392 一般发展用户
51393 低价值用户
Name: 用户类别, Length: 51394, dtype: object