前言
為了對比不同策略的效果,如新策略點擊率的提升是否顯著,常需要進行A/B測試。但測試是有成本的,樣本量小時不能判斷出差異是否是由抽樣誤差引起,樣本量太大時如果效果不好則會造成難以挽回的損失。如何科學地選擇樣本量呢?需要了解A/B測試的統計學原理
一、 A/B測試的統計學原理
(一)大數定律和中心極限定理
A/B 測試樣本量的選取基於大數定律和中心極限定理。通俗地講:
1. 大數定律:當試驗條件不變時,隨機試驗重復多次以后,隨機事件的頻率近似等於隨機事件的概率。
2. 中心極限定理:對獨立同分布且有相同期望和方差的n個隨機變量,當樣本量很大時,隨機變量
近似服從標准正態分布N(0,1)。
根據大數定律和中心極限定理,當樣本量較大(大於30)時,可以通過Z檢驗(也稱U檢驗,是為了檢驗在零假設情況下測試數據能否可以接近正態分布的一種統計測試)來檢驗測試組和對照組兩個樣本均值差異的顯著性。
注:樣本量小於30時,可進行t檢驗。
(二)假設檢驗
在進行假設檢驗時,我們有兩個假設:原假設H0(兩個樣本沒有顯著性差異)和備擇假設H1(兩個樣本有顯著性差異)。相應地,我們可能會犯兩類錯誤:
第I類錯誤:H0為真,H1為假時,拒絕H0,犯第I類錯誤(即錯誤地拒絕H0)的概率記為alpha。
第II類錯誤:H0為假,H1為真時,接受H0,犯第II類錯誤(即錯誤地接受H0)的概率記為beta。
1. 犯第I類錯誤的概率alpha與置信水平1-alpha
通常,將犯第I類錯誤的概覽alpha(0.05)稱為顯著性,把沒有1-alpha(0.95)稱為置信水平,即有1-alpha的概率正確接受了H0。
一般,alpha取值為0.05或更小的數值,即容忍犯第I類錯誤的概率最大為alpha。
2. 犯第II類錯誤的概率beta與統計功效power=1-beta
通常,將犯第II類錯誤的概率稱為beta;將1-beta稱為統計功效,即正確拒絕H0的概率。
一般,beta取10%~20%,則統計功效的取值為80%~90%。
犯第一類錯誤的概覽alpha與犯第二類錯誤的概覽beta之間的關系如下圖:
3. 統計顯著性p-value
當p-value<alpha時,即原假設成立的概率小於預設的顯著性水平,可拒絕原假設。p-value只說明兩個樣本有沒有顯著性差異,並不說明差異的大小。
根據統計學原理計算樣本量,需要根據顯著性水平查正態分布表,工作中用到的比較少,這里省略。
工作中可用python中的已有的包和函數計算。
二、樣本量計算的python實現
Python統計包statsmodels.stats.power中,有一個NormalIndPower工具,可以用其中的solve_power函數實現。
Solve_power函數中的參數如下:
(1)參數effect_size : 兩個樣本均值之差/ 標准差 (原來樣本值*(1-原來樣本值))的開方
(2)nobs1:樣本1的樣本量,樣本2的樣本量=樣本1的樣本量*ratio
(3)alpha:顯著性水平,一般取0.05
(4)power:統計功效,一般去0.8
(5)ratio: 樣本2的樣本量/樣本1的樣本量,一般取1
(6)alternative:字符串str類型,默認為‘two-sided’,也可以為單邊檢驗:’larger’ 或’small’
例:目前的點擊率CTR是0.3,我們要想提升10%,將點擊率提升到0.33,測試組和對照組的樣本量相同。
計算如下:
from statsmodels.stats.power import NormalIndPower import math effect_size = 0.03/math.sqrt(0.3*(1-0.3)) ztest = NormalIndPower() num = ztest.solve_power( effect_size = effect_size, nobs1 = None, alpha = 0.05, power= 0.8, ratio=1, alternative = 'two-sided') print (num)
3662.8015711721328
注意,得到的值是總的樣本數量,每組還得除以組數;檢測效果變化值越小,需要的樣本量越大;檢測效果變化值越大,需要的樣本量越小。因為,變化效果越小,越有可能是抽樣誤差引起的;為了避免抽樣誤差的影響,需要增大樣本量。
https://abtestguide.com/abtestsize/
使用Z檢驗需要知道方差,但是我們一般都是不知道方差的,因此下面還可以只用卡方檢驗
三、卡方檢驗
- Z檢驗,即檢驗實驗組與對照組服從分布的均值是否相等
- 卡方檢驗,即檢驗實驗組是否服從理論分布(將對照組看成理論分布)
求解過程python
from statsmodels.stats.power import GofChisquarePower GofChisquarePower().solve_power( effect_size=None, nobs=None, alpha=None, power=None, n_bins=2, )