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%这个目标 设计应该重做