A/Bテストの統計的仮説検定と必要データ数

# 概要
自分の復習のために、A/Bテストの効果を統計的仮説検定する方法を記述する。
また、A/Bテストに必要なデータ数を計算するための方法を示す。
検算と理解の促進のために、随所でPythonによるシミュレーションを行い、導出した式の精度を確認する。

最後に、[AdobeのSample Size Calculator](https://docs.adobe.com/content/target-microsite/testcalculator.html)を再現することで、結果の検証を行う。
# 前提
サイトX, Yの訪問者の広告のクリック率$p_1, p_2$に有意差があるかどうか検証したい。
訪問者が広告をクリックしたときに1, しなかったときに0を取る数列を$\{x_i\}, \{y_i\}$とする。
それぞれの平均$\bar{x}, \bar{y}$は、データ数が増えれば増えるほど$p_1, p_2$に近づくため、差分$|\bar{x}-\bar{y}|$が大きければクリック率$p_1, p_2$の間に有意差があるといえるが、それを統計的に確かめたい。



今、

帰無仮説 $H_0: p_1=p_2$
対立仮説 $H_1: p_1 \neq p_2$

とし、帰無仮説を棄却することで対立仮説が成り立つ、すなわち二つのサイトのクリック率に有意差があることを示す。

# 帰無仮説の棄却

トータルクリック数$\sum{x_i}, \sum{y_i}$はサンプルサイズが大きくなると正規分布で近似できることが知られている。よって

$\sum x_i \sim N\big(n_1 p_1, n_1p_1(1-p_1)\big)$
$\sum y_i \sim N\big(n_2 p_2, n_2p_2(1-p_2)\big)$

が近似的に成り立つ(ただし、$n_1, n_2$は$\{x_i\}, \{y_i\}$のサイズ、すなわちサイトX,Yの訪問者数である)。

これより、

$\bar{x} \sim N \big(p_1, \frac{p_1(1-p_1)}{n_1} \big)$
$\bar{y} \sim N \big(p_2, \frac{p_2(1-p_2)}{n_2}\big)$

が成り立つ。よって、正規分布の性質から、

$\bar{x} - \bar{y} \sim N(p_1 - p_2, \frac{p_1(1-p_1)}{n_1} + \frac{p_2(1-p_2)}{n_2})$

が成り立つ。これを前提に検定統計量$u$を計算する。

$u = (\bar{x} - \bar{y}) \quad \big/ \quad \sqrt{\frac{p_1(1-p_1)}{n_1} + \frac{p_2(1-p_2)}{n_2}}$

この式には未知数$p_1, p_2$が含まれているため、$\bar{x}, \bar{y}$で近似する。よって、

$u = (\bar{x} - \bar{y}) \quad \big/ \quad \sqrt{\frac{\bar{x}(1-\bar{x})}{n_1} + \frac{\bar{y}(1-\bar{y})}{n_2}}$

となる。この$u$は正規分布

$u \sim N\big( (p_1 - p_2) \big/ \sqrt{\frac{p_1(1-p_1)}{n_1} + \frac{p_2(1-p_2)}{n_2}}, 1^2 \big)$

に従う。



今、帰無仮説$H_0$が成り立つと仮定する。このとき、検定統計量$u$は

$u \sim N(0, 1^2)$

に従う。よって、有意水準が5%のとき、検定統計量$u$が標準正規分布の95%区間に入っていなければ仮説$H_0$を棄却すればよい。

すなわち、

$u > Z_{0.975} \quad or \quad u < Z_{0.025}$

のとき、$u$を棄却する。ここでは$Z_{p}$を標準正規分布で累積確率が$p$となるような点とした。



# 実装と検証

X, Yを0, 1列として外部から与えるとき、上記の検定は以下のように実装できる。

```python
def test(X, Y, alpha):
    p1_ = X.mean()
    p2_ = Y.mean()
    u = (p1_ - p2_) / np.sqrt(p1_*(1-p1_)/n1 + p2_*(1-p2_)/n2)
    # 検定統計量が従う分布
    lower, upper = sp.stats.norm.interval(alpha=1-alpha)

    if u < lower or u > upper:
        print("有意差あり")
        return True
    else:
        print("有意差なし")
        return False
```

この検定の妥当性を検証する。これは帰無仮説$H_0: p_1=p_2$が成り立つときに、棄却率が$\alpha=0.05$となることを検証すればよい。

今、$p_1, p_2$がともに0.08のときに、それぞれ訪問者数が$4000, 5000$となるシチュエーションを10万回シミュレーションし、棄却率を計算する。

```python
p1, p2 = 0.08, 0.08
n1, n2 = 4000, 5000
res = []
for i in range(100000):
    # サンプル生成
    X = sp.stats.bernoulli.rvs(size=n1, p=p1)
    Y = sp.stats.bernoulli.rvs(size=n2, p=p2)
    res.append(test(X, Y, alpha=0.05))

print(f"棄却率: {np.mean(res)}")
```

この実行結果は棄却率: 0.05024となり、おおむね検定が正しそうなことが確認できた。

# 検出力
今、$H_0$が成り立つ際の棄却率$\alpha$は0.05となった。だが、$H_1$が成り立つ際の棄却率はどうだろうか？
上のプログラムを用いて、$p_1, p_2=0.08, 0.10$で$n_1, n_2=400, 400$のとき、すなわち$H_1$が成り立つ際の棄却率を確認しよう。
実際に実行すると棄却率: 0.16753　となった。つまり、$H_1$が成り立つにも関わらず検定によって、有意差が検出できない確率が高いことがわかる(わずか16.8%でしか検出できない)。

この$H_1$が成り立つ際の棄却率は検出力と呼ばれ、$1-\beta$と表記される($\beta$は第二種の過誤率)。
検出力はデータ数が増えるにつれて向上していく。検出力が低い状態でA/Bテストを行い、有意差がないと判断してしまうと、本当は改善が見られた際の有意差を見逃してしまう。よって、この検出力を計算して知りたい。



検定統計量$u$は正規分布

$u \sim N\big( (p_1 - p_2) \big/ \sqrt{\frac{p_1(1-p_1)}{n_1} + \frac{p_2(1-p_2)}{n_2}}, 1^2 \big)$

に従うことを思い出して欲しい。検定では、

$u > Z_{0.975} \quad or \quad u < Z_{0.025}$

のとき、$u$を棄却していた。$H_1$が成り立つ状況下で、この棄却率を計算すればよい。これが検出力となる。

$H_0$が成り立つ状況下では$p_1, p_2$が未知でも、棄却率は計算できた。しかし、$H_1$が成り立つ状況では$p_1, p_2$の値が必要となる。サイトのA/Bテストであれば、$p_1$は既存のサイトのクリック率として手持ちのデータから計算できることが多い。一方で$p_2$は新サイトの訪問率にあたるので完全に未知である。これは、もう期待する訪問率を適当に置くしかない。

# 実装と検証

簡単に実装できる。

```python
def power(p1, p2, n1, n2, alpha):
    u = (p1 - p2) / np.sqrt(p1*(1-p1)/n1+p2*(1-p2)/n2)
    model = sp.stats.norm(loc=u)
    lower, upper = sp.stats.norm.interval(alpha=1-alpha)
    power = 1 - (model.cdf(upper) - model.cdf(lower))
    print (f"検出力: {power}")
    return power
```

$p_1, p_2=0.08, 0.10$、そして$n_1, n_2=400, 400$としてこのpowerを呼び出すと、

```
検出力: 0.16736
```

と出力された。これは、検出力の節で得られた棄却率0.16753と近い値となっており、それなりに正確そうだ。

# 必要なサンプルサイズ

A/Bテストの必要サンプルサイズを見積もるときは、検出力が求める基準に達するサイズを用いる。検出力の基準は0.8が使われることが多い。

上で示した検出力のプログラムは軽いので、何度かpowerを実行し必要なサンプルサイズを求めることとする。なお、A/Bテストでは、それぞれのサイトにほぼ同数のアクセスを流せるため、$n_1=n_2$とする。



今、既存のサイトのクリック率が8%, 新規が10%とすると以下のようなプログラムで必要サンプルサイズが求められる。

```python
lower_n, upper_n = 10, 100000
alpha = 0.05
target_power = 0.8
p1 = 0.08
p2 = 0.10

while True:
    n = int((lower_n + upper_n) / 2)
    p = power(p1, p2, n, n, alpha)
    if p > target_power:
        upper_n = n
    else:
        lower_n = n
    if (upper_n - lower_n) <= 1:
        break

print (f"必要サンプルサイズ: {upper_n}")
```

この出力結果は3211となる。[Adobe Sample Size Calculator](https://docs.adobe.com/content/target-microsite/testcalculator.html)は3210となるため、ほぼ一致することがわかる。なお、ここでのサンプルサイズは**1サイトあたり**であり、A/Bテストをするには２倍のサンプルが必要である。



# 補足

検定力を求める際に、$p_1,p_2=0.08, 0.10$としたが、帰無仮説が棄却された際に、$p_2=0.10$が立証されるわけではないことに注意が必要である。あくまで、有意差があったに過ぎない。もし、サイトXがサイトYより、25%以上のクリック率を持つことを検定したいのであれば、

帰無仮説 $H_0: 1.25 p_1 = p_2$
対立仮説 $H_1:1.25p_1 > p_2$

とするような仮説検定が必要である。



参考資料

- [統計学入門](https://www.amazon.co.jp/%E7%B5%B1%E8%A8%88%E5%AD%A6%E5%85%A5%E9%96%80-%E5%9F%BA%E7%A4%8E%E7%B5%B1%E8%A8%88%E5%AD%A6%E2%85%A0-%E6%9D%B1%E4%BA%AC%E5%A4%A7%E5%AD%A6%E6%95%99%E9%A4%8A%E5%AD%A6%E9%83%A8%E7%B5%B1%E8%A8%88%E5%AD%A6%E6%95%99%E5%AE%A4/dp/4130420658)
- [サンプルサイズの決め方](https://www.amazon.co.jp/%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%AE%E6%B1%BA%E3%82%81%E6%96%B9-%E7%B5%B1%E8%A8%88%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%83%BC-%E6%B0%B8%E7%94%B0-%E9%9D%96/dp/4254126654/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=16V3TLKWDBDMY&dchild=1&keywords=%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%AE%E6%B1%BA%E3%82%81%E6%96%B9&qid=1610903925&s=books&sprefix=sanpuru%2Cstripbooks%2C259&sr=1-1)