※こちらはミツモアAdvent Calendar 2024の12/10の記事です
こんにちは、ミツモアプロダクト部データグループの増田(@masudahiroto.bsky.social)です。ミツモアでは、データドリブンでプロダクトを改善するために数多くのABテストを実施しています。その際に、事前に必要なサンプルサイズを見積もり、実験期間を算出しています。
先日、ランダムなABテストではなくDID(差分の差分法)を使った場合に、有意差検定やサンプルサイズ計算をどのように進めるべきかについて考える機会がありました。本記事では、その際に考えたメモを公開します。
ABテストとDIDの違い
ABテストとは?
ABテストは、サンプルを2つの群に分けて、片方に施策を実施し、その結果を比較する手法です。施策を行う方の群をトリートメント群、行わない方の群をコントロール群と呼びます。マーケティングや製品開発など、さまざまな分野で広く活用されています。
例えば:
- バナー広告のデザインを変更してクリック率を比較。
- サービスの購入プロセスを変更してコンバージョン率を比較。
- メール配信方法を調整してリピート率を向上させる。
ここではABテストではサンプルをランダムに分割するものとして話を進めます。
DID分析とは?
DID分析(Difference In Difference: 差分の差分法)は、トリートメント群とコントロール群の介入前後の変化を比較することで、介入の純粋な効果を推定する手法です。DIDの効果は次の式で定義されます:
ここで:
- :トリートメント群の介入後のコンバージョン率。
- :トリートメント群の介入前のコンバージョン率。
- :コントロール群の介入後のコンバージョン率。
- :コントロール群の介入前のコンバージョン率。
トリートメント群における介入後と介入前の差 には介入による効果だけでなく、時系列的な自然変動が含まれています。この自然変動を補正するため、コントロール群の介入後と介入前の差 を用いてその影響を差し引き、純粋な介入による効果を算出します。
違いについて
実験に使用するサンプルをランダムに分けられる場合はABテストを使用します。一方でコントロール群とトリートメント群をランダムに割り当てることが難しい場合、サンプルの分け方に起因して直接比較が難しくなり、このような場合にDIDを使用します。
例えば、特定地域でのみテレビCMを放映するなどのケースでは、地域ごとに介入の有無が決まるためランダム割り当てが不可能です。テレビCMでは、全国に一度に放送するとコストがかかるため、一部地域でのみ放送しその効果を事前に検証することがあります。この場合、地域によりサンプルが分かれてしまうため、ランダムら割り当てではなく、効果測定にはDIDなどの手法が必要になります。
ランダムなABテストにおける検定理論とサンプルサイズ計算
検定統計量の定義
この記事では、簡単のためコンバージョン率をKPI指標としたABテストとDIDを仮定して話を進めます。(より正確には、ベルヌーイ分布の期待値をKPI指標とする場合を仮定します。)
ABテストでは、コントロール群(A)とトリートメント群(B)の効果を比較します。それぞれの真のコンバージョン率を , とします。それに対し、実際にABテストを行い観測されるコンバージョン率を , とします。
, はサンプル平均として以下の正規分布に従います(中心極限定理より近似)。ここで , はそれぞれ各群のサンプル数です。
これらが正規分布に近似されることを利用し、統計量 を下記のように定義します。
帰無仮説(施策の効果なし、すなわち )の下では、 は標準正規分布 に従います。この性質を利用して統計検定を行うことができます。つまり、 の値を実際に観測値より計算し、標準正規分布から有意水準を超えて外れた部分にあるのであれば、帰無仮説を否定し、対立仮説(施策の効果あり、すなわち)を採用します。計算の分母には, がありますが、実際にはこれらの値は分からないので、観測値の , を代用して計算します。
サンプルサイズの計算
検定の設計では、以下の要素を考慮してサンプルサイズを計算します:
- 期待効果 ( , ): 期待できる効果。過去のデータの集計や施策として見込まれる効果をもとに決定する。 とする。
- 有意水準 ( ):帰無仮説を棄却する基準(通常0.05)。
- 検出力 ( ):有意差を検出する確率(通常0.80)。
この仮説のもとで、統計量 は下記の分布に従います。
はこの分布に従いますが、これを帰無仮説の通り に従うとして検定が行われます。そして、有意水準を超えると棄却されます。そのため、有意水準 の閾値を超え、検出力 を満たすためには、次の条件を満たす必要があります。
ここで、は標準正規分布 のパーセンタイルを表しています。両群のサンプルサイズが等しい ( )と仮定すると、上式を変形して必要なサンプルサイズが求まります。
DID分析における検定理論とサンプルサイズ計算
DIDの仮定
DIDが有効に機能するためには、次の仮定が必要です:
- 平行トレンド仮定:コントロール群とトリートメント群が介入前に同じトレンド(時系列的な変化)を持つこと。この仮定により、コントロール群の介入後と介入前の変化が、トリートメント群の介入前後の変化の「自然なトレンド」を表すとみなします。
- その他の外部条件の均一性:外的要因(例:季節性や経済環境の変動)がコントロール群とトリートメント群の両方に均等に作用すること。
以降の有意差検定やサンプル数計算は、この前提のもと算出しています。ですが、実際はこの仮定が完全に真となることはないです。この仮定の「成り立たなさ」に起因する観測のブレを考慮できていないので、ここで行っているDIDにおける有意差検定やサンプル数の計算はあまり意味のあるものではないかもしれません。これが、DIDの文脈であまり検定などの話を聞かない理由かと推測します。
DID効果の分散と検定統計量
観測される確率変数 , , , の分布は、中心極限定理により正規分布に近似されます。
ここで は各グループのサンプルサイズです。
DIDの推定値は次の式で表されます:
DID推定値の分散は、独立な観測を仮定して次のように計算されます:
それぞれの分散を代入すると:
効果の有意性を評価するための検定統計量 $Z$ は次のように定義されます:
帰無仮説として効果なしを仮定すると、 は標準正規分布 に従うことになります。実際にこの値を観測値をもとに計算し、標準正規分布と有意水準で比べることで、検定を行うことができます。
サンプルサイズの計算
ABテストと同様にしてDIDに必要なサンプル数を検出します。ABテストの場合と同様に期待効果 ( )、検出力 ( ) 、有意水準( )を仮定します。
期待効果の仮定のもとで、 と定義します。統計量 は次の正規分布に従います:
は上式の通りです。こちらもABテストの場合と同様、 帰無仮説では ですが、実際には上記の分布として、有意水準で棄却されるのに必要なサンプル数を算出します。有意水準 に対応するZ値 と検出力に対応するZ値 を用いると、次の不等式が成り立つ必要があります:
すべての群のサンプルサイズが等しい ( for all )と仮定します。すべての式を代入すると、必要なサンプルサイズ は以下の式で計算されます:
ランダムなABテストの式と比べると、DIDでは4つの群の観測値を使い効果を算出するため、分散が大きくなり、それに比例して必要なサンプル数も増加することがわかります。
サンプル数計算のpythonプログラム
上記で導出した式を使用してABテストとDIDに必要なサンプル数を算出するプログラムを共有します。
ABテストの場合
import scipy.stats as stats def calculate_sample_size(p_A, p_B, alpha=0.05, power=0.80): # 差分を計算する delta = p_A - p_B # Z値の計算 Z_alpha_over_2 = stats.norm.ppf(1 - alpha/2) Z_beta = stats.norm.ppf(power) # サンプルサイズの計算 pooled_prob = (p_A * (1 - p_A) + p_B * (1 - p_B)) sample_size = ((Z_alpha_over_2 + Z_beta) ** 2 * pooled_prob) / delta**2 return sample_size # 例: コンバージョン率40%のグループAと50%のグループBで計算 p_A = 0.4 p_B = 0.5 alpha = 0.05 power = 0.80 needed_sample_size = calculate_sample_size(p_A, p_B, alpha, power) print(f"必要なサンプルサイズ: {needed_sample_size:.2f}(各グループ)") # 必要なサンプルサイズ: 384.60(各グループ)
DIDの場合
import scipy.stats as stats def calculate_sample_size_did(p_00, p_01, p_10, p_11, alpha=0.05, power=0.80): # DIDを計算する DID = (p_11 - p_10) - (p_01 - p_00) # Z値の計算 Z_alpha_over_2 = stats.norm.ppf(1 - alpha/2) Z_beta = stats.norm.ppf(power) # 各群の分散の合計を計算する variance_sum = (p_00 * (1 - p_00) + p_01 * (1 - p_01) + p_10 * (1 - p_10) + p_11 * (1 - p_11)) # サンプルサイズの計算 sample_size = ((Z_alpha_over_2 + Z_beta) ** 2 * variance_sum) / DID**2 return sample_size # 例:介入なしの場合は40%→35%の変化、介入ありの場合は40%→45%の変化として計算 p_00 = 0.4 # 介入なし、前 p_01 = 0.35 # 介入なし、後 p_10 = 0.4 # 介入あり、前 p_11 = 0.45 # 介入あり、後 alpha = 0.05 power = 0.80 needed_sample_size = calculate_sample_size_did(p_00, p_01, p_10, p_11, alpha, power) print(f"必要なサンプルサイズ: {needed_sample_size:.2f}(各群)") # 必要なサンプルサイズ: 749.57(各群)
結び
ABテストに必要なサンプル数の計算から始め、同様の式をDIDの場合にも当てはめてサンプル数の計算を行いました。いままでABテストのサンプル数の計算は理論の部分を知らないままプログラムを適用して行っていましたが、DIDについて考えたことで、理論の部分まで学ぶことができよかったです。
ミツモアでは様々な職種のエンジニアを積極的に採用しています! ご興味がある方はぜひ気軽に面談しましょう!
データ分析領域でも絶賛人材を募集しています。ELT構築〜アナリティクスエンジニア領域に強い人材を特に募集しています。データエンジニア以外でも採用可能なポジションもある可能性がありますので、マネージャーの古田(@crazysrot) 宛でも公式からでもどしどしご応募ください