ある研究者が新しい教育プログラムの効果を検証したいと考えています。何人の生徒を対象にすればよいでしょうか。少なすぎれば本当の効果を見逃し(検出力不足)、多すぎれば時間と費用の無駄になります。
この「何人必要か」を決めるのが検出力分析(power analysis)であり、その鍵となるのが効果量(effect size)です。
統計的有意性(p値が小さいか)と実際的な重要性(効果が大きいか)は別物です。サンプルサイズが十分大きければ、実際には些細な差でも「統計的に有意」になります。効果量は「差の大きさ」を標準化した指標であり、結果の実際的な意味を評価するために不可欠です。
効果量と検出力を理解すると、以下のような実践的な判断ができます。
- 実験計画: 必要なサンプルサイズの事前決定
- 結果の解釈: p値だけでなく効果の大きさの評価
- 研究の再現性: 検出力不足による偽陰性の回避
- メタ分析: 複数の研究結果の統合
本記事の内容
- 効果量の定義と種類(Cohen’s d, η², r, オッズ比)
- 検出力の定義と影響要因
- サンプルサイズ設計の手順
- Pythonでの検出力分析と可視化
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
効果量とは
日常のアナロジー
「薬Aを飲むと痛みが減る」と言われても、どのくらい減るのかが重要です。10段階の痛みスコアが10から9に下がるのと、10から3に下がるのでは、同じ「有意な差」でも意味が全く異なります。
効果量は「差の大きさ」を「ばらつき」で割ることで標準化した指標です。ばらつきに比べて差が大きいほど、効果量は大きくなります。
Cohen’s d(2群の平均の差)
最もよく使われる効果量指標です。
$$ \begin{equation} d = \frac{\bar{X}_1 – \bar{X}_2}{S_p} \end{equation} $$
ここで $S_p = \sqrt{\frac{(n_1-1)S_1^2 + (n_2-1)S_2^2}{n_1 + n_2 – 2}}$ はプールされた標準偏差です。
Cohenの目安:
| Cohen’s d | 効果の大きさ |
|---|---|
| 0.2 | 小さい |
| 0.5 | 中程度 |
| 0.8 | 大きい |
$d = 0.5$ は「2群の分布が重なりの面積で約67%重なる程度の差」を意味します。
その他の効果量指標
η²(イータ二乗): ANOVAでの効果量。全変動のうち群間変動が占める割合。
$$ \eta^2 = \frac{SS_B}{SST} $$
相関係数 r: 2変数の関連の強さ。
オッズ比(OR): 2群の事象発生率の比較。
$$ \text{OR} = \frac{p_1/(1-p_1)}{p_2/(1-p_2)} $$
効果量の概念を理解したところで、検出力の計算方法を見ていきましょう。
検出力の計算
2標本t検定の検出力
$H_0: \mu_1 = \mu_2$ vs $H_1: \mu_1 \neq \mu_2$ の両側検定で、サンプルサイズ $n$(各群)、効果量 $d$、有意水準 $\alpha$ のとき、
- 棄却点を計算: $t_{\text{crit}} = t_{\alpha/2, 2n-2}$
- 非心度パラメータを計算: $\delta = d\sqrt{n/2}$
- 検出力を計算: $\text{Power} = 1 – F_{t}(t_{\text{crit}}; 2n-2, \delta) + F_{t}(-t_{\text{crit}}; 2n-2, \delta)$
ここで $F_t(\cdot; \nu, \delta)$ は非心t分布の累積分布関数です。
サンプルサイズの決定
検出力を目標値(通常80%)に設定し、必要なサンプルサイズを逆算します。
近似的に、2標本t検定(各群 $n$)に必要なサンプルサイズは
$$ \begin{equation} n \approx \frac{2(z_{\alpha/2} + z_\beta)^2}{d^2} \end{equation} $$
ここで $z_\beta = z_{0.2} = 0.842$(Power = 0.80のとき)、$z_{\alpha/2} = 1.96$($\alpha = 0.05$、両側)です。
$d = 0.5$ の場合:
$$ n \approx \frac{2(1.96 + 0.842)^2}{0.25} = \frac{2 \times 7.85}{0.25} \approx 63 $$
各群約63人、合計126人が必要です。
Pythonで検出力分析とサンプルサイズ設計を実装しましょう。
Pythonでの実装と可視化
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
fig, axes = plt.subplots(2, 2, figsize=(14, 11))
# (a) 効果量の直感的理解
ax = axes[0][0]
x = np.linspace(-4, 6, 500)
d_values = [0.2, 0.5, 0.8, 1.2]
for d in d_values:
# 2つの正規分布
pdf1 = stats.norm.pdf(x, 0, 1)
pdf2 = stats.norm.pdf(x, d, 1)
# 重なりの計算
overlap = np.sum(np.minimum(pdf1, pdf2)) * (x[1] - x[0])
ax.fill_between(x, pdf1, alpha=0.1, color='blue')
ax.fill_between(x, pdf2, alpha=0.1, color='red')
ax.set_xlim(-3, 4)
# 効果量ごとの分布を個別にプロット
for i, d in enumerate([0.2, 0.5, 0.8]):
offset = i * 0.03
ax.annotate(f'd={d}', xy=(d/2, 0.35 - offset),
fontsize=10, ha='center')
d_example = 0.8
pdf1 = stats.norm.pdf(x, 0, 1)
pdf2 = stats.norm.pdf(x, d_example, 1)
ax.plot(x, pdf1, 'b-', linewidth=2, label='Group 1')
ax.plot(x, pdf2, 'r-', linewidth=2, label=f'Group 2 (d={d_example})')
ax.fill_between(x, np.minimum(pdf1, pdf2), alpha=0.3, color='purple',
label='Overlap region')
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Density', fontsize=12)
ax.set_title(f"Cohen's d = {d_example}: Visual Meaning", fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
# (b) 検出力曲線
ax = axes[0][1]
n_range = np.arange(5, 201)
d_values = [0.2, 0.3, 0.5, 0.8, 1.0]
alpha = 0.05
for d in d_values:
powers = []
for n in n_range:
# 非心度パラメータ
ncp = d * np.sqrt(n / 2)
# 棄却点
t_crit = stats.t.ppf(1 - alpha/2, 2*n - 2)
# 検出力
power = (1 - stats.nct.cdf(t_crit, 2*n-2, ncp)
+ stats.nct.cdf(-t_crit, 2*n-2, ncp))
powers.append(power)
ax.plot(n_range, powers, linewidth=2, label=f'd = {d}')
ax.axhline(0.8, color='red', linewidth=1.5, linestyle='--', alpha=0.5,
label='Power = 0.80')
ax.set_xlabel('Sample size per group $n$', fontsize=12)
ax.set_ylabel('Power', fontsize=12)
ax.set_title(f'Power Curves ($\\alpha = {alpha}$, two-sided)', fontsize=13)
ax.legend(fontsize=9, ncol=2)
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 1.05)
# (c) 必要サンプルサイズの計算
ax = axes[1][0]
d_range = np.linspace(0.1, 1.5, 100)
target_powers = [0.70, 0.80, 0.90, 0.95]
for target_power in target_powers:
z_beta = stats.norm.ppf(target_power)
z_alpha = stats.norm.ppf(1 - alpha/2)
n_required = 2 * (z_alpha + z_beta)**2 / d_range**2
ax.plot(d_range, n_required, linewidth=2,
label=f'Power = {target_power:.0%}')
ax.set_xlabel("Cohen's d", fontsize=12)
ax.set_ylabel('Required $n$ per group', fontsize=12)
ax.set_title('Required Sample Size', fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 500)
ax.set_xlim(0.1, 1.5)
# (d) p値と効果量の関係
ax = axes[1][1]
np.random.seed(42)
n_sim = 1000
for n in [20, 50, 200]:
d_sim_range = np.linspace(0, 1.5, 30)
median_p = []
for d in d_sim_range:
p_vals = []
for _ in range(200):
g1 = np.random.normal(0, 1, n)
g2 = np.random.normal(d, 1, n)
_, p = stats.ttest_ind(g1, g2)
p_vals.append(p)
median_p.append(np.median(p_vals))
ax.semilogy(d_sim_range, median_p, linewidth=2, label=f'n = {n}')
ax.axhline(0.05, color='red', linewidth=1.5, linestyle='--', label='$\\alpha = 0.05$')
ax.set_xlabel("True Cohen's d", fontsize=12)
ax.set_ylabel('Median p-value', fontsize=12)
ax.set_title('p-value vs Effect Size', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_ylim(1e-10, 1)
plt.tight_layout()
plt.savefig('effect_size_power.png', dpi=150, bbox_inches='tight')
plt.show()
# サンプルサイズの表
print("\n必要サンプルサイズ(各群、α=0.05、両側、Power=0.80):")
z_b = stats.norm.ppf(0.80)
z_a = stats.norm.ppf(0.975)
for d in [0.2, 0.3, 0.5, 0.8, 1.0]:
n_req = int(np.ceil(2 * (z_a + z_b)**2 / d**2))
print(f" d = {d}: n = {n_req} per group, total = {2*n_req}")
このグラフから効果量と検出力の関係が明確にわかります。
-
左上(効果量の視覚化): Cohen’s d = 0.8のとき2つの分布は重なりが約52%です。この重なりが効果量の直感的な意味であり、重なりが少ないほど効果が大きいことを示します
-
右上(検出力曲線): 効果量ごとの検出力-サンプルサイズ曲線です。d = 0.2の小さな効果を検出するには各群400人近く必要ですが、d = 0.8の大きな効果なら各群25人程度で十分です
-
左下(必要サンプルサイズ): 効果量に対する必要サンプルサイズのグラフです。効果量が小さいほどサンプルサイズが急増します。Power = 0.80の場合、d = 0.5で各群63人、d = 0.2で各群394人が必要です
-
右下(p値と効果量): 同じ効果量でもサンプルサイズによってp値が大きく異なります。n = 200ではd = 0.2でもp < 0.05になりますが、n = 20ではd = 0.8でもp値の中央値が0.05をやっと下回る程度です
まとめ
本記事では、効果量と検出力の理論とサンプルサイズ設計を解説しました。
- 効果量は差の大きさを標準化した指標であり、p値とは独立に効果の実際的な重要性を評価する
- Cohen’s d は2群の平均差を標準偏差で割った値で、0.2(小)、0.5(中)、0.8(大)が目安
- 検出力は真の効果を検出する確率で、効果量・サンプルサイズ・有意水準に依存する
- Power = 0.80 が標準的な目標。d = 0.5 の中程度の効果を検出するには各群約63人が必要
- p値だけでなく効果量と信頼区間を報告することが推奨される
次のステップとして、以下の記事も参考にしてください。
- A/Bテストの統計学 — 検出力分析の実践的応用
- 多重検定問題 — 複数検定での検出力
- ベイズ統計 vs 頻度主義 — 検定の別のアプローチ