はじめに
「新しい薬は本当に効果があるのか?」「このコインは本当に公平なのか?」「A/Bテストでコンバージョン率に差があるのか?」
こうした疑問に対して、データに基づいて客観的に判断する方法が 統計的仮説検定(statistical hypothesis testing) です。統計的検定は、科学研究、医学、品質管理、マーケティングなど、あらゆる分野でデータに基づく意思決定の基盤となっています。
本記事では、統計的検定の基本概念を、具体例と Python コードを交えてわかりやすく解説します。
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
統計的検定とは何か
統計的検定とは、観測データから仮説の妥当性を確率的に判断する方法 です。
私たちが何かを主張したいとき(たとえば「この薬には効果がある」)、直接それを証明するのではなく、「効果がない」という仮説を立て、それがデータと矛盾するかどうかを調べる というアプローチを取ります。
データと矛盾するならば「効果がない」という仮説を棄却し、結果として「効果がある」と結論づけます。これは背理法に似た論理構造です。
帰無仮説と対立仮説
統計的検定では、2つの仮説を設定します。
帰無仮説 $H_0$(null hypothesis)
棄却したい方の仮説 です。「差がない」「効果がない」「変化がない」など、現状維持や無効果を主張する仮説を設定します。
$$ H_0: \text{「差がない」「効果がない」「パラメータは基準値に等しい」} $$
たとえば、コインの公平性を検証する場合は
$$ H_0: p = 0.5 $$
と設定します。ここで $p$ はコインの表が出る確率です。
対立仮説 $H_1$(alternative hypothesis)
主張したい方の仮説 です。帰無仮説が棄却されたときに採択される仮説です。
$$ H_1: \text{「差がある」「効果がある」「パラメータは基準値と異なる」} $$
コインの例では
$$ H_1: p \neq 0.5 $$
です。この場合、$p > 0.5$ と $p < 0.5$ の両方を考えるので 両側検定 と呼びます。$H_1: p > 0.5$ のように片方だけを考える場合は 片側検定 と呼びます。
検定の手順
統計的検定は、次の4つのステップで行います。
ステップ1: 仮説の設定
帰無仮説 $H_0$ と対立仮説 $H_1$ を明確に設定します。
ステップ2: 検定統計量の計算
観測データから 検定統計量(test statistic) を計算します。検定統計量とは、帰無仮説が正しいと仮定したときに、データがどの程度「極端」であるかを定量化した値です。
ステップ3: 棄却域の決定またはp値の計算
事前に定めた 有意水準 に基づいて棄却域を設定するか、検定統計量から p値 を計算します。
ステップ4: 判定
検定統計量が棄却域に入れば(またはp値が有意水準以下であれば)$H_0$ を棄却し、$H_1$ を採択します。そうでなければ $H_0$ を棄却しません。
重要な注意点として、「$H_0$ を棄却しない」ことは「$H_0$ が正しい」ことの証明ではありません。あくまで「$H_0$ を棄却するだけの十分な証拠がデータから得られなかった」という意味です。
有意水準 $\alpha$
有意水準(significance level) $\alpha$ は、帰無仮説が実際には正しいにもかかわらず、誤って棄却してしまう確率の上限を表します。
$$ \alpha = P(\text{$H_0$ を棄却} \mid \text{$H_0$ が真}) $$
慣例的に $\alpha = 0.05$(5%)が最もよく使われます。これは「帰無仮説が正しい場合でも、100回検定を行えば約5回は誤って棄却してしまう」ことを許容する、という意味です。
分野によっては $\alpha = 0.01$(1%)や $\alpha = 0.001$(0.1%)を用いることもあります。素粒子物理学では「$5\sigma$」($\alpha \approx 2.87 \times 10^{-7}$)という極めて厳しい基準が使われます。
p値の定義と解釈
p値の定義
p値(p-value) とは、帰無仮説 $H_0$ が正しいと仮定したとき、観測されたデータ以上に極端な結果が得られる確率 です。
$$ p\text{値} = P(\text{検定統計量が観測値以上に極端} \mid H_0 \text{が真}) $$
p値の解釈
- $p$ 値が 小さい → 帰無仮説のもとでは観測データが非常にまれな事象 → $H_0$ を棄却する根拠が強い
- $p$ 値が 大きい → 帰無仮説のもとでも観測データはそれほど珍しくない → $H_0$ を棄却する根拠が弱い
判定規則は単純です。
$$ p \text{値} \leq \alpha \implies H_0 \text{を棄却(統計的に有意)} $$
$$ p \text{値} > \alpha \implies H_0 \text{を棄却しない(有意でない)} $$
p値に関する注意
p値はしばしば誤解されます。以下の点に注意してください。
- p値は $H_0$ が正しい確率ではありません。$H_0$ が正しいと仮定した条件つき確率です。
- p値が小さいことは、効果が大きいことを意味しません。サンプルサイズが十分に大きければ、実質的に無意味な小さな差でも統計的に有意になり得ます。
- p値は 効果の大きさ(effect size) とは別の概念です。実務的な判断には効果量や信頼区間も合わせて検討すべきです。
第1種の過誤と第2種の過誤
統計的検定では、2種類の誤りが生じ得ます。
| $H_0$ が真 | $H_0$ が偽 | |
|---|---|---|
| $H_0$ を棄却 | 第1種の過誤(偽陽性) | 正しい判定 |
| $H_0$ を棄却しない | 正しい判定 | 第2種の過誤(偽陰性) |
第1種の過誤(Type I error)
帰無仮説が実際には正しいのに、誤って棄却してしまう誤りです。その確率は有意水準 $\alpha$ で制御されます。
$$ P(\text{第1種の過誤}) = P(\text{$H_0$ を棄却} \mid \text{$H_0$ が真}) = \alpha $$
たとえば、実際には効果のない薬を「効果あり」と判定してしまうケースです。
第2種の過誤(Type II error)
帰無仮説が実際には誤っているのに、棄却できない誤りです。その確率を $\beta$ で表します。
$$ P(\text{第2種の過誤}) = P(\text{$H_0$ を棄却しない} \mid \text{$H_0$ が偽}) = \beta $$
たとえば、実際には効果のある薬を「効果なし」と判定してしまうケースです。
$\alpha$ と $\beta$ のトレードオフ
サンプルサイズが固定されている場合、$\alpha$ を小さくすると $\beta$ は大きくなる傾向があります。つまり、偽陽性を減らそうとすると偽陰性が増えます。両方を同時に小さくするには、サンプルサイズを大きくする ことが有効です。
検出力(1 – $\beta$)
検出力(power) は、帰無仮説が実際に誤っているときに、正しくそれを棄却できる確率です。
$$ \text{検出力} = 1 – \beta = P(\text{$H_0$ を棄却} \mid \text{$H_0$ が偽}) $$
検出力が高いほど、実際に存在する効果を見逃しにくい検定だといえます。一般的に、検出力が 0.8(80%)以上であることが望ましいとされています。
検出力に影響を与える要因は以下のとおりです。
- 効果量: 真の効果が大きいほど検出しやすい
- サンプルサイズ: 大きいほど検出力が上がる
- 有意水準 $\alpha$: 大きいほど検出力が上がる(ただし偽陽性も増える)
- データのばらつき: ばらつきが小さいほど検出力が上がる
具体例: コインの公平性の検定(二項検定)
ここでは、具体的な数値を使って検定の流れを一通り体験してみましょう。
問題設定
あるコインを100回投げたところ、65回表が出ました。このコインは公平(表と裏が同じ確率)と言えるでしょうか?有意水準 $\alpha = 0.05$ で両側検定を行います。
ステップ1: 仮説の設定
$$ H_0: p = 0.5 \quad \text{(コインは公平)} $$
$$ H_1: p \neq 0.5 \quad \text{(コインは公平でない)} $$
ステップ2: 検定統計量の計算
表が出る回数 $X$ は、帰無仮説のもとで二項分布 $B(n, p_0)$ に従います。
$$ X \sim B(100,\, 0.5) \quad \text{($H_0$ のもとで)} $$
二項分布の正規近似を用いると、検定統計量 $Z$ は次のように計算されます。
$$ Z = \frac{X – np_0}{\sqrt{np_0(1 – p_0)}} $$
具体的な値を代入します。
$$ Z = \frac{65 – 100 \times 0.5}{\sqrt{100 \times 0.5 \times 0.5}} $$
分子を計算します。
$$ 65 – 50 = 15 $$
分母を計算します。
$$ \sqrt{100 \times 0.25} = \sqrt{25} = 5 $$
したがって
$$ Z = \frac{15}{5} = 3.0 $$
ステップ3: p値の計算
両側検定なので、$|Z| \geq 3.0$ となる確率を求めます。標準正規分布の性質から
$$ p\text{値} = 2 \times P(Z \geq 3.0) = 2 \times (1 – \Phi(3.0)) $$
ここで $\Phi$ は標準正規分布の累積分布関数です。
$$ \Phi(3.0) \approx 0.99865 $$
$$ p\text{値} = 2 \times (1 – 0.99865) = 2 \times 0.00135 = 0.0027 $$
ステップ4: 判定
$$ p\text{値} = 0.0027 < \alpha = 0.05 $$
p値が有意水準を下回るため、帰無仮説 $H_0$ を棄却します。すなわち、このコインは公平でない(表が出る確率が0.5ではない)と結論づけられます。
Python での実装
二項検定と正規近似による検定
import numpy as np
from scipy import stats
# --- 問題設定 ---
n = 100 # 試行回数
x = 65 # 表が出た回数
p0 = 0.5 # 帰無仮説のもとでの表の確率
alpha = 0.05 # 有意水準
# --- 方法1: 正規近似による検定 ---
z_stat = (x - n * p0) / np.sqrt(n * p0 * (1 - p0))
p_value_normal = 2 * (1 - stats.norm.cdf(abs(z_stat)))
print("=" * 50)
print("方法1: 正規近似による Z 検定")
print("=" * 50)
print(f"観測値: {x} / {n} 回 (観測比率 = {x/n:.2f})")
print(f"検定統計量 Z = {z_stat:.4f}")
print(f"p値 = {p_value_normal:.6f}")
print(f"有意水準 α = {alpha}")
if p_value_normal < alpha:
print(f"判定: p値 ({p_value_normal:.6f}) < α ({alpha}) → H₀を棄却")
else:
print(f"判定: p値 ({p_value_normal:.6f}) ≥ α ({alpha}) → H₀を棄却しない")
# --- 方法2: scipy.stats.binom_test の後継 binomtest ---
result = stats.binomtest(x, n, p0, alternative="two-sided")
print()
print("=" * 50)
print("方法2: 正確二項検定 (scipy.stats.binomtest)")
print("=" * 50)
print(f"観測値: {x} / {n} 回 (観測比率 = {x/n:.2f})")
print(f"p値 = {result.pvalue:.6f}")
print(f"有意水準 α = {alpha}")
if result.pvalue < alpha:
print(f"判定: p値 ({result.pvalue:.6f}) < α ({alpha}) → H₀を棄却")
else:
print(f"判定: p値 ({result.pvalue:.6f}) ≥ α ({alpha}) → H₀を棄却しない")
# 信頼区間も確認
ci = result.proportion_ci(confidence_level=0.95)
print(f"95%信頼区間: [{ci.low:.4f}, {ci.high:.4f}]")
棄却域の可視化
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# --- パラメータ ---
alpha = 0.05
z_critical = stats.norm.ppf(1 - alpha / 2) # 両側検定の臨界値
z_observed = 3.0 # 観測された検定統計量
# --- 標準正規分布の描画 ---
x = np.linspace(-4, 4, 1000)
y = stats.norm.pdf(x)
fig, ax = plt.subplots(figsize=(10, 5))
# 分布全体
ax.plot(x, y, "k-", linewidth=2, label="標準正規分布 $N(0, 1)$")
# 棄却域(左側)を塗りつぶす
x_left = x[x <= -z_critical]
ax.fill_between(x_left, stats.norm.pdf(x_left), color="red", alpha=0.4,
label=f"棄却域 ($|Z| > {z_critical:.3f}$)")
# 棄却域(右側)を塗りつぶす
x_right = x[x >= z_critical]
ax.fill_between(x_right, stats.norm.pdf(x_right), color="red", alpha=0.4)
# 採択域を塗りつぶす
x_accept = x[(x >= -z_critical) & (x <= z_critical)]
ax.fill_between(x_accept, stats.norm.pdf(x_accept), color="blue", alpha=0.1,
label="採択域")
# 臨界値の線
ax.axvline(-z_critical, color="red", linestyle="--", linewidth=1.5,
label=f"臨界値 $\\pm {z_critical:.3f}$")
ax.axvline(z_critical, color="red", linestyle="--", linewidth=1.5)
# 観測された検定統計量
ax.axvline(z_observed, color="green", linestyle="-", linewidth=2.5,
label=f"観測値 $Z = {z_observed:.1f}$")
# 装飾
ax.set_xlabel("$Z$", fontsize=14)
ax.set_ylabel("確率密度", fontsize=14)
ax.set_title("コインの公平性の検定(両側検定, $\\alpha = 0.05$)", fontsize=15)
ax.legend(fontsize=11, loc="upper left")
ax.grid(True, alpha=0.3)
# 注釈
ax.annotate(
f"$Z = {z_observed:.1f}$\n棄却域に入る\n→ $H_0$ を棄却",
xy=(z_observed, 0.01),
xytext=(z_observed + 0.3, 0.15),
fontsize=11,
arrowprops=dict(arrowstyle="->", color="green", linewidth=1.5),
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightyellow", edgecolor="gray"),
)
plt.tight_layout()
plt.savefig("hypothesis_testing_rejection_region.png", dpi=150, bbox_inches="tight")
plt.show()
p値の可視化
p値が分布のどの領域に対応するかを視覚的に示します。
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# --- パラメータ ---
z_observed = 3.0
p_value = 2 * (1 - stats.norm.cdf(z_observed))
# --- 描画 ---
x = np.linspace(-4, 4, 1000)
y = stats.norm.pdf(x)
fig, ax = plt.subplots(figsize=(10, 5))
# 分布全体
ax.plot(x, y, "k-", linewidth=2, label="標準正規分布 $N(0, 1)$")
# p値に対応する領域(左側)
x_p_left = x[x <= -z_observed]
ax.fill_between(x_p_left, stats.norm.pdf(x_p_left), color="orange", alpha=0.6,
label=f"p値の領域 (p = {p_value:.4f})")
# p値に対応する領域(右側)
x_p_right = x[x >= z_observed]
ax.fill_between(x_p_right, stats.norm.pdf(x_p_right), color="orange", alpha=0.6)
# 観測値の線
ax.axvline(z_observed, color="green", linestyle="-", linewidth=2.5,
label=f"観測値 $Z = {z_observed:.1f}$")
ax.axvline(-z_observed, color="green", linestyle="-", linewidth=2.5)
# 装飾
ax.set_xlabel("$Z$", fontsize=14)
ax.set_ylabel("確率密度", fontsize=14)
ax.set_title("p値の図示: $|Z| \\geq 3.0$ となる確率", fontsize=15)
ax.legend(fontsize=11, loc="upper left")
ax.grid(True, alpha=0.3)
# 注釈
ax.annotate(
f"p値 = {p_value:.4f}\nα = 0.05 より小さい\n→ 統計的に有意",
xy=(z_observed, stats.norm.pdf(z_observed)),
xytext=(1.5, 0.25),
fontsize=11,
arrowprops=dict(arrowstyle="->", color="orange", linewidth=1.5),
bbox=dict(boxstyle="round,pad=0.3", facecolor="lightyellow", edgecolor="gray"),
)
plt.tight_layout()
plt.savefig("hypothesis_testing_pvalue.png", dpi=150, bbox_inches="tight")
plt.show()
上記の3つのコードを実行すると、以下の結果が得られます。
- 正規近似による Z 検定: $Z = 3.0$、$p = 0.0027$ で帰無仮説を棄却
- 正確二項検定: scipy の
binomtestによる厳密な計算でも同様に棄却 - 棄却域の図: 観測値 $Z = 3.0$ が棄却域(赤い領域)に入っていることを視覚的に確認
- p値の図: オレンジ色の領域がp値に対応し、非常に小さい面積であることを確認
まとめ
本記事では、統計的仮説検定の基本概念を体系的に解説しました。
- 統計的検定 は、データから仮説の妥当性を確率的に判断する方法です。
- 帰無仮説 $H_0$ は「差がない」「効果がない」という仮説、対立仮説 $H_1$ は主張したい仮説です。
- 検定は「仮説設定 → 検定統計量の計算 → 棄却域またはp値 → 判定」の4ステップで行います。
- 有意水準 $\alpha$ は第1種の過誤の確率の上限であり、通常 0.05 を用います。
- p値 は帰無仮説のもとで観測データ以上に極端な結果が得られる確率であり、$\alpha$ と比較して判定します。
- 第1種の過誤(偽陽性)と 第2種の過誤(偽陰性)の2種類の誤りがあり、トレードオフの関係にあります。
- 検出力($1 – \beta$)は実際の効果を正しく検出できる確率であり、サンプルサイズを大きくすることで改善できます。
- コインの公平性を例に、二項検定の全手順を数式と Python コードで実演しました。
次の記事では、連続データに対する検定手法として t検定(1標本、2標本、対応ありの3種類)と、カテゴリデータに対する $\chi^2$(カイ二乗)検定 を解説します。