当前位置: 首页 > news >正文

数据处理与统计分析篇-day11-RFM模型案例

会员价值度模型介绍

会员价值度用来评估用户的价值情况,是区分会员价值的重要模型和参考依据,也是衡量不同营销效果的关键指标之一。

价值度模型一般基于交易行为产生,衡量的是有实体转化价值的行为。常用的价值度模型是RFM

RFM模型是根据会员

  • 最近一次购买时间R(Recency)

  • 购买频率F(Frequency)

  • 购买金额M(Monetary)计算得出RFM得分

  • 通过这3个维度来评估客户的订单活跃价值,常用来做客户分群或价值区分

  • RFM模型基于一个固定时间点来做模型分析,不同时间计算的的RFM结果可能不一样

RFM用户类别
重要价值用户
重要发展用户
重要保持用户
重要挽留用户
一般价值用户
一般发展用户
一般保持用户
一般挽留用户

RFM模型的基本实现过程:

  1. 设置要做计算时的截止时间节点(例如2017-5-30),用来做基于该时间的数据选取和计算。

  2. 在会员数据库中,以今天为时间界限向前推固定周期(例如1年),得到包含每个会员的会员ID、订单时间、订单金额的原始数据集。一个会员可能会产生多条订单记录。

  3. 数据预计算。从订单时间中找到各个会员距离截止时间节点最近的订单时间作为最近购买时间;以会员ID为维度统计每个用户的订单数量作为购买频率;将用户多个订单的订单金额求和得到总订单金额。由此得到R、F、M三个原始数据量。

  4. R、F、M分区。对于F和M变量来讲,值越大代表购买频率越高、订单金额越高;但对R来讲,值越小代表离截止时间节点越近,因此值越好。对R、F、M分别使用五分位法做数据分区(三分位也可以,分位数越多划分得越详细)。需要注意的是,对于R来讲需要倒过来划分,离截止时间越近的值划分越大。这样就得到每个用户的R、F、M三个变量的分位数值。

  5. 将3个值组合或相加得到总的RFM得分。对于RFM总得分的计算有两种方式,一种是直接将3个值拼接到一起,例如RFM得分为312、333、132;另一种是直接将3个值相加求得一个新的汇总值,例如RFM得分为6、9、6。

Excel实现RFM划分案例

以某电商公司为例

  1. R:例如:正常新用户注册1周内交易,7天是重要的值,日用品采购周期是1个月,30天是重要的值

  2. F:例如:1次购买,2次购买,3次购买,4~10次,10次以上

  3. M:例如:客单价300,热销单品价格240 等

常见的确定RFM划分区间的套路

  • 业务实际判断

  • 平均值或中位数

  • 二八法则

1. 提取用户最近一次的交易时间,算出距离计算时间的差值

获取当前时间=TODAY()

计算时间间隔

2. 根据天数长短赋予对应的R值,R值由我们自定,时间间隔越短R值越高

=IF(D2>60,1,IF(D2>30,2,IF(D2>14,3,IF(D2>7,4,5))))

3. 从历史数据中取出所有用户的购买次数,根据次数多少赋予对应的F分值;购买次数越多、F值越大

=IF(E2>10,5,IF(E2>3,4,IF(E2>2,3,IF(E2>1,2,1))))

4. 从历史数据中汇总,求得该用户的交易总额,根据金额大小赋予对应的M值;交易总额越大、M值越大

=IF(F2>1000,5,IF(F2>500,4,IF(F2>300,3,IF(F2>230,2,1))))

5. 分别求出RFM的中值,例如中位数,用中值和用户的实际值进行比较,高于中值的为高,否则为低

6. 在得到不同会员的RFM之后,根据步骤⑤产生的两种结果有两种应用思路

6.1 思路1:基于3个维度值做用户群体划分和解读,对用户的价值度做分析

  • 比如,RFM得分为212的会员的F是1,往往购买频率较低,那就可以针对购买频率低的客户应定期发送促销活动邮件

  • 比如,RFM得分为321的会员虽然购买频率高但是订单金额低等,这些客户往往具有较高的购买黏性,可以考虑通过关联或搭配销售的方式提升订单金额。

6.2 思路2:基于RFM的汇总得分评估所有会员的价值度,并可以做价值度排名。同时,该得分还可以作为输入维度与其他维度一起作为其他数据分析和挖掘模型的输入变量,为分析建模提供基础。

RFM小结

  • R就是距离自定义的时间点最近一次购买的时间间隔、间隔越小得分越高

  • F就是自定义的时间范围内购买频率、次数越多得分越高

  • M就是自定义的时间范围内购买总金额,总额越大得分越高

  • RFM的区间和其对应的得分由我们自定义

RFM计算案例

案例背景

用户价值细分

是了解用户价值度的重要途径,针对交易数据分析的常用模型是RFM模型

业务对RFM的结果要求
  1. 对用户做分组

  2. 将每个组的用户特征概括和总结出来,便于后续精细化运营不同的客户群体,且根据不同群体做定制化或差异性的营销和关怀

用户分群

规划目标将RFM的3个维度分别做3个区间的离散化

  1. 用户群体最大有3×3×3=27个

  2. 划分区间过多则不利于用户群体的拆分

  3. 区间过少则可能导致每个特征上的用户区分不显著

交付结果
  1. 给业务部门做运营的分析结果要导出为Excel文件,用于做后续分析和二次加工使用

  2. RFM的结果还会供其他模型的建模使用,RFM本身的结果可以作为新的局部性特征,因此数据的输出需要有本地文件和写数据库两种方式

数据说明
  1. 案例的数据集为 data/sales.xlsx

  2. 选择近4年订单数据,从不同的年份对比不同时间下各个分组的绝对值变化情况,方便了解会员的波动

  3. 程序输出RFM得分数据写入本地文件sales_rfm_score.xlsx和MySQL数据库sales_rfm_score表中

用到的技术点

通过Python代码手动实现RFM模型,主要用到的库包括:

  1. time、numpy和pandas

  2. 在结果展示时使用了pyecharts的3D柱形图

案例数据

案例数据是某企业从2015年到2018年共4年的用户订单抽样数据,数据来源于销售系统

数据在Excel中包含5个sheet,前4个sheet以年份为单位存储为单个sheet中,最后一张会员等级表为用户的等级表

前4张表的数据概要如下。

  1. 特征变量数:4

  2. 数据记录数:30774/41278/50839/81349

    是否有NA值:有

    是否有异常值:有

  3. 具体数据特征如下(前4张表的数据字段说明):

    会员ID:每个会员的ID唯一,由纯数字组成,整型

    提交日期:订单日提交日期

    订单号:订单ID,每个订单的ID唯一,由纯数字组成,整型

    订单金额:订单金额,浮点型数据

  4. 会员等级表中是所有会员的会员ID对应会员等级的情况,包括以下两个字段

    会员ID:该ID可与前面的订单表中的会员ID关联

    会员等级:会员等级以数字区分,数字越大,级别越高

代码

导入模块

import pandas as pd
import numpy as np
import os
​
import pyecharts.options as opts
from pyecharts.charts import Bar3D
​
from sqlalchemy import create_engine
​
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示汉字
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
​
os.chdir(r'D:\CodeProject\03data_processing_analysis\my_project')
os.getcwd()

读取数据
# 因为读取的是5张表, 所以获取的是: 字典, 键表名, 值: 该表的数据
sheet_names = ['2015', '2016', '2017', '2018', '会员等级']
​
sheet_data = pd.read_excel('data/sales.xlsx', sheet_name=sheet_names)
sheet_data
#%%
print(type(sheet_data))
​
# 查看某年具体信息
sheet_data['2015'].describe()
sheet_data['2015'].info()
sheet_data['2015'].isnull().sum()
​
# 查看所有数据
for sheet_name in sheet_names:print('\n\n下面信息是表: ', sheet_name, '\n')print(sheet_data[sheet_name].describe())print(sheet_data[sheet_name].info())

数据读取结果说明:

  1. 每个sheet中的数据都能正常读取,无任何错误

  2. 日期列(提交日期)已经被自动识别为日期格式,后期不必转换

  3. 订单金额的分布是不均匀的,里面有明显的极值

    例如2016年的数据中,最大值为174900,最小值仅为0.1

    极大极小值相差过大,数据会受极值影响

  1. 订单金额中的最小值包括0、0.1这样的金额,可能为非正常订单,与业务方沟通后确认

    最大值的订单金额有效,通常是客户一次性购买多个大型商品

    而订单金额为0.1元这类使用优惠券支付的订单,没有实际意义

    除此0、0.1这样的金额之外,所有低于1元的订单均有这个问题,因此需要在后续处理中去掉

  2. 有的表中存在缺失值记录,但数量不多,选择丢弃或填充均可

数据预处理
  1. 删掉缺失值, 筛选出金额大于1的数据, 给表新增一列, 表示固定的统计时间

for sheet_name in sheet_names[:-1]:     # 只处理前4张表# 1. 删除空值sheet_data[sheet_name] = sheet_data[sheet_name].dropna()# 2. 筛选大于1的数据# 写法1sheet_data[sheet_name] = sheet_data[sheet_name][sheet_data[sheet_name]['订单金额'] > 1]# 写法2# sheet_data[sheet_name] = sheet_data[sheet_name].query('订单金额 > 1')# 3. 给表新增1列, 表示该年的统计时间sheet_data[sheet_name]['max_year_date'] = sheet_data[sheet_name]['提交日期'].max()
​
# 查看所有数据
for sheet_name in sheet_names:print('\n\n下面信息是表: ', sheet_name, '\n')print(sheet_data[sheet_name].describe())print(sheet_data[sheet_name].info())
  1. 把前四年的数据, 拼接到一起

# 1. 合并前4张表.    list(sheet_data.values())[:-1] => [df1, df2, df3, df4]
# data: 数据,   date: 日期
# sheet_data.values(): 从字典中, 获取所有的: 值, 即: 每个值都是1个df对象.
# list(sheet_data.values()): 把数据格式转成 列表.
data_merge = pd.concat(list(sheet_data.values())[:-1])
​
# 2. 给表新增1列, year, 表示该订单所属的: 年份. 
data_merge['year'] = data_merge['提交日期'].dt.year
​
# 3. 给表新增1列, date_inverval 表示: 订单上次生成的时间(即: 该订单距统计时间的间隔. ). 
data_merge['date_interval'] = data_merge['max_year_date'] - data_merge['提交日期']
​
# 4. 把上述的 时间间隔转成天, 即: 364 days => 364
data_merge['date_interval'] = data_merge['date_interval'].dt.days
​
# 5. 查看处理后的结果集. 
data_merge

代码说明:

  1. 汇总所有数据: 将4年的数据使用pd.concat方法合并为一个完整的dataframe data_merge,后续的所有计算都能基于同一个dataframe进行,而不用写循环代码段对每个年份的数据单独计算

  2. 获取各自年份数据:

    先计算各自年份的最大日期与每个行的日期的差,得到日期间隔

    再增加一列新的字段,为每个记录行发生的年份,使用data_merge['提交日期'].dt.year实现

  3. 关于pandas的 datetime类型

    dt是pandas中Series时间序列datetime类属性的访问对象

    除了代码中用到的year外,还包括:date、dayofweek、dayofyear、days_in_month、freq、days、hour、microsecond、minute、month、quarter、second、time、week、weekday、weekday_name、weekofyear等

  4. 转换日期间隔为数字:通过 .dt.days 方式获取时间差列中的间隔天数数字

  1. 根据 会员id 和 年 进行分组, 计算: date_inverval: min => 间隔时间, 订单号: count => 频次, 订单金额: sum => 总金额

# 获取RFM值
​
# M
# # data_merge.groupby(['会员ID', 'year']).订单金额.sum()
# data_merge.pivot_table(index='会员ID', columns='year', values='订单金额', aggfunc='sum')
# 
# # F
# # data_merge.groupby(['会员ID', 'year']).订单号.count()
# data_merge.pivot_table(index='会员ID', columns='year', values='订单号', aggfunc='count')
# 
# # R
# # data_merge.groupby(['会员ID', 'year']).date_inverval.min()
# data_merge.pivot_table(index='会员ID', columns='year', values='date_inverval', aggfunc='min')
​
rfm_gb = data_merge.groupby(['year', '会员ID'], as_index=False).agg({# 最小时间间隔'date_inverval':'min',# 购买频次'订单号':'count',# 总金额'订单金额':'sum'
})
​
# 修改列名
rfm_gb.columns = ['year', '会员ID', 'r', 'f', 'm']
rfm_gb

代码说明:

  1. 上面代码框中的第一行代码,是基于年份和会员ID,分别做RFM原始值的聚合计算

  2. 这里使用groupby分组,以year和会员ID为联合主键,设置as_index=False意味着year和会员ID不作为index列,而是普通的数据框结果列。后面的agg方法实际上是一个“批量”聚合功能的函数,它实现了对date_interval、提交日期、订单金额三列分别以min、count、sum做聚合计算的功能。否则,我们需要分别写3条goupby来实现3个聚合计算

统计分析
  1. 介绍 pd.cut(要处理的列, 划分几段, 生成的值, 是否包含左边界值:最小值)

# 参1: 要处理的字段
# 参2: bins表示划分几个区间, 如果传入固定值, 则会等分, 如果传入的是列表, 则自定义划分(区间左开右闭)
# 参3: labels表示每个区间对应的值
# 参4: include_lowest表示是否包含左边界最小值
pd.cut(rfm_gb['r'], bins=3, labels=[3, 2, 1], include_lowest=True) # 生成规则, 默认: 包右不包左
  1. 自定义三个列的划分

在做RFM划分时,基本逻辑是分别对R、F、M做离散化操作,然后再计算RFM。而离散化本身有多种方法可选,由于我们要对数据做RFM离散化,因此需要先看下数据的基本分布状态

# 1. 查看数据的分布情况.
rfm_gb.iloc[:, 2:].describe().T
# 2. 手动编写, r, f, m这三列值的 划分区间.
r_bins = [-1, 79, 255, 365]     # Recency: 最小购买间隔时间
f_bins = [0, 2, 5, 130]         # Frequency: 购买频次
m_bins = [1, 69, 1199, 206252]  # Monetary: 总金额

为什么做上边的自定义区间划分呢?汇总后的数据总共有14万条,从基本概要看出

  1. r和m区间划分:

    r和m的数据分布相对较为离散,表现在min、25%、50%、75%和max的数据没有特别集中

    而从f(购买频率)则可以看出,大部分用户的分布都趋近于1,表现是从min到75%的分段值都是1且mean(均值)才为1.365

    所以我们可以选择25%和75%作为r和m区间划分的2个边界值

  1. f的分布情况说明

    r和m本身能较好地区分用户特征,而f则无法区分(大量的用户只有1个订单)

    行业属性(家电)原因,1年购买1次比较普遍(其中包含新客户以及老客户在当年的第1次购买)

    与业务部门沟通,划分时可以使用2和5来作为边界

    • 业务部门认为当年购买>=2次可被定义为复购用户(而非累计订单的数量计算复购用户)

    • 业务部门认为普通用户购买5次已经是非常高的次数,超过该次数就属于非常高价值用户群体

    • 该值是基于业务经验和日常数据报表获得的

区间边界的基本原则如下

  1. 中间2个边界值:r和m是分别通过25%和75%的值获取的,f是业务与数据部门定义的。

  2. 最小值边界:比各个维度的最小值小即可。

  3. 最大值边界:大于等于各个维度的最大值即可

  4. 最小值边界为什么要小于各个维度的最小值:

    • 这是由于在边界上的数据归属有一个基本准则,要么属于区间左侧,要么属于区间右侧。如,f_bins中的2处于边界上,要么属于左侧区间,要么属于右侧区间

    • 在后续使用pd.cut方法中,对于自定义边界实行的是左开右闭的原则,即数据属于右侧区间,f_bins中的2就属于右侧区间。最左侧的值是无法划分为任何区间的,因此,在定义最小值时,一定要将最小值的边界值

    • 举例:[1,2,3,4,5],假如数据划分的区间边界是[1,3,5],即划分为2份

      • 其中的2/3被划分到(1,3]区间中

      • 3/4/5被划分到(3,5]区间中

      • 1无法划分到任何一个正常区间内

演示 pd.cut() 函数, 方便的将一列连续型数据切分成类别型(即: 把数据分成指定的n个区间, 包右不包左) 
pd.cut()
参1: 要处理的字段
参2: bins表示划分几个区间, 如果传入固定值, 则会等分, 如果传入的是列表, 则自定义划分(区间左开右闭)
参3: labels表示每个区间对应的值
参4: include_lowest表示是否包含左边界最小值
​
# 3. 具体的获取 r, f, m评分的过程. 
# Recency: 最小购买间隔时间, 越小越好, 即: 值越小, 评分越高
# rfm_gb['r_label'] = pd.cut(rfm_gb['r'], bins=r_bins, labels=[3, 2, 1])
# Frequency: 购买频次, 越大越好, 即: 值越大, 评分越高
# rfm_gb['f_label'] = pd.cut(rfm_gb['f'], bins=f_bins, labels=[1, 2, 3])
# Monetary: 总金额, 越大越好, 即: 值越大, 评分越高
# rfm_gb['m_label'] = pd.cut(rfm_gb['m'], bins=m_bins, labels=[1, 2, 3])
# 查看结果.
# rfm_gb
​
# 4. 优化上述的代码, 划分区间的时候, 我们可以通过: for循环生成.
# list1 = [i for i in range(4 - 1, 0, -1)]
# list1
​
# Recency: 最小购买间隔时间, 越小越好, 即: 值越小, 评分越高
rfm_gb['r_label'] = pd.cut(rfm_gb['r'], bins=r_bins, labels=[i for i in range(len(r_bins) - 1, 0, -1)])
# Frequency: 购买频次, 越大越好, 即: 值越大, 评分越高
rfm_gb['f_label'] = pd.cut(rfm_gb['f'], bins=f_bins, labels=[i + 1 for i in range(len(f_bins) - 1)])
# Monetary: 总金额, 越大越好, 即: 值越大, 评分越高
rfm_gb['m_label'] = pd.cut(rfm_gb['m'], bins=m_bins, labels=[i + 1 for i in range(len(m_bins) - 1)])
# 查看结果.
rfm_gb

代码说明:

  • 每个rfm的过程使用了pd.cut方法,基于自定义的边界区间做划分

  • labels用来显示每个离散化后的具体值。F和M的规则是值越大,等级越高

  • 而R的规则是值越小,等级越高,因此labels的规则与F和M相反

  • 在labels指定时需要注意,4个区间的结果是划分为3份

  1. 转换RFM评分类型, 拼接RFM评分, 获取最终结果

# 1. 查看结果的各列的类型
rfm_gb.info()
#%%
# 2. 把 r_label, f_lable, m_label 转换为 str 类型.
rfm_gb['r_label'] = rfm_gb['r_label'].astype(str)
rfm_gb['f_label'] = rfm_gb['f_label'].astype(str)
rfm_gb['m_label'] = rfm_gb['m_label'].astype(str)
​
# 3. 查看处理后的结果
rfm_gb.info()
rfm_gb
#%%
# 4. 具体的拼接, 获取 用户分区结果的动作.
rfm_gb['rfm_group'] = rfm_gb['r_label'] + rfm_gb['f_label'] + rfm_gb['m_label']
​
# 5. 查看处理后的结果
rfm_gb
  • 代码说明:

    • 将3列作为字符串组合为新的分组

      • 代码中,先针对3列使用astype方法将数值型转换为字符串型

      • 然和直接利用字符串拼接, 将RFM字段拼接到一起, 方便后续的用户分群

      • 出了直接利用字符串拼接外, 还可以用Series.str.cat()方法实现

    • 【了解】Series.str.cat(others=None, sep=None, na_rep=None) 的说明:

      • 参数:

        • others : 列表或复合列表,默认为None,如果为None则连接本身的元素

        • sep : 字符串 或者None,默认为None

        • na_rep : 字符串或者 None, 默认 None。如果为None缺失值将被忽略。

      • 返回值:

        • concat : 序列(Series)/索引(Index)/字符串(str)

      • 示例代码

        print(pd.Series(['a', 'b', 'c']).str.cat(['A', 'B', 'C']))
        print(pd.Series(['a', 'b', 'c']).str.cat(['A', 'B', 'C'], sep=','))
        # 返回结果如下
        0    aA
        1    bB
        2    cC
        dtype: object
        0    a,A
        1    b,B
        2    c,C
        dtype: object

结果导出
  1. 导出到Excel

# 导出数据到 Excel文件, 不导出索引列.
rfm_gb.to_excel('output/sale_rfm_gb_result.xlsx', index=False)
  1. 导出到MySQL

# 准备动作
# 1. 安装pymysql这个库, 如果你没装的话. 
# pip install pymysql         # 去DOS窗口中执行.
​
# 2. 导包, 放到第1个单元格即可.
# from sqlalchemy import create_engine
#%%
# 具体的代码实现
# mysql+pymysql => 底层操作的是MySQL数据库, 底层依赖pymysql包
# root:123456@localhost:3306/rfm_db?charset=utf-   => 数据库用户名:密码@主机地址:端口号/数据库名称?字符集
# 1. 创建连接对象(引擎对象)
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/rfm_db?charset=utf8')
# engine
​
# 2. 导出数据到数据表中.
# 参1: 要被导出的数据集 => 即: df对象
# 参2: 引擎对象
# 参3: 是否包含索引列, False => 不包含索引列, True => 包含索引列
# 参4: 数据表存在了怎么办, 默认是: fail, 如果表存在了, 则报错.  append => 追加数据, replace => 替换数据
rfm_gb.to_sql('rfm_table', engine, index=False ,if_exists='append')
​
# 3. 提示
print('导出成功!')
#%%
# 还可以通过 pandas直接从数据库中读取数据.
pd.read_sql('show tables;', engine)
pd.read_sql('select * from rfm_table limit 10;', engine)
pd.read_sql('select count(1) from rfm_table;', engine)
结果可视化
  1. 分组统计, 获取结果数据

# 1. 绘制图形的时候, 我们只需要三个列, 分别是: 年份, rfm分组, 用户数量. 
display_data =  rfm_gb.groupby(['year', 'rfm_group'], as_index=False).会员ID.count()
# 2. 修改列名
display_data.columns = ['year', 'rfm_group', 'number']
# 3. 修改 rfm_group列的 数据类型 => int
display_data['rfm_group'] = display_data['rfm_group'].astype(int)
# 4. 查看结果.
display_data.info()
  1. 绘图

# 如果你的环境中没有安装 pyecharts, 则需要安装.
# pip install pyecharts
​
# 显示图形
# from pyecharts.commons.utils import JsCode
# import pyecharts.options as opts
​
# 颜色池
range_color = ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf','#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
​
range_max = int(display_data['number'].max())
c = (Bar3D()#设置了一个3D柱形图对象.add("",#图例[d.tolist() for d in display_data.values],#数据xaxis3d_opts=opts.Axis3DOpts(type_="category", name='分组名称'),#x轴数据类型,名称,rfm_groupyaxis3d_opts=opts.Axis3DOpts(type_="category", name='年份'),#y轴数据类型,名称,yearzaxis3d_opts=opts.Axis3DOpts(type_="value", name='会员数量'),#z轴数据类型,名称,number).set_global_opts( # 全局设置visualmap_opts=opts.VisualMapOpts(max_=range_max, range_color=range_color), #设置颜色,及不同取值对应的颜色title_opts=opts.TitleOpts(title="RFM分组结果"),#设置标题)
)
c.render()           #数据保存到本地的网页中.
# c.render_notebook() #在notebook中显示

结果图

输出3D图像中

  1. X轴为RFM_Group分组、Y轴为年份、Z轴为用户数量

  2. 该3D图可旋转、缩放,以便查看不同细节

  3. 调节左侧的滑块条,用来显示或不显示特定数量的分组结果

案例结论

基于图形的交互式分析

重点人群分布:212群体

  • 在整个分组中,212群体的用户是相对集中且变化最大的

  • 从2015年到2017年用户群体数量变化不大,但到2018年增长了近一倍

  • 这部分人群将作为重点分析人群

重点分组分布:

  • 除了212人群外,312、213、211及112人群都在各个年份占据很大数量

  • 虽然各自规模不大,但组合起来的总量超过212本身,也要重点做分析。

  • 如果拖动左侧的滑块,仅过滤出用户数量在4085以内的分组结果。观察图形发现,很多分组的人群非常少,甚至没有人

RFM用户特征分析

经过上面的分析,得到了要分析的重点客户群体。可根据用户的量级分为两类

  • 第1类是用户群体占比超过10%的群体

  • 第2类是占比在个位数的群体。这两类人由于量级不同,因此需要分别有针对性的策略场景。

  • 除此以外,我们还会增加第3类人群,虽然从用户量级上小,但是单个人的价值度非常高。

第1类人群:212、211、312、112、213;占比超过10%的群体。由于这类人群基数大,必须采取批量操作和运营的方式落地运营策略,一般需要通过系统或产品实现,而不能主要依赖于人工

  • 212:可发展的一般性群体。这类群体购买新近度和订单金额一般,且购买频率低。考虑到其最大的群体基础,以及在新近度和订单金额上都可以,因此可采取常规性的礼品兑换和赠送、购物社区活动、签到、免运费等手段维持并提升其消费状态。

  • 211:可发展的低价值群体。这类群体相对于212群体在订单金额上表现略差,因此在211群体策略的基础上,可以增加与订单相关的刺激措施,例如组合商品优惠券发送、积分购买商品等

  • 312:有潜力的一般性群体。这类群体购买新近度高,说明最近一次购买发生在很短时间之前,群体对于公司尚有比较熟悉的接触渠道和认知状态;购物频率低,说明对网站的忠诚度一般;订单金额处于中等层级,说明其还具有可提升的空间。因此,可以借助其最近购买的商品,为其定制一些与上次购买相关的商品,通过向上销售等策略提升购买频次和订单金额

  • 112:可挽回的一般性群体。这类群体购买新近度较低,说明距离上次购买时间较长,很可能用户已经处于沉默或预流失、流失阶段;购物频率低,说明对网站的忠诚度一般;订单金额处于中等层级,说明其还可能具有可提升的空间。因此,对这部分群体的策略首先是通过多种方式(例如邮件、短信等)触达客户并挽回,然后通过针对流失客户的专享优惠(例如流失用户专享优惠券)措施促进其消费。在此过程中,可通过增加接触频次和刺激力度的方式,增加用户的回访、复购以及订单价值回报

  • 213:可发展的高价值群体。这类人群发展的重点是提升购物频率,因此可指定不同的活动或事件来触达用户,促进其回访和购买,例如不同的节日活动、每周新品推送、高价值客户专享商品等。

第2类人群:占比为1%~10%的群体。这部分人群数量适中,在落地时无论是产品还是人工都可接入

  • 311:有潜力的低价值群体。这部分用户与211群体类似,但在购物新近度上更好,因此对其可采取相同的策略。除此以外,在这类群体的最近接触渠道上可以增加营销或广告资源投入,通过这些渠道再次将客户引入网站完成消费。

  • 111:这是一类在各个维度上都比较差的客户群体。一般情况下,会在其他各个群体策略和管理都落地后才考虑他们。主要策略是先通过多种策略挽回客户,然后为客户推送与其类似的其他群体,或者当前热销的商品或折扣非常大的商品。在刺激消费时,可根据其消费水平、品类等情况,有针对性地设置商品暴露条件,先在优惠券及优惠商品的综合刺激下使其实现消费,再考虑消费频率以及订单金额的提升。

  • 313:有潜力的高价值群体。这类群体的消费新近度高且订单金额高,但购买频率低,因此只要提升其购买频次,用户群体的贡献价值就会倍增。提升购买频率上,除了在其最近一次的接触渠道上增加曝光外,与最近一次渠道相关的其他关联访问渠道也要考虑增加营销资源。另外,213中的策略也要组合应用其中

  • 113:可挽回的高价值群体。这类群体与112群体类似,但订单金额贡献更高,因此除了应用112中的策略外,可增加部分人工的参与来挽回这些高价值客户,例如线下访谈、客户电话沟通等

第3类群体:占比非常少,但却是非常重要的群体

  • 333:绝对忠诚的高价值群体。虽然用户绝对数量只有355,但由于其各方面表现非常突出,因此可以倾斜更多的资源,例如设计VIP服务、专享服务、绿色通道等。另外,针对这部分人群的高价值附加服务的推荐也是提升其价值的重点策略

  • 233、223和133:一般性的高价值群体。这类群体的主要着手点是提升新近购买度,即促进其实现最近一次的购买,可通过DM、电话、客户拜访、线下访谈、微信、电子邮件等方式直接建立用户挽回通道,以挽回这部分高价值用户

  • 322、323和332:有潜力的普通群体。这类群体最近刚完成购买,需要提升的是购买频次及购买金额。因此可通过交叉销售、个性化推荐、向上销售、组合优惠券、打包商品销售等策略,提升其单次购买的订单金额及促进其重复购买

案例应用

针对上述得到的分析结论,会员部门采取了以下措施

  • 分别针对3类群体,按照公司实际运营需求和当前目标,制定了不同的群体落地的排期

  • 录入数据库的RFM得分数据已经应用到其他数据模型中,成为建模输入的关键维度特征之一

案例注意点

R 最近一次消费的间隔时间

F 消费频率

M 消费总额

不同品类、行业对于RFM的依赖度是有差异的,即使是一个公司在不同的发展阶段和周期下,3个维度的优先级上也会有调整

  • 大家电等消费周期较长的行业,R和M会更重要一些

  • 快消等消费周期短且快的行业,更看重R和F

  • 具体要根据当前运营需求与业务部门沟通

对R、F、M区间的划分是一个离散化的过程,具体需要划分为几个区间需要与业务方确认

  • 本案例划分为3个区间,结果对于业务分析而言有些多,意味着业务方需要制定十几套甚至更多的策略

  • 如果业务方要求简化,也可以划分为2个区间,这样出来的分组数最多有8组,策略制定更加简单

  • 具体是划分为2个还是3个,取决于当前业务方有多少资源可以投入到这个事情中来。

R、F、M的权重打分

  • 除了案例中提到的建模方式外,结合业务经验的专家打分法也是常用的思路,这时推荐结合AHP层次分析法打分,这样出来的权重结果更加科学、严谨。

  • 虽然订单数据库中的数据质量相对较高,但可能由于数据采集、数据库同步、ETL、查询、误操作等问题,还是会导致NA值的出现,而NA值的处理非常重要。

  • R、F、M三个维度的处理(包括计算、离散化、组合、转换)之前都需要注意其数据类型和格式,尤其是有关时间项的转换操作应提前完成

小结

  1. RFM模型是经典的一种用户分群方法,操作起来比较简单,如果数据量不是很大的时候,直接使用Excel就可以实现

  2. RFM并不是在所有业务场景下都可以使用,一般用于零售行业(复购率相对高的行业)

  3. 使用Python的cut方法对数据进行分组,需要注意分组区间默认是左开右闭

  4. 使用Pyecharts可以方便的绘制出可以交互的3D图

相关文章:

数据处理与统计分析篇-day11-RFM模型案例

会员价值度模型介绍 会员价值度用来评估用户的价值情况,是区分会员价值的重要模型和参考依据,也是衡量不同营销效果的关键指标之一。 价值度模型一般基于交易行为产生,衡量的是有实体转化价值的行为。常用的价值度模型是RFM RFM模型是根据…...

【PostgreSQL】PostgreSQL数据库允许其他IP连接到数据库(Windows Linux)

要让PostgreSQL数据库允许其他IP连接到数据库,需要进行以下几个步骤的配置: 1. 修改postgresql.conf文件 首先,需要修改PostgreSQL的主配置文件postgresql.conf,允许数据库监听所有IP的连接请求。 1.1 找到postgresql.conf文件…...

通义千问:让我的编程工作效率翻倍的秘密武器

在日益繁忙的工作环境中,选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度,甚至让团队协作更加顺畅。在这篇博客中,我将分享一个让我工作效率翻倍的编程工具——通义千问大…...

2.Seata 1.5.2 集成Springcloud-alibaba

一.Seata-server搭建已完成前提下 详见 Seata-server搭建 二.Springcloud 项目集成Seata 项目整体测试业务逻辑是创建订单后(为了演示分布式事务,不做前置库存校验),再去扣减库存。库存不够的时候,创建的订单信息数…...

python 图像绘制问题: 使用turtle库绘制蟒蛇

turtle (海龟)库是turtle绘图体系的python实现。 1969年诞生,主要用于程序设计入门。 import turtle turtle.setup(650, 350, 200, 200) # 设置窗体(宽,高,窗体左上角x坐标,y坐标) turtl…...

大模型分布式训练并行技术(七)-自动并行

近年来,随着Transformer、MOE架构的提出,使得深度学习模型轻松突破上万亿规模参数,传统的单机单卡模式已经无法满足超大模型进行训练的要求。因此,我们需要基于单机多卡、甚至是多机多卡进行分布式大模型的训练。 而利用AI集群&a…...

网络安全等级保护 | 规范企业网络系统安全使用 | 天锐股份助力等保制度落地

在当今数字化高速发展的时代,网络安全对于企业的重要性日益凸显。而近年来,数据泄露、网络攻击等安全事件频发,给企业和个人带来了前所未有的挑战。在这一背景下,网络安全等级保护制度(简称“等保”)作为国…...

Springboot使用redis,以及解决redis缓存穿透,击穿,雪崩等问题

1.Redis面试题-缓存穿透,缓存击穿,缓存雪崩 1 穿透: 两边都不存在(皇帝的新装) (返回空值)(互斥锁)(黑名单) (布隆过滤器) 2 击穿:一个或多个热…...

pve 命令开启关闭虚拟机

命令 #查看集群资源状况 #pvesh get /cluster/resources #取得虚拟机当前状态 #pvesh get /nodes/<节点id>/qemu/<虚拟机id>/status/current #pvesh get /nodes/www/qemu/107/status/current#关闭虚拟机 #pvesh create /nodes/<节点id>/qemu/<虚拟机id&…...

【达梦数据库】临时表的使用测试

目录 背景问题复现问题原因解决方法 背景 用户在使用临时表的过程中&#xff0c;执行commit提交命令之后&#xff0c;临时表的数据被清空&#xff0c;无法被接下来的存储过程复用。 问题复现 -----------------------------提交删除行----------------------------- --创建临…...

【GUI设计】基于Matlab的图像去噪GUI系统(8),matlab实现

博主简介&#xff1a; 如需获取设计的完整源代码或者有matlab图像代码项目需求/合作&#xff0c;可联系主页个人简介提供的联系方式或者文末的二维码。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本次案例是基于Matlab的图像去噪GUI系统&am…...

【计算机科学导论】

计算机科学的本质就是解决问题&#xff0c;我们计算机由输入设备&#xff0c;处理设备和输出设备组成。 处理设备看做一个大黑盒&#xff0c;目的就是接收处理数据&#xff0c;然后发送到输出设备。计算机中存储数据就是2进制&#xff0c;0和1&#xff0c;0代表关&#xff0c;…...

【C++】I/O流的使用介绍

文章目录 什么是 I/O 流&#xff1f;C I/O 流的基本类型常用的 I/O 操作1. 标准输入输出2. 文件输入输出3. 字符串流 什么是 I/O 流&#xff1f; 在 C 中&#xff0c;I/O 流是数据的输入和输出通道。流的本质是一个字节序列&#xff0c;提供了抽象的方式来读写数据。C 使用流对…...

深度学习:(八)深层神经网络参数与流程

深层神经网络 符号规定 L L L &#xff1a;表示神经网络的层数&#xff1b; l l l &#xff1a;表示第几层&#xff1b; n [ l ] n^{[~l~]} n[ l ] &#xff1a;表示第 l l l 层的节点数&#xff1b; a [ l ] a^{[~l~]} a[ l ] &#xff1a;表示第 l l l 层中的激活函数&…...

`pattern = r“(\d+)(CNY|JPY|HKD|EUR|GBP|fen|cents|sen|eurocents|pence)“

pattern r"(\d)(CNY|JPY|HKD|EUR|GBP|fen|cents|sen|eurocents|pence)" 是一个正则表达式&#xff0c;用于匹配特定格式的字符串。 正则表达式解析 整体结构&#xff1a; r"..."&#xff1a;前缀 r 表示这是一个原始字符串&#xff08;Raw String&#x…...

宝塔面板部署雷池社区版教程

宝塔面板部署雷池社区版教程 简单介绍一下宝塔面板&#xff0c;安全高效的服务器运维面板&#xff0c;使用宝塔面板的人非常多 在网站管理上&#xff0c;许多用户都是通过宝塔面板进行管理&#xff0c;宝塔面板的Nginx默认监听端口为80和443&#xff0c;这就导致共存部署时雷池…...

【击败100%】258. 各位相加

首次出现&#xff0c;代码用时击败了100%的用户&#xff0c;开心~ 题目 给定一个非负整数 num&#xff0c;反复将各个位上的数字相加&#xff0c;直到结果为一位数。返回这个结果。 示例 1: 输入: num 38 输出: 2 解释: 各位相加的过程为&#xff1a; 38 --> 3 8 -->…...

【alist】宝塔面板docker里的alist默认admin无法登录

宝塔docker安装完alist&#xff0c;根据页面的提示账号密码死活登录不上&#xff0c;提示密码有问题 页面提示&#xff1a; 数据存储目录 /www/dk_project/dk_app/dk_alist 使用说明请参考&#xff1a; >使用教程 默认账号密码&#xff08;admin/admin) 首次登录后点击个人…...

【击败100%】1281. 整数的各位积和之差

击败了100%的用户&#xff0c;开心~ 题目 给你一个整数 n&#xff0c;请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。 示例 1&#xff1a; 输入&#xff1a;n 234 输出&#xff1a;15 解释&#xff1a; 各位数之积 2 * 3 * 4 24 各位数之和 2 3 4 …...

Flink基本概念和算子使用

基础概念 Flink是一个框架和分布式处理引擎&#xff0c;用于对无界数据流和有界数据流进行有状态计算&#xff0c;它的核心目标是“数据流上的有状态计算”。 有界流和无界流 有界流&#xff1a;具有明确的开始和结束时间&#xff0c;数据量有限。适合使用批处理技术&#xf…...

Kafka 3.0.0集群部署教程

1、集群规划 主机名 ip地址 node.id process.roles kafka1 192.168.0.29 1 broker,controller Kafka2 192.168.0.30 2 broker,controller Kafka3 192.168.0.31 3 broker,controller 2、将kafka包上传以上节点/app目录下 mkdir /app 3、解压kafka包 所有节点 …...

昇思MindSpore进阶教程-格式转换

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 MindSpore中可以把用于训练网络模型的数据…...

搜索软件 Everything 的安装与使用教程

一、Everything简介 适用于 Windows 的免费搜索工具 Everything 是 Windows 的即时搜索引擎。发现、整理并轻松访问文件和文件夹&#xff0c;一切尽在指尖&#xff01; PS&#xff1a;Everything无法对文件内容进行搜索&#xff0c;只能根据文件名和路径进行搜索 二、Everyt…...

oracle 如何判断当前时间在27号到当月月底

在Oracle中&#xff0c;您可以使用TRUNC和LAST_DAY函数来判断当前时间是否在27号到当月月底之间。以下是一个SQL示例&#xff1a; SELECT CASE WHEN TRUNC(SYSDATE) > TRUNC(SYSDATE, DD) 26 AND TRUNC(SYSDATE) < LAST_DAY(SYSDATE) THEN 当前时间在27号到当月月底之间…...

Django 配置邮箱服务,实现发送信息到指定邮箱

一、这里以qq邮箱为例&#xff0c;打开qq邮箱的SMTP服务 二、django项目目录设置setting.py 文件 setting.py 添加如下内容&#xff1a; # 发送邮件相关配置 EMAIL_BACKEND django.core.mail.backends.smtp.EmailBackend EMAIL_USE_TLS True EMAIL_HOST smtp.qq.com EMAIL…...

Git使用手册

1、初识Git 概述&#xff1a;Git 是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理项目版本管理。 知识点补充&#xff1a; 版本控制&#xff1a;一种记录一个或若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 分布式&#xff1a;每个人…...

sql-labs靶场

第一关&#xff08;get传参&#xff0c;单引号闭合&#xff0c;有回显&#xff0c;无过滤&#xff09; ?id-1 union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schemasecurity) -- 第二关&#xff08;get传参&#xff0c;无闭…...

【Redis入门到精通二】Redis核心数据类型(String,Hash)详解

目录 Redis数据类型 1.String类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 2.Hash类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 Redis数据类型 查阅Redis官方文档可知&#xff0c;Redis提供给用户的核心数据…...

如何快速免费搭建自己的Docker私有镜像源来解决Docker无法拉取镜像的问题(搭建私有镜像源解决群晖Docker获取注册表失败的问题)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Docker无法拉取镜像 📒📒 解决方案 📒🔖 方法一:免费快速搭建自己的Docker镜像源🎈 部署🎈 使用🔖 备用方案⚓️ 相关链接 🚓️📖 介绍 📖 在当前的网络环境下,Docker镜像的拉取问题屡见不鲜(各类Nas查询…...

QT 获取视频帧Opencv获取清晰度

先展示结果&#xff1a; 1.获取摄像头的分辨率 mResSize.clear();mResSize camera_->supportedViewfinderResolutions();ui->comboBox_resulation->clear();int i0;foreach (QSize msize, mResSize) {qDebug()<<msize;ui->comboBox_resulation->addItem(…...