Rで A/Bテスト:比率の差の検定 を試みます。
A/Bテストのシナリオは以下の通りです
背景
あなたは若者向けファッションECサイトのマーケティング担当者です。現在、商品詳細ページに使われているキャッチコピーが少しありきたりだと感じており、よりユーザーの心に響くコピーに変更することで、購入率(コンバージョン率, CVR)が向上するのではないかと仮説を立てました。
テストの目的
新しいキャッチコピーが、既存のコピーよりも購入率を向上させるかどうかを統計的に検証します。
テスト設計
- Aグループ(コントロール群): 既存のキャッチコピーを表示するユーザーグループ。
- キャッチコピー: 「最新トレンドをお手頃価格で」
- 過去のデータから、このグループの購入率(ベースラインCVR)は 5% であると分かっています。
- Bグループ(テスト群): 新しいキャッチコピーを表示するユーザーグループ。
- キャッチコピー: 「5分で決まる、今日の主役コーデ」
- この変更により、購入率が 7% に向上すると期待しています(+2%ポイントの改善)。
統計的仮説
- 帰無仮説 (H₀): 新旧キャッチコピーでの購入率に差はない (CVRₐ = CVRₑ)。
- 対立仮説 (H₁): 新しいキャッチコピーBの購入率は、既存のコピーAよりも高い (CVRₐ < CVRₑ)。
続いて A/Bテストに入ります。
ステップ1: 事前設計(サンプルサイズの計算)
# 期待する効果を検出するために、どれくらいのユーザー数が必要か計算します。
# パラメータ設定
<- 0.80 # 検出力
power <- 0.05 # 有意水準
sig_level <- 0.05 # AグループのベースラインCVR
p1 <- 0.07 # Bグループの期待するCVR
p2
# サンプルサイズの計算 (片側検定)
# H1: p1 < p2 を検証したいため、alternative = "one.sided"
<- power.prop.test(
power_test_result p1 = p1,
p2 = p2,
power = power,
sig.level = sig_level,
alternative = "one.sided"
)
# 計算結果の表示
print(power_test_result)
# 各グループに必要なサンプルサイズを抽出
<- ceiling(power_test_result$n)
sample_size_per_group cat(paste("\n各グループに必要なサンプルサイズ:", sample_size_per_group, "人\n"))
Two-sample comparison of proportions power calculation
n = 1742.434
p1 = 0.05
p2 = 0.07
sig.level = 0.05
power = 0.8
alternative = one.sided
NOTE: n is number in *each* group
各グループに必要なサンプルサイズ: 1743 人
ステップ2: シミュレーションデータの生成
# 上記で計算したサンプルサイズに基づき、架空のテスト結果を生成します。
# 実際には、このデータはテスト期間中に収集されます。
<- 20250610
seed set.seed(seed)
# シミュレーションのパラメータ
<- sample_size_per_group # Aグループのユーザー数
n_A <- sample_size_per_group # Bグループのユーザー数
n_B
# 「真の」CVR (シナリオ通りに設定)
<- 0.05
true_cvr_A <- 0.07
true_cvr_B
# 二項分布に従う乱数を生成して、各ユーザーの購入(1) or 非購入(0)をシミュレート
# rbinom(n, size, prob)
# n: 生成する乱数の数 (今回はユーザー数)
# size: 1回の試行数 (1ユーザーあたり1回なので1)
# prob: 成功確率 (CVR)
<- rbinom(n_A, 1, true_cvr_A)
data_A <- rbinom(n_B, 1, true_cvr_B)
data_B
# 生成されたデータの一部を確認
cat("\nAグループのデータ(最初の10件):", data_A[1:10], "\n")
cat("Bグループのデータ(最初の10件):", data_B[1:10], "\n")
Aグループのデータ(最初の10件): 0 0 0 0 0 1 0 0 0 1
Bグループのデータ(最初の10件): 0 0 0 0 0 0 0 0 0 0
ステップ3: 集計と統計的検定
# 生成したデータを使って、実際にCVRを計算し、統計的な差があるか検定します。
# 各グループのコンバージョン数(購入者数)を計算
<- sum(data_A)
conversions_A <- sum(data_B)
conversions_B
# 各グループの観測されたCVRを計算
<- conversions_A / n_A
observed_cvr_A <- conversions_B / n_B
observed_cvr_B
cat("\n--- テスト結果の集計 ---\n")
cat(paste("Aグループ: ユーザー数", n_A, "人, 購入者数", conversions_A, "人, 観測CVR:", round(observed_cvr_A, 4), "\n"))
cat(paste("Bグループ: ユーザー数", n_B, "人, 購入者数", conversions_B, "人, 観測CVR:", round(observed_cvr_B, 4), "\n"))
# 比率の差の検定 (prop.test)
# Bグループの方がCVRが高いことを検証したいので、alternative = "greater" を指定
# これは「(Aの比率 - Bの比率) < 0」と同じ意味です
<- prop.test(
test_result x = c(conversions_A, conversions_B), # 各グループの成功数(購入者数)
n = c(n_A, n_B), # 各グループの試行回数(ユーザー数)
alternative = "less", # p1 < p2 を検定
conf.level = 0.95
)
# 検定結果の表示
cat("\n--- 統計的検定の結果 ---\n")
print(test_result)
--- テスト結果の集計 ---
Aグループ: ユーザー数 1743 人, 購入者数 84 人, 観測CVR: 0.0482
Bグループ: ユーザー数 1743 人, 購入者数 103 人, 観測CVR: 0.0591
--- 統計的検定の結果 ---
2-sample test for equality of proportions with continuity correction
data: c(conversions_A, conversions_B) out of c(n_A, n_B)
X-squared = 1.8308, df = 1, p-value = 0.08801
alternative hypothesis: less
95 percent confidence interval:
-1.000000000 0.002223189
sample estimates:
prop 1 prop 2
0.04819277 0.05909352
ステップ4: 結果の解釈と結論
# p値を取得
<- test_result$p.value
p_value
cat("\n--- 結論 ---\n")
cat(paste("p値:", round(p_value, 4), "\n"))
cat(paste("有意水準:", sig_level, "\n\n"))
if (p_value < sig_level) {
cat("結論: p値が有意水準 (0.05) よりも小さいため、帰無仮説を棄却します。\n")
cat("新しいキャッチコピーBは、既存のコピーAに比べて購入率を統計的に有意に向上させると言えます。\n")
cat("ビジネス判断: 新しいキャッチコピーを採用することを推奨します。\n")
else {
} cat("結論: p値が有意水準 (0.05) よりも大きいため、帰無仮説を棄却できません。\n")
cat("新しいキャッチコピーBが購入率を向上させるという統計的に有意な証拠は得られませんでした。\n")
cat("ビジネス判断: 現状では、新しいキャッチコピーへの変更は見送るべきです。\n")
}
--- 結論 ---
p値: 0.088
有意水準: 0.05
結論: p値が有意水準 (0.05) よりも大きいため、帰無仮説を棄却できません。
新しいキャッチコピーBが購入率を向上させるという統計的に有意な証拠は得られませんでした。
ビジネス判断: 現状では、新しいキャッチコピーへの変更は見送るべきです。
以上です。