検出力と標本サイズ設計

統計的仮説検定を行う前に「どのくらいのサンプルがあれば、見つけたい効果を見逃さずに済むか」を事前に設計することは極めて重要です。この設計の中心にあるのが 検出力(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つが決まります。

  1. 有意水準 $\alpha$: $\alpha$ を大きくすると検出力は上がる
  2. 効果量 $d$: 効果量が大きいほど検出力は上がる
  3. 標本サイズ $n$: $n$ を大きくすると検出力は上がる
  4. 検出力 $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つが定まる

次のステップとして、以下の記事も参考にしてください。