AAテストのための統計ノート

前回、[ABテストのための統計ノート](https://qiita.com/birdwatcher/items/8887afabe73513fbf264)を公開しました。
今回はABテストをやる前に行うAAテストの話です。（ABテスト後に使える知識もあります）
ABテストでは、2つのグループに異なる施策を行いグループ間に差があるかを見ますが、AAテストでは同じ施策を行った集団を2つに分けて差が無いことを確認します。
「ランダムに分けたから大丈夫だろう」という過信は禁物です。

このページでは、広告表示のアルゴリズムの変更を例に説明します。
**ストーリー**
あなたは、広告表示のアルゴリズム変更してクリック率に効果があるかどうかを、これからABテストで確認しようとしています。
ただ、不安になったあなたはABテスト前にAAテストを実施することにしました。

- [確認用notebook](https://github.com/birdwatcherYT/AAtest/blob/main/AAtest.ipynb)

# AAテスト
既にユーザーに提供済みの施策であれば、過去に蓄積したログデータを使ってAAテストを行うことができます。
実際にABテストを行うときを想定してAAテストを行います。
事前にグループの分け方と評価指標を設定しておきます。

**グループの分け方**
たとえば、
- ページがリロードされるたびにランダムにアルゴリズムが切り替える
- ユーザーごとに使うアルゴリズムを決めておく

などです。

**評価指標**
たとえば、
- クリック率 = クリック数 / 表示回数
- 平均コンバージョン数

などです。

## やり方

1. 以下を1000回繰り返し、p値を1000個生成
    1. データをランダムに2つに分割する
        - 例: ユーザーごとにランダム
    2. 2群間でABテストで行う予定の検定を行いp値を記録する
        - 例: クリック率に差があるかどうかの検定
2. p値の分布が一様分布になっているか確認する

一様分布であれば、その分割方法と検定方法で問題ないため、p値が1に近い分割方法を採用する。
一様分布でない場合、何かがおかしいので調査する必要がある。

<table>
<tr><td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294661/f608f2c0-409e-2933-ff00-2292d5c2972c.png" width=300></td>
<td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294661/0def883c-34c1-2c0b-4a15-9ea174d740cc.png" width=300>
</td>
</tr>
<tr><td style="text-align:center;">失敗例</td><td style="text-align:center;">成功例</td></tr>
</table>


たとえば、

- 外れ値がある
- ランダム化単位と分析単位が異なる

などが考えられる。

ランダム化単位と分析単位が異なる場合、相関のあるサンプリングになってしまい、分散を正しく評価することができず、AAテストに失敗します。

具体例:

- ランダム化単位: ユーザー
- 分析単位: インプレッション（表示）

ランダム化単位と分析単位を合わせられるなら合わせるのがシンプルです。
単位を合わせられない場合は、対策として正しい分散を推定してあげる必要があります。
ちなみに、先程のヒストグラムの失敗例と成功例は、それぞれ間違った分散を使った場合と正しい分散を使った場合の例です。
間違った分散を使ってしまうと、ランダムに切ってもほぼ毎回有意差があると判断されてしまいます。

**参考文献**
- [非劣性検定を用いたA/Aテストの方法](https://note.com/dd_techblog/n/n9a4e99b73da2)
- [なぜAAテストにおけるp値は一様分布になるのか？](https://zenn.dev/morishita/articles/3c77d00b303c76)

# ランダム化単位と分析単位が異なる場合
以下のどちらかの手法で正しい分散を推定できます。
- ブートストラップ
- デルタ法

この分散を使って検定にかけることで正しい検定を行えます。

以降では、クリック率の例で、ランダム化単位をユーザー、分析単位を表示として説明する。

## ブートストラップで差の分散を推定する
以下のようなログテーブルがある状況で、ブートストラップのやり方を説明します。

| ユーザーID | クリックの有無 |
| --- | --- |
| 0000 | 1 |
| 0000 | 0 |
| … |  |
| 0312 | 1 |

1. ランダム化単位で集計したテーブルを作成する
    
    
    | ユーザーID | クリック回数 | 表示回数 |
    | --- | --- | --- |
    | 0000 | 5 | 7 |
    | 0001 | 10 | 32 |
    | … |  |  |
    | 9999 | 0 | 12 |
2. 2つのグループに分ける
    
    
    | ユーザーID | クリック回数 | 表示回数 | グループ |
    | --- | --- | --- | --- |
    | 0000 | 5 | 7 | A |
    | 0001 | 10 | 32 | B |
    | … |  |  |  |
    | 9999 | 0 | 12 | A |
3. 任意の回数以下の処理を行う（100回とか1000回とか）
    1. A群から、サイズ$|A|$（ユーザー数）で**復元抽出**したブートストラップ平均を求める
        $\displaystyle ブートストラップ平均 = \frac{サンプリングしたデータの合計クリック回数}{サンプリングしたデータの合計表示回数}$

    2. B群も同様にブートストラップ平均を求める
    3. $差 = A群のブートストラップ平均 - B群のブートストラップ$ を保存
4. `3.3.`で保存した差のデータについて分散$\hat \sigma_{\rm bootstrap}^2$を求める
5. 検定
    ウェルチのt検定の式は、
    
    $$
    \displaystyle
    t=\frac{\bar x_A-\bar x_B}{\sqrt{s_A^2/n_A+s_B^2/n_B}}
    $$
    
    であったが、
    
    $$
    \displaystyle
    t=\frac{\bar x_A-\bar x_B}{\sqrt{\hat\sigma_{\rm bootstrap}^2}}
    $$
    
    分母のsqrt内を、推定した分散$\hat \sigma_{\rm bootstrap}^2$で置き換えて使う。
    
    - $\bar x_A= A群のクリック合計 / A群の表示合計$

**注意点**
- 復元抽出なので、replace=Trueで行う
- サイズ$|A|$は、ランダム化単位で集計後のレコード数（今回はユーザー数）

**余談**
3.のブートストラップサンプルはランダム化単位ごとにサンプリングしないといけないので、1.のように事前にランダム化単位でテーブルを作っておくことをおすすめする。（これに気づかず時間溶かした）


**参考文献**
- [なぜAAテストにおけるp値は一様分布になるのか？](https://zenn.dev/morishita/articles/3c77d00b303c76)

## デルタ法で分散を推定する

デルタ法は確率変数の式をテイラー展開で一次近似する方法です。

<details><summary>説明</summary>
任意の2変数関数$f$について、平均値$(\mu_X,\mu_Y)$まわりで一次近似。
$$
f(X, Y) \approx f(\mu_X, \mu_Y)
+f_X'(\mu_X, \mu_Y)(X-\mu_X) +f_Y'(\mu_X, \mu_Y)(Y-\mu_Y)
$$
分散$\mathrm{Var}[f(X,Y)]$を計算する。

```math
\begin{align*}
\mathrm{Var}[f(X,Y)]
&\approx\mathrm{Var}[f(\mu_X,\mu_Y)+f_X'(\mu_X,\mu_Y)(X-\mu_X)+f_Y'(\mu_X,\mu_Y)(Y-\mu_Y)] \\
&=f_X'(\mu_X, \mu_Y)^2\mathrm{Var}[X] +f_Y'(\mu_X, \mu_Y)^2\mathrm{Var}[Y]+2f_X'(\mu_X,\mu_Y)f_Y'(\mu_X,\mu_Y)\mathrm{Cov}(X,Y)
\end{align*}
```

これが分散を求める一般式になる。
</details>

ユーザー単位でランダム化するため、求めるべきクリック率の分散は、$\mathrm{Var}[\bar X/\bar Y]$です。

```math
\begin{align*}
\mathrm{Var}\frac{\bar X}{\bar Y} &\approx \frac{1}{E[\bar Y]^2} \mathrm{Var}\bar X+\frac{E[\bar X]^2}{E[\bar Y]^4}\mathrm{Var}\bar Y-2\frac{E[\bar X]}{E[\bar Y]^3}\mathrm{cov}(\bar X,\bar Y)
\\
&=\frac{1}{n}\underbrace{\left[ \frac{1}{E[Y]^2} \mathrm{Var} X+\frac{E[X]^2}{E[Y]^4}\mathrm{Var} Y-2\frac{E[X]}{E[ Y]^3}\mathrm{cov}( X, Y)\right]}_{=: \hat \sigma^2}\\
&=\hat \sigma^2/n
\end{align*}
```

<details><summary>式変形</summary>

- $f(X,Y)=X/Y, f_X'(X,Y)=1/Y, f_Y'(X,Y)=-X/Y^2$
- $\displaystyle \mathrm{Var} \bar X= \mathrm{Var}[\frac{1}{n}\sum_iX_i]= \frac{1}{n^2}\mathrm{Var}[\sum_iX_i]= \frac{1}{n^2}\sum_i\mathrm{Var}[X_i]=\frac{1}{n}\mathrm{Var} X$
    - $X_i$は互いに無相関であるから和をそのまま外に出せる
- $\displaystyle E [\bar X]=E[\frac{1}{n}\sum_i X_i]=\frac{1}{n}\sum_i E[X_i]=E[X]$
- $\displaystyle \mathrm{Cov} (\bar X, \bar Y) 
=\mathrm{Cov} (\frac{1}{n} \sum_iX_i, \frac{1}{n} \sum_iY_i) 
=\frac{1}{n^2}\mathrm{Cov} ( \sum_iX_i, \sum_iY_i) 
=\frac{1}{n^2}\sum_i\sum_j\mathrm{Cov} ( X_i, Y_j) =\frac{1}{n^2}\sum_i\mathrm{Cov} ( X_i, Y_i) 
 =\frac{1}{n}\mathrm{Cov} ( X, Y)$
    - $i\ne j$のとき無相関

以上の式変形を用いると、

$$
\mathrm{Var}\frac{\bar X}{\bar Y} \approx\frac{1}{n}\left[ \frac{1}{E[Y]^2} \mathrm{Var} X+\frac{E[X]^2}{E[Y]^4}\mathrm{Var} Y-2\frac{E[X]}{E[ Y]^3}\mathrm{cov}( X, Y)\right]
$$
</details>

ただし、
- $\bar X$: 1ユーザーの平均クリック回数
- $\bar Y$: 1ユーザーの平均表示回数
- $n$: ユーザー数

です。

各群について、$\mathrm{Var}[\bar X/\bar Y]$ を計算することで、分散を推定できる。

ウェルチのt検定の式は、

$$
\displaystyle
t=\frac{\bar x_A-\bar x_B}{\sqrt{s_A^2/n_A+s_B^2/n_B}}
$$

であったが、

$$
t=\frac{\bar x_A-\bar x_B}{\sqrt{\mathrm{Var}[\bar X_A / \bar Y_A]+\mathrm{Var}[\bar X_B / \bar Y_B]}}
$$

$s_A^2/n_A, s_B^2/n_B$を推定したことになるため、$\mathrm{Var}[\bar X_A/\bar Y_A],\mathrm{Var}[\bar X_B/\bar Y_B]$に置き換えて検定すればよい。

- $\bar x_A: A群のクリック合計 / A群の表示合計$

**補足**
$s^2$を$\hat \sigma^2$で置き換えているだけなため、[必要なサンプルサイズの式](https://qiita.com/birdwatcher/items/8887afabe73513fbf264)がそのまま使える。
ただし、ここで推定されるサンプル数はユーザー数になることに注意。
また、デルタ法で推定した分散とブートストラップで推定した分散の値は一致するため、デバッグとして確認しておくとよい。

ブートストラップの差の分散の結果を用いてサンプル数を求める場合は、各群の分散$s_A^2,s_B^2$を求めるために、
$$
s_A^2/n_A+s_B^2/n_B=\sigma_{\rm bootstrap}^2
$$で、$s^2:=s_A^2=s_B^2$と仮定して、$$
s^2=\frac{\sigma_{\rm bootstrap}^2}{1/n_A+1/n_B}
$$を各群の分散として、[必要なサンプルサイズの式](https://qiita.com/birdwatcher/items/8887afabe73513fbf264)に使えばよいはず。（たぶん）

**参考文献**
- [確率変数の比の分布における平均と分散をデルタ法で求める](https://www.szdrblog.info/entry/2018/11/18/154952)
- [検索エンジンのABテストで発生するユーザー内相関を突破する
](https://www.m3tech.blog/entry/search-ab)
- [【A/Bテスト】誤差の伝搬を理解する【デルタ法、bootstrap法】](https://qiita.com/tetsuro731/items/fab033c97cf230527bce)
- [All about Sample-Size Calculations for A/B Testing: Novel Extensions & Practical Guide](https://arxiv.org/pdf/2305.16459.pdf)
- A/Bテスト実践ガイド 18章

# まとめ
- AAテストでランダムに切ったときのp値の分布の一様性を確認
- ランダム化単位と分析単位が異なる場合は分散の推定が必要
    - ブートストラップ
        - 利点: 式変形が不要
        - 欠点: 計算時間がかかる
    - デルタ法
        - 利点: 計算が早い
        - 欠点: 式の導出が必要