カイ二乗検定の理論と使い方を解説

カイ二乗検定(chi-square test)は、カテゴリカルデータの分析に使われる最も基本的な検定手法です。観測されたデータが期待される分布に従うか(適合度検定)、2つの変数が独立か(独立性の検定)を判断します。

アンケートの分析、品質管理、遺伝学のメンデルの法則の検証など、カテゴリカルデータが関わるあらゆる場面で活用されます。

本記事の内容

  • カイ二乗分布の定義と性質
  • 適合度検定(goodness-of-fit test)
  • 独立性の検定(test of independence)
  • Pythonでの実装

前提知識

この記事を読む前に、以下の概念を理解しておくと理解が深まります。

  • 正規分布の基本
  • 仮説検定の基本的な枠組み

カイ二乗分布

定義

$Z_1, Z_2, \dots, Z_k$ が独立に標準正規分布 $N(0, 1)$ に従うとき、

$$ \chi^2 = Z_1^2 + Z_2^2 + \cdots + Z_k^2 $$

は自由度 $k$ のカイ二乗分布 $\chi^2(k)$ に従います。

確率密度関数

$$ f(x) = \frac{1}{2^{k/2} \Gamma(k/2)} x^{k/2 – 1} e^{-x/2} \quad (x > 0) $$

性質

  • 期待値: $E[\chi^2] = k$
  • 分散: $\mathrm{Var}(\chi^2) = 2k$
  • 非負の値のみを取る($\chi^2 \geq 0$)
  • $k$ が大きいとき、正規分布に近似できる(中心極限定理)

適合度検定

設定

$k$ 個のカテゴリがあり、各カテゴリの観測度数 $O_i$ と理論的な期待度数 $E_i$ が与えられているとき、データが理論的な分布に従うかを検定します。

帰無仮説: $H_0$: データは理論分布に従う

検定統計量:

$$ \chi^2 = \sum_{i=1}^{k} \frac{(O_i – E_i)^2}{E_i} $$

$H_0$ のもとで、$\chi^2 \sim \chi^2(k – 1)$(自由度 $k-1$)に近似的に従います。

直感的な理解

各カテゴリで「観測値と期待値のずれ」を計算し、期待値で正規化して合計しています。ずれが大きいほど $\chi^2$ 値が大きくなり、帰無仮説に対する証拠が強くなります。

具体例: サイコロの公正性の検定

サイコロを120回振った結果が以下のとき、サイコロが公正かを検定します。

1 2 3 4 5 6
観測度数 $O_i$ 15 23 18 21 24 19
期待度数 $E_i$ 20 20 20 20 20 20

$$ \chi^2 = \frac{(15-20)^2}{20} + \frac{(23-20)^2}{20} + \frac{(18-20)^2}{20} + \frac{(21-20)^2}{20} + \frac{(24-20)^2}{20} + \frac{(19-20)^2}{20} $$

$$ = \frac{25 + 9 + 4 + 1 + 16 + 1}{20} = \frac{56}{20} = 2.8 $$

自由度 $k – 1 = 5$ のカイ二乗分布で、$\chi^2_{0.05}(5) = 11.07$ です。$2.8 < 11.07$ なので帰無仮説を棄却できません。つまり、サイコロが不正であるという十分な証拠はありません。

独立性の検定

設定

2つのカテゴリカル変数が独立かどうかを分割表(クロス表)を用いて検定します。

$r \times c$ の分割表で、セル $(i, j)$ の観測度数を $O_{ij}$、期待度数を $E_{ij}$ とします。

2つの変数が独立なら、

$$ E_{ij} = \frac{R_i \times C_j}{N} $$

ここで $R_i$ は第 $i$ 行の合計、$C_j$ は第 $j$ 列の合計、$N$ は全体の合計です。

検定統計量:

$$ \chi^2 = \sum_{i=1}^{r} \sum_{j=1}^{c} \frac{(O_{ij} – E_{ij})^2}{E_{ij}} $$

自由度は $(r-1)(c-1)$ です。

具体例: 性別と商品の好みの独立性

商品A 商品B 商品C 合計
男性 30 20 50 100
女性 40 30 30 100
合計 70 50 80 200

独立を仮定した場合の期待度数:

$$ E_{11} = \frac{100 \times 70}{200} = 35, \quad E_{12} = \frac{100 \times 50}{200} = 25, \quad E_{13} = \frac{100 \times 80}{200} = 40 $$

商品A 商品B 商品C
男性 期待 35 25 40
女性 期待 35 25 40

$$ \chi^2 = \frac{(30-35)^2}{35} + \frac{(20-25)^2}{25} + \frac{(50-40)^2}{40} + \frac{(40-35)^2}{35} + \frac{(30-25)^2}{25} + \frac{(30-40)^2}{40} $$

$$ = \frac{25}{35} + \frac{25}{25} + \frac{100}{40} + \frac{25}{35} + \frac{25}{25} + \frac{100}{40} = 0.714 + 1.0 + 2.5 + 0.714 + 1.0 + 2.5 = 8.428 $$

自由度 $(2-1)(3-1) = 2$、$\chi^2_{0.05}(2) = 5.991$ なので、$8.428 > 5.991$ より帰無仮説を棄却します。性別と商品の好みには関連があると言えます。

Pythonでの実装

適合度検定

import numpy as np
from scipy import stats

# サイコロの公正性の検定
observed = np.array([15, 23, 18, 21, 24, 19])
expected = np.array([20, 20, 20, 20, 20, 20])

# 手計算
chi2_manual = np.sum((observed - expected)**2 / expected)
df = len(observed) - 1
p_manual = 1 - stats.chi2.cdf(chi2_manual, df)

print(f"手計算: χ² = {chi2_manual:.3f}, df = {df}, p = {p_manual:.4f}")

# SciPyによる検定
chi2_scipy, p_scipy = stats.chisquare(observed, expected)
print(f"SciPy:  χ² = {chi2_scipy:.3f}, p = {p_scipy:.4f}")

if p_scipy < 0.05:
    print("結論: サイコロは公正でない可能性がある(有意水準5%)")
else:
    print("結論: サイコロが不正であるという証拠は不十分")

独立性の検定

import numpy as np
from scipy import stats

# 分割表
observed = np.array([[30, 20, 50],
                     [40, 30, 30]])

chi2, p, dof, expected = stats.chi2_contingency(observed)

print("観測度数:")
print(observed)
print(f"\n期待度数:")
print(np.round(expected, 1))
print(f"\nχ² = {chi2:.3f}")
print(f"自由度 = {dof}")
print(f"p値 = {p:.4f}")

if p < 0.05:
    print("結論: 2変数は独立でない(関連がある)")
else:
    print("結論: 2変数が独立でないとは言えない")

カイ二乗分布の可視化

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

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

x = np.linspace(0, 25, 500)

for df in [1, 2, 3, 5, 10]:
    ax.plot(x, stats.chi2.pdf(x, df), linewidth=2, label=f'$k = {df}$')

ax.set_xlabel('$x$')
ax.set_ylabel('Density')
ax.set_title('Chi-squared distribution $\\chi^2(k)$')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 0.5)

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

適合度検定のシミュレーション

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

np.random.seed(42)

# 公正なサイコロのシミュレーション
n_simulations = 10000
n_rolls = 120
chi2_values = []

for _ in range(n_simulations):
    rolls = np.random.randint(1, 7, n_rolls)
    observed = np.array([np.sum(rolls == i) for i in range(1, 7)])
    expected = np.full(6, n_rolls / 6)
    chi2 = np.sum((observed - expected)**2 / expected)
    chi2_values.append(chi2)

fig, ax = plt.subplots(figsize=(10, 6))
ax.hist(chi2_values, bins=50, density=True, alpha=0.7, color='steelblue',
        edgecolor='white', label='Simulated')

x = np.linspace(0, 20, 200)
ax.plot(x, stats.chi2.pdf(x, 5), 'r-', linewidth=2, label='$\\chi^2(5)$ theory')
ax.axvline(x=stats.chi2.ppf(0.95, 5), color='green', linestyle='--', linewidth=2,
           label=f'Critical value ($\\alpha=0.05$) = {stats.chi2.ppf(0.95, 5):.2f}')

ax.set_xlabel('$\\chi^2$')
ax.set_ylabel('Density')
ax.set_title('Chi-squared goodness-of-fit test: Simulated distribution')
ax.legend()
ax.grid(True, alpha=0.3)

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

まとめ

本記事では、カイ二乗検定の理論と使い方を解説しました。

  • カイ二乗分布: 標準正規変数の二乗和が従う分布
  • 適合度検定: 観測データが理論分布に従うかを検定
  • 独立性の検定: 2つのカテゴリカル変数が独立かを検定
  • 検定統計量: $\chi^2 = \sum (O_i – E_i)^2 / E_i$
  • 注意: 期待度数が5未満のセルがある場合はフィッシャーの正確検定を使うべき

次のステップとして、分散分析(ANOVA)について学びましょう。