概念+实战讲解,一文带你了解RFM模型【kaggle项目实战分享】数据分析

article/2025/10/2 16:06:01

大家早上好,本人姓吴,如果觉得文章写得还行的话也可以叫我吴老师。欢迎大家跟我一起走进数据分析的世界,一起学习!

感兴趣的朋友可以关注我或者我的数据分析专栏,里面有许多优质的文章跟大家分享哦。


(有需要完整代码和数据的可以评论留下你的邮箱,我会尽快发送给你!)


今日份学习分享,请查收!

目录

  • 必须要看的前言
  • 一、什么是RFM模型?
    • R值:Recency, 最近一次消费
    • F值:Frequency, 消费频率
    • M值:Monetary, 消费金额
  • 二、实践应用有哪些?
    • 基于RFM模型进行客户细分
    • 通过RFM模型评分后输出目标用户
    • 基于RFM的常用策略
    • 补充
  • 三、kaggle项目实战讲解
    • 1 数据探索与数据清洗
      • 1.1 数据探索
      • 1.2 缺失值统计
      • 1.3 日期格式的转换
      • 1.4 去重
      • 1.5 异常值处理
    • 2 用户分类
    • 3 分类结果
    • 4 结论与建议
  • 结束语

必须要看的前言

RFM模型是衡量客户价值和客户创利能力的重要工具和手段。在众多的客户关系管理(CRM)的分析模式中,RFM模型是被广泛提到的。

所以RFM模型是数据分析师必须掌握的知识点,而本篇文章详细介绍RFM模型的同时,还附带了kaggle项目实战,收藏本篇文章,你还怕搞不懂RFM模型,不懂怎么对用户进行分类吗?

一、什么是RFM模型?

RFM模型通过一个客户的近期购买行为、购买的总体频率以及花了多少钱3项指标来描述该客户的价值状况。

R值:Recency, 最近一次消费

  • 最近一次消费指的是上一次购买距离当前的时间。比方说最近一次买车什么时候,上一次买唱片是几时。

理论上,上一次消费时间越近的顾客应该是比较好的顾客,对提供即时的商品或是服务也最有可能会有反应。营销人员若想业绩有所成长,只能靠偷取竞争对手的市场占有率,而如果要密切地注意消费者的购买行为,那么最近的一次消费就是营销人员第一个要利用的工具。

功能:R值的功能不仅在于提供的促销信息而已,营销人员的消费报告可以监督事业的健全度。优秀的营销人员会定期查看消费分析,以掌握趋势。月报告如果显示上一次购买很近的客户,(消费为1个月)人数如增加,则表示该公司是个稳健成长的公司;反之,如上一次消费为一个月的客户越来越少,则是该公司迈向不健全之路的征兆。

F值:Frequency, 消费频率

  • 消费频率是顾客在限定的期间内所购买的次数。我们可以说最常购买的顾客,往往也是满意度最高的顾客。增加顾客购买的次数意味着从竞争对手处偷取市场占有率,由别人的手中赚取营业额。

基于这个指标,我们可以把客户分为五等分:如购买一次的客户为新客户,购买两次的客户为潜力客户,购买三次的客户为老客户,购买四次的客户为成熟客户,购买五次及以上则为忠实客户。运营人员的目标是要让消费者不断升级。

注意:不同类型的商品的消费频率往往有着较大的差距,如婚礼类产品和零食类产品,前者往往也就购买一次差不多得了(多了社会就乱套了哈哈),后者属于易耗品,消耗评论搞,相对容易产生重复购买,所以说F值不适合用作跨类目比较。

M值:Monetary, 消费金额

  • 消费金额同消费频率一样,有限定的时间范围,指的是一段时间(通常是1年)内的消费金额。它同时也能验证帕累托法则(俗称二八法则),即80%的收入来自于20%的客户。

M值是RFM模型中相对于R值和F值最难使用,但最具有价值的指标。同一品牌美妆类,价格浮动范围基本在某个特定消费群的可接受范围内,加上单一品类购买频次不高,所以对于一般店铺而言,M值对客户细分的作用相对较弱。

二、实践应用有哪些?

基于RFM模型进行客户细分

CRM实操时可以选择RFM模型中的1-3个指标进行客户细分,如下表所示。切记细分指标需要在自己可操控的合理范围内,并非越多越好,一旦用户细分群组过多,一来会给自己的营销方案执行带来较大的难度,而来可能会遗漏用户群或者对同个用户造成多次打扰。

如何选择最终指标有两个参考标准:店铺的客户基数,店铺的商品和客户结构。

在这里插入图片描述

店铺的客户基数:在店铺自身客户数量不大的情况下,选择1-2个维度进行细分即可;反之可以选择2-3个指标进行用户用类。

店铺的商品和客户结构:如果在店铺的商品层次比较单一,客单价差异幅度不大的情况下,购买频次(F值)和消费金额(M值)高度相关的情况下,可以只选择比较容易操作的购买频次(F值)代替消费金额(M值)。对于刚刚开店还没形成客户粘性的店铺,则可以放弃购买频次(F值),直接用最后一次消费(R值)或者消费金额(M值)。

通过RFM模型评分后输出目标用户

RFM模型评分主要有三个部分:

  1. 确定RFM三个指标的分段和每个分段的分值;

  2. 计算每个客户RFM三个指标的得分;

  3. 计算每个客户的总得分,并且根据总得分筛选出优质的客户

还是以上图为例。
在这里插入图片描述
此时我们将每个用户在每个指标下所得的分数相加,就可得到最终的分数。

但这里需要注意的是,对于每个指标下所对应的每个分值不应像上图一样,应根据不同的店铺进行进一步的赋值(听其他网友说可以用AHP层次分析法,我暂时还没有去了解)。
并且,相加时最好先给每个指标设一个权重,比方说最终的计算公式可以是:score = 0.5R+0.3F+0.2M。
具体的权重设置也可以参考上文提到的两个参考标准

基于RFM的常用策略

RFM非常适用于提供多种商品的企业,这些商品单价相对不高,或者相互间有互补性,具有多次重复购买的必要,这些企业可能提供如下商品:日用消费品、服装、小家电等;RFM也适用于这类企业,它们既提供高价值耐用商品、同时又提供配套的零部件或维修服务,如下:精密机床、成套生产设备、打印机等;RFM对于商品批发、原材料贸易、以及一些服务业(如旅行、保险、运输、快递、娱乐等)的企业也很适用。

RFM可以用来提高客户的交易次数。业界常用的DM(直接邮寄),常常一次寄发成千上万封邮购清单,其实这是很浪费钱的。根据统计(以一般邮购日用品而言),如果将所有R(Recency)的客户分为五级,最好的第五级回函率是第四级的三倍,因为这些客户刚完成交易不久,所以会更注意同一公司的产品信息。如果用M(Monetary)来把客户分为五级,最好与次好的平均回复率,几乎没有显著差异。

有些人会用客户绝对贡献金额来分析客户是否流失,但是绝对金额有时会曲解客户行为。因为每个商品价格可能不同,对不同产品的促销有不同的折扣,所以采用相对的分级(例如R、F、M都各分为五级)来比较消费者在级别区间的变动,则更可以显现出相对行为。企业用R、F的变化,可以推测客户消费的异动状况,根据客户流失的可能性,列出客户,再从M(消费金额)的角度来分析,就可以把重点放在贡献度高且流失机会也高的客户上,重点拜访或联系,以最有效的方式挽回更多的商机。

补充

以上三个指标会将维度细分出4份,这样就能够细分出4x4x4=64类用户,再根据每类用户精准营销……显然64类用户已超出普通人脑的计算范畴了,更别说针对64类用户量体定制营销策略。实际运用上,我们只需要把每个维度做一次两分即可,这样在3个维度上我们依然得到了8组用户。

(编号次序RFM,1代表高,0代表低)
重要价值客户(111):最近消费时间近、消费频次和消费金额都很高,必须是VIP啊!
重要保持客户(011):最近消费时间较远,但消费频次和金额都很高,说明这是个一段时间没来的忠诚客户,我们需要主动和他保持联系。
重要发展客户(101):最近消费时间较近、消费金额高,但频次不高,忠诚度不高,很有潜力的用户,必须重点发展。
重要挽留客户(001):最近消费时间较远、消费频次不高,但消费金额高的用户,可能是将要流失或者已经要流失的用户,应当给予挽留措施。

三、kaggle项目实战讲解

项目来源:
https://www.kaggle.com/carrie1/ecommerce-data

项目简介:
这是一个跨国数据集,其中包含在英国注册的商店于2010年12月1日至2011年12月9日之间发生的所有在线零售交易。该公司主要销售独特的全天候礼品,许多客户对象是批发商。

本次项目的主要目的是利用RFM模型进行用户分类。

PS: 本次项目是在jupyter上运行的。

导入模块

import pandas as pd
import numpy as np
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei'] # 设置汉字字体,优先使用黑体

加载数据

df = pd.read_csv('data.csv',encoding = 'ISO-8859-1', dtype = {'CustomerID':str})

接下来正式开始进行分析啦!

1 数据探索与数据清洗

1.1 数据探索

df.shape

在这里插入图片描述

df

在这里插入图片描述
数据包含541910行,8个字段,字段内容为:

InvoiceNo: 订单编号,每笔交易有6个整数,退货订单编号开头有字母’C’。
StockCode: 产品编号,由5个整数组成。
Description: 产品描述。
Quantity: 产品数量,有负号的表示退货。
InvoiceDate: 订单具体时间。
UnitPrice: 单价(英镑),单位产品的价格。
CustomerID:客户编号,每个客户编号由5位数字组成。
Country: 国家的名称,每个客户所在国家/地区的名称。

df.info()

在这里插入图片描述
我们不难发现,我们需要进行日期格式的转换,以及常规的缺失值统计、去重以及异常值的检测与处理。

1.2 缺失值统计

# 先统计缺失率
df.apply(lambda x :sum(x.isnull())/len(x),axis=0)

在这里插入图片描述

# Description是用于产品描述,与后续分析无关,删去。
df.drop(['Description'],axis=1,inplace=True)
df.head()

在这里插入图片描述

# 这里的CustomerID对于后续分析仅起到标识作用,不做删除,可以用unknown填充。
df['CustomerID'] = df['CustomerID'].astype('str')
df.info()

在这里插入图片描述

df['CustomerID'] = df['CustomerID'].fillna('unknown')

1.3 日期格式的转换

df['date'] = [x.split(' ')[0] for x in df['InvoiceDate']]
df['date'] = pd.to_datetime(df['date'])
df['month'] = df['date'].astype('datetime64[M]')
df[['date', 'month']]

在这里插入图片描述

1.4 去重

df = df.drop_duplicates()
df.shape

在这里插入图片描述

1.5 异常值处理

在这里,我们把退货订单看做异常数据(即数量为负数或者货单价为负数的数据)。

df[(df['Quantity']<0) | (df['UnitPrice']<0)]

在这里插入图片描述

# 除去异常数据
df = df[(df['Quantity']>0) & (df['UnitPrice']>0)]
df[(df['Quantity']<0) | (df['UnitPrice']<0)]

在这里插入图片描述

2 用户分类

#  首先计算R值
R_value = df.groupby('CustomerID')['date'].max()
R_value = (df['date'].max() - R_value).dt.days  # 这里将2011-12-9作为当前日期进行计算
R_value

在这里插入图片描述

# 将2010-12-1至2011-12-9视为F值的区间段,计算每个客户所下单的数量
F_value = df.groupby('CustomerID')['InvoiceNo'].nunique()
F_value

在这里插入图片描述

# 首先计算每个订单的消费金额
df['amount'] = df['Quantity'] * df['UnitPrice']
# 再计算M值
M_value = df.groupby('CustomerID')['InvoiceNo'].nunique()
M_value = df.groupby('CustomerID')['amount'].sum()
M_value

在这里插入图片描述

R_value.describe()

在这里插入图片描述

R_value.hist(bins = 30)

在这里插入图片描述

M_value.describe()

在这里插入图片描述

M_value.hist(bins = 30)

在这里插入图片描述

M_value.plot.box()

在这里插入图片描述
可见非常不均。

M_value[M_value<2000].hist(bins = 30)

在这里插入图片描述

# 分位数 观察出异常值还是很严重
F_value.quantile([0.1,0.2,0.3,0.4,0.5,0.9,1])

在这里插入图片描述

F_value.hist(bins = 30)

在这里插入图片描述

F_value.plot.box()

在这里插入图片描述

F_value[F_value<30].hist(bins = 30)

在这里插入图片描述
一样是非常的不均。

# 每个指标下都分为五个等级
R_bins = [0,30,90,180,360,720]
F_bins = [1,2,5,10,20,5000]
M_bins = [0,500,2000,5000,10000,200000]

先是R值:

R_score = pd.cut(R_value,R_bins,labels=[5,4,3,2,1],right=False)
R_score

在这里插入图片描述
接着是F值:

F_score = pd.cut(F_value,F_bins,labels=[1,2,3,4,5],right=False)
F_score

在这里插入图片描述
最后是M值:

M_score = pd.cut(M_value,M_bins,labels=[1,2,3,4,5],right=False)
M_score

在这里插入图片描述

生成新的数据框看一看:

rfm = pd.concat([R_score,F_score,M_score],axis=1)
rfm.rename(columns={'date':'R_score','InvoiceNo':'F_score','amount':'M_score'},inplace=True)
rfm

在这里插入图片描述

rfm.info()

在这里插入图片描述
改下数据格式:

rfm['R_score'] = rfm['R_score'].astype('float')
rfm['F_score'] = rfm['F_score'].astype('float')
rfm['M_score'] = rfm['M_score'].astype('float')
rfm.describe()

在这里插入图片描述
按平均值在每个指标下划分价值高低:

rfm['R'] = np.where(rfm['R_score']>3.82,'高','低')
rfm['F'] = np.where(rfm['F_score']>2.03,'高','低')
rfm['M'] = np.where(rfm['M_score']>1.89,'高','低')
rfm

在这里插入图片描述

# 将三个指标结合
rfm['RFM']=rfm['R']+rfm['F']+rfm['M']
rfm

在这里插入图片描述

def rfm2grade(x):if x=='高高高':return '高价值客户'elif x=='高低高':return '重点发展客户'elif x=='低高高':return '重点保持客户'elif x=='低低高':return '重点挽留客户'elif x=='高高低':return '一般价值客户'elif x=='高低低':return '一般发展客户'elif x=='低高低':return '一般保持客户'else:return '一般挽留客户'  rfm['用户等级']=rfm['RFM'].apply(rfm2grade)
rfm

在这里插入图片描述

3 分类结果

rfm['用户等级'].value_counts() 

在这里插入图片描述

rfm['用户等级'].hist(figsize=(12,9))

在这里插入图片描述

# 分类占比
rfm['用户等级'].value_counts() / rfm['用户等级'].value_counts().sum()

在这里插入图片描述

rfm['用户等级'].value_counts().plot(kind = 'pie', # 选择图形类型figsize = (15, 9),autopct='%.1f%%', # 饼图中添加数值标签title = 'RFM用户分类', # 为饼图添加标题textprops = {'fontsize':8},# 设置文本标签的属性值subplots=True)
plt.legend(loc=2, bbox_to_anchor=(1.05,1.0),borderaxespad = 0.)  # 添加注解

在这里插入图片描述

4 结论与建议

从用户分类占比的结果来看,高价值客户和重要发展客户共占总数的47%,是公司收入的重要来源。

  • 高价值客户(111)
    RFM三个值都很高,要提供vip服务。

  • 重点发展客户(101)
    消费频率低,但是其他两个值很高,就要想办法提高他的消费频率,建议及时推送公司活动信息或新品相关信息来吸引客户。

  • 重点保持客户(011)
    最近消费距离现在时间较远,也就是F值低,但是消费频次和消费金额高。这种用户,是一段时间没来的忠实客户。应该主动和他保持联系,提高复购率。可以赠送优惠券或推送商品折扣信息来增加购买次数。

  • 重点挽留客户(001)
    最近消费时间距离现在较远、消费频率低,但消费金额高。这种用户,即将流失,要主动联系用户,调查清楚哪里出了问题,并想办法挽回。当然同样可以赠送优惠券或推送商品折扣信息来增加购买次数。

  • 一般发展客户(100)
    公司应获取客户的详细数据信息用户画像, 从中了解客户的消费属性。建议对此类客户进行精准营销和及时推送产品信息。

当然,最终的营销策略应基于公司自身的财政投入决定。

RFM也不可以用过头,而造成高交易的客户不断收到信函。每一个企业应该设计一个客户接触频率规则,如购买三天或一周内应该发出一个感谢的电话或Email,并主动关心消费者是否有使用方面的问题,一个月后发出使用是否满意的询问,而三个月后则提供交叉销售的建议,并开始注意客户的流失可能性,不断地创造主动接触客户的机会。这样一来,客户再购买的机会也会大幅提高。

结束语

为方便需要的朋友运行代码,我也把完整的代码和数据文件放到了网盘上,需要的朋友自取。
链接:https://pan.baidu.com/s/1qzVvW2tFYquertL6jbWx8w
提取码:1024

引用鸣谢:https://www.jianshu.com/p/4b60880f24e2


推荐关注的专栏

👨‍👩‍👦‍👦 机器学习:分享机器学习实战项目和常用模型讲解
👨‍👩‍👦‍👦 数据分析:分享数据分析实战项目和常用技能整理


关注我,了解更多相关知识!


CSDN@报告,今天也有好好学习


http://chatgpt.dhexx.cn/article/E40VaW5P.shtml

相关文章

三线性插值(三维线性插值)

三线性插值&#xff08;trilinear interpolation&#xff09;主要是用于在一个3D的立方体中&#xff0c;通过给定顶点的数值然后计算立方体中其他点的数值的线性插值方法。 具体推导过程见参考资料1&#xff0c;这里直接给出最终公式&#xff1a; 其中&#xff0c;坐标(x,y,z…

快速三线性插值

转载自https://lianera.github.io/post/2018/fast-trilinear-interpolation/ 快速三线性插值 最近需要对一个体素进行插值&#xff0c;并且应用到一张大图像上。这个本来用三线性插值很容易就实现了&#xff0c;但是体素的尺寸很小&#xff0c;长宽高大概20x15x10的大小&#x…

线性插值、双线性插值、双三次插值学习笔记-图像处理

缺失值之线性插值 interpolate用法 在series中有两个空值 用图的方式表示出四个点 使用线性插值后的结果如下 使用代码演示 线性插值后的结果 再加入一条数据 结果如下 使用pandas中的DataFrame 运行结果&#xff0c;默认在垂直方向上使用线性插值 设置水平方向上的线性插值 …

插值(Interpolation)

前言 插值计算普遍存在于图像处理中,最近在做畸变优化时,看了一些资料中提到了插值&双线性插值,开始没明白,觉得一定很难,直接跳过,到最终写代码时,又不得不使用.于是就这里对这个功能进行学习,记录,并使用vs2017进行实际验证,验证后发现这个原理其实并不复杂. 插值 插值…

三点线性插值

问题&#xff1a; 在三角形的三个顶点具有3个不同颜色&#xff0c;如何通过插值计算出三角形中每个点的颜色&#xff1f; 应用实例&#xff1a;高洛德着色使用3个顶点的颜色进行线性插值&#xff0c;结果如下图&#xff1a; 解决方案&#xff1a; 显然&#xff0c;无论是线性…

线性插值和双线性插值

最近在学数字图像处理中旋转变换的问题,发现旋转以后图片有一些不连续点,于是试着用双线性插值法进行解决。下面就介绍下插值的原理: 线性插值 如果你只处理分离的数据、想知道分离点之间的某些值,需要用到某种类型的插值。这种情况如图5-17坐标所示。对某些分离的(整数)…

没想到会用到:线性插值(Linear Interpolation)原理及使用

各位博友们大家好&#xff0c;小弟遇到一些问题经常会去看大家的博客&#xff0c;所以也想加入大伙的阵营&#xff0c;每每看到一些好的东西&#xff0c;有些心得体会什么的也想与大伙分享。 1.关于插值 插值&#xff0c;它根据已知的数据序列&#xff08;也可以理解为坐标中一…

线性插值 多项式插值 样条插值 牛顿插值总结

项目github地址&#xff1a;bitcarmanlee easy-algorithm-interview-and-practice 欢迎大家star&#xff0c;留言&#xff0c;一起学习进步 1.什么是插值 在数值分析中&#xff0c;插值(interpolation)是一种通过已知的、离散的数据点&#xff0c;在范围内推求新数据点的过程…

常用线性插值的介绍和应用(双线性插值,三线性插值,平滑曲线插值)

常用线性插值的介绍和应用 线性插值 插值是计算机图形学中非常常用的技术。通常&#xff0c;数据是在常规网格上指定的&#xff08;值写在2D或3D网格的顶点位置&#xff09;或在线上&#xff08;在一维的情况下&#xff09;&#xff0c;但是程序需要在该网格上的随机位置求值。…

线性插值 np.interp()

线性插值是指插值函数为一次多项式的插值方式&#xff0c;其在插值节点上的插值误差为零。线性插值相比其他插值方式&#xff0c;如抛物线插值&#xff0c;具有简单、方便的特点。线性插值的几何意义即为概述图中利用过A点和B点的直线来近似表示原函数。 线性插值法是认为现象…

我与插值萍水相逢:线性插值(Linear Interpolation)原理及使用

各位博友们大家好&#xff0c;小弟遇到一些问题经常会去看大家的博客&#xff0c;所以也想加入大伙的阵营&#xff0c;每每看到一些好的东西&#xff0c;有些心得体会什么的也想与大伙分享。 1.关于插值 插值&#xff0c;它根据已知的数据序列&#xff08;也可以理解为坐标中一…

对线性插值的理解

【插值】 插值是用已知点求未知点的一种方法&#xff0c;而且通常是用两个已知点求一个未知点。&#xff08;如果是用很多已知点求未知点一般用曲线拟合&#xff09; 既然是用两个已知点求一个未知点&#xff0c;那么两个已知点之间的距离要尽可能的小&#xff0c;这样求出来…

python判断是否为数字类型_python判断字符串是否为数字

以下实例通过创建自定义函数 is_number() 方法来判断字符串是否为数字:# -*- coding: UTF-8 -*- # Filename : test.py # author by : www.runoob.com def is_number(s): try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.…

使用正则表达式判断字符串是否为数字类型

java 判断字符串是否是数字 1.用JAVA自带的函数 publicstaticbooleanisNumeric(Stringstr){ for(inti0;i System.out.println(str.charAt(i)); if(!Character.isDigit(str.charAt(i))){ returnfalse; returntrue; 2.用正则表达式 首先要importjava.util.regex.Pattern和java.ut…

java判断字符串是否为数字

一&#xff1a;判断java中的字符串是否为数字&#xff0c;可以通过正则表达式来判断&#xff1b;其判断逻辑如下&#xff1a; 1、根据阿里巴巴代码规范&#xff0c;将Pattern设置为全局常量&#xff0c;通过 -?[0-9](\\\\.[0-9])? 进行匹配是否为数字 private static final P…

二次型化为标准型

将二次型化为标准形有利于我们了解二次型的简单形式、二次型的各种参数如正负惯性指数、得到二次型的规范形、对称矩阵合同的简单形等等。另外&#xff0c;化标准形也是解析几何化简二次曲线和二次曲面的需要。 下面&#xff0c;我们以两道题目为例说明计算二次型的标准形的2种…

二次型的标准型、规范型

若二次型只有平方项&#xff0c;则称二次型为标准型 如果标准型中&#xff0c;系数只有1&#xff0c;-1和0&#xff0c;那么称为二次型的规范型&#xff0c;因为标准型中&#xff0c;1&#xff0c;-1&#xff0c;0的个数是由正负惯性指数决定的&#xff0c;而合同的矩阵正负惯…

二次型,正定二次型

二次型&#xff1a;含有n个变量 x 1 , x 2 , . . . x n x_1,x_2,...x_n x1​,x2​,...xn​的二次齐次函数: f ( x 1 , x 2 , . . . x n ) a 11 x 1 2 a 12 x 1 x 2 a 13 x 1 x 3 a 14 x 1 x 4 . . . a 1 n x 1 x n f(x_1,x_2,...x_n)a_{11}x_1^2a_{12}x_1x_2a_{13}x_1x_3…

二次型的正定

实数二次型的类型 设为一个实二次型&#xff0c;若 自变量不全为0 若恒成立&#xff0c;则称f为一个正定二次型&#xff0c;称A为正定矩阵 若恒成立&#xff0c;则称f为一个半正定二次型&#xff0c;称A为半正定矩阵 若恒成立&#xff0c;则称f为一个负定二次型&#xff0…