χ²検定とは?適合度検定と独立性検定をわかりやすく解説

はじめに

「このサイコロは本当に公平なのか?」「性別と購買行動に関連はあるのか?」「アンケートの回答分布は理論的な予測と一致するのか?」

こうした カテゴリデータ(度数データ) に関する問いに答えるための手法が $\chi^2$(カイ二乗)検定 です。t 検定が「平均値の差」を扱うのに対し、$\chi^2$ 検定は「観測された度数と期待される度数のずれ」を扱います。

データがカテゴリ(「はい/いいえ」「A/B/C」など)の場合には平均値を計算しても意味がありません。このようなカテゴリデータに対して適用できるのが $\chi^2$ 検定です。$\chi^2$ 検定には大きく分けて2つの種類があります。

  1. 適合度検定(goodness-of-fit test): 観測された度数が、ある理論的な分布に合致するかを検定する
  2. 独立性検定(test of independence): 2つのカテゴリ変数の間に関連があるかを検定する

本記事では、$\chi^2$ 分布の成り立ちから始めて、2つの検定それぞれについて $\chi^2$ 統計量の導出・手計算の具体例・Python 実装を丁寧に解説します。

本記事の内容

  • $\chi^2$ 分布の定義と性質
  • 適合度検定の理論とサイコロの例での手計算
  • 独立性検定の理論と分割表を使った手計算
  • イェーツの連続性補正
  • $\chi^2$ 検定の前提条件
  • Python(scipy.stats)での実装

前提知識

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

帰無仮説・対立仮説、p値、有意水準といった基本概念は上記の記事で解説しています。本記事ではこれらの概念を前提として進めます。

$\chi^2$ 分布とは

正規分布の2乗和から $\chi^2$ 分布へ

$\chi^2$ 検定を理解するには、まず $\chi^2$ 分布(カイ二乗分布) がどのような分布かを知る必要があります。

$Z_1, Z_2, \dots, Z_k$ が互いに独立に標準正規分布 $N(0, 1)$ に従うとき、その2乗和

$$ Q = Z_1^2 + Z_2^2 + \cdots + Z_k^2 = \sum_{i=1}^{k} Z_i^2 $$

自由度 $k$ の $\chi^2$ 分布 に従います。これを

$$ Q \sim \chi^2(k) $$

と書きます。直感的に言えば、「正規分布に従う値を2乗して足し合わせたもの」が $\chi^2$ 分布です。2乗しているため、値は常に非負になります。

$\chi^2$ 分布の確率密度関数

自由度 $k$ の $\chi^2$ 分布の確率密度関数は次のとおりです。

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

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

$\chi^2$ 分布の特徴

$\chi^2$ 分布には以下の特徴があります。

  • 定義域は非負: 2乗和なので必ず $x \geq 0$
  • 右に裾を引く非対称な分布: 特に自由度が小さいとき顕著
  • 平均 = 自由度: $E[Q] = k$
  • 分散 = 自由度の2倍: $\text{Var}(Q) = 2k$
  • 自由度が大きくなると正規分布に近づく: 中心極限定理により、$k$ が大きいとき $\chi^2(k)$ は近似的に $N(k, 2k)$ に従う
  • 加法性(再生性): $Q_1 \sim \chi^2(k_1)$, $Q_2 \sim \chi^2(k_2)$ が独立ならば $Q_1 + Q_2 \sim \chi^2(k_1 + k_2)$

自由度によって形状が大きく変わる点が重要です。自由度 1 や 2 では原点付近に値が集中して強い右裾を持ちますが、自由度が増えるにつれて山が右に移動し、より対称に近い形状になります。

t 分布との関係

前回の記事で登場した t 分布は、$\chi^2$ 分布を用いて定義されます。$Z \sim N(0,1)$、$V \sim \chi^2(\nu)$ が独立のとき

$$ T = \frac{Z}{\sqrt{V / \nu}} \sim t(\nu) $$

です。つまり、t 分布は「標準正規変量を $\chi^2$ 変量の平方根で割ったもの」として構成されています。

適合度検定

適合度検定とは

適合度検定(goodness-of-fit test) は、観測されたカテゴリの度数分布が、理論的に期待される分布と一致するかどうかを検定します。

たとえば「このサイコロは公平か?」という問いは、6つの目の観測度数が、公平なサイコロから期待される均等な度数と合っているかどうかを調べることに相当します。

仮説の設定

$k$ 個のカテゴリがあり、各カテゴリ $i$ の理論的な出現確率が $p_i$(ただし $\sum_{i=1}^{k} p_i = 1$)であるという仮説を検定します。

$$ H_0: \text{各カテゴリの出現確率は } p_1, p_2, \dots, p_k \text{ に等しい} $$

$$ H_1: \text{少なくとも1つのカテゴリで出現確率が理論値と異なる} $$

$\chi^2$ 統計量の導出

$n$ 回の試行を行い、カテゴリ $i$ の観測度数を $O_i$(Observed)、帰無仮説のもとでの期待度数を $E_i = n \cdot p_i$(Expected)とします。

帰無仮説が正しいとき、$n$ が十分に大きければ、中心極限定理と多項分布の性質により、各 $O_i$ は近似的に正規分布に従います。

$$ O_i \approx N(E_i, \; E_i(1 – p_i)) $$

これを標準化すると

$$ \frac{O_i – E_i}{\sqrt{E_i}} \approx N(0, 1) $$

が得られます(厳密には分散は $E_i(1-p_i)$ ですが、$\chi^2$ 統計量としてまとめると補正されます)。この標準化された値を2乗して全カテゴリにわたって合計すると、$\chi^2$ 統計量が定義されます。

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

この統計量の各項 $(O_i – E_i)^2 / E_i$ の意味を確認しましょう。

  • 分子 $(O_i – E_i)^2$: 観測度数と期待度数のずれの2乗
  • 分母 $E_i$: 期待度数で割ることで、期待度数の大きさに対する相対的なずれを評価

$\chi^2$ の値が大きいほど、観測データが帰無仮説の予測から大きくずれていることを意味します。

自由度

$O_1, O_2, \dots, O_k$ には $\sum_{i=1}^{k} O_i = n$ という制約があるため、$k$ 個の変量のうち自由に動けるのは $k – 1$ 個です。したがって、$\chi^2$ 統計量は帰無仮説のもとで近似的に 自由度 $k – 1$ の $\chi^2$ 分布 に従います。

$$ \chi^2 \sim \chi^2(k – 1) $$

もし期待度数の計算にデータから推定したパラメータを $m$ 個使っている場合は、自由度はさらに $m$ だけ減って $\text{df} = k – 1 – m$ となります。

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

サイコロを 60 回振り、各目の出た回数が以下のとおりでした。

1 2 3 4 5 6 合計
観測度数 $O_i$ 8 12 7 14 9 10 60

このサイコロは公平と言えるでしょうか?有意水準 $\alpha = 0.05$ で検定します。

ステップ1: 仮説の設定

$$ H_0: p_1 = p_2 = p_3 = p_4 = p_5 = p_6 = \frac{1}{6} \quad \text{(サイコロは公平)} $$

$$ H_1: \text{少なくとも1つの } p_i \neq \frac{1}{6} \quad \text{(サイコロは公平でない)} $$

ステップ2: 期待度数の計算

帰無仮説のもとで、各目の期待度数は

$$ E_i = 60 \times \frac{1}{6} = 10 \quad (i = 1, 2, \dots, 6) $$

です。

ステップ3: $\chi^2$ 統計量の計算

各カテゴリの寄与を1つずつ計算します。

$$ \frac{(O_1 – E_1)^2}{E_1} = \frac{(8 – 10)^2}{10} = \frac{4}{10} = 0.4 $$

$$ \frac{(O_2 – E_2)^2}{E_2} = \frac{(12 – 10)^2}{10} = \frac{4}{10} = 0.4 $$

$$ \frac{(O_3 – E_3)^2}{E_3} = \frac{(7 – 10)^2}{10} = \frac{9}{10} = 0.9 $$

$$ \frac{(O_4 – E_4)^2}{E_4} = \frac{(14 – 10)^2}{10} = \frac{16}{10} = 1.6 $$

$$ \frac{(O_5 – E_5)^2}{E_5} = \frac{(9 – 10)^2}{10} = \frac{1}{10} = 0.1 $$

$$ \frac{(O_6 – E_6)^2}{E_6} = \frac{(10 – 10)^2}{10} = \frac{0}{10} = 0.0 $$

合計すると

$$ \chi^2 = 0.4 + 0.4 + 0.9 + 1.6 + 0.1 + 0.0 = 3.4 $$

ステップ4: 自由度と臨界値

自由度は $k – 1 = 6 – 1 = 5$ です。

有意水準 $\alpha = 0.05$、自由度 5 の $\chi^2$ 分布の臨界値は

$$ \chi^2_{0.05, 5} = 11.07 $$

ステップ5: 判定

$$ \chi^2 = 3.4 < 11.07 = \chi^2_{0.05, 5} $$

$\chi^2$ 統計量が臨界値を下回るため、帰無仮説 $H_0$ を棄却しません。このデータからは、サイコロが公平でないとは言えません。

独立性検定

独立性検定とは

独立性検定(test of independence) は、2つのカテゴリ変数が 互いに独立かどうか を検定します。データは 分割表(クロス集計表、contingency table) の形で整理されます。

たとえば「性別と商品の購入傾向に関連はあるか?」という問いは、性別(男/女)と購入(する/しない)の2つのカテゴリ変数の独立性を検定することに相当します。

分割表と仮説の設定

$r$ 行 $c$ 列の分割表を考えます。行変数が $r$ 個のカテゴリ、列変数が $c$ 個のカテゴリを持ちます。

$B_1$ $B_2$ $\cdots$ $B_c$ 行計
$A_1$ $O_{11}$ $O_{12}$ $\cdots$ $O_{1c}$ $R_1$
$A_2$ $O_{21}$ $O_{22}$ $\cdots$ $O_{2c}$ $R_2$
$\vdots$ $\vdots$ $\vdots$ $\ddots$ $\vdots$ $\vdots$
$A_r$ $O_{r1}$ $O_{r2}$ $\cdots$ $O_{rc}$ $R_r$
列計 $C_1$ $C_2$ $\cdots$ $C_c$ $N$

ここで $O_{ij}$ はセル $(i, j)$ の観測度数、$R_i$ は第 $i$ 行の合計、$C_j$ は第 $j$ 列の合計、$N$ は総度数です。

仮説は次のように設定します。

$$ H_0: \text{変数 A と変数 B は独立である} $$

$$ H_1: \text{変数 A と変数 B は独立でない(関連がある)} $$

期待度数の計算

2つの変数が独立であるとき、セル $(i, j)$ の確率は行と列の周辺確率の積に等しくなります。

$$ P(\text{行} = i \text{ かつ 列} = j) = P(\text{行} = i) \times P(\text{列} = j) $$

周辺確率をデータから推定すると $\hat{P}(\text{行} = i) = R_i / N$、$\hat{P}(\text{列} = j) = C_j / N$ なので、期待度数は次のように計算されます。

$$ \begin{align} E_{ij} &= N \times P(\text{行} = i \text{ かつ 列} = j) \\ &= N \times \frac{R_i}{N} \times \frac{C_j}{N} \\ &= \frac{R_i \times C_j}{N} \end{align} $$

つまり、期待度数 = 行計 $\times$ 列計 $\div$ 総度数 という簡潔な式で計算できます。

$\chi^2$ 統計量

観測度数 $O_{ij}$ と期待度数 $E_{ij}$ から、$\chi^2$ 統計量を計算します。

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

この統計量は、帰無仮説のもとで近似的に自由度 $(r-1)(c-1)$ の $\chi^2$ 分布に従います。

$$ \chi^2 \sim \chi^2\bigl((r-1)(c-1)\bigr) $$

自由度が $(r-1)(c-1)$ である理由

分割表には $r \times c$ 個のセルがありますが、以下の制約が存在します。

  • 各行の度数の合計は $R_i$ に固定: $r$ 個の行制約のうち独立なものは $r – 1$ 個
  • 各列の度数の合計は $C_j$ に固定: $c$ 個の列制約のうち独立なものは $c – 1$ 個
  • 総度数 $N$ が固定という制約は行制約と列制約に共通

自由に動ける期待度数の数は

$$ r \times c – 1 – (r – 1) – (c – 1) = rc – r – c + 1 = (r-1)(c-1) $$

となります。

具体例: 性別と商品購入の関連

ある商品の購入行動を性別ごとに調査し、以下の分割表を得ました。

購入した 購入しなかった 行計
男性 40 60 100
女性 55 45 100
列計 95 105 200

有意水準 $\alpha = 0.05$ で、性別と購入行動に関連があるかを検定します。

ステップ1: 仮説の設定

$$ H_0: \text{性別と購入行動は独立である} $$

$$ H_1: \text{性別と購入行動は独立でない(関連がある)} $$

ステップ2: 期待度数の計算

$E_{ij} = R_i \times C_j / N$ を各セルで計算します。

$$ E_{11} = \frac{100 \times 95}{200} = \frac{9500}{200} = 47.5 $$

$$ E_{12} = \frac{100 \times 105}{200} = \frac{10500}{200} = 52.5 $$

$$ E_{21} = \frac{100 \times 95}{200} = \frac{9500}{200} = 47.5 $$

$$ E_{22} = \frac{100 \times 105}{200} = \frac{10500}{200} = 52.5 $$

期待度数の表:

購入した 購入しなかった
男性 47.5 52.5
女性 47.5 52.5

すべてのセルで $E_{ij} \geq 5$ であることを確認します。

ステップ3: $\chi^2$ 統計量の計算

各セルの寄与を計算します。

$$ \frac{(40 – 47.5)^2}{47.5} = \frac{(-7.5)^2}{47.5} = \frac{56.25}{47.5} \approx 1.184 $$

$$ \frac{(60 – 52.5)^2}{52.5} = \frac{(7.5)^2}{52.5} = \frac{56.25}{52.5} \approx 1.071 $$

$$ \frac{(55 – 47.5)^2}{47.5} = \frac{(7.5)^2}{47.5} = \frac{56.25}{47.5} \approx 1.184 $$

$$ \frac{(45 – 52.5)^2}{52.5} = \frac{(-7.5)^2}{52.5} = \frac{56.25}{52.5} \approx 1.071 $$

合計すると

$$ \chi^2 \approx 1.184 + 1.071 + 1.184 + 1.071 = 4.511 $$

ステップ4: 自由度と判定

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

有意水準 $\alpha = 0.05$、自由度 1 の $\chi^2$ 分布の臨界値は

$$ \chi^2_{0.05, 1} = 3.841 $$

$$ \chi^2 = 4.511 > 3.841 = \chi^2_{0.05, 1} $$

$\chi^2$ 統計量が臨界値を上回るため、帰無仮説 $H_0$ を棄却します。性別と商品の購入行動には統計的に有意な関連があると結論づけられます。観測データからは、女性の方が購入率が高い傾向が見て取れます。

イェーツの連続性補正

なぜ補正が必要か

$\chi^2$ 検定は、離散的な度数データに対して連続的な $\chi^2$ 分布で近似しています。特に $2 \times 2$ の分割表 で期待度数が小さい場合、この連続近似の誤差が大きくなります。

イェーツの連続性補正(Yates’s correction for continuity) は、この離散-連続のギャップを軽減するための修正です。

補正後の $\chi^2$ 統計量

$2 \times 2$ の分割表に対するイェーツの補正は、各セルの $|O_{ij} – E_{ij}|$ から $0.5$ を引いてから2乗します。

$$ \chi^2_{\text{Yates}} = \sum_{i=1}^{2} \sum_{j=1}^{2} \frac{\left(|O_{ij} – E_{ij}| – 0.5\right)^2}{E_{ij}} $$

補正により $\chi^2$ 統計量は小さくなるため、検定はより保守的になります(帰無仮説を棄却しにくくなります)。

先ほどの例にイェーツの補正を適用

先ほどの性別と購入行動の例($2 \times 2$ 分割表)に補正を適用してみましょう。

$$ \chi^2_{\text{Yates}} = \frac{(|40 – 47.5| – 0.5)^2}{47.5} + \frac{(|60 – 52.5| – 0.5)^2}{52.5} + \frac{(|55 – 47.5| – 0.5)^2}{47.5} + \frac{(|45 – 52.5| – 0.5)^2}{52.5} $$

各項を計算します。

$$ \frac{(7.5 – 0.5)^2}{47.5} = \frac{(7.0)^2}{47.5} = \frac{49.0}{47.5} \approx 1.032 $$

$$ \frac{(7.5 – 0.5)^2}{52.5} = \frac{(7.0)^2}{52.5} = \frac{49.0}{52.5} \approx 0.933 $$

$$ \frac{(7.5 – 0.5)^2}{47.5} = \frac{(7.0)^2}{47.5} = \frac{49.0}{47.5} \approx 1.032 $$

$$ \frac{(7.5 – 0.5)^2}{52.5} = \frac{(7.0)^2}{52.5} = \frac{49.0}{52.5} \approx 0.933 $$

合計すると

$$ \chi^2_{\text{Yates}} \approx 1.032 + 0.933 + 1.032 + 0.933 = 3.930 $$

臨界値 $\chi^2_{0.05, 1} = 3.841$ と比較すると、イェーツ補正後も $3.930 > 3.841$ なのでぎりぎり帰無仮説を棄却しますが、補正なしの $4.511$ に比べて値がかなり小さくなっていることがわかります。

イェーツの補正を使うべきか

イェーツの補正については統計学者の間でも意見が分かれています。

  • 補正を推奨する立場: 離散データに対する連続分布の近似を改善でき、第1種の過誤を適切に制御できる
  • 補正に否定的な立場: 過度に保守的になり、検出力が低下する(本来棄却すべき帰無仮説を棄却できなくなる)

近年では、サンプルサイズが十分に大きい場合は補正なしで問題ない とされることが多いです。一方、期待度数が 5 に近いような小規模データでは補正が有効です。

期待度数が非常に小さい場合(5未満のセルがある場合)は、$\chi^2$ 検定の近似自体が不適切なので、フィッシャーの正確検定(Fisher’s exact test) を使うべきです。

$\chi^2$ 検定の前提条件

$\chi^2$ 検定を正しく適用するためには、以下の条件を確認する必要があります。

1. 期待度数が十分に大きい

最も重要な前提条件は、全てのセルの期待度数 $E_i$(または $E_{ij}$)が 5 以上 であることです。

$$ E_i \geq 5 \quad \text{(全てのセルについて)} $$

$\chi^2$ 検定は $\chi^2$ 分布による近似に基づいているため、期待度数が小さすぎると近似の精度が悪化します。一般的なガイドラインは以下のとおりです。

  • 全てのセルで $E_i \geq 5$: $\chi^2$ 検定をそのまま適用可能
  • $E_i < 5$ のセルがある: カテゴリを統合するか、フィッシャーの正確検定を検討
  • $E_i < 1$ のセルがある: $\chi^2$ 検定は使用不可

2. 独立な観測

各観測は互いに独立でなければなりません。同一の個体が複数のセルに寄与しないようにする必要があります。

3. データは度数(カウント)

$\chi^2$ 検定の入力は度数(何回観測されたか)であり、割合やパーセンテージではありません。割合で計算すると誤った結果になるため、必ず元の度数データを使います。

Python での実装

適合度検定

import numpy as np
from scipy import stats

# =============================================
# 適合度検定: サイコロは公平か?
# =============================================
print("=" * 55)
print("適合度検定: サイコロの公平性")
print("=" * 55)

# 観測度数
observed = np.array([8, 12, 7, 14, 9, 10])

# 期待度数(公平なサイコロ: 各目が 60/6 = 10 回)
n_total = observed.sum()
expected = np.array([n_total / 6] * 6)

print(f"観測度数: {observed}")
print(f"期待度数: {expected}")
print(f"総試行回数: {n_total}")

# --- 手計算で χ² 統計量を求める ---
chi2_manual = np.sum((observed - expected)**2 / expected)
df = len(observed) - 1  # 自由度 = カテゴリ数 - 1
p_value_manual = 1 - stats.chi2.cdf(chi2_manual, df)

print(f"\n【手計算】")
print(f"χ² 統計量 = {chi2_manual:.4f}")
print(f"自由度 = {df}")
print(f"p値 = {p_value_manual:.4f}")

# --- scipy.stats.chisquare で検定 ---
chi2_stat, p_value = stats.chisquare(f_obs=observed, f_exp=expected)

print(f"\n【scipy.stats.chisquare】")
print(f"χ² 統計量 = {chi2_stat:.4f}")
print(f"p値 = {p_value:.4f}")

# 判定
alpha = 0.05
chi2_critical = stats.chi2.ppf(1 - alpha, df)
print(f"\n臨界値 (α={alpha}, df={df}): {chi2_critical:.4f}")
if p_value < alpha:
    print(f"判定: p値 ({p_value:.4f}) < α ({alpha}) → H₀を棄却")
else:
    print(f"判定: p値 ({p_value:.4f}) ≥ α ({alpha}) → H₀を棄却しない")
print("→ サイコロが公平でないとは言えない")

# 各カテゴリの寄与を確認
print("\n各目の寄与:")
for i in range(6):
    contrib = (observed[i] - expected[i])**2 / expected[i]
    print(f"  目{i+1}: O={observed[i]:2d}, E={expected[i]:.1f}, "
          f"(O-E)²/E = {contrib:.4f}")

独立性検定

import numpy as np
from scipy import stats

# =============================================
# 独立性検定: 性別と購入行動に関連はあるか?
# =============================================
print("=" * 55)
print("独立性検定: 性別と購入行動")
print("=" * 55)

# 分割表(観測度数)
#           購入した  購入しなかった
# 男性         40         60
# 女性         55         45
observed = np.array([
    [40, 60],
    [55, 45]
])

print("観測度数:")
print(observed)

# --- 手計算で期待度数を求める ---
row_totals = observed.sum(axis=1)
col_totals = observed.sum(axis=0)
n_total = observed.sum()

expected_manual = np.outer(row_totals, col_totals) / n_total
print(f"\n期待度数:")
print(expected_manual)

# 手計算で χ² 統計量
chi2_manual = np.sum((observed - expected_manual)**2 / expected_manual)
df_manual = (observed.shape[0] - 1) * (observed.shape[1] - 1)
print(f"\n【手計算】")
print(f"χ² 統計量 = {chi2_manual:.4f}")
print(f"自由度 = {df_manual}")

# --- scipy.stats.chi2_contingency ---
# correction=False: イェーツの補正なし
chi2_stat, p_value, dof, expected_table = stats.chi2_contingency(
    observed, correction=False
)

print(f"\n【scipy.stats.chi2_contingency (補正なし)】")
print(f"χ² 統計量 = {chi2_stat:.4f}")
print(f"自由度 = {dof}")
print(f"p値 = {p_value:.4f}")

# correction=True: イェーツの補正あり(2×2の場合のデフォルト)
chi2_yates, p_yates, dof_yates, _ = stats.chi2_contingency(
    observed, correction=True
)

print(f"\n【scipy.stats.chi2_contingency (イェーツ補正あり)】")
print(f"χ² 統計量 = {chi2_yates:.4f}")
print(f"p値 = {p_yates:.4f}")

# 判定
alpha = 0.05
print(f"\n有意水準 α = {alpha}")
if p_value < alpha:
    print(f"判定(補正なし): p値 ({p_value:.4f}) < α → H₀を棄却")
else:
    print(f"判定(補正なし): p値 ({p_value:.4f}) ≥ α → H₀を棄却しない")

if p_yates < alpha:
    print(f"判定(補正あり): p値 ({p_yates:.4f}) < α → H₀を棄却")
else:
    print(f"判定(補正あり): p値 ({p_yates:.4f}) ≥ α → H₀を棄却しない")

# 期待度数のチェック
if np.all(expected_table >= 5):
    print("\n前提条件: すべての期待度数が5以上 → OK")
else:
    print(f"\n警告: 期待度数が5未満のセルがあります(最小値: {expected_table.min():.2f})")

$\chi^2$ 分布の可視化

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

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

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

# --- 左図: 自由度を変えた χ² 分布 ---
ax = axes[0]
for k, color in [(1, "red"), (2, "orange"), (3, "green"),
                  (5, "blue"), (10, "purple")]:
    ax.plot(x, stats.chi2.pdf(x, df=k), linewidth=2, color=color,
            label=f"$k={k}$")

ax.set_xlabel("$x$", fontsize=14)
ax.set_ylabel("Probability density", fontsize=14)
ax.set_title("$\\chi^2$ distribution for various degrees of freedom",
             fontsize=13)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 25)
ax.set_ylim(0, 0.5)

# --- 右図: 適合度検定の棄却域(サイコロの例) ---
ax = axes[1]
k = 5
x_plot = np.linspace(0, 25, 1000)
y_plot = stats.chi2.pdf(x_plot, df=k)
chi2_critical = stats.chi2.ppf(0.95, df=k)
chi2_observed = 3.4

ax.plot(x_plot, y_plot, "k-", linewidth=2, label=f"$\\chi^2({k})$")

# 棄却域を塗りつぶし
x_reject = x_plot[x_plot >= chi2_critical]
ax.fill_between(x_reject, stats.chi2.pdf(x_reject, df=k),
                color="red", alpha=0.4,
                label=f"Rejection region ($\\alpha=0.05$)")

# 臨界値の線
ax.axvline(chi2_critical, color="red", linestyle="--", linewidth=1.5,
           label=f"Critical value = {chi2_critical:.2f}")

# 観測値の線
ax.axvline(chi2_observed, color="green", linestyle="-", linewidth=2.5,
           label=f"Observed $\\chi^2 = {chi2_observed:.1f}$")

ax.set_xlabel("$\\chi^2$", fontsize=14)
ax.set_ylabel("Probability density", fontsize=14)
ax.set_title("Goodness-of-fit test (dice example, $df=5$)", fontsize=13)
ax.legend(fontsize=10, loc="upper right")
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 25)
ax.set_ylim(0, 0.18)

plt.tight_layout()
plt.savefig("chi_squared_distribution.png", dpi=150, bbox_inches="tight")
plt.show()

まとめ

本記事では、$\chi^2$(カイ二乗)検定の2つの種類について、理論的な導出から Python 実装まで解説しました。

  • $\chi^2$ 分布 は、独立な標準正規変量の2乗和が従う分布であり、自由度 $k$ のとき平均 $k$、分散 $2k$ を持ちます。
  • 適合度検定 は、観測度数が理論的な期待度数と一致するかを検定します。$\chi^2$ 統計量は $\sum (O_i – E_i)^2 / E_i$ で計算し、自由度は $k – 1$ です。
  • 独立性検定 は、2つのカテゴリ変数が独立かどうかを分割表を用いて検定します。期待度数は $E_{ij} = R_i C_j / N$(行計 $\times$ 列計 $\div$ 総度数)で計算し、自由度は $(r-1)(c-1)$ です。
  • イェーツの連続性補正 は $2 \times 2$ 分割表において離散-連続のギャップを軽減しますが、サンプルサイズが十分ならば補正なしでも問題ありません。期待度数が小さい場合はフィッシャーの正確検定を検討してください。
  • $\chi^2$ 検定の前提条件として、全てのセルの期待度数が 5 以上 であることを確認する必要があります。
  • Python では scipy.stats.chisquare(適合度検定)と scipy.stats.chi2_contingency(独立性検定)で簡潔に実行できます。

次のステップとして、3群以上の平均値の差を検定する手法に進んでみてください。

  • F 検定 / 分散分析(ANOVA): 3群以上の平均の差を一度に検定する手法