🔍 1. A/Bテスト結果の可視化
基本的な可視化
📌 結果の可視化方法
数値だけでなく、グラフで視覚的に示すことが重要
主要な可視化:
1. 棒グラフ:CVRの比較
2. 信頼区間付き棒グラフ:不確実性を表示
3. 時系列グラフ:日次の推移
4. 分布グラフ:ユーザー行動の違い
なぜ可視化が重要か:
・数値だけでは直感的に理解しにくい
・ステークホルダーへの説明がしやすい
・異常値やパターンを発見できる
Pythonでの可視化
# A/Bテスト結果の可視化
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
# 日本語フォント設定(環境に応じて変更)
plt.rcParams[‘font.family’] = ‘DejaVu Sans’
# サンプルデータ
np.random.seed(42)
A_data = np.random.binomial(1, 0.10, 1000) # Aグループ: CVR 10%
B_data = np.random.binomial(1, 0.12, 1000) # Bグループ: CVR 12%
# 基本統計
A_cvr = A_data.mean()
B_cvr = B_data.mean()
A_se = stats.sem(A_data)
B_se = stats.sem(B_data)
print(“【A/Bテスト結果】”)
print(f”Aグループ: CVR {A_cvr:.2%} (n={len(A_data)})”)
print(f”Bグループ: CVR {B_cvr:.2%} (n={len(B_data)})”)
print(f”差: {B_cvr – A_cvr:.2%}ポイント”)
print(f”相対改善: {(B_cvr – A_cvr) / A_cvr:.1%}”)
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 1. 棒グラフ
groups = [‘A (Control)’, ‘B (Treatment)’]
cvrs = [A_cvr, B_cvr]
colors = [‘#667eea’, ‘#764ba2’]
bars = axes[0].bar(groups, cvrs, color=colors, alpha=0.7)
axes[0].set_ylabel(‘CVR’, fontsize=12)
axes[0].set_title(‘CVR Comparison’, fontsize=14, fontweight=’bold’)
axes[0].set_ylim(0, max(cvrs) * 1.3)
# 値を表示
for bar, cvr in zip(bars, cvrs):
height = bar.get_height()
axes[0].text(bar.get_x() + bar.get_width()/2., height,
f'{cvr:.2%}’, ha=’center’, va=’bottom’, fontsize=11)
# 2. 信頼区間付き棒グラフ
ci_A = 1.96 * A_se # 95%信頼区間
ci_B = 1.96 * B_se
axes[1].bar(groups, cvrs, yerr=[ci_A, ci_B],
color=colors, alpha=0.7, capsize=10, ecolor=’black’)
axes[1].set_ylabel(‘CVR’, fontsize=12)
axes[1].set_title(‘CVR with 95% CI’, fontsize=14, fontweight=’bold’)
axes[1].set_ylim(0, max(cvrs) * 1.3)
plt.tight_layout()
plt.savefig(‘ab_test_visualization.png’, dpi=150, bbox_inches=’tight’)
plt.show()
print(“\n→ グラフを保存しました: ab_test_visualization.png”)
# 出力例
【A/Bテスト結果】
Aグループ: CVR 9.70% (n=1000)
Bグループ: CVR 12.30% (n=1000)
差: 2.60%ポイント
相対改善: 26.8%
→ グラフを保存しました: ab_test_visualization.png
時系列での推移確認
# 日次CVRの推移を可視化
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 14日間のシミュレーションデータ
np.random.seed(42)
days = 14
daily_users = 150 # 各群の1日あたりユーザー数
# 日次データ生成
dates = pd.date_range(‘2024-01-01’, periods=days)
A_daily_cvr = []
B_daily_cvr = []
for day in range(days):
A_conversions = np.random.binomial(daily_users, 0.10)
B_conversions = np.random.binomial(daily_users, 0.12)
A_daily_cvr.append(A_conversions / daily_users)
B_daily_cvr.append(B_conversions / daily_users)
# データフレーム作成
df = pd.DataFrame({
‘Date’: dates,
‘A_CVR’: A_daily_cvr,
‘B_CVR’: B_daily_cvr
})
# 累積CVR
df[‘A_cumulative’] = np.cumsum([int(cvr * daily_users) for cvr in A_daily_cvr]) / \
(np.arange(1, days+1) * daily_users)
df[‘B_cumulative’] = np.cumsum([int(cvr * daily_users) for cvr in B_daily_cvr]) / \
(np.arange(1, days+1) * daily_users)
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 1. 日次CVR
axes[0].plot(df[‘Date’], df[‘A_CVR’], ‘o-‘, label=’A (Control)’, color=’#667eea’)
axes[0].plot(df[‘Date’], df[‘B_CVR’], ‘s-‘, label=’B (Treatment)’, color=’#764ba2′)
axes[0].set_xlabel(‘Date’)
axes[0].set_ylabel(‘Daily CVR’)
axes[0].set_title(‘Daily CVR Trend’, fontweight=’bold’)
axes[0].legend()
axes[0].tick_params(axis=’x’, rotation=45)
# 2. 累積CVR
axes[1].plot(df[‘Date’], df[‘A_cumulative’], ‘o-‘, label=’A (Control)’, color=’#667eea’)
axes[1].plot(df[‘Date’], df[‘B_cumulative’], ‘s-‘, label=’B (Treatment)’, color=’#764ba2′)
axes[1].set_xlabel(‘Date’)
axes[1].set_ylabel(‘Cumulative CVR’)
axes[1].set_title(‘Cumulative CVR Trend’, fontweight=’bold’)
axes[1].legend()
axes[1].tick_params(axis=’x’, rotation=45)
plt.tight_layout()
plt.show()
print(“【観察ポイント】”)
print(“・日次CVRは日によって変動が大きい”)
print(“・累積CVRは安定してくる”)
print(“・最終判断は累積CVRで行う”)
📊 2. 統計的検定
t検定での比較
📌 A/Bテストでの検定方法
2標本t検定(対応なし)を使用
仮説:
・H₀:AグループとBグループのCVRに差はない
・H₁:AグループとBグループのCVRに差がある
判定基準:
・p < 0.05 → 統計的に有意な差あり
・p ≥ 0.05 → 統計的に有意な差なし
Pythonでの検定
# ============================================
# A/Bテストの統計的検定
# ============================================
# なぜ検定が必要か?
# → CVRの差が「偶然」か「本当の効果」かを判断
# → 「B群のCVRが高い」だけでは不十分
# → 統計的に有意かどうかで意思決定
from scipy import stats
import numpy as np
# ============================================
# サンプルデータの作成
# ============================================
# np.random.binomial(n, p, size):
# → n回の試行でp%の確率で成功する実験をsize回繰り返す
# → n=1なら、0(失敗)か1(成功)のデータ
#
# 例: binomial(1, 0.10, 1000)
# → 1000人のユーザーが、10%の確率で成約する
np.random.seed(42) # 再現性のための乱数シード
# A群: CVR=10%のコントロールグループ
A_data = np.random.binomial(1, 0.10, 1000)
# B群: CVR=12%のトリートメントグループ(改善版)
B_data = np.random.binomial(1, 0.12, 1000)
# ============================================
# 基本統計の計算
# ============================================
print(“【基本統計】”)
print(f”Aグループ(コントロール):”)
print(f” サンプル数: {len(A_data)}人”)
print(f” 成約数: {A_data.sum()}人”)
print(f” CVR: {A_data.mean():.2%}”) # .mean()で成約率(1の割合)
print(f”\nBグループ(トリートメント):”)
print(f” サンプル数: {len(B_data)}人”)
print(f” 成約数: {B_data.sum()}人”)
print(f” CVR: {B_data.mean():.2%}”)
# 改善幅の計算
diff_absolute = B_data.mean() – A_data.mean() # 絶対差(ポイント)
diff_relative = diff_absolute / A_data.mean() # 相対差(%)
print(f”\n差: {diff_absolute:.2%}ポイント(絶対)”)
print(f”相対改善: {diff_relative:.1%}”)
# ============================================
# t検定の実行
# ============================================
# stats.ttest_ind(): 独立2標本t検定
# → 2つの独立したグループの平均を比較
# → A/Bテストでは、AとBは別々のユーザー(独立)
#
# 戻り値:
# t_stat: t統計量(差の大きさを標準化した値)
# p_value: p値(この差が偶然である確率)
t_stat, p_value = stats.ttest_ind(B_data, A_data)
print(“\n【統計的検定(2標本t検定)】”)
print(f”t統計量: {t_stat:.4f}”)
print(f”p値: {p_value:.4f}”)
# ============================================
# 判定(有意水準α=0.05)
# ============================================
alpha = 0.05
print(f”\n【判定】(有意水準α={alpha})”)
if p_value < alpha:
print(f"p値({p_value:.4f}) < α({alpha})")
print("→ 帰無仮説を棄却")
print("→ AとBのCVRに統計的に有意な差あり")
if B_data.mean() > A_data.mean():
print(“→ Bパターンの導入を推奨!”)
else:
print(“→ Aパターンの継続を推奨”)
else:
print(f”p値({p_value:.4f}) ≥ α({alpha})”)
print(“→ 帰無仮説を棄却できない”)
print(“→ AとBのCVRに統計的に有意な差があるとは言えない”)
print(“→ Aパターンを継続(変更の根拠が不十分)”)
# 出力例
【基本統計】
Aグループ:
サンプル数: 1000人
成約数: 97人
CVR: 9.70%
Bグループ:
サンプル数: 1000人
成約数: 123人
CVR: 12.30%
差: 2.60%ポイント
相対改善: 26.8%
【統計的検定】
t統計量: 1.8534
p値: 0.0640
【判定】(有意水準α=0.05)
p値(0.0640) ≥ α(0.05)
→ 帰無仮説を棄却できない
→ AとBのCVRに統計的に有意な差があるとは言えない
→ Aパターンを継続(変更しない)
比率の検定(カイ二乗検定)
# ============================================
# 比率の検定(カイ二乗検定)
# ============================================
# カイ二乗検定とは?
# → 2つのカテゴリ変数の関連性を検定
# → A/Bテストでは「グループ」と「成約/非成約」の関連を見る
# → t検定と同様の結論が得られることが多い
from scipy.stats import chi2_contingency
import numpy as np
# テスト結果データ
A_total, A_conversions = 1000, 97 # A群: 1000人中97人が成約
B_total, B_conversions = 1000, 123 # B群: 1000人中123人が成約
# ============================================
# クロス集計表(分割表)の作成
# ============================================
# 行: グループ(A, B)
# 列: 結果(成約, 非成約)
#
# | | 成約 | 非成約 |
# |——|——|——–|
# | A群 | 97 | 903 |
# | B群 | 123 | 877 |
contingency_table = [
[A_conversions, A_total – A_conversions], # A群: [成約, 非成約]
[B_conversions, B_total – B_conversions] # B群: [成約, 非成約]
]
print(“【クロス集計表】”)
print(f” 成約 非成約”)
print(f”A群: {A_conversions:4d} {A_total – A_conversions:4d}”)
print(f”B群: {B_conversions:4d} {B_total – B_conversions:4d}”)
# ============================================
# chi2_contingency(): カイ二乗検定を実行
# ============================================
# 戻り値:
# chi2: カイ二乗統計量(大きいほど差が大きい)
# p_value: p値(この差が偶然である確率)
# dof: 自由度
# expected: 期待度数(独立な場合の理論値)
chi2, p_value, dof, expected = chi2_contingency(contingency_table)
print(“\n【カイ二乗検定】”)
print(f”χ²(カイ二乗)= {chi2:.4f}”)
print(f”自由度 = {dof}”)
print(f”p値 = {p_value:.4f}”)
# 判定
if p_value < 0.05:
print("\n→ 統計的に有意な差あり(p < 0.05)")
else:
print("\n→ 統計的に有意な差なし(p ≥ 0.05)")
print("\n【t検定 vs カイ二乗検定】")
print("・どちらも同様の結論になることが多い")
print("・t検定: 連続データの平均比較に適している")
print("・カイ二乗検定: カテゴリデータの関連性に適している")
print("・CVRの場合、どちらも使用可能(結果はほぼ同じ)")
📐 3. 信頼区間の計算
信頼区間とは
📌 信頼区間の意味
真の差がどの範囲にあるかを示す
例:
・観測された差:2.0%ポイント
・95%信頼区間:[0.5%, 3.5%]
解釈:
「真の差は、95%の確率で0.5%〜3.5%の範囲にある」
ポイント:
・信頼区間が0を含まない → 統計的に有意
・信頼区間が0を含む → 統計的に有意でない
信頼区間の計算
# 信頼区間の計算
import numpy as np
from scipy import stats
def calculate_ci(data, confidence=0.95):
“””単一グループの信頼区間を計算”””
mean = np.mean(data)
se = stats.sem(data)
margin = se * stats.t.ppf((1 + confidence) / 2, len(data) – 1)
return mean, mean – margin, mean + margin
def calculate_diff_ci(data1, data2, confidence=0.95):
“””2グループの差の信頼区間を計算”””
diff = np.mean(data2) – np.mean(data1)
se1 = stats.sem(data1)
se2 = stats.sem(data2)
se_diff = np.sqrt(se1**2 + se2**2)
df = len(data1) + len(data2) – 2
margin = se_diff * stats.t.ppf((1 + confidence) / 2, df)
return diff, diff – margin, diff + margin
# サンプルデータ
np.random.seed(42)
A_data = np.random.binomial(1, 0.10, 1000)
B_data = np.random.binomial(1, 0.12, 1000)
# 各グループの信頼区間
A_mean, A_ci_low, A_ci_high = calculate_ci(A_data)
B_mean, B_ci_low, B_ci_high = calculate_ci(B_data)
# 差の信頼区間
diff, diff_ci_low, diff_ci_high = calculate_diff_ci(A_data, B_data)
print(“【95%信頼区間】”)
print(f”\nAグループのCVR:”)
print(f” 点推定: {A_mean:.2%}”)
print(f” 95%CI: [{A_ci_low:.2%}, {A_ci_high:.2%}]”)
print(f”\nBグループのCVR:”)
print(f” 点推定: {B_mean:.2%}”)
print(f” 95%CI: [{B_ci_low:.2%}, {B_ci_high:.2%}]”)
print(f”\nCVRの差(B – A):”)
print(f” 点推定: {diff:.2%}ポイント”)
print(f” 95%CI: [{diff_ci_low:.2%}, {diff_ci_high:.2%}]”)
print(“\n【判定】”)
if diff_ci_low > 0:
print(“→ 信頼区間の下限が0より大きい”)
print(“→ 統計的に有意に改善!”)
elif diff_ci_high < 0:
print("→ 信頼区間の上限が0より小さい")
print("→ 統計的に有意に悪化...")
else:
print("→ 信頼区間が0を含む")
print("→ 統計的に有意な差があるとは言えない")
# 出力例
【95%信頼区間】
AグループのCVR:
点推定: 9.70%
95%CI: [7.87%, 11.53%]
BグループのCVR:
点推定: 12.30%
95%CI: [10.27%, 14.33%]
CVRの差(B – A):
点推定: 2.60%ポイント
95%CI: [-0.17%, 5.37%]
【判定】
→ 信頼区間が0を含む
→ 統計的に有意な差があるとは言えない
✅ 4. 実装判断のフレームワーク
判断の4象限
📌 統計的有意性 × 実務的重要性
実装判断は「統計的に有意かどうか」だけでなく、「実務的に意味があるか」も考慮します。
|
統計的に有意 |
統計的に有意でない |
| 実務的に重要 |
✓ 実装すべき(理想的) |
△ 慎重に判断(サンプル不足?) |
| 実務的に重要でない |
△ コスト次第(改善は小さい) |
× 実装しない(現状維持) |
判断基準の具体例
💡 実装判断の具体例
ケース1:p=0.001, 改善幅=+3%
・統計的に有意 ✓
・実務的に重要 ✓
→ 実装すべき
ケース2:p=0.03, 改善幅=+0.5%
・統計的に有意 ✓
・実務的に重要? △
→ コストが低ければ実装、高ければ見送り
ケース3:p=0.15, 改善幅=+2%
・統計的に有意でない ×
・実務的には重要そう
→ サンプル不足の可能性、再テストを検討
ケース4:p=0.60, 改善幅=+0.1%
・統計的に有意でない ×
・実務的に重要でない ×
→ 現状維持
完全な分析クラス
# A/Bテスト分析の完全なクラス
import numpy as np
from scipy import stats
class ABTestAnalyzer:
“””A/Bテスト分析ツール”””
def __init__(self, A_data, B_data, alpha=0.05, min_improvement=0.01):
self.A_data = np.array(A_data)
self.B_data = np.array(B_data)
self.alpha = alpha
self.min_improvement = min_improvement
def analyze(self):
“””完全な分析を実行”””
print(“=” * 60)
print(“A/Bテスト分析レポート”)
print(“=” * 60)
# 基本統計
A_cvr = self.A_data.mean()
B_cvr = self.B_data.mean()
diff = B_cvr – A_cvr
print(“\n【1. 基本統計】”)
print(f”Aグループ(現行):”)
print(f” サンプル数: {len(self.A_data):,}人”)
print(f” 成約数: {self.A_data.sum():,}人”)
print(f” CVR: {A_cvr:.2%}”)
print(f”\nBグループ(新):”)
print(f” サンプル数: {len(self.B_data):,}人”)
print(f” 成約数: {self.B_data.sum():,}人”)
print(f” CVR: {B_cvr:.2%}”)
print(f”\n差(B – A): {diff:.2%}ポイント”)
if A_cvr > 0:
print(f”相対改善率: {(diff / A_cvr * 100):.1f}%”)
# t検定
t_stat, p_value = stats.ttest_ind(self.B_data, self.A_data)
print(“\n【2. 統計的検定】”)
print(f”t統計量: {t_stat:.4f}”)
print(f”p値: {p_value:.4f}”)
# 信頼区間
se1 = stats.sem(self.A_data)
se2 = stats.sem(self.B_data)
se_diff = np.sqrt(se1**2 + se2**2)
df = len(self.A_data) + len(self.B_data) – 2
margin = se_diff * stats.t.ppf(0.975, df)
ci_lower = diff – margin
ci_upper = diff + margin
print(f”\n【3. 95%信頼区間】”)
print(f”差の推定値: {diff:.2%}”)
print(f”95%CI: [{ci_lower:.2%}, {ci_upper:.2%}]”)
# 判定
print(“\n【4. 判定】”)
統計的有意 = p_value < self.alpha
実務的重要 = abs(diff) >= self.min_improvement
print(f”統計的有意性(p < {self.alpha}): {'✓ Yes' if 統計的有意 else '× No'}")
print(f"実務的重要性(|差| ≥ {self.min_improvement:.1%}): {'✓ Yes' if 実務的重要 else '× No'}")
print("\n【5. 推奨アクション】")
if 統計的有意 and 実務的重要:
if diff > 0:
print(“✅ Bパターンに切り替えることを推奨”)
print(f” 期待される改善: {diff:.2%}ポイント”)
else:
print(“❌ Bパターンは悪化。Aパターンを継続”)
elif 統計的有意 and not 実務的重要:
print(“⚠️ 統計的には有意だが、実務的な改善は小さい”)
print(” コスト次第で判断”)
elif not 統計的有意 and 実務的重要:
print(“⚠️ 改善幅は大きそうだが、統計的に有意でない”)
print(” サンプル不足の可能性。テスト期間延長を検討”)
else:
print(“❌ 現状維持(Aパターン継続)”)
print(“=” * 60)
return {
‘A_cvr’: A_cvr,
‘B_cvr’: B_cvr,
‘diff’: diff,
‘p_value’: p_value,
‘ci_lower’: ci_lower,
‘ci_upper’: ci_upper,
‘統計的有意’: 統計的有意,
‘実務的重要’: 実務的重要
}
# 使用例
np.random.seed(42)
A_data = np.random.binomial(1, 0.10, 1000)
B_data = np.random.binomial(1, 0.12, 1000)
analyzer = ABTestAnalyzer(A_data, B_data, min_improvement=0.01)
results = analyzer.analyze()
# 出力例
============================================================
A/Bテスト分析レポート
============================================================
【1. 基本統計】
Aグループ(現行):
サンプル数: 1,000人
成約数: 97人
CVR: 9.70%
Bグループ(新):
サンプル数: 1,000人
成約数: 123人
CVR: 12.30%
差(B – A): 2.60%ポイント
相対改善率: 26.8%
【2. 統計的検定】
t統計量: 1.8534
p値: 0.0640
【3. 95%信頼区間】
差の推定値: 2.60%
95%CI: [-0.15%, 5.35%]
【4. 判定】
統計的有意性(p < 0.05): × No
実務的重要性(|差| ≥ 1.0%): ✓ Yes
【5. 推奨アクション】
⚠️ 改善幅は大きそうだが、統計的に有意でない
サンプル不足の可能性。テスト期間延長を検討
============================================================
📝 5. 分析レポートの作成
レポートに含めるべき要素
📌 A/Bテストレポートの構成
1. エグゼクティブサマリー
・テストの目的
・結論と推奨アクション
・期待されるビジネスインパクト
2. テスト設計
・仮説
・テスト期間とサンプルサイズ
・対象ユーザー
3. 結果
・基本統計(CVR、サンプル数)
・可視化(棒グラフ、時系列)
・統計的検定(p値、信頼区間)
4. 結論と次のステップ
・実装判断
・追加で検討すべきこと
・今後のテスト案
レポート自動生成
# レポート自動生成
def generate_report(results, test_name=”A/Bテスト”):
“””マークダウン形式のレポートを生成”””
report = f”””
# {test_name} 分析レポート
## エグゼクティブサマリー
| 指標 | 値 |
|——|—–|
| Aグループ CVR | {results[‘A_cvr’]:.2%} |
| Bグループ CVR | {results[‘B_cvr’]:.2%} |
| 改善幅 | {results[‘diff’]:.2%}ポイント |
| p値 | {results[‘p_value’]:.4f} |
| 統計的有意 | {‘Yes’ if results[‘統計的有意’] else ‘No’} |
## 結論
“””
if results[‘統計的有意’] and results[‘実務的重要’]:
if results[‘diff’] > 0:
report += “**✅ Bパターンへの切り替えを推奨します。**\n\n”
report += f”期待される改善: {results[‘diff’]:.2%}ポイント\n”
else:
report += “**❌ Bパターンは現行より悪化しています。Aパターンを継続してください。**\n”
elif results[‘統計的有意’]:
report += “**⚠️ 統計的には有意ですが、実務的な改善は小さいです。**\n\n”
report += “コストと工数を考慮して判断してください。\n”
elif results[‘実務的重要’]:
report += “**⚠️ 改善幅は大きいですが、統計的に有意ではありません。**\n\n”
report += “テスト期間の延長を検討してください。\n”
else:
report += “**❌ 現状維持を推奨します。**\n\n”
report += “統計的にも実務的にも有意な差は見られませんでした。\n”
report += f”””
## 信頼区間
差の95%信頼区間: [{results[‘ci_lower’]:.2%}, {results[‘ci_upper’]:.2%}]
“””
if results[‘ci_lower’] > 0:
report += “→ 下限が0を超えているため、確実に改善が見込めます。\n”
elif results[‘ci_upper’] < 0:
report += "→ 上限が0を下回っているため、確実に悪化します。\n"
else:
report += "→ 信頼区間が0を含むため、効果の方向は不確実です。\n"
return report
# 使用例
report = generate_report(results, "購入ボタン色変更テスト")
print(report)
📝 STEP 21 のまとめ
✅ このステップで学んだこと
1. 可視化
- 棒グラフでCVRを比較
- 信頼区間付きグラフで不確実性を表示
- 時系列グラフで推移を確認
2. 統計検定
- 2標本t検定でAとBを比較
- p < 0.05 で統計的に有意
3. 信頼区間
- 真の差の範囲を提示
- 0を含むかどうかで有意性を判断
4. 実装判断
- 統計的有意性 × 実務的重要性で判断
- コストとベネフィットを考慮
💡 最も大切なポイント
A/Bテストの分析では、統計と実務の両方を考慮することが重要です!
分析の流れ:
1. 基本統計を確認(CVR、サンプル数)
2. 可視化で直感的に理解
3. 統計的検定(p値)で有意性を確認
4. 信頼区間で不確実性を把握
5. 実務的重要性を評価
6. 総合的に実装判断
次のSTEP 22では、統計的有意性と実務的重要性の違いを深掘りします!
🎯 次のステップの予告
STEP 22では、「統計的有意性と実務的重要性の違い」を学びます。p値が小さくても実装すべきでないケースを理解しましょう!
❓ よくある質問
Q1: 信頼区間が0を含むが、p値が0.04(有意)になることはありますか?
いいえ、通常はありません。
理由:
・95%信頼区間が0を含まない ⇔ p < 0.05(両側検定)
・95%信頼区間が0を含む ⇔ p ≥ 0.05
もし矛盾している場合:
・計算ミスの可能性
・片側検定と両側検定の混同
・異なる検定手法を使っている
確認方法:
同じデータ、同じ検定手法で計算すれば一致するはず
Q2: 信頼区間の幅が広い場合、どうすれば良いですか?
サンプルサイズを増やすことで、信頼区間は狭くなります。
信頼区間が広い原因:
1. サンプルサイズが小さい
2. データのばらつきが大きい
対処法:
1. テスト期間を延長
・サンプル数を2倍にすると、信頼区間の幅は約1/√2に
・サンプル数を4倍にすると、幅は約半分に
2. セグメントを絞る
・全ユーザーではなく、特定セグメントに絞る
・ばらつきが小さくなる可能性
例:
n=100: 95%CI [-1%, 5%](幅6%)
n=400: 95%CI [0.5%, 3.5%](幅3%)
Q3: 複数のKPIで結果が異なる場合、どう判断すれば良いですか?
主要KPI、副次KPI、ガードレールKPIの優先順位で判断します。
例:
・主要KPI(CVR):有意に改善 ✓
・副次KPI(CTR):有意差なし
・ガードレールKPI(収益):悪化 ×
判断:
→ ガードレールKPIが悪化しているので、実装しない
優先順位:
1. ガードレールKPIをチェック(悪化ならNG)
2. 主要KPIで判断
3. 副次KPIは参考程度
理想的な結果:
・主要KPI:改善 ✓
・副次KPI:改善または変化なし
・ガードレールKPI:悪化なし ✓
Q4: t検定とカイ二乗検定、どちらを使うべきですか?
CVRの比較では、どちらも使用可能です。
t検定:
・連続データの平均を比較
・0/1データでも使用可能
・信頼区間を直接計算しやすい
カイ二乗検定:
・カテゴリデータの関連性を検定
・CVR(成約/非成約)に適している
・クロス集計表を使用
実務的には:
・どちらも同様の結論になることが多い
・t検定の方が信頼区間を求めやすい
・カイ二乗検定はExcelで簡単に計算可能
推奨:両方計算して、結果が一致することを確認
Q5: テスト結果が有意にならなかった場合、効果がないと言えますか?
いいえ、「効果がない」とは言えません。
「有意でない」の意味:
・「効果がないことを証明した」ではない
・「効果があることを証明できなかった」
考えられる理由:
1. 本当に効果がない
2. サンプルサイズが不足(検出力不足)
3. 変更が小さすぎる
4. 測定指標が不適切
対処法:
・テスト期間を延長してサンプルを増やす
・より大胆な変更をテスト
・他のKPIも確認
報告の仕方:
「統計的に有意な差は検出されなかった」と報告
「効果がなかった」とは言わない
Q6: 分析レポートでステークホルダーに説明するコツは?
結論を先に、詳細は後に、グラフを活用します。
1. 結論を先に
・「Bパターンに切り替えるべき」または「現状維持」
・忙しい人でも3秒で理解できるように
2. グラフを活用
・数字だけでなく、視覚的に表示
・信頼区間付きの棒グラフが効果的
3. ビジネスインパクトを示す
・「CVRが2%上がる」より「月間売上が100万円増える」
・ステークホルダーが関心を持つ指標に換算
4. 不確実性も伝える
・「最悪でも0.5%の改善、最良で3.5%の改善」
・信頼区間を使って範囲で説明
5. 専門用語を避ける
・「p値が0.03で有意」より「偶然でこの結果が出る確率は3%」