仮説検定の全体像 — 帰無仮説・対立仮説・p値の正しい理解

新しい薬が従来薬より効果があるかを調べたいとします。50人に新薬、50人に従来薬を投与して症状改善スコアを比較した結果、新薬の平均スコアが2.3ポイント高かったとします。

しかし、この差は「本当に新薬が効いている」からなのか、「たまたまこのサンプルでそうなっただけ」なのかを区別する必要があります。50人という限られたサンプルでは、薬の効果がなくても偶然2.3ポイントの差が出ることはありうるのです。

「偶然では説明しにくいほどの差があるか」を統計的に判断する手続きが仮説検定(hypothesis testing)です。20世紀初頭にフィッシャー、ネイマン、ピアソンらが体系化したこの方法論は、現代の科学研究の基礎となっています。

仮説検定の理論を理解すると、以下のような判断が適切にできるようになります。

  • 臨床試験: 新薬の承認判断のための統計的根拠
  • 品質管理: 製品の品質基準の適合検査
  • A/Bテスト: ウェブサービスの改善効果の評価
  • 科学研究: 実験結果の統計的有意性の判断

本記事の内容

  • 仮説検定の基本的な枠組み(帰無仮説と対立仮説)
  • 第一種の過誤と第二種の過誤
  • 検定統計量と棄却域
  • p値の定義と正しい解釈
  • 検出力とサンプルサイズの関係
  • Pythonでの実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

仮説検定の基本的な枠組み

日常のアナロジー

刑事裁判のプロセスを考えてみましょう。

  • 無罪の推定(推定無罪): 被告人は証拠がない限り無罪と見なされる → 帰無仮説
  • 有罪の立証: 検察が「合理的な疑いを超えて」有罪を証明する必要がある → 対立仮説の支持
  • 冤罪のリスク: 無罪の人を有罪にしてしまう → 第一種の過誤
  • 見逃しのリスク: 有罪の人を無罪にしてしまう → 第二種の過誤

仮説検定もこれと同じ構造です。「効果がない」を出発点とし、データによって「効果がある」ことの十分な証拠があるかを判断します。

帰無仮説と対立仮説

帰無仮説 $H_0$: 「差がない」「効果がない」「変化がない」— 検証したい主張の否定

対立仮説 $H_1$(または $H_a$): 「差がある」「効果がある」— 研究者が示したい主張

例:

問題 $H_0$ $H_1$
新薬の効果 $\mu_{\text{新}} = \mu_{\text{旧}}$ $\mu_{\text{新}} > \mu_{\text{旧}}$
コインの公平性 $p = 0.5$ $p \neq 0.5$
2群の差 $\mu_1 – \mu_2 = 0$ $\mu_1 – \mu_2 \neq 0$

対立仮説は片側検定($>$ または $<$)と両側検定($\neq$)に分かれます。方向性に事前の理由がない限り、両側検定が標準的です。

検定の手続き

仮説検定は以下の5ステップで行います。

  1. $H_0$ と $H_1$ を設定する
  2. 有意水準 $\alpha$ を決める(通常 $\alpha = 0.05$)
  3. 検定統計量を計算する
  4. p値を計算するか、棄却域と比較する
  5. 結論を述べる

「帰無仮説のもとで、手元のデータ以上に極端な結果が得られる確率」がp値です。p値が有意水準 $\alpha$ より小さければ、「帰無仮説を棄却する」と結論します。

ここで重要な概念である2種類の過誤について見ていきましょう。

第一種の過誤と第二種の過誤

定義

仮説検定には2種類の間違いがありえます。

$H_0$ が真 $H_0$ が偽
$H_0$ を棄却しない 正しい判断 第二種の過誤 ($\beta$)
$H_0$ を棄却する 第一種の過誤 ($\alpha$) 正しい判断(検出力 $1-\beta$)

第一種の過誤(偽陽性, Type I error): 実際には効果がないのに「効果がある」と結論する。確率は $\alpha$(有意水準)

第二種の過誤(偽陰性, Type II error): 実際には効果があるのに「効果がある」と結論できない。確率は $\beta$

検出力(power): 効果がある場合にそれを正しく検出する確率 $1 – \beta$

トレードオフ

$\alpha$ を小さくすれば偽陽性は減りますが、その代わりに $\beta$ が大きくなり(検出力が下がり)、本当の効果を見逃しやすくなります。

$\alpha = 0.05$ という慣習的な値は、偽陽性率を5%に抑えつつ、実用的な検出力を確保するバランスです。

非対称性

$\alpha$ と $\beta$ は非対称に扱われます。$\alpha$ は検定の前に固定しますが、$\beta$ は効果の大きさとサンプルサイズに依存して変化します。

これは裁判のアナロジーに対応します。冤罪(第一種の過誤)を避けることが最優先され、見逃し(第二種の過誤)のリスクは二次的に考慮されます。

2種類の過誤の関係を理解したところで、検定の具体的な手続き(検定統計量とp値)を見ていきましょう。

検定統計量とp値

検定統計量

検定統計量(test statistic)は、データを1つの数値に要約し、帰無仮説からの逸脱の程度を測る量です。

例えば、1標本のt検定統計量は

$$ \begin{equation} T = \frac{\bar{X} – \mu_0}{S/\sqrt{n}} \end{equation} $$

$H_0: \mu = \mu_0$ のもとで $T \sim t_{n-1}$ に従います。

p値の定義

p値(p-value)は、帰無仮説が正しいとき、手元のデータ以上に極端な検定統計量が得られる確率です。

両側検定の場合:

$$ \begin{equation} p = P(|T| \geq |t_{\text{obs}}| \mid H_0) = 2P(T \geq |t_{\text{obs}}| \mid H_0) \end{equation} $$

片側検定($H_1: \mu > \mu_0$)の場合:

$$ p = P(T \geq t_{\text{obs}} \mid H_0) $$

p値の正しい解釈

p値について最もよくある誤解を正しておきましょう。

p値は「$H_0$ が正しい確率」ではありません。p値は「$H_0$ のもとでデータが生じる確率」であり、「データのもとで $H_0$ が正しい確率」(ベイズ的な事後確率)とは異なります。

正しい解釈: 「$H_0$ が正しいと仮定したとき、今回のデータまたはそれ以上に極端なデータが得られる確率がp値である」

p値が0.03なら、「$H_0$ のもとで、これほど極端な結果は3%の確率でしか起きない。したがって $H_0$ は疑わしい」と解釈します。

棄却域との関係

棄却域は $\alpha$ に基づいて事前に決められた領域で、検定統計量がこの領域に入れば $H_0$ を棄却します。

$$ \text{棄却域} = \{T : |T| > t_{\alpha/2, n-1}\} \quad \text{(両側検定)} $$

p値 $< \alpha$ と検定統計量が棄却域に入ることは同値です。

次に、検定の性能を定量的に評価する「検出力」の概念を見ていきましょう。

検出力

定義

検出力(power)は、対立仮説が正しいときに正しく帰無仮説を棄却する確率です。

$$ \begin{equation} \text{Power} = P(\text{$H_0$ を棄却} \mid H_1 \text{が真}) = 1 – \beta \end{equation} $$

検出力に影響する要因

  1. 効果量(effect size): 真の差が大きいほど検出しやすい
  2. サンプルサイズ $n$: $n$ が大きいほど検出力が高い
  3. 有意水準 $\alpha$: $\alpha$ が大きいほど検出力が高い(ただし偽陽性も増える)
  4. 分散 $\sigma^2$: ノイズが小さいほど検出力が高い

検出力の計算

1標本t検定($H_0: \mu = \mu_0$ vs $H_1: \mu = \mu_1$、$\mu_1 > \mu_0$)の検出力は

$$ \text{Power} = P\left(T > t_{\alpha, n-1} \mid \mu = \mu_1\right) = P\left(\frac{\bar{X} – \mu_0}{S/\sqrt{n}} > t_\alpha \mid \mu = \mu_1\right) $$

$H_1$ のもとで $T$ は非心t分布に従い、非心度パラメータは

$$ \delta = \frac{\mu_1 – \mu_0}{\sigma/\sqrt{n}} = \frac{d\sqrt{n}}{1} $$

ここで $d = (\mu_1 – \mu_0)/\sigma$ は標準化効果量(コーエンのd)です。

Pythonで仮説検定の全体像を可視化しましょう。

Pythonでの実装と可視化

仮説検定の全体構造の可視化

帰無仮説と対立仮説の分布、棄却域、2種類の過誤、検出力を一つの図で示します。

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# (a) 仮説検定の構造
ax = axes[0]
mu0 = 0  # H0のもとでの平均
mu1 = 2  # H1のもとでの平均
sigma = 1
n = 25
se = sigma / np.sqrt(n)
alpha = 0.05

x = np.linspace(-4*se, mu1 + 4*se, 500)

# H0の分布
h0_pdf = stats.norm.pdf(x, mu0, se)
# H1の分布
h1_pdf = stats.norm.pdf(x, mu1, se)

# 棄却域(片側)
z_crit = stats.norm.ppf(1 - alpha) * se
ax.plot(x, h0_pdf, 'b-', linewidth=2, label='$H_0: \\mu = 0$')
ax.plot(x, h1_pdf, 'r-', linewidth=2, label=f'$H_1: \\mu = {mu1}$')

# 第一種の過誤(α)
x_alpha = x[x >= z_crit]
ax.fill_between(x_alpha, stats.norm.pdf(x_alpha, mu0, se), alpha=0.3,
                color='blue', label=f'Type I error ($\\alpha = {alpha}$)')

# 第二種の過誤(β)
x_beta = x[x <= z_crit]
ax.fill_between(x_beta, stats.norm.pdf(x_beta, mu1, se),
                where=(x_beta <= z_crit), alpha=0.3,
                color='red', label='Type II error ($\\beta$)')

# 検出力
x_power = x[x >= z_crit]
ax.fill_between(x_power, stats.norm.pdf(x_power, mu1, se), alpha=0.15,
                color='green', label='Power ($1-\\beta$)')

ax.axvline(z_crit, color='black', linewidth=1.5, linestyle='--',
           label=f'Critical value = {z_crit:.3f}')

beta = stats.norm.cdf(z_crit, mu1, se)
power = 1 - beta
ax.set_title(f'Hypothesis Testing Structure\n(Power = {power:.3f})', fontsize=12)
ax.set_xlabel('$\\bar{X}$', fontsize=12)
ax.set_ylabel('Density', fontsize=12)
ax.legend(fontsize=7, loc='upper right')
ax.grid(True, alpha=0.3)

# (b) p値の可視化
ax = axes[1]
n_test = 30
mu_0 = 100
sigma_test = 15

# データ生成(μ=105の母集団から)
np.random.seed(42)
sample = np.random.normal(105, sigma_test, n_test)
xbar = np.mean(sample)
s = np.std(sample, ddof=1)
t_obs = (xbar - mu_0) / (s / np.sqrt(n_test))
p_val = 2 * (1 - stats.t.cdf(abs(t_obs), n_test - 1))

x_t = np.linspace(-5, 5, 500)
t_pdf = stats.t.pdf(x_t, n_test - 1)
ax.plot(x_t, t_pdf, 'b-', linewidth=2, label=f'$t_{{29}}$ under $H_0$')

# p値の領域(両側)
ax.fill_between(x_t, t_pdf, where=(x_t >= abs(t_obs)), alpha=0.4,
                color='red', label=f'p-value = {p_val:.4f}')
ax.fill_between(x_t, t_pdf, where=(x_t <= -abs(t_obs)), alpha=0.4,
                color='red')
ax.axvline(t_obs, color='green', linewidth=2, linestyle='--',
           label=f'$t_{{obs}} = {t_obs:.2f}$')
ax.axvline(-t_obs, color='green', linewidth=2, linestyle='--')

# 棄却域
t_crit = stats.t.ppf(1 - 0.025, n_test - 1)
ax.axvline(t_crit, color='black', linewidth=1, linestyle=':', alpha=0.5)
ax.axvline(-t_crit, color='black', linewidth=1, linestyle=':', alpha=0.5)

ax.set_xlabel('t statistic', fontsize=12)
ax.set_ylabel('Density', fontsize=12)
ax.set_title(f'p-value Visualization\n($\\bar{{x}}={xbar:.1f}$, $H_0: \\mu={mu_0}$)', fontsize=12)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)

# (c) 検出力曲線
ax = axes[2]
n_values = [10, 30, 50, 100]
d_range = np.linspace(0, 1.5, 100)  # Cohen's d

for n_pow in n_values:
    powers = []
    for d in d_range:
        # 非心度パラメータ
        ncp = d * np.sqrt(n_pow)
        # 棄却点
        t_crit = stats.t.ppf(1 - 0.05/2, n_pow - 1)
        # 検出力(非心t分布の裾)
        power = 1 - stats.nct.cdf(t_crit, n_pow - 1, ncp) + stats.nct.cdf(-t_crit, n_pow - 1, ncp)
        powers.append(power)
    ax.plot(d_range, powers, linewidth=2, label=f'n = {n_pow}')

ax.axhline(0.8, color='red', linewidth=1.5, linestyle='--', alpha=0.5,
           label='Power = 0.80')
ax.axhline(0.05, color='gray', linewidth=1, linestyle=':', alpha=0.5)
ax.set_xlabel("Cohen's d (effect size)", fontsize=12)
ax.set_ylabel('Power', fontsize=12)
ax.set_title('Power Curves (two-sided $\\alpha = 0.05$)', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 1.05)

plt.tight_layout()
plt.savefig('hypothesis_testing_overview.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフから仮説検定の全体構造が理解できます。

  1. 左図: 帰無仮説(青)と対立仮説(赤)の分布が重なっている部分に誤りが生じます。青い塗りつぶし(棄却域に入る $H_0$ の確率)が第一種の過誤 $\alpha$、赤い塗りつぶし(棄却域に入らない $H_1$ の確率)が第二種の過誤 $\beta$ です。緑の領域が検出力 $1 – \beta$ です

  2. 中央図: 実際のデータから計算された検定統計量 $t_{\text{obs}}$ と、帰無仮説のもとでのt分布を示しています。赤い塗りつぶしの面積がp値であり、$t_{\text{obs}}$ 以上に極端な値が得られる確率を表しています

  3. 右図: サンプルサイズと効果量に対する検出力の変化を示しています。効果量が大きいほど、サンプルサイズが大きいほど検出力が高くなります。Power = 0.80(赤破線)を達成するには、Cohen’s d = 0.5の効果に対して $n \approx 50$ が必要です

p値のシミュレーション

p値の分布を帰無仮説と対立仮説の両方のもとで調べます。

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

np.random.seed(42)

n = 30
n_sim = 10000
alpha = 0.05

fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))

# (a) H0のもとでのp値の分布
ax = axes[0]
p_values_h0 = []
for _ in range(n_sim):
    sample = np.random.normal(0, 1, n)  # H0: mu = 0
    t_stat = np.mean(sample) / (np.std(sample, ddof=1) / np.sqrt(n))
    p_val = 2 * (1 - stats.t.cdf(abs(t_stat), n - 1))
    p_values_h0.append(p_val)

ax.hist(p_values_h0, bins=50, density=True, color='lightblue',
        edgecolor='gray', linewidth=0.5, alpha=0.7)
ax.axhline(1, color='red', linewidth=2, linestyle='--', label='Uniform(0,1)')
ax.axvline(alpha, color='green', linewidth=2, linestyle='--',
           label=f'$\\alpha = {alpha}$ (reject rate = {np.mean(np.array(p_values_h0) < alpha):.3f})')
ax.set_xlabel('p-value', fontsize=12)
ax.set_ylabel('Density', fontsize=12)
ax.set_title('p-value Distribution under $H_0$', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)

# (b) H1のもとでのp値の分布
ax = axes[1]
effect_sizes = [0.2, 0.5, 0.8]
colors = ['orange', 'red', 'darkred']

for d, color in zip(effect_sizes, colors):
    p_values_h1 = []
    for _ in range(n_sim):
        sample = np.random.normal(d, 1, n)  # H1: mu = d
        t_stat = np.mean(sample) / (np.std(sample, ddof=1) / np.sqrt(n))
        p_val = 2 * (1 - stats.t.cdf(abs(t_stat), n - 1))
        p_values_h1.append(p_val)
    reject_rate = np.mean(np.array(p_values_h1) < alpha)
    ax.hist(p_values_h1, bins=50, density=True, alpha=0.4, color=color,
            label=f'd={d}: power={reject_rate:.3f}')

ax.axvline(alpha, color='green', linewidth=2, linestyle='--', label=f'$\\alpha = {alpha}$')
ax.set_xlabel('p-value', fontsize=12)
ax.set_ylabel('Density', fontsize=12)
ax.set_title('p-value Distribution under $H_1$', fontsize=13)
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 1)

plt.tight_layout()
plt.savefig('p_value_distribution.png', dpi=150, bbox_inches='tight')
plt.show()

このグラフからp値の重要な性質が確認できます。

  1. 左図: 帰無仮説が正しいとき、p値は $[0, 1]$ 上の一様分布に従います。これはp値の基本的な性質であり、有意水準 $\alpha = 0.05$ で棄却される割合がちょうど5%になることを保証しています。赤い破線(一様分布の密度 = 1)にヒストグラムがぴったり沿っています

  2. 右図: 対立仮説が正しいとき、p値は0に近い値に集中します。効果量が大きいほど($d = 0.8$)p値の分布は強く0に偏り、検出力が高くなります。$d = 0.2$(小さな効果)では $\alpha = 0.05$ より小さいp値の割合(検出力)は15%程度であり、サンプルサイズが不足していることがわかります

まとめ

本記事では、仮説検定の全体像を体系的に解説しました。

  • 帰無仮説 $H_0$ は「差がない」という出発点。対立仮説 $H_1$ は研究者が示したい主張
  • 第一種の過誤(偽陽性)の確率 $\alpha$ は検定前に固定する。第二種の過誤(偽陰性)の確率 $\beta$ は効果量とサンプルサイズに依存する
  • p値は「$H_0$ のもとでデータ以上に極端な結果が得られる確率」であり、「$H_0$ が正しい確率」ではない
  • p値は $H_0$ のもとで一様分布に従い、$H_1$ のもとでは0に近い値に集中する
  • 検出力 $1 – \beta$ は効果量とサンプルサイズの増加とともに向上する。Power = 0.80が標準的な目標

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