t検定(1標本・2標本・対応あり)の理論と実装

t検定は、母分散が未知のときに母平均に関する仮説を検定する手法で、実用上最も広く使われている検定のひとつです。z検定が母分散既知を前提とするのに対し、t検定は標本から推定した分散を使うため、現実のデータ分析に適しています。

t検定は1908年にウィリアム・ゴセット(ペンネーム: Student)がギネスビール醸造所の品質管理のために考案しました。小標本でも信頼性の高い推論ができるこの手法は、100年以上経った今でもデータ分析の基本ツールです。

本記事の内容

  • t分布の定義と性質
  • 1標本t検定の検定統計量の導出
  • 対応のある2標本t検定
  • 独立2標本t検定(等分散の場合とウェルチの場合)
  • scipy.stats での実装

前提知識

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

t分布とは

定義

$Z \sim N(0, 1)$ と $V \sim \chi^2_k$(自由度 $k$ のカイ二乗分布)が独立のとき、

$$ T = \frac{Z}{\sqrt{V / k}} $$

自由度 $k$ のt分布 $t_k$ に従います。

確率密度関数

$$ f(t) = \frac{\Gamma\left(\frac{k+1}{2}\right)}{\sqrt{k\pi}\,\Gamma\left(\frac{k}{2}\right)}\left(1 + \frac{t^2}{k}\right)^{-\frac{k+1}{2}} $$

ここで $\Gamma(\cdot)$ はガンマ関数です。

t分布の性質

  • 平均 0 で左右対称
  • 自由度が小さいほど裾が重い(標準正規分布より外れ値が出やすい)
  • 自由度 $k \to \infty$ で標準正規分布 $N(0, 1)$ に収束

なぜz検定ではなくt検定が必要か

z検定の検定統計量は $Z = \frac{\bar{X} – \mu_0}{\sigma / \sqrt{n}}$ ですが、実際には母標準偏差 $\sigma$ は未知であり、標本標準偏差 $s$ で推定する必要があります。

$$ s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(X_i – \bar{X})^2} $$

$\sigma$ を $s$ で置き換えたとき、検定統計量は

$$ T = \frac{\bar{X} – \mu_0}{s / \sqrt{n}} $$

となりますが、$s$ は確率変数であり、$T$ は標準正規分布には従いません。$T$ が従う分布を導出しましょう。

t分布に従うことの導出

$H_0: \mu = \mu_0$ のもとで $X_i \sim N(\mu_0, \sigma^2)$ です。

$$ Z = \frac{\bar{X} – \mu_0}{\sigma / \sqrt{n}} \sim N(0, 1) $$

また、

$$ \frac{(n-1)s^2}{\sigma^2} = \sum_{i=1}^{n}\left(\frac{X_i – \bar{X}}{\sigma}\right)^2 \sim \chi^2_{n-1} $$

が成り立ちます。正規母集団において $\bar{X}$ と $s^2$ は独立であることが知られています。

$T$ を変形します。

$$ \begin{align} T &= \frac{\bar{X} – \mu_0}{s / \sqrt{n}} \\ &= \frac{(\bar{X} – \mu_0) / (\sigma / \sqrt{n})}{s / \sigma} \\ &= \frac{Z}{\sqrt{s^2 / \sigma^2}} \\ &= \frac{Z}{\sqrt{\chi^2_{n-1} / (n-1)}} \end{align} $$

これはまさにt分布の定義の形であり、$T \sim t_{n-1}$ です。

1標本t検定

問題設定

母集団 $X \sim N(\mu, \sigma^2)$($\sigma^2$ 未知)から $n$ 個の標本を抽出します。

$$ H_0: \mu = \mu_0, \quad H_1: \mu \neq \mu_0 $$

検定統計量

$$ T = \frac{\bar{X} – \mu_0}{s / \sqrt{n}} \sim t_{n-1} \quad (\text{$H_0$ のもと}) $$

棄却域(両側検定)

$$ |T| > t_{\alpha/2, n-1} $$

ここで $t_{\alpha/2, n-1}$ は自由度 $n-1$ のt分布の上側 $\alpha/2$ 点です。

p値

$$ p = 2P(T_{n-1} \geq |t_{\text{obs}}|) $$

対応のある2標本t検定

問題設定

同一の被験者に対して処理前後の測定を行い、処理の効果を検定します。$n$ 組のペアデータ $(X_i, Y_i)$ に対し、差 $D_i = X_i – Y_i$ を計算します。

$$ H_0: \mu_D = 0, \quad H_1: \mu_D \neq 0 $$

検定統計量

差の標本平均 $\bar{D}$ と標本標準偏差 $s_D$ を用いて、

$$ T = \frac{\bar{D}}{s_D / \sqrt{n}} \sim t_{n-1} \quad (\text{$H_0$ のもと}) $$

これは差 $D_i$ に対する1標本t検定と同じ形です。

なぜ対応を考慮するのか

対応のあるデータでは、個体差の影響が差 $D_i$ を取ることで相殺されます。たとえば薬の効果を調べるとき、被験者ごとの体重の違いなどの個人差は $D_i = X_i – Y_i$ を取ることで除去されるため、検出力が向上します。

独立2標本t検定

問題設定

2つの独立な母集団からの標本を比較します。

$$ X_1, \dots, X_{n_1} \sim N(\mu_1, \sigma_1^2), \quad Y_1, \dots, Y_{n_2} \sim N(\mu_2, \sigma_2^2) $$

$$ H_0: \mu_1 = \mu_2, \quad H_1: \mu_1 \neq \mu_2 $$

等分散の場合(Studentのt検定)

$\sigma_1^2 = \sigma_2^2 = \sigma^2$ を仮定するとき、共通の分散をプールした分散で推定します。

$$ s_p^2 = \frac{(n_1 – 1)s_1^2 + (n_2 – 1)s_2^2}{n_1 + n_2 – 2} $$

ここで $s_1^2, s_2^2$ はそれぞれの群の標本分散です。

検定統計量は次のようになります。

$$ T = \frac{\bar{X} – \bar{Y}}{s_p\sqrt{\dfrac{1}{n_1} + \dfrac{1}{n_2}}} \sim t_{n_1 + n_2 – 2} $$

導出

$H_0$ のもとで $\bar{X} – \bar{Y} \sim N(0, \sigma^2(1/n_1 + 1/n_2))$ です。

$\sigma^2$ を $s_p^2$ で推定すると、z検定の統計量の $\sigma$ を $s_p$ に置き換えた形になり、

$$ T = \frac{(\bar{X} – \bar{Y}) / \sqrt{\sigma^2(1/n_1 + 1/n_2)}}{s_p / \sigma} = \frac{Z}{\sqrt{\chi^2_{n_1+n_2-2}/(n_1+n_2-2)}} $$

$Z$ と $\chi^2$ が独立であることから $T \sim t_{n_1+n_2-2}$ が得られます。

等分散でない場合(ウェルチのt検定)

$\sigma_1^2 \neq \sigma_2^2$ の場合、ウェルチのt検定(Welch’s t-test) を使います。

$$ T = \frac{\bar{X} – \bar{Y}}{\sqrt{\dfrac{s_1^2}{n_1} + \dfrac{s_2^2}{n_2}}} $$

自由度は ウェルチ-サタスウェイトの近似 で求めます。

$$ \nu = \frac{\left(\dfrac{s_1^2}{n_1} + \dfrac{s_2^2}{n_2}\right)^2}{\dfrac{(s_1^2/n_1)^2}{n_1 – 1} + \dfrac{(s_2^2/n_2)^2}{n_2 – 1}} $$

この $\nu$ は一般に整数にならないため、近い値に丸めるか、連続的な自由度として扱います。

ウェルチのt検定は等分散を仮定しないため、等分散かどうか不明な場合は常にウェルチのt検定を使うのが安全です。

Pythonでの実装

1標本t検定

import numpy as np
from scipy import stats

# データ: ある学校の生徒30人のテスト点数
np.random.seed(42)
scores = np.random.normal(72, 10, 30)

# 帰無仮説: 母平均は70点
mu_0 = 70

# 1標本t検定
t_stat, p_value = stats.ttest_1samp(scores, mu_0)

print("=== 1標本t検定 ===")
print(f"標本サイズ: n = {len(scores)}")
print(f"標本平均: {np.mean(scores):.4f}")
print(f"標本標準偏差: {np.std(scores, ddof=1):.4f}")
print(f"検定統計量: t = {t_stat:.4f}")
print(f"p値 (両側): {p_value:.4f}")
print(f"α = 0.05 での判定: {'棄却' if p_value < 0.05 else '棄却しない'}")

対応のあるt検定

import numpy as np
from scipy import stats

# データ: 10人の被験者のダイエット前後の体重
np.random.seed(42)
before = np.random.normal(70, 8, 10)
after = before - np.random.normal(2, 3, 10)  # 平均2kg減少

# 対応のあるt検定
t_stat, p_value = stats.ttest_rel(before, after)

print("=== 対応のある2標本t検定 ===")
print(f"被験者数: n = {len(before)}")
print(f"前: 平均 = {np.mean(before):.2f} kg")
print(f"後: 平均 = {np.mean(after):.2f} kg")
print(f"差の平均: {np.mean(before - after):.2f} kg")
print(f"検定統計量: t = {t_stat:.4f}")
print(f"p値 (両側): {p_value:.4f}")
print(f"α = 0.05 での判定: {'棄却' if p_value < 0.05 else '棄却しない'}")

独立2標本t検定(ウェルチ)

import numpy as np
from scipy import stats

# データ: 2つの教授法でのテスト点数
np.random.seed(42)
method_a = np.random.normal(75, 10, 25)
method_b = np.random.normal(70, 12, 30)

# ウェルチのt検定(equal_var=False)
t_stat, p_value = stats.ttest_ind(method_a, method_b, equal_var=False)

print("=== 独立2標本t検定(ウェルチ)===")
print(f"群A: n={len(method_a)}, 平均={np.mean(method_a):.2f}, SD={np.std(method_a, ddof=1):.2f}")
print(f"群B: n={len(method_b)}, 平均={np.mean(method_b):.2f}, SD={np.std(method_b, ddof=1):.2f}")
print(f"検定統計量: t = {t_stat:.4f}")
print(f"p値 (両側): {p_value:.4f}")
print(f"α = 0.05 での判定: {'棄却' if p_value < 0.05 else '棄却しない'}")

# 等分散を仮定した場合との比較
t_stat_eq, p_value_eq = stats.ttest_ind(method_a, method_b, equal_var=True)
print(f"\n--- 参考: 等分散仮定 (Student) ---")
print(f"検定統計量: t = {t_stat_eq:.4f}")
print(f"p値 (両側): {p_value_eq:.4f}")

t分布とz検定の比較を可視化

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

x = np.linspace(-5, 5, 1000)

fig, ax = plt.subplots(figsize=(10, 6))

# 標準正規分布
ax.plot(x, stats.norm.pdf(x), 'k-', linewidth=2.5, label='$N(0, 1)$')

# 各自由度のt分布
for df, color in zip([2, 5, 10, 30], ['red', 'orange', 'green', 'blue']):
    ax.plot(x, stats.t.pdf(x, df), color=color, linewidth=1.5,
            linestyle='--', label=f'$t_{{{df}}}$')

ax.set_xlabel('$t$', fontsize=13)
ax.set_ylabel('Probability Density', fontsize=13)
ax.set_title('t-distribution vs Standard Normal Distribution', fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_ylim(bottom=0)
plt.tight_layout()
plt.show()

3種類のt検定の使い分けフローチャート

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

# シミュレーション: 3種類のt検定の検出力比較
np.random.seed(42)
n_sim = 5000
alpha = 0.05

# 真の効果がある状況
mu1, mu2 = 5.0, 4.0
sigma = 2.0
n = 15

reject_paired = 0
reject_ind = 0

for _ in range(n_sim):
    # 対応のあるデータ(個体差あり)
    subject_effect = np.random.normal(0, 5, n)
    x_paired = mu1 + subject_effect + np.random.normal(0, sigma, n)
    y_paired = mu2 + subject_effect + np.random.normal(0, sigma, n)

    # 対応のあるt検定
    _, p_paired = stats.ttest_rel(x_paired, y_paired)
    if p_paired < alpha:
        reject_paired += 1

    # 独立2標本t検定(対応を無視)
    _, p_ind = stats.ttest_ind(x_paired, y_paired, equal_var=True)
    if p_ind < alpha:
        reject_ind += 1

power_paired = reject_paired / n_sim
power_ind = reject_ind / n_sim

fig, ax = plt.subplots(figsize=(8, 5))
bars = ax.bar(['Paired t-test', 'Independent t-test'],
              [power_paired, power_ind],
              color=['steelblue', 'coral'], edgecolor='black')
ax.axhline(y=0.80, color='gray', linestyle='--', linewidth=1.5,
           label='Target power = 0.80')

ax.set_ylabel('Power ($1 - \\beta$)', fontsize=13)
ax.set_title('Paired vs Independent t-test: Power Comparison', fontsize=14)
ax.legend(fontsize=11)
ax.set_ylim(0, 1)
ax.grid(True, alpha=0.3, axis='y')

for bar, val in zip(bars, [power_paired, power_ind]):
    ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.02,
            f'{val:.3f}', ha='center', fontsize=12, fontweight='bold')

plt.tight_layout()
plt.show()

print(f"対応のあるt検定の検出力: {power_paired:.4f}")
print(f"独立2標本t検定の検出力: {power_ind:.4f}")

対応のあるデータに対して対応のあるt検定を使うと、個体差を除去できるため検出力が大幅に向上します。

まとめ

本記事では、t検定の理論と実装について解説しました。

  • t分布: $T = Z / \sqrt{\chi^2_k / k}$ で定義。自由度が小さいほど裾が重く、$k \to \infty$ で正規分布に収束
  • 1標本t検定: $T = \frac{\bar{X} – \mu_0}{s / \sqrt{n}} \sim t_{n-1}$。母分散未知で母平均を検定
  • 対応のあるt検定: 差 $D_i$ に対する1標本t検定。個体差を除去して検出力を向上
  • 独立2標本t検定: 等分散なら $t_{n_1+n_2-2}$、等分散でなければウェルチの近似自由度を使用
  • 実用上のポイント: 等分散かどうか不明なときはウェルチのt検定が安全

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