公司级abtest实战--带分组的abtest应该怎么做?

本文最早发表在csdn时间为:2023-04-17

数据来源为工作中接触到的某公司后台数据,在完成工作相关分析后,本人对该部分数据虚拟重建用以复盘整理

学习是为了不落后,
整理则是为了不忘记。

通过本文您将学习到:

  • 假设检验 相关知识
  • python (numpy 、pandas 、 scipy )
  • abtest原则
  • 实验过程
  • 指标确定原则

阅读本篇文章之前,可以先记住两个指标:组内方差、组间方差

组内方差: 每组的内部方差求和

组间方差: 某公司下属8个部门的营业额(单位万元)为:80,85,96,110,125,130,145,160
假设以是否超过100万元来分组
第一组 80,85,96
第二组 110,125,130,145,160
计算:
总算术平均数(X总 bar) = 116.375
第一组内算术平均数(X1 bar) = 87
第二组内算术平均数(X2 bar) = 134
组间方差(δ) = [ (87 - 116.375)^2 × 3 + (134 - 116.375) ^2 × 5 ] ÷ ( 3 + 5 )
= 517.73
可见,上面的计算没有把8个标志值分别与总算术平均数进行比较,而只是分别在两个组中取出算术平均数。

一、背景

时下国内都在卷价格战,于是涌现了一批查价平台,刚好某个发单平台开展一个报价模式,即用户下单,承接方(服务方或卖家)报价,通过问卷调查发现,约70%的用户反馈当价格分离比较大时,会影响用户的下单,增大犹豫,而当承接方所报价格接近一致时,他们下单意愿更强,且研究数据发现,供应商报价组内方差越低,指派率越高。(不要小看这个结论,这是我们花了大量数据区论证的,从商品类目、距离、时效、服务难度、时间等维度对比,这一块的方向大概是做好组内报价的分布,通过建立组内方差+指派率线性关系 ,当关系不明显时,模拟abtest 测试)

二、策略

老板:我要赚更多钱

管理:让更多人下单

领导:让这个指派率高起来

运营:TMD,我也不知道怎么办,船长,让这个指派的商家变多,不指派的变少

作为一个数据局分析师 我们要敏锐的抓住业务的需求 ,把他的需求转化为可以量化的数据指标

于是船长(黑人问号):赚钱=下单量 指派率 成交率*单价**

既然已经知道价格集中对指派供应商行为能产生重大影响,那我作为数据分析师为业务提供3条运营策略:
1.产品侧: 建立行业规范级别的参考价,用以影响服务商报价
2.产品侧: 对供应商高价行为进行延后、折叠、拦截展示
3.运营侧: 运用补贴策略对高价补贴,对低价报价进行引导报合适价,并根据1中的测算价补充一口价订单(参考打车平台一口价)

如用户有一笔运输订单,心里预期在280,但目前平台供应商报价300,引导供应商报价280,平台额外补贴20,让成交双方的心里预期在280达成一致,这个策略可以在两方都进行,一方面拔高用户价格,另一方降低师傅心里价格,从而平衡预期,这在我们以往运营发现普遍有效

价格集中度怎么衡量?容后再议~

三、运营实施

3.1 插曲

于是,在紧锣密鼓的产品上线后

运营吭呲吭呲的配置之后,抛出了新的问题:

1.如何证明这个上线后,我们的集中度变小了?

2.怎么证明我们的指派率变高了?

我当时就震惊了!!! (UC震惊部重大新闻)

什么,好你个运营,干这么多年,策略有效性都没方案?

我:你们以往怎么认定的?

对面:高了就有效,低了就没效~

我:你们都不用ABtest 吗?

对面:什么是ABtest?

当时的心情是承重的!!

鲁迅说:运营不知ABtest,犹如盲人摸象啊!

我当即表示,当然是ABtest 了 ,

3.2 指标

什么是ABtest?

简单讲,ABtest 就是双盲实验(假设检验的一种),把用户拖到两个池子里,在给定一个误差范围,在这个极小的误差范围内,这两组数据的某些指标相差甚远,那么说明数据被这个策略影响了,当实验组比对照组表现优秀时,说明策略变有效,否则策略无效或者使之变差。

这里不过多展开ABtest的知识了

那么,我们已经知道要做ABtest ,对应的指标选取应该选哪个尼?

以下我们对过程重洗梳理一遍:

首先我们发现报价差异影响了指派行为,该节点转化率较低,那我们的大节点拆分公式应该含“指派率”这个指标:

GMV=下单量 * 指派率 * 指派成交率

通过数据分析发现,价格越集中,指派率越高,那“集中“ 这个描述,是业务描述,我们需要转化为数据指标,

因此,对于这种大型业务,一个类目会产生无数个订单,一个订单下会有一组报价,一组报价会有一个方差,这时候,显然我们单纯的将每组的方差加起来(即组内方差,只能衡量每个组内分散的情况)不太适用,因此引入组件方差(理论上,一个类目下的服务在一个地区时间段内,价格是差不多的,因此要看整体是否向一个中心集中,“组间方差/n"就能代表这个”组” 到 ““中心”” 的集中度)

在这里,等于让价格集中,数据上,等于让报价的组间方差(SSA)和组内方差(SSE)变小!!(这里我们选择”组间方差/N“ ,N为报价个数 )

于是主策略:让每组的报价更加集中,关注的指标变成,每一组的方差都变小

于是一类指标我们定位为:即SSA,故:H1:SSA/n变小(h0为:两组SSA/n 一样)

在这里插入图片描述

那么问题来了,虽然我们之前证明了SSA变小会影响指派率,SSA越小指派率越高,但是,这里SSA 下降后 ,他的指派率有没有提高我们是不是也应该关注?否则SSA变小了,结果指派率变低,那不是南辕北辙了吗?

所以我们的二类指标出来了:即指派率,故:h1:实验组指派率变变大(h0为:两组指派率 一样)

ok,到这里,本来已经可以交代了,因为我们的一类指标在证明我们产品有效,而二类指标在证明我们策略合理,但还记得我们前文他提到的内卷吗?

我们的策略其实还做了一点小心思,那就是将参考价往下调了一个预期,这里想要通过引导供应商报出更极限的价格,于是我们还要测试,对照组和实验组的价格是否在一个水平;

因此,我们的三类指标出来了,即:报价均值,故:h1:实验组的报价均值更小(h0:两组的报价均值无差异)

在这里插入图片描述

以上三个指标分别对应方差检验(F检验)、百分比检验(P检验),均值检验(独立样本均值t检验

这个项目是直接把所有检验全上可一遍~ 有点可怕,但往往最可怕的,最难易接受的,就是现实~

3.3实验

通过在几个一线城市的灰度测试,对满足条件的用户和供应商进行哈希处理,最后通过推单系统撮合,于是我们收到一组这样的数据:

订单表:

分组类型城市类目订单编号是否指派
A1组:实验组_after深圳电脑NO_20241111
A2组:对照组_after深圳电脑NO_2024XXXXX
B组:上线前_before深圳电脑NO_2023XXXXXX

报价表:

订单编号报价供应商id
NO_20241111101gy_101
NO_20241111192gy_301
NO_2024111178gy_501
3.3.1实验记录表

通过以上两个表,代入我们上面的公式,就可以计算“SSA/n” 、指派率、报价均值(xbar)这些指标(假设在深圳开展实验):

得到以下表格:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为我们实验过程需要考虑时间打来的问题,所以 A1 A2 为一组AA 实验,而A1 和B 为AB实验,所以实验前我们其实是有预期效果的

比如我期待我们参考价干预了报价,那么我们的预期是:A1组的效果最好(ssa/n 最小,指派率最高、报价均值最低),A2和B应该接近。

3.3.2实验代码

那其中假设检验的F_left 和F_right 以及其他的临界值怎么计算尼?那当然是用python 手搓一份了,

清洗函数如下:

import numpy as np 
import pandas as pd
pd_a=pd.read_csv(r'a组vx:captain_data.csv')
pd_b=pd.read_csv(r'b组vx:captain_data.csv')

def df_get_ssa_s_xbarbar(df_wat,level_3_name,random_n):
    pd_a=df_wat
    pd_a.columns=[ 'order_id','order_no',
                  '省','市','区县','一级类目','二级类目','三级类目','报价价格']
# ]
    pd_a.global_order_trace_id=pd_a.index
    df_avg=pd_a.groupby(['order_no'])['报价价格'].agg('mean')  #  计算报价组内均价
    df_avg_df=pd.DataFrame(df_avg)#'order_no',  计算总报价均价
    df_avg_df.columns=['xbar'] 
    pd_a_xbar=pd_a.merge(df_avg_df, on='order_no',right_index=False)
    city_01=['深圳市']
    pd_a_xbar_city_01=pd_a_xbar[pd_a_xbar..isin(city_01)]
    pd_a_xbar_city_01_ch_desk=pd_a_xbar_city_01[pd_a_xbar_city_01.三级类目==level_3_name ]
    
    print('清洗前订单数',len(pd_a_xbar_city_01_ch_desk.order_no.unique()))
    print('清洗前报价数',len(pd_a_xbar_city_01_ch_desk))
    # pd_a_xbar_city_01_ch_desk=pd_a_xbar_city_01_ch_desk.sample(n=20000,random_state=22)  
    std_4=3*np.std(pd_a_xbar_city_01_ch_desk.报价价格)  # 3倍标准差异常值检测
    pd_a_xbar_city_01_ch_desk=pd_a_xbar_city_01_ch_desk[(pd_a_xbar_city_01_ch_desk.报价价格>=40)&(pd_a_xbar_city_01_ch_desk.报价价格<=std_4)]  #  去除异常值
    
    #随机抽取开关
#     random_n=len(pd_a_xbar_city_01_ch_desk.order_no.unique())
    
    random_order_no_list=pd.DataFrame({'order_no':pd_a_xbar_city_01_ch_desk.order_no.unique()}).sample(random_n,random_state=26).order_no.tolist()#随机抽取数据
    pd_a_xbar_city_01_ch_desk=pd_a_xbar_city_01_ch_desk[pd_a_xbar_city_01_ch_desk.order_no.isin(random_order_no_list)]
    abtest_df=pd_a_xbar_city_01_ch_desk.报价价格.values
    pd_a_xbar_city_01_ch_desk['xbarbar']=pd_a_xbar_city_01_ch_desk.报价价格.mean()
    
    # pd_a_xbar_city_01_ch_desk['xbarbar']=pd_a_xbar_city_01_ch_desk.报价价格.mean()
    pd_a_xbar_city_01_ch_desk['xbar_xbarbar_v2']=(pd_a_xbar_city_01_ch_desk.xbar-pd_a_xbar_city_01_ch_desk.xbarbar)**2
    mean=pd_a_xbar_city_01_ch_desk.报价价格.mean()
    ssa=pd_a_xbar_city_01_ch_desk.xbar_xbarbar_v2.sum()
    ssa_s=np.sqrt(ssa/len(pd_a_xbar_city_01_ch_desk))
    ss=np.std(pd_a_xbar_city_01_ch_desk.报价价格)
    d=len(pd_a_xbar_city_01_ch_desk)
    print('清洗后订单数',len(pd_a_xbar_city_01_ch_desk.order_no.unique()))
    print('清洗后报价数',d)
    print('ssa_s:%s,\nxbarbar:%s,\nss:%s'%(ssa_s,mean,ss))
    
    return mean,ssa_s,ss,d,abtest_df
    #26

清洗函数为计算待测变量以及去除异常值使用,代码中"std_4=3*np.std(pd_a_xbar_city_01_ch_desk.报价价格) # 3倍标准差异常值检测" 将所有3倍标准差外的异常报价数据去除~,并对外传递报价数量,用来计算假设检验中临界值。

假设检验主函数 :

函数将 使用 scipy 包的统计学模块 stats 快速计算,得到各个临界值,代码如下~

mean_1,ssa_s_1,ss_1,d_1,abtest_df_1=df_get_ssa_s_xbarbar(pd_a,'笔记本',2343)
print('********************************************************')
mean_2,ssa_s_2,ss_2,d_2,abtest_df_2=df_get_ssa_s_xbarbar(pd_b,'笔记本',2343)
print('********************************************************')
from scipy.stats import f
F=ssa_s_1**2/ssa_s_2**2
fr=f.ppf(0.975,d_1-1,d_2-1)
fl=f.ppf(0.025,d_1-1,d_2-1)

print(f'实验数据F值为:{F}')
print(f'alpha=0.975的双尾检验对应的临界值为:{fr}')
print(f'alpha=0.025的双尾检验对应的临界值为:{fl}')
# f.ppf(0.975,13229,22027)
if F>fr :
    print('由于F>fr,落在右侧拒绝域,拒绝H0,接受H1,两组数据有显著性差异,a组数据方差比b组高,实验成功!!')
elif  F<fl:
    print('由于F<fl,落在左侧拒绝域,拒绝H0,接受H1,两组数据有显著性差异,a组数据方差比b组低,实验成功!!')
    
else:
    print('由于fl<=F<=fr接受H0,拒绝H1,两组数据无差异,实验失败!!')
    
    
print('********************************************************')    
#标准误
σ_bak=np.sqrt(np.std(abtest_df_1)**2/d_1+np.std(abtest_df_2)**2/d_2)
σ_bak


from scipy import stats
# Z_x1_x2=np.mean(abtest_df_1)-np.mean(abtest_df_2)
Z=(np.mean(abtest_df_1)-np.mean(abtest_df_2))/σ_bak
# Z
p_z=stats.norm.cdf(Z,0,1)

# Z_test= stats.norm.ppf(p_z,np.mean(abtest_df_1)-np.mean(abtest_df_2),σ_bak)  #alpha,均值,s
# z_test_l = stats.norm.ppf(0.025,np.mean(abtest_df_1)-np.mean(abtest_df_2),σ_bak)  #alpha,均值,s
# z_test_r = stats.norm.ppf(0.975,np.mean(abtest_df_1)-np.mean(abtest_df_2),σ_bak)  #alpha,均值,s

z_test_l = stats.norm.ppf(0.025,0,1)  #alpha,均值,s
z_test_r = stats.norm.ppf(0.975,0,1)  #alpha,均值,s

print(f'实验数据Z值为:{Z}')
# print(f'实验数据Z_test 对应值为:{Z_test}')
print(f'alpha=0.975的双尾检验对应的临界值为:{z_test_r}')
print(f'alpha=0.025的双尾检验对应的临界值为:{z_test_l}')
print(f':Z_test对应的 Z_p值为:{p_z}' )
# f.ppf(0.975,13229,22027)
if Z>z_test_r:#p_z>0.975 :
    print('由于Z>z_test_r,落在右侧拒绝域,拒绝H0,接受H1,两组数据有显著性差异!!A组数据比B组数据均值高')
elif  Z<z_test_l:#p_z<0.025:
    print('  由于Z<z_test_l,落在左侧拒绝域,拒绝H0,接受H1,两组数据有显著性差异,B组数据比A组数据均值高')
    
else:
    print('由于0.025<=P_z<=0.975,接受H0,拒绝H1,两组数据无差异,实验失败!!')

如果你不会假设检验原理或者Python,那么,可以去找网络上的工具测算~

于是,我们有开始了多轮测试进行参数调整包括但不限于:

调整参考价的参数、类目展示时间、营销手段、直播,最终我们得到,参考价能有效干预供应商报价,能够使得供应商报价更集中,从SSA/n 从xxxx降到了xx,且在一定时间内,供应商平均报价价格普遍降低N元,用户指派率从XX.xx%提升到XX.xx%

四、总结

ok , 所以到此,一个提升商品指派率的项目基本走完,而分析师,全程参与数据建模、数据方案输出

数据结果验证、产品效果验证,业务运营方案设计、指标设计、原理讲解等,希望这篇讲解,能帮助各位在数分工作中,所向披靡~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Captain_Data

打赏一下~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值