A/Bテストにおける「効果量」の非対称性

ab-testing
Author

statditto

Published

August 10, 2024

はじめに

A/Bテストのサンプルサイズ設計を行う際に必要となる「効果量」についてのお話です。両側検定を行うときに、ちょっとだけ気にしておいた方がいいかもしれないことを発見したので、備忘録として書いておきます。解釈に誤りを含んでいそうなので、やさしい鉞をお待ちしています。

library(tidyverse)
library(pwr)

具体例

T群がC群を有意に上回る例

まずは例として、母比率の差の検定について考えます。まずは次の設定のもとでサンプルサイズ設計を行う事を考えてみましょう。C群とT群のイベント発生率をそれぞれ\(p_1, p_2\)とおいてテスト設計を行います。検出力(\(\beta\))や有意水準(\(\alpha\))などのパラメータは以下の通りとします。

\[ \begin{aligned} p_1 &= 0.1\\ p_2 &= 0.2\\ 1 - \beta &= 0.8\\ \alpha &= 0.05 \end{aligned} \]

\(p_2-p_1=0.1\)なので、10%ptの効果を期待しているテストということになります。これらの条件下で、母比率の差の検定(両側検定)のサンプルサイズ設計を行ってみます。

power.prop.test(p1 = 0.1, p2 = 0.2, power = 0.8)

     Two-sample comparison of proportions power calculation 

              n = 198.9634
             p1 = 0.1
             p2 = 0.2
      sig.level = 0.05
          power = 0.8
    alternative = two.sided

NOTE: n is number in *each* group

実行結果から、各群で199例程度必要であることがわかります。実際にA/Bテストを行った結果、次のようなデータが得られたとします。

test1 <- data.frame(group = c("C", "T"),
           impression = c(199,199),
           click = c(20,38))
test1
  group impression click
1     C        199    20
2     T        199    38

得られたデータを用いて検定してみましょう。

test1 %>% 
  select(impression, click) %>% 
  as.matrix() %>% 
  prop.test()

    2-sample test for equality of proportions with continuity correction

data:  .
X-squared = 4.2814, df = 1, p-value = 0.03853
alternative hypothesis: two.sided
95 percent confidence interval:
 0.00430651 0.13372019
sample estimates:
   prop 1    prop 2 
0.9086758 0.8396624 

T群がC群を有意に上回るという結果が出ました。事前の設計にもとづいて「T群はC群と比較して10%pt程度の効果が認められた」のような解釈ができると思います。ここまではよくある検定の例ですね。

T群がC群を有意に下回る例

では、A/Bテストで次のような結果が得られた場合はどうでしょうか。

test2 <- data.frame(group = c("C", "T"),
           impression = c(199,199),
           click = c(20,8))
test2
  group impression click
1     C        199    20
2     T        199     8

この新しいデータを用いて検定してみましょう。

test2 %>% 
  select(impression, click) %>% 
  as.matrix() %>% 
  prop.test()

    2-sample test for equality of proportions with continuity correction

data:  .
X-squared = 3.9891, df = 1, p-value = 0.0458
alternative hypothesis: two.sided
95 percent confidence interval:
 -0.103690718 -0.001662998
sample estimates:
   prop 1    prop 2 
0.9086758 0.9613527 

T群がC群を有意に下回るという結果が出ました。この場合はどのような解釈になるのでしょうか。「T群はC群と比較して-10%pt程度の効果が認められた」と言っていいのでしょうか。この解釈では\(p_2=0.0\)という主張になってしまうのでちょっと怪しそうですね。単純に効果を対称にとってしまうと、負の確率が出てきてしまうことがあるため、ちょっとではなく怪しいです。

T群がC群を有意に下回った時の解釈

この検定で何を検定したのか、もう一度考え直してみましょう。設計の時点では、T群がC群を上回る方向の効果を見込んでテスト設計を行っていました。下回る方向の効果については一切考慮していません。そこで、サンプルサイズ、有意水準、検出力、C群のイベント発生率を固定したもとで、T群がC群を下回る方向のイベント発生率を計算してみます。関数の仕様上、コード上では\(p_1, p_2\)が逆転していることに注意してください。

power.prop.test(n = 198.9634, p2 = 0.1, power = 0.8)

     Two-sample comparison of proportions power calculation 

              n = 198.9634
             p1 = 0.03074435
             p2 = 0.1
      sig.level = 0.05
          power = 0.8
    alternative = two.sided

NOTE: n is number in *each* group

T群のイベント発生率が0.03という結果となりました。つまり、先ほどの下回る方向に有意だった結果は「T群はC群と比較して-7%pt程度の効果が認められた」という解釈ができることがわかります。 つまり、今回行った母比率の差の検定(両側検定)では、上回る方向には10%pt、下回る方向に7%ptの効果を仮定して検定を行っていたことになります

「効果量」の定義

具体例でぼかしていた「効果量」の定義についてです。先ほどまでは割合の差\(p_2-p_1\)のことを効果と呼んでいましたが、一般に「効果量」は標準化されたものを指すことが多いようです。今回のような割合の差では、Cohen’s h1が使われることがあります。Cohen’s hは、比率を逆正弦変換すると近似的に分散が均一になるという性質を利用した効果量の尺度です。効果量を同じスケールで議論できるようになるというメリットがあります。具体例で計算した上側と下側の二つの効果について、Cohen’s hを計算してみましょう。

ES.h(0.1,0.2)
[1] -0.2837941
ES.h(0.1,0.03)
[1] 0.2953351

おおむね大きさが一致することがわかりました。上側と下側で比率の差自体は異なりましたが、「効果量」はおおむね一致していたことがわかります。

「効果量」の非対称性

上側と下側の効果の大きさにどんな関係があるのか、簡単な実験で調べてみることにします。基準となる\(p_1\)を0.01から0.50まで0.01刻み、上側の効果\(\delta\)を0.01から0.49まで0.01刻みで用意します。検出力(\(1-\beta=0.8\))や有意水準(\(\alpha=0.05\))は固定し、それぞれの組み合わせで上側の効果と下側の効果の大きさ(比率の差)やCohen’s hを比べてみたいと思います。

grid <- expand_grid(
  p1 = seq(0.01, 0.5, 0.01),
  delta = seq(0.01, 0.49, 0.01)
)

calculate_under <- function(p1, delta) {
  p2 <- p1 + delta
  n <- power.prop.test(p1 = p1, p2 = p2, power = 0.8)$n

  under <- tryCatch({
      power.prop.test(n = n, p2 = p1, power = 0.8)$p1
    }, warning = function(w) {
      under <- NA
    }, error = function(e) {
      under <- NA
    })

  if (!is.na(under) && under > p1) {
    under <- NA
  }
  return(under)
}

results <- grid %>%
  mutate(
    p2 = p1 + delta,
    under = map2_dbl(p1, delta, calculate_under),
    rate = (p1 - under) / delta,
    hrate = ES.h(p1,under)/ES.h(p1,p2) %>% abs()
  )

まずは(下側の効果の大きさ)/(上側の効果の大きさ)を可視化してみます。

# ヒートマップを作成
ggplot(results, aes(x = p1, y = delta, fill = rate)) +
  geom_tile() +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 1, na.value = "grey50") +
  labs(title = "下側の効果と上側の効果の比", x = "p1", y = "delta", fill = "rate") +
  theme_minimal()

基準となる\(p_1\)が小さければ小さいほど、上側の効果が大きければ大きいほどこの比率は相対的に小さくなっていくことがわかります。同様にCohen’s hの比も確認してみます。

# ヒートマップを作成
ggplot(results, aes(x = p1, y = delta, fill = hrate)) +
  geom_tile() +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 1, na.value = "grey50") +
  labs(title = "下側のCohen's hと上側のCohen's hの比", x = "p1", y = "delta", fill = "rate") +
  theme_minimal()

上側の効果が大きいときを覗いて、比がほぼ1を保っていることがわかります。母比率の差の検定(両側検定)における「効果量」は、Cohen’s hの意味では上側も下側もほぼ一致していることが分かりました。

非対称性はいつあらわれるのか

検定統計量の形に依存すると考えられます。今回の例に出した母比率の差の検定では、正規近似をして検定統計量をつくります。その検定統計量の式中にはプール分散が含まれています。その影響で比率の差に違いが生まれています。検定統計量の形をみることで、非対称性があらわれるかどうかを判別することが出来ます。例えば、平均値の差の検定では、t統計量の式から対称性があると分かります。

おわりに

普段テスト設計をするときには、片方の効果しか意識せずに行う、もしくは対称であると思い込んでしまっていることが多いのではないかと思います。少なくとも僕が調べた限りでは、これが明示されている文献はありませんでした2。 上側と下側の効果の大きさが異なることがあること自体は、当たり前といえば当たり前の話です。幸いにも誤った解釈をしたことはありませんでしたが、本来はテスト設計の時点でこれを意識すべきでした。反省しています;;A/Bテスト、なにもわからない。

実務上どう考えるべきか?という観点では、まだ自分の中で結論が出ていません3。実験結果から、上側も下側もCohen’s hの意味では同じ効果量であることは分かりました。しかし、ビジネス的な観点では10%ptと-7%ptは同じ意味合いではないことの方が多いと思います。対処法として、テスト設計時点で興味がある方向に絞り片側検定で設計する、もしくはベイジアンA/Bを利用する4という手段があるかな?と思っていますが、もし他のアイデアや考え方をお持ちの方がいればぜひ教えて頂きたいです。

Enjoy!

Footnotes

  1. wikipediaが割と詳しいですが、Cohenの出している書籍(Statistical Power Analysis for the Behavioral Sciences)の方がもっと詳しいです↩︎

  2. 知っている方がいれば教えて欲しいです。おうちにある本には載っていませんでした。↩︎

  3. 実務でそこまで細かいことを気にしなくとも良いのでは?という気もしています↩︎

  4. ベイジアンA/Bなら下回る方も所与の仮説を検証できるのでハッピー(ほんとうにそうか?↩︎