在实际业务中,我们经常会遇到频率类事件的假设检验,这类数据包括:留存率、点击率、转化率。我们甚至可以把大多数AB测试类的场景都理解为「频率的假设检验」。本文试图创建一个一般性的频率假设检验工具的EXCEL实现,并介绍其工作原理。文件参考这里(https://github.com/threecifanggen/data-science-tools/blob/master/excel/%E4%BA%8C%E9%A1%B9%E5%88%86%E5%B8%83%E6%A3%80%E9%AA%8C.xlsx)。
定义问题
其实,在具体工作中,我们都会遇到比例数据来作为指标衡量一个数据,可以大致将这些指标抽象成三类数据:正事件触发次数/用户数,触发正事件的人数/人数,正事件触发次数/总事件发生次数。我们在文章中将以「平均发生次数」(Average Count)、「转化率」(Transfer Rate)、「发生率」(Positive Rate)来表述这三类指标。
名称 | 平均发生次数 | 转化率 | 发生率 |
---|---|---|---|
例子 | 人均成单量、人均点击量 | 留存率、成单转化率 | 按钮点击率、流程退出率 |
定义 | 正事件触发次数/用户数 | 触发正事件的人数/用户数 | 正事件触发次数/总时间发生次数 |
建议场景 | 单次触发具有实际意义; 和收入相关的内容; 较为综合的衡量产品的指标 | 与用户运营相关的指标; 评价用户粘性 | 判断某个功能好坏; 与产品细节优化相关 |
数据分布 | 指数分布为主,小概率为泊松分布 | 二项分布(Beta分布) | 二项分布(Beta分布) |
问题抽象
事实上,我们单纯从数学抽象上说,上面的比例数据,事实上可以抽象为两类问题,一个是「每个用户具有一个观测量」,比例事实上反应的是这个观测量的平均值;另一类则是「频率」,反应的是一个事件发生的概率(只是这个频率的维度是以事件为主还是用户为主)。我们本文就限定在「频率」这个抽象问题的范围内,考虑如何比较两组频率的差异。理解了这个抽象,我们可以同时分析次留、转化率、AB测试的数据。特别的,我们采用的统计工具在以下场合,会比直接比较两组的频率更有效:
- 当抽样数据过小时或极度不平衡时。譬如在产品仅在冷启动时期,这时候的流量相对较小,单纯比较频率容易有抽样误差;
- 快速决策的时候,特别地,需要决定何时终止比较的时候。譬如,在每单位时间成本极大的时候,如果进行AB测试,需要很快得到结果,终止测试。如果使用直接比较频率,我们往往无法做出何时终止测试的判断,下面的方法(特别是基于贝叶斯的方法)可以很快地在验证成功之后终止测试。
数学工具
首先,我们可以假设一个事件的发生频率为
$$f = X/N$$
其中,$X$为正例发生次数,$N$则为事件发生总次数。譬如,在留存率(次留)中,$X$表示第二日使用产品的用户数,$N$表示计算当日的注册用户数。
我们很容易地可以做出假设:$X$属于二项分布,即:
$$X \sim B(N, p)$$
我们现在可以将其转化为两组二项数据的比较。
方案一:假设检验
这是最简单可以想到的方案。我们假设两组数据其参数分别为$p_1$,$p_2$。则可以建立如下假设:
- H0: $p_1 = p_2$
- H1: $$p_1 \ne p_2$$
则统计量$z$ 满足
$$z = \frac{\hat{p_1} - \hat{p_2}}{\sqrt{\hat{p}(1 - \hat{p})(\frac{1}{N_1} + \frac{1}{N_2})}}$$
其中,
$$\hat{p} = \frac{N_1\hat{p_1} + N_2\hat{p_2}}{N_1 + N_2}$$
因为$z \sim N(0, 1)$,我们就可以基于这个计算出相应的显著性值并进行比较。
可以在Excel的这部分看到结果,其中显著性大小我们这里分为0,*, **, ,***,****, *****五档,可以基于不同的数据量调整判断标准。
方案二: 贝叶斯估计
在大多数场合下,特别是指数分布族内的分布中,我们一般会使用$Beta(\alpha, \beta)$分布作为先验分布,当然这么选择会有很多好处,首先:
- 当$\alpha$,$\beta$足够大时,
$$Beta(\alpha, \beta) \rightarrow B(\alpha + \beta, \frac{\alpha}{\alpha + \beta})$$。这样我们就可以刻画出以二项分布扩展的任何分布。 - 其次,当先验分布为
$$Beta(\alpha, \beta)$$,且新的观察量$N$中,有$k$个正例。则其后验分布可以算出为
$$Beta(\alpha + k, \alpha + N - k)$$,计算非常简单。 - $Beta(1, 1)$为均匀分布,非常方便的可以作为无信息的先验分布。
接下来,我们可以基于两组数据,来求出两个$Beta$分布,然后基于这两个$Beta$分布进行抽样,抽样的具体结果在Excel的这个位置:
然后抽样结果的汇总数据,包括,每个例子的值还有分布,展示在这里:
最后我们可以在这里看结果,这里提供以下指标,就是两个比例的抽样均值,还有就是给出两组数据之间差值正负的比例($P(A>B)$、$P(B \ge A)$这两项),差值的相关信息(包括平均数、方差、中位数)。基于这些量,我们就能很容易做出两组数据何者更好的
AB测试的一个动态推广
示例可以参考这里: https://github.com/threecifan...
AB测试的时候,我们很想很快知道测试结果,因为某些测试本身可能会影响到功能、收入等情况,这时候,我们就需要知道何时终止测试。而上面的贝叶斯的方案,可以给我们提供一个解决这一问题的发想。
首先,我们要把AB测试的数据结果看成两(或多)组时间序列数据,在快速测试时,我们可能使用按分钟的维度来监控这两组数据。譬如我们现在要测试两个按钮设计的点击率,则需要统计每一分组内,每个按钮点击次数和未点击次数,譬如生成下列数据,一个是0.62概率一个0.65,我们生成100个时间段的数据,且故意让两个数据很接近而且抽样非常不平均。
import numpy as np
from scipy.stats import binom, beta
from plotnine import *
from itertools import accumulate
import pandas as pd
n1, p1 = 10000, 0.62
n2, p2 = 10000, 0.65
seed_a = binom.rvs(1, p1, size=2000)
seed_b = binom.rvs(1, p2, size=10000)
np.random.shuffle(a)
np.random.shuffle(b)
a = [(sum(i==1), sum(i==0)) for i in np.array_split(seed_a, 100)]
b = [(sum(i==1), sum(i==0)) for i in np.array_split(seed_b, 100)]
然后,我们主要通过$max(P(p_a > p_b), P(p_a \le p_b))$,来判断是不是具有显著的差异。显然,当这个值越接近于1,表示这抽样数据里面两个组的数据有差别的可能性越大,我们就可以基于此来判断是否可以终止实验。核心的抽样算法实现如下,其中beta.rvs(a, b, size=n)
就表示生成n
个beta分布的数据。然后我们可以应用贝叶斯的方法,很快地随着每批数据进来快速地产生新的后验概率并基于此抽样。
def gennerate_avg(n=100000):
def helper(row):
res = np.sum(
beta.rvs(row['a_x'] + 1, row['a_y'] + 1, size=n) >
beta.rvs(row['b_x'] + 1, row['b_y'] + 1, size=n)
) / n
return res if res >= 1 - res else 1 - res
return helper
最后,我们就可以实时地展示两幅图,来动态判断是不是有把握可以终止实验,做出判断了。第一幅是两个点击率随数据进来后,渐渐趋于稳定的比较图,这张图主要展示,此时a、b组点击率的变化。只有当a、b组点击率不发生明显波动时,我们才能做出判断。这个是防止我们过早地终止实验。其次,这张图也一定程度直观告诉我们,两个点击率哪个大哪个小。
第二幅图则是$$max(P(p_a > p_b), P(p_a \le p_b))$$的演化图,它衡量的是两组数具有差异的可能性。我们可以添加我们能容忍错误的可能性,比如下图的绿线表示我们的容忍底线0.999,即我们保证抽样的99.9%的数据都显示出有一组大于另一组的差异时,我们就可以终止实验了。比如次例子中,我们大致在第60分钟时,就可以终止实验,而不需要积累大量数据。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。