RFM模型

RFM模型介绍

RFM模型是一种用于客户价值分析的模型,通过对客户的消费行为进行分析,将客户分为不同的类别,以便企业更好地了解客户、制定更有效的营销策略和提高客户满意度。RFM模型的三个指标分别为最近一次购买时间(Recency)、购买频率(Frequency)和消费金额(Monetary),因此也被称为RFM分析。

  • 最近一次购买时间(Recency)指客户最近一次购买产品或服务的时间,购买时间越近,说明客户越活跃,对企业的贡献也越大。

  • 购买频率(Frequency)指客户在一段时间内购买产品或服务的次数,购买频率越高,说明客户对企业的忠诚度和购买意愿越强。

  • 消费金额(Monetary)指客户在一段时间内购买产品或服务的金额,消费金额越高,说明客户的购买能力和对企业的贡献越大。

image-20230517183156947

RFM计算方法

RFM模型的计算方法是将客户的消费数据按照时间顺序进行排序,然后将客户分为不同的类别,例如将客户分为高、中、低三个层次,或将客户分为ABC三个等级。不同的企业可以根据自己的实际情况来确定分级标准。

二等分

  • 将RFM三个指标分别可以取高和低两个值,共有8个组合(111 011 101 001 110 100 010 000)
    • 高于7天R-高,低于7于R-低
    • 高于10单F-高,低于10单F-低
    • 高于1000元M-高,低于1000元M-低

三等分

  • 将RFM三个指标进行三等分-高中低-27组合

五等分※

  • 对RFM进行打分:
    • R: 1-3天=5分 4-7天=4分 8-15天=3分 16-30天=2分 31~天=1分
    • F: >=200单数=5分 150-199单数=4分 100-149单数=3分 50-99单数=2分 1-49单数=1分
    • M: >=20W=5分 10-19W=4分 5-9W=3分 1-4W=2分 <=1W =1分
  • 对RFM进行指标计算:
    • R: R的分值大于X-取1 R的分值小于X-取0 X=所有R值的平均值
    • F: F的分值大于Y-取1 F的分值小于Y-取0 Y=所有F值的平均值
    • M: M的分值大于Z-取1 M的分值小于Z-取0 Z=所有M值的平均值

标签元数据

四级标签-id=69

inType=elasticsearch
esNodes=192.168.88.166:9200
esIndex=tfec_tbl_orders
esType=_doc
selectFields=memberid,ordersn,orderamount,finishtime

image-20230805170642236

1
2
3
4
5
6
7
8
9
# 五级标签
70,重要价值用户,,111
71,重要发展用户,,101
72,重要保持用户,,011
73,重要挽留用户 ,,001
74, 一般价值用户,,110
75,一般发展用户 ,,100
76, 一般保持用户,,010
77,低价值用户,,000

标签计算流程

1
2
3
4
5
6
7
8
9
# 1、计算 R/F/M的值
# R 用户id分组消费时间求最大值,
# F 用户id分组 count计数 (去重计数)
# M 用户id分组 金额求和
# 2、R/F/M 分别打5分
# 3、计算 R/F/M的平均值
# 4、基于平均值 计算每个维度的0,1标签 高于平均值的记1,低于平均值记0
# 5、R/F/M 三个维度的0,1标签拼接到一起:111,101,010,011....
# 6、和5级标签的rule匹配,给用户打上标签

标签计算代码

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from pyspark.sql import DataFrame, Window
import pyspark.sql.functions as F
from pyspark.sql.types import StringType

from UserProfile.offline.base.TagComputeBase import TagComputeBase

class RFMTagsCpt(TagComputeBase):
def tagCompute(self, es_df: DataFrame, tag5_df: DataFrame):
# es_df.show()
# +----------+--------+-----------+--------------------+
# |finishtime|memberid|orderamount| ordersn|
# +----------+--------+-----------+--------------------+
# |1594137600| 342| 1.0|jd_15062716252125282|
# |1594310400| 405| 3699.0|jd_15062720080896457|

# tag5_df.show()
# +---+----+
# | id|rule|
# +---+----+
# | 70| 111|
# | 71| 101|


# 1、计算 R/F/M的值
# R 用户id分组消费时间求最大值,(求最后一次下单的时间距当前时间多久--current_date()要有括号
rAgg = F.datediff(F.date_sub(F.current_date(), 1093), F.from_unixtime(F.max(F.col('finishtime')), format='yyyy-MM-dd')).alias('recencyStr')
# F 用户id分组 count计数 (去重计数) (判断用户的订单量
fAgg = F.countDistinct(F.col('ordersn')).alias('frequencyStr')
# M 用户id分组 金额求和
mAgg = F.sum(F.col('orderamount')).alias('monetaryStr')
rfm_df = es_df.groupBy('memberid').agg(rAgg, fAgg, mAgg)
# rfm_df.show()
# +--------+----------+------------+------------------+
# |memberid|recencyStr|frequencyStr| monetaryStr|
# +--------+----------+------------+------------------+
# | 747| 7| 111|202788.09013915993|
# | 303| 7| 97|168033.97006225586|

# 通过describe函数查看平均值, 最大值, 最小值, 来划分区间
# rfm_df.describe().show()
# +-------+-----------------+-------------------+------------------+--------------------+
# |summary| memberid| recencyStr| frequencyStr| monetaryStr|
# +-------+-----------------+-------------------+------------------+--------------------+
# | count| 950| 950| 950| 950|
# | mean| 475.5| 7.017894736842106|126.44736842105263| 366299.1070466522|
# | stddev|274.3856774687775|0.14035852584856282|39.001376166866656| 3276502.465723986|
# | min| 1| 7| 81| 114856.25006103516|
# | max| 950| 9| 355|1.0018663619982529E8|
# +-------+-----------------+-------------------+------------------+--------------------+

# 2、R/F/M 分别打分(1-5分)
score_df = rfm_df.select(rfm_df.memberid.alias('userId'),

F.when((F.col('recencyStr') < 7), 5) \
.when((F.col('recencyStr') >= 7) & (F.col('recencyStr') < 14), 4) \
.when((F.col('recencyStr') >= 14) & (F.col('recencyStr') < 30), 3) \
.when((F.col('recencyStr') >= 30) & (F.col('recencyStr') < 60), 2) \
.otherwise(1).alias('rScore'),

F.when((F.col('frequencyStr') > 300),5)
.when(F.col('frequencyStr').between(200, 300),4)
.when(F.col('frequencyStr').between(150, 200),3)
.when(F.col('frequencyStr').between(100, 150),2)
.otherwise(1).alias('fScore'),

F.when(F.col('monetaryStr') > 800000, 5)
.when(F.col('monetaryStr').between(500000, 800000), 4)
.when(F.col('monetaryStr').between(300000, 500000), 3)
.when(F.col('monetaryStr').between(200000, 300000), 2)
.otherwise(1).alias('mScore')
)
# score_df.show()
# +------+------+------+-----+
# |userId|rScore|fScore|mScore|
# +------+------+------+-----+
# | 747| 4| 2| 2|
# | 303| 4| 1| 1|

# 3、计算 R/F/M的平均值
avg_df = score_df.select(F.avg('rScore').alias('rAvg'),
F.avg('fScore').alias('fAvg'),
F.avg('mScore').alias('mAvg')
)
avg_row = avg_df.rdd.collect()[0]
# [Row(rAvg=4.0, fAvg=2.1210526315789475, mAvg=1.7789473684210526)]

# 4、基于平均值 计算每个维度的0,1标签 高于;平均值的记1,低于平均值记0
rfm_tag_df = score_df.select('userId',
F.when(F.col('rScore') > avg_row.rAvg, 1).otherwise(0).alias('r'),
F.when(F.col('fScore') > avg_row.fAvg, 1).otherwise(0).alias('f'),
F.when(F.col('mScore') > avg_row.mAvg, 1).otherwise(0).alias('m')
)
# rfm_tag_df.show()
# +------+---+---+---+
# |userId| r| f| m|
# +------+---+---+---+
# | 747| 0| 0| 1|
# | 303| 0| 0| 0|

# 5、R/F/M 三个维度的0,1标签拼接到一起:111,101,010,011....
rfm_concat_df = rfm_tag_df.select('userId', F.concat('r', 'f', 'm').alias('rfm'))
# rfm_concat_df.show()
# +------+---+
# |userId|rfm|
# +------+---+
# | 747|001|
# | 303|000|

# 6、和5级标签的rule匹配,给用户打上标签
result_df = rfm_concat_df.join(tag5_df, on=tag5_df.rule==rfm_concat_df.rfm, how='left')\
.select(rfm_concat_df.userId.cast(StringType()).alias('userId'), tag5_df.id.cast(StringType()).alias('tagsId') )

return result_df

if __name__ == '__main__':
appName = 'RFM标签计算'
tag4Id = 69
# 创建标签计算对象
rfmTagCompute = RFMTagsCpt(appName, tag4Id)
rfmTagCompute.execute()