統計的仮説検定を行う前に「どのくらいのサンプルがあれば、見つけたい効果を見逃さずに済むか」を事前に設計することは極めて重要です。この設計の中心にあるのが 検出力(power) と 標本サイズ(sample size) の関係です。
検出力分析(power analysis)を怠ると、効果が存在するのにそれを検出できない(第2種の過誤を犯す)リスクが高まり、研究に費やした時間やコストが無駄になります。臨床試験の計画、A/Bテストの設計、品質管理の実験計画など、あらゆる場面で検出力分析は必須です。
本記事の内容
- 検出力の定義と意味
- 効果量(effect size)の概念
- 必要サンプルサイズの計算式の導出
- $\alpha$, $\beta$, 効果量, $n$ の4要素の関係
- Pythonでの検出力曲線のプロット
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
検出力とは
定義
検出力(power) とは、対立仮説 $H_1$ が真であるときに、正しく帰無仮説 $H_0$ を棄却できる確率です。
$$ \text{検出力} = 1 – \beta = P(\text{$H_0$ を棄却} \mid H_1 \text{ が真}) $$
ここで $\beta$ は第2種の過誤の確率です。
検出力の目安
一般に、検出力は 80%以上 を確保することが推奨されています(Cohen, 1988)。つまり $\beta \leq 0.20$ です。
| 検出力 | 解釈 |
|---|---|
| $< 0.50$ | 不十分。効果を検出できる確率が半分以下 |
| $0.50 \sim 0.80$ | やや不足。サンプル追加を検討 |
| $\geq 0.80$ | 十分。多くの分野で推奨される水準 |
| $\geq 0.90$ | 高い。臨床試験などで求められることがある |
効果量(Effect Size)
定義
効果量は、検出したい効果の大きさを標準化した指標です。母平均の検定(z検定)の場合、コーエンのd(Cohen’s d) が使われます。
$$ d = \frac{|\mu_1 – \mu_0|}{\sigma} $$
ここで $\mu_0$ は帰無仮説のもとでの母平均、$\mu_1$ は対立仮説のもとでの母平均、$\sigma$ は母標準偏差です。
コーエンの目安
Cohen(1988)は、効果量の大きさについて以下の目安を提案しています。
| 効果量 $d$ | 解釈 |
|---|---|
| 0.2 | 小さい効果(small) |
| 0.5 | 中程度の効果(medium) |
| 0.8 | 大きい効果(large) |
他の検定における効果量
| 検定 | 効果量 | 定義 |
|---|---|---|
| z検定・t検定 | Cohen’s $d$ | $d = |\mu_1 – \mu_0| / \sigma$ |
| 相関 | $r$ | 相関係数そのもの |
| $\chi^2$ 検定 | Cohen’s $w$ | $w = \sqrt{\sum (p_{1i} – p_{0i})^2 / p_{0i}}$ |
| 分散分析 | Cohen’s $f$ | $f = \sigma_m / \sigma$ |
検出力に影響する4要素
検出力は以下の4つの要素に依存します。任意の3つを定めれば、残りの1つが決まります。
- 有意水準 $\alpha$: $\alpha$ を大きくすると検出力は上がる
- 効果量 $d$: 効果量が大きいほど検出力は上がる
- 標本サイズ $n$: $n$ を大きくすると検出力は上がる
- 検出力 $1 – \beta$: 目標とする検出力の水準
数学的な関係
片側z検定の場合、検出力は次の式で表されます(前記事で導出済み)。
$$ 1 – \beta = \Phi(\delta – z_\alpha) $$
ここで $\delta$ は非心度パラメータで、
$$ \delta = \frac{(\mu_1 – \mu_0)\sqrt{n}}{\sigma} = d\sqrt{n} $$
です。この式から、検出力は $d\sqrt{n}$ の増加関数であることがわかります。
必要サンプルサイズの導出
片側検定の場合
検出力を $1 – \beta$ 以上にするために必要な標本サイズ $n$ を導出します。
$$ 1 – \beta = \Phi(\delta – z_\alpha) $$
$\Phi$ の逆関数を取ると、
$$ \delta – z_\alpha = z_{1-\beta} = -z_\beta $$
ここで $z_{1-\beta} = \Phi^{-1}(1 – \beta)$ です。$\Phi^{-1}(1 – \beta) = -\Phi^{-1}(\beta) = -z_\beta$ を利用しました。
$$ \delta = z_\alpha + z_{1-\beta} = z_\alpha – z_\beta $$
$\delta = d\sqrt{n}$ を代入して $n$ について解くと、
$$ \begin{align} d\sqrt{n} &= z_\alpha + z_{1-\beta} \\ \sqrt{n} &= \frac{z_\alpha + z_{1-\beta}}{d} \\ n &= \left(\frac{z_\alpha + z_{1-\beta}}{d}\right)^2 \end{align} $$
これが 必要サンプルサイズの公式(片側検定) です。
両側検定の場合
両側検定では、$z_\alpha$ を $z_{\alpha/2}$ に置き換えます。
$$ n = \left(\frac{z_{\alpha/2} + z_{1-\beta}}{d}\right)^2 $$
具体的な数値例
$\alpha = 0.05$(両側)、$1 – \beta = 0.80$、$d = 0.5$(中程度の効果量)のとき、
$$ z_{\alpha/2} = z_{0.025} = 1.960, \quad z_{1-\beta} = z_{0.80} = 0.842 $$
$$ n = \left(\frac{1.960 + 0.842}{0.5}\right)^2 = \left(\frac{2.802}{0.5}\right)^2 = 5.604^2 \approx 31.4 $$
したがって、$n = 32$ 以上の標本が必要です。
2標本t検定の場合
独立2標本の場合は各群のサンプルサイズが必要です。等しい群サイズ $n_1 = n_2 = n$ を仮定すると、
$$ n = 2\left(\frac{z_{\alpha/2} + z_{1-\beta}}{d}\right)^2 $$
係数2がかかる理由は、2標本の差の標準誤差が $\sigma\sqrt{2/n}$ となるためです。
Pythonでの実装
検出力曲線のプロット
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
def power_z_test(n, d, alpha=0.05, alternative='two-sided'):
"""
z検定の検出力を計算する関数
Parameters
----------
n : int or array
標本サイズ
d : float
効果量(Cohen's d)
alpha : float
有意水準
alternative : str
'two-sided' or 'one-sided'
Returns
-------
power : float or array
検出力
"""
if alternative == 'two-sided':
z_crit = stats.norm.ppf(1 - alpha / 2)
else:
z_crit = stats.norm.ppf(1 - alpha)
delta = d * np.sqrt(n)
power = 1 - stats.norm.cdf(z_crit - delta)
return power
# 効果量ごとの検出力曲線
ns = np.arange(5, 201)
effect_sizes = [0.2, 0.5, 0.8]
colors = ['blue', 'green', 'red']
fig, ax = plt.subplots(figsize=(10, 6))
for d, color in zip(effect_sizes, colors):
powers = power_z_test(ns, d, alpha=0.05, alternative='two-sided')
ax.plot(ns, powers, color=color, linewidth=2, label=f'd = {d}')
# 検出力80%を達成するnを見つける
n_target = ns[np.argmax(powers >= 0.80)] if np.any(powers >= 0.80) else None
if n_target is not None:
ax.axvline(x=n_target, color=color, linestyle=':', alpha=0.5)
ax.annotate(f'n={n_target}', xy=(n_target, 0.80),
xytext=(n_target + 10, 0.75), fontsize=10, color=color)
ax.axhline(y=0.80, color='gray', linestyle='--', linewidth=1.5,
label='Power = 0.80')
ax.set_xlabel('Sample Size $n$', fontsize=13)
ax.set_ylabel('Power ($1 - \\beta$)', fontsize=13)
ax.set_title('Power Curve by Effect Size ($\\alpha$ = 0.05, two-sided)', fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 1)
ax.set_xlim(5, 200)
plt.tight_layout()
plt.show()
必要サンプルサイズの計算
import numpy as np
from scipy import stats
def required_sample_size(d, alpha=0.05, power=0.80, alternative='two-sided'):
"""
必要サンプルサイズを計算する関数(1標本z検定)
Parameters
----------
d : float
効果量(Cohen's d)
alpha : float
有意水準
power : float
目標検出力
alternative : str
'two-sided' or 'one-sided'
Returns
-------
n : int
必要サンプルサイズ(切り上げ)
"""
if alternative == 'two-sided':
z_alpha = stats.norm.ppf(1 - alpha / 2)
else:
z_alpha = stats.norm.ppf(1 - alpha)
z_beta = stats.norm.ppf(power)
n = ((z_alpha + z_beta) / d) ** 2
return int(np.ceil(n))
# 効果量ごとの必要サンプルサイズ
print("=== 必要サンプルサイズ(α = 0.05, 両側, 検出力 = 0.80)===")
for d in [0.2, 0.3, 0.5, 0.8, 1.0]:
n = required_sample_size(d, alpha=0.05, power=0.80, alternative='two-sided')
print(f" 効果量 d = {d:.1f}: n = {n}")
print()
print("=== 必要サンプルサイズ(α = 0.05, 両側, 検出力 = 0.90)===")
for d in [0.2, 0.3, 0.5, 0.8, 1.0]:
n = required_sample_size(d, alpha=0.05, power=0.90, alternative='two-sided')
print(f" 効果量 d = {d:.1f}: n = {n}")
4要素の関係を3Dで可視化
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# αと検出力の関係を効果量別にプロット
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左: n固定、αと効果量を変化
n_fixed = 30
alphas = np.linspace(0.001, 0.15, 100)
for d, color, ls in zip([0.3, 0.5, 0.8], ['blue', 'green', 'red'], ['-', '--', '-.']):
powers = []
for a in alphas:
z_crit = stats.norm.ppf(1 - a / 2)
delta = d * np.sqrt(n_fixed)
pw = 1 - stats.norm.cdf(z_crit - delta)
powers.append(pw)
axes[0].plot(alphas, powers, color=color, linestyle=ls, linewidth=2,
label=f'd = {d}')
axes[0].axhline(y=0.80, color='gray', linestyle=':', linewidth=1.5)
axes[0].set_xlabel('$\\alpha$', fontsize=12)
axes[0].set_ylabel('Power', fontsize=12)
axes[0].set_title(f'Power vs $\\alpha$ (n = {n_fixed})', fontsize=13)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3)
# 右: 効果量とnの関係(等高線)
d_range = np.linspace(0.1, 1.0, 100)
n_range = np.arange(5, 201)
D, N = np.meshgrid(d_range, n_range)
z_crit = stats.norm.ppf(1 - 0.05 / 2)
Delta = D * np.sqrt(N)
Power_grid = 1 - stats.norm.cdf(z_crit - Delta)
contour = axes[1].contourf(D, N, Power_grid, levels=20, cmap='RdYlGn')
plt.colorbar(contour, ax=axes[1], label='Power')
# 検出力80%の等高線を強調
axes[1].contour(D, N, Power_grid, levels=[0.80], colors='black',
linewidths=2, linestyles='--')
axes[1].set_xlabel('Effect Size $d$', fontsize=12)
axes[1].set_ylabel('Sample Size $n$', fontsize=12)
axes[1].set_title('Power as a function of $d$ and $n$ ($\\alpha$ = 0.05)', fontsize=13)
plt.tight_layout()
plt.show()
statsmodelsによる検出力分析
実務では statsmodels の検出力分析関数を使うのが便利です。
from statsmodels.stats.power import NormalIndPower, TTestIndPower
# z検定の検出力分析
z_power = NormalIndPower()
# 必要サンプルサイズの計算
n_z = z_power.solve_power(effect_size=0.5, alpha=0.05, power=0.80,
alternative='two-sided')
print(f"z検定: d=0.5, α=0.05, power=0.80 → n = {np.ceil(n_z):.0f}")
# t検定の検出力分析
t_power = TTestIndPower()
# 独立2標本t検定の必要サンプルサイズ(各群)
n_t = t_power.solve_power(effect_size=0.5, alpha=0.05, power=0.80,
alternative='two-sided')
print(f"2標本t検定: d=0.5, α=0.05, power=0.80 → 各群 n = {np.ceil(n_t):.0f}")
# 検出力の計算
power_val = t_power.solve_power(effect_size=0.5, nobs1=64, alpha=0.05,
alternative='two-sided')
print(f"2標本t検定: d=0.5, α=0.05, n=64 → power = {power_val:.4f}")
まとめ
本記事では、検出力と標本サイズ設計について解説しました。
- 検出力 $1 – \beta$: 対立仮説が真のときに正しく $H_0$ を棄却できる確率。80%以上が目安
- 効果量 $d$: 検出したい効果の大きさを標準化した指標(Cohen’s d: 0.2=小、0.5=中、0.8=大)
- 必要サンプルサイズ: $n = \left(\frac{z_{\alpha/2} + z_{1-\beta}}{d}\right)^2$(1標本、両側検定)
- 4要素の関係: $\alpha$, $\beta$, 効果量 $d$, 標本サイズ $n$ は相互に依存し、3つが決まれば残り1つが定まる
次のステップとして、以下の記事も参考にしてください。