RFM模型与AB测试

1 推广效果分析 画图

有三个连续型的变量 ,要看他们之间的关系, 应该可以想到使用气泡图(特殊的散点图)

  • x, y 散点半径大大小

Matplotlib 绘图可以满足我们所有的绘图需求

  • 可以绘制文字, 绘制辅助线...

关于绘图的部分, 记住API是没必要的, 主要要掌握的是什么场景使用什么图形

  • 直方,散点(气泡), 柱状,折线,饼,热力图(相关性),箱线图(提琴图)

2 RFM 用户价值分群模型

精细化运营、精准营销

  • 服务好老用户, 避免用户的流失, 能够从竞品的用户中挖过来新的用户

  • 精细化运营, 精准营销很重要的一个环节就是给用户分群

    • 给用户分成不同的群体, 认为相同的群体用户有一些共性, 针对共性来指定运营的策略

    • AIPL AAAA AAAAA AARRR

    • RFM

数仓、数分的同学在精细化运营、精准营销起到的作用

  • 提供数据的支持, 给用户贴上不同的标签, 这些标签是精细化运营、精准营销的抓手

    • 标签是通过一系列的规则计算出来的
  • 给精细化运营、精准营销这些动作设计一系列考核指标, 用来评估运营的效果

2.1 RFM介绍

R recency 最近 分成两类 7天以内来过 高 低

F frequency 次数 90天以内 >15次 高 低

M monetory 金额 90天以内消费 >1500 高 低

计算RFM需要什么样的数据

  • 带着会员ID的购物流水就可以计算RFM

    • id 时间 金额

RFM适合落地的业务

  • 必须有消费,频率不能太低, 最适合的业务就是电商,外卖,旅游,打车

RFM如何使用

  • 最简单的用法就是三个维度做二分 高低 高低 高低 给用户分成8群

  • 还可以考虑做三分 低中高 1,2,3 给用户分成27群

  • 也可以考虑分5份 1,2,3,4,5 每个维度来计算均值, 高于均值的 高, 低于均值的是低 还是划分成8群

2.2 RFM计算思路

ID, 时间,金额

① 用户ID做分组聚合, 计算出R, F , M的聚合值

  • R 用户ID分组, 取时间的max() 用今天的时间 - 时间的最大值 得到R : 最后一次访问 距今7天

  • F 用户ID分组, 计数 统计有多少笔订单 (有些业务需要先筛选一下数据,比如最近90天, 最近180天) 90天以内来了10次

  • M 用户ID分组 对金额求和 一共花了1000元

② 制定打分的标准, 依据这个标准给 R, F , M的聚合值转换成 1,2,3

pd.cut()

③ 把三个维度的分数拼接到一起 , 用户贴上对应的标签

④ 结果的保存

  • 存一份到Excel

  • 再保存一份到Mysql

⑤ 画图分析一下 RFM分群之后的结果

  • 哪一组人多

  • 要关注的比较重要的组, 人数情况

2.3 代码实现

加载数据

复制代码
import pandas as pd
sheet_names = ['2015','2016','2017','2018']
rfm_data = pd.read_excel('C:/Develop/顺义48/day01/02_代码/data/sales.xlsx', sheet_name=sheet_names)
#%%
for sheet_name in sheet_names:
    print(rfm_data[sheet_name].head())
    print(rfm_data[sheet_name].info())
    print(rfm_data[sheet_name].describe())
    print('==================================')

通过数据查看之后, 发现了一些问题

  • 数据有缺失 有两年的数据各少了一条

  • 数据可能存在异常, 订单的金额最小 0.0 0.1 0.5 最大的十几万 这些需要跟业务方进行沟通 询问是否有问题

数据预处理

复制代码
# 数据从excel读取出来之后, 返回的是一个dict key是sheet_name  values 就是每一个工作簿的数据
data_merge = pd.concat(rfm_data.values())
# 去缺失值 
data_merge.dropna(inplace=True)
# 保留订单金额>1
data_merge.query('订单金额>1', inplace=True)
# 提取年份  每一年单独计算RFM 2015 ~2018 4年的数据, 计算4个RFM
data_merge['年份'] =data_merge['提交日期'].dt.year

计算时间间隔, 这里以每一年最后一天为基准, 计算每一年的RFM标签

复制代码
# 按年份获取,每一年日期的最大值   
data_merge.groupby('年份')['提交日期'].agg(max) # 分组聚合, 聚合之后每一组只有一个结果
#%%
data_merge['每年RFM计算日期'] = data_merge.groupby('年份')['提交日期'].transform('max')
#%%
# 计算消费日期(提交日期) 举例计算日期的时间间隔  
data_merge['时间间隔'] = (data_merge['每年RFM计算日期']-data_merge['提交日期']).dt.days

计算RFM的聚合值

复制代码
#%%
# 时间间隔 取最小值, 订单金额:sum  提交日期: count
rfm_df = data_merge.groupby(['年份','会员ID'],as_index=False).agg({'时间间隔':'min','提交日期':'count','订单金额':'sum'})
# 重命名一下列名
rfm_df.columns = ['年份', '会员ID', 'R', 'F', 'M']

给RFM三个维度打分

复制代码
## 确定RFM的划分区间
#%%
rfm_df.iloc[:,2:].describe()
#%%
# 27  R 1,2,3   F 1,2,3  M 1,2,3
r_bins= [-1,79,255,365]
f_bins = [0,2,5,130] # 和业务方讨论, 或者这个规则直接由业务方提供
m_bins= [0,69,1199,206252]
rfm_df['r_score'] =pd.cut(rfm_df['R'],bins=r_bins,labels=[i for i in range(len(r_bins)-1,0,-1)])
rfm_df['f_score'] =pd.cut(rfm_df['F'],bins=f_bins,labels=[i for i in range(1,len(r_bins))])
rfm_df['m_score'] =pd.cut(rfm_df['M'],bins=m_bins,labels=[i for i in range(1,len(r_bins))])
#%%
rfm_df.info()
#%%
# pd.cut之后得到的是 category这个类型, 这种类型不能直接拼接, 先转换成字符串 astype(str)
rfm_df['rfm_group'] = rfm_df['r_score'].astype(str)+rfm_df['f_score'].astype(str)+rfm_df['m_score'].astype(str)
​

绘图数据准备

复制代码
# 统计每年 RFM 不同组的人数
display_data = rfm_df.groupby(['rfm_group','年份'])['会员ID'].count().reset_index()
# 给数据列重名民
display_data.columns = ['rfm_group','year','number']
# 查看数据类型
display_data.info()
#%%
display_data['rfm_group'] = display_data['rfm_group'].astype(int)

pyecharts绘图

复制代码
import pyecharts
pyecharts.__version__
#%%
# pip install pyecharts -i https://pypi.tuna.tsinghua.edu.cn/simple
#%%
# 显示图形
import pyecharts.options as opts
from pyecharts.charts import Bar3D # 3D柱形图
​
# 颜色池
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_group
        yaxis3d_opts=opts.Axis3DOpts(type_="category", name='年份'),#y轴数据类型,名称,year
        zaxis3d_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('rfm.html') #在目录中生成一个HTML的页面

2.4 RFM小结

RFM体现了分群的思路, 将用户划分成不同的群体是很重要的精细化运营、精准营销的手段

RFM 最适合落地的业务就是电商

  • 有一些业务 比如 信息流(微博,今日头条) 没有很多收费的地方但是用户使用的频率很高, 很多用户也不会产生消费, 这个时候可以使用RFM的变种, RFE E代表活跃 (浏览,收藏,发表评论, 转发,点赞)

RFM和计算的时间密切相关的

  • 对于某个用户来说, 计算RFM的时间差了一天,可能会落到不同的组中

  • RFM的值需要经常更新

3 AB测试

3.1 AB测试概述

页面结构的调整, 头图设计的调整, 算法策略的调整, 按钮调整, 功能修改, 在正式上线之前, 一般都会走AB测试

  • 使用AB测试的前提, 用户量足够大

AB测试的流程

  • 明确这次调整的KPI是啥

    • 比如 我们调整了推荐算法, 目的是为了提升推荐内容的点击率, 先要明确当前的点击率是多少, 目标要提升到多少

    • 加入当前的推荐文章点击率8% 我们想要提升到10%

  • 计算参与实验的人数 可以使用网站来帮助计算

    • α 显著性水平 一般设置为5% 按照AB测试的流程, 完成了AB测试, AB测试的结果显示, 新的方案比老的方案好,依然有可能会犯错, 这个犯错的概率就是α 显著性水平, 这个错误是由抽样导致的

    • β 统计功效 一般设置为20% 按照AB测试的流程, 完成了AB测试, AB测试的结果显示,新的方案不行,要保留老的方案,依然有可能会犯错, 这个犯错的概率就是β, 这个错误是由抽样导致的

  • 开始实验

    • 选对照组 使用的是老的策略, 看到的是老的设计 避免在进行实验的过程中,外部条件的变化导致关键指标的波动

    • 选测试组 使用新的策略,看到的是新的设计

  • 收集数据计算结果

3.2 辛普森悖论

辛普森悖论为英国统计学家E.H.辛普森于1951年提出,即在某个条件下的两组数据,分别讨论时都会满足某种性质,可是一旦合并考虑,却可能导致相反的结论。

AB测试中产生辛普森悖论的原因:流量分割不均匀导致的实验组与对照组的用户特征不一致

AB测试中如何避免辛普森悖论

  • 要得到科学可信的 AB 测试试验结果,就必须合理的进行正确的流量分割,保证试验组和对照组里的用户特征是一致的,并且都具有代表性,可以代表总体用户特征。

3.3 AB测试案例

计算参与实验人数

复制代码
import pandas as pd
import statsmodels.stats.api as sms
from math import ceil
#%% md
## 计算参与实验人数
#%%
# 转化率当前是13%  我们要提升到15%
effect_size = sms.proportion_effectsize(0.13,0.15)
# 计算参与实验人数  power 统计功效 一般选择0.8   alpha 显著性水平 一般取5%
required_n = sms.NormalIndPower().solve_power(effect_size,power=0.8,alpha=0.05,ratio=1)
required_n = ceil(required_n)
required_n

实验结果数据计算

复制代码
​
df = pd.read_csv('C:/Develop/顺义48/day01/02_代码/data/ab_data.csv')
df.head()
#%%
df.info()
#%%
df.describe()
#%%
df['group'].value_counts()
#%%
df['user_id'].nunique() # 294478
#%%
df['user_id'].value_counts()
#%%
df[df['user_id']==842042]

数据处理, 经过基本数据探查之后, 发现有的用户既是对照组的,也是实验组的, 有的用户 老页面新页面都看到过 要把这类数据筛选掉

只保留看过一个页面的用户

复制代码
session_counts = df['user_id'].value_counts()
# 保留在数据中记录数小于两条的  获取索引, 索引实际上就是用户的id
saved_users = session_counts[session_counts<2].index
# saved_users 只有一条记录的用户ID, 在数据中把这些用户筛选出来 
df = df[df['user_id'].isin(saved_users)]

计算转化率

复制代码
​
required_n
# 随机抽取一部分样本 4720条 控制组(对照组) 随机筛选  治疗组(实验组)做随机筛选
# random_state 随机数种子, 保证每次得到的抽样的样本是一样的, 目的是结果可以复现
rand_state = 10
control_sample = df[df['group']=='control'].sample(n=required_n,random_state=rand_state)
treatment_sample = df[df['group']=='treatment'].sample(n=required_n,random_state=rand_state)
ab_test = pd.concat([control_sample,treatment_sample])
ab_test.groupby('group')['converted'].mean()
#%%
## 根据实验结果 得出结论, 新的设计不能满足 转化率到15%这个目标 设计应该重做
相关推荐
Elastic 中国社区官方博客5 分钟前
Elasticsearch:Ingest architectures - 摄取架构
大数据·elasticsearch·搜索引擎·架构·全文检索
深圳信迈科技DSP+ARM+FPGA33 分钟前
基于全国产复旦微JFM7K325T+ARM人工智能数据处理平台
人工智能·复旦微jfm7k325t
m0_6896182835 分钟前
生物墨水:3D组织生物打印的基石
人工智能·笔记
锋.谢37 分钟前
深入研究深度学习
人工智能·深度学习
james的分享39 分钟前
大数据之ZooKeeper
大数据·hadoop·开源软件
Papicatch1 小时前
【人工智能】-- 智能家居
图像处理·人工智能·python·人脸识别·智能家居
沽漓酒江1 小时前
机器学习第四十六周周报 FMP
人工智能·机器学习
汀、人工智能2 小时前
AI Agent技术的最新进展与改变世界的典型项目巡礼
人工智能·agent
过于真实呢2 小时前
3-5 提高模型效果:归一化
人工智能·python·自然语言处理
The Open Group2 小时前
The Open Group 2024架构·AI标准峰会——合作伙伴+演讲嘉宾预热征集中!
人工智能·架构