1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【A/Bテストの罠】p < 0.05だから有意で施策を判断していませんか? - t検定の実務的な落とし穴

1
Posted at

はじめに

A/Bテストの結果レポートを見ていて、こんな経験はありませんか?

  • 「p値が0.04なので、新デザインを採用します!」と報告したら、上司から「で、どれくらい売上が上がるの?」と聞かれて固まった
  • サンプルサイズを決めずにテストを開始し、「いつ止めればいいのか」がわからなくなった
  • scipy.stats.ttest_ind() を呼び出したが、equal_var 引数を True にすべきか False にすべきか自信がない
  • 「対応のあるt検定」と「2標本t検定」、どっちを使うべきか毎回迷う

これらは全て、t検定の「使い方」は知っていても、その「背後にある仕組み」を直感的に理解できていないことが原因です。

本記事では、A/Bテストでよく使われるt検定について、実務で本当に必要な3つのポイントを解説します。

  1. p値の正体 — 「サンプリングの偏り」という視点
  2. 3つのt検定の使い分け
  3. 「テスト開始前」に必要なサンプルサイズ設計

1. p値の正体:「サンプリングの偏り」という視点

よくある誤解

「p値が0.05未満 = この施策には効果がある確率が95%」

これは誤りです。

p値の正しい定義は:

帰無仮説(=差がない)が正しいと仮定したときに、観測されたデータ(あるいはそれ以上に極端なデータ)が得られる確率」

…と言われても、ピンとこないですよね。私もそうでした。

Pythonでp値を「体感」してみる

「差がないはずの母集団」からランダムサンプリングを繰り返してみると、p値の正体が見えてきます。

```python
import numpy as np
from scipy import stats

母集団:CVR=2%の仮想ユーザー10万人(A群・B群で本来は差がない)

np.random.seed(42)
population_A = np.random.binomial(1, 0.02, 100000)
population_B = np.random.binomial(1, 0.02, 100000) # 本当は同じ確率

1000回サンプリングして「たまたま有意になる」回数を数える

false_positive = 0
for _ in range(1000):
sample_A = np.random.choice(population_A, 1000)
sample_B = np.random.choice(population_B, 1000)
_, p = stats.ttest_ind(sample_A, sample_B)
if p < 0.05:
false_positive += 1

print(f"差がないのに有意になった回数: {false_positive}/1000")

出力例: 差がないのに有意になった回数: 47/1000

```

差が全くない母集団なのに、約5%の確率で「有意差あり」と誤判定されます。これがp値の正体です。

つまりp値とは、「偶然この差が出る確率」を示しているだけで、ビジネス的なインパクトとは無関係なのです。


2. 3つのt検定、どれを使えばいい?

scipy.stats にはt検定の関数が3つあります。これらの使い分けを間違えると、正しい結論が得られません。

検定 関数 使用シーン
1標本t検定 ttest_1samp 基準値(例:過去の平均単価3,000円)との比較
2標本t検定 ttest_ind A群とB群(異なるユーザー)の比較 ← A/Bテストの主役
対応のあるt検定 ttest_rel 同一ユーザーの施策前後の比較

落とし穴:equal_var 引数

A/Bテストで最も使う ttest_ind ですが、こんなコードを書いていませんか?

```python

よくあるコード

t, p = stats.ttest_ind(group_A, group_B)
```

実は、デフォルトは equal_var=True(スチューデントのt検定)です。これは2群の分散が等しいことを仮定しています。

しかし、実務のA/Bテストでは、サンプルサイズや分散が群ごとに異なることが普通です。そのため、ウェルチのt検定を使うのが安全です。

```python

推奨:ウェルチのt検定

t, p = stats.ttest_ind(group_A, group_B, equal_var=False)
```


3. 「テスト開始前」のサンプルサイズ設計

なぜサンプルサイズ設計が必要なのか

サンプルサイズを決めずにA/Bテストを開始すると、こんな問題が起きます:

  • 早めに止めすぎ:本当は効果があるのに「有意差なし」と判定(第二種の過誤)
  • だらだら続けすぎ:p値が偶然0.05を下回るのを待つ「p-hacking」に陥る

必要な3要素

サンプルサイズを算出するには、以下の3つを事前に決めます:

  1. 検出したい効果量(Effect Size):例えば「CVRを2.0%→2.2%に改善したい」
  2. 有意水準(α):通常0.05
  3. 検出力(Power, 1-β):通常0.80

statsmodelsで計算する

```python
from statsmodels.stats.power import NormalIndPower
from statsmodels.stats.proportion import proportion_effectsize

CVR 2.0% → 2.2% を検出したい

effect_size = proportion_effectsize(0.022, 0.020)

analysis = NormalIndPower()
n = analysis.solve_power(
effect_size=effect_size,
alpha=0.05,
power=0.80,
alternative='two-sided'
)
print(f"各群に必要なサンプルサイズ: {int(n)}人")

出力: 各群に必要なサンプルサイズ: 78232人

```

0.2ポイントの差を検出するには、各群78,000人以上必要だとわかります。

これを知らずに「1日500人で1週間回しました!」と分析しても、そもそも検出できないテストを回していたことになります。


実務で陥りやすい3つの罠

ここまでの話を踏まえた、実務での注意点をまとめます。

罠1:p-hacking

「もう少しデータを集めればp<0.05になりそう」と延長を繰り返す。サンプルサイズは事前に決めて固定しましょう。

罠2:効果量を見ない

p値だけ見て「有意でした!」と報告。ビジネス判断には効果量(差の大きさ)と信頼区間も必須です。

罠3:検定の選択ミス

施策前後の比較で ttest_ind を使う(正しくは ttest_rel)など。データの取られ方から選びましょう。


さらに深く学びたい方へ

ここまで読んで「もっと体系的に、手を動かしながら学びたい」と思った方へ。

私が作成したUdemy講座 「【Python × t検定マスタードリル】p値とサンプルサイズ設計を、シミュレーションで直感理解する」 では、本記事の内容を含めて、以下を約3時間・全5セクションでドリル形式で学べます。

  • セクション2:p値の正体をサンプリング・シミュレーションで体感
  • セクション3:3つのt検定を5つのビジネスケースで解き分け
  • セクション4statsmodels で必要nを逆算する実践ドリル
  • セクション5:ECサイトUI改善のA/Bテスト最終演習(データ読込→検定→ビジネス判断まで)

🎁 Qiita読者限定クーポン(30日間限定・半額)
👉 【Python】t検定マスタードリル|Udemy

数式ではなく Pythonコードとシミュレーションで直感理解 することを徹底した講座です。Google Colabがあればすぐ始められます。


まとめ

  • p値は「差がない母集団から偶然この差が出る確率」であり、ビジネスインパクトとは別物
  • t検定は「比較対象」と「データの取られ方」で3種類を使い分ける
  • A/Bテストは始める前にサンプルサイズを決めることが最重要
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?