新しい薬が従来薬より効果があるかを調べたいとします。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ステップで行います。
- $H_0$ と $H_1$ を設定する
- 有意水準 $\alpha$ を決める(通常 $\alpha = 0.05$)
- 検定統計量を計算する
- p値を計算するか、棄却域と比較する
- 結論を述べる
「帰無仮説のもとで、手元のデータ以上に極端な結果が得られる確率」が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} $$
検出力に影響する要因
- 効果量(effect size): 真の差が大きいほど検出しやすい
- サンプルサイズ $n$: $n$ が大きいほど検出力が高い
- 有意水準 $\alpha$: $\alpha$ が大きいほど検出力が高い(ただし偽陽性も増える)
- 分散 $\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()
このグラフから仮説検定の全体構造が理解できます。
-
左図: 帰無仮説(青)と対立仮説(赤)の分布が重なっている部分に誤りが生じます。青い塗りつぶし(棄却域に入る $H_0$ の確率)が第一種の過誤 $\alpha$、赤い塗りつぶし(棄却域に入らない $H_1$ の確率)が第二種の過誤 $\beta$ です。緑の領域が検出力 $1 – \beta$ です
-
中央図: 実際のデータから計算された検定統計量 $t_{\text{obs}}$ と、帰無仮説のもとでのt分布を示しています。赤い塗りつぶしの面積がp値であり、$t_{\text{obs}}$ 以上に極端な値が得られる確率を表しています
-
右図: サンプルサイズと効果量に対する検出力の変化を示しています。効果量が大きいほど、サンプルサイズが大きいほど検出力が高くなります。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値の重要な性質が確認できます。
-
左図: 帰無仮説が正しいとき、p値は $[0, 1]$ 上の一様分布に従います。これはp値の基本的な性質であり、有意水準 $\alpha = 0.05$ で棄却される割合がちょうど5%になることを保証しています。赤い破線(一様分布の密度 = 1)にヒストグラムがぴったり沿っています
-
右図: 対立仮説が正しいとき、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が標準的な目標
次のステップとして、以下の記事も参考にしてください。
- 1標本t検定 — 母平均の検定の具体的な方法
- 効果量と検出力 — サンプルサイズ設計の実践
- ベイズ統計 vs 頻度主義 — 仮説検定の別のアプローチ