F検定と分散分析(ANOVA)をわかりやすく解説

はじめに

「3つの異なる肥料を使ったとき、作物の収穫量に差はあるか?」「4種類の教育法で学生の成績に差があるか?」

こうした 3群以上の平均を同時に比較する問題 に対して統計的に答える手法が 分散分析(ANOVA: Analysis of Variance) です。

「2群の比較なら t 検定を使えばいいのでは?」と思うかもしれません。しかし、3群以上の比較で t 検定を繰り返し適用すると深刻な問題が生じます。たとえば3群 A, B, C があるとき、t 検定を A vs B、A vs C、B vs C の3回行う必要があります。各検定の有意水準を $\alpha = 0.05$ とすると、少なくとも1回は誤って帰無仮説を棄却してしまう確率(ファミリーワイズ誤り率, FWER)は

$$ \text{FWER} = 1 – (1 – 0.05)^3 = 1 – 0.9574 \approx 0.143 $$

となり、名目上の 5% よりもはるかに大きくなります。群数が増えるほど状況は悪化し、$k$ 群では $\binom{k}{2} = k(k-1)/2$ 回の検定が必要です。$k = 5$ なら 10 回の検定となり、FWER は約 40% にまで膨張します。これが 多重比較問題(multiple comparison problem) です。

分散分析は、この問題を回避し、1回の検定で「すべての群の母平均が等しいか」 を判定します。そのとき使われる統計量が F 統計量 であり、帰無仮説のもとで F 分布 に従います。

本記事の内容

  • F 分布の定義と性質(カイ二乗分布の比)
  • 等分散の F 検定(2群の分散比の検定)
  • 一元配置 ANOVA の理論(SST = SSB + SSW の分解を丁寧に導出)
  • F 統計量の構成と自由度
  • 具体的な数値例による手計算
  • ANOVA の前提条件
  • Python(scipy.stats)での実装と手計算の検証

前提知識

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

t 検定で扱った仮説検定の基本(帰無仮説・対立仮説・p 値・有意水準・第1種の過誤)や t 分布の考え方を前提として進めます。

F 分布とは

カイ二乗分布の復習

F 分布を理解するには、まず カイ二乗分布 を押さえておく必要があります。

標準正規分布 $N(0,1)$ に従う独立な確率変数 $Z_1, Z_2, \dots, Z_k$ があるとき、その二乗和

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

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

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

直感的に言えば、カイ二乗分布は「正規分布に従う変数のばらつきの大きさ」を測る分布です。

正規母集団 $N(\mu, \sigma^2)$ からサイズ $n$ の標本を取ったとき、不偏分散 $s^2$ と母分散 $\sigma^2$ の間には

$$ \frac{(n-1)\,s^2}{\sigma^2} \sim \chi^2(n-1) $$

の関係が成り立ちます。つまり、標本分散に基づく量はカイ二乗分布に従います。

F 分布の定義

F 分布 は、2つの独立なカイ二乗確率変数の比として定義されます。$U \sim \chi^2(d_1)$、$V \sim \chi^2(d_2)$ が独立であるとき、

$$ F = \frac{U / d_1}{V / d_2} $$

自由度 $(d_1, d_2)$ の F 分布 に従います。

$$ F \sim F(d_1, d_2) $$

ここで $d_1$ を 第1自由度(分子の自由度)、$d_2$ を 第2自由度(分母の自由度) と呼びます。F 分布は「2つの分散の推定値の比がどのような値をとるか」を記述する分布です。

F 分布の確率密度関数

自由度 $(d_1, d_2)$ の F 分布の確率密度関数は次のとおりです。

$$ f(x) = \frac{1}{B\!\left(\frac{d_1}{2},\, \frac{d_2}{2}\right)} \left(\frac{d_1}{d_2}\right)^{d_1/2} \frac{x^{(d_1/2)-1}}{\left(1 + \frac{d_1}{d_2}\,x\right)^{(d_1+d_2)/2}} \quad (x > 0) $$

ここで $B(\cdot, \cdot)$ はベータ関数です。

$$ B(a, b) = \frac{\Gamma(a)\,\Gamma(b)}{\Gamma(a + b)} $$

F 分布の性質

F 分布にはいくつかの重要な性質があります。

  • 正の値のみ: 分散の比なので $F \geq 0$
  • 非対称: 右に裾が広い形状で、左右対称ではない
  • 逆数の性質: $F \sim F(d_1, d_2)$ ならば $1/F \sim F(d_2, d_1)$
  • t 分布との関係: $T \sim t(\nu)$ ならば $T^2 \sim F(1, \nu)$。つまり、2群の ANOVA は t 検定と等価
  • 期待値: $d_2 > 2$ のとき $E[F] = d_2 / (d_2 – 2)$
  • 自由度が大きくなると 1 付近に集中: $d_1, d_2$ が共に大きくなると分布の裾は狭くなる

等分散の F 検定

F 分布の最も基本的な応用は、2つの母集団の分散が等しいかどうかを検定する ことです。これが 等分散の F 検定 です。t 検定(Student の t 検定)の前提条件である等分散性を確認するためにしばしば用いられます。

問題設定と仮説

2つの独立な正規母集団 $N(\mu_1, \sigma_1^2)$ と $N(\mu_2, \sigma_2^2)$ から、それぞれ $n_1$ 個、$n_2$ 個のサンプルを取ります。

$$ H_0: \sigma_1^2 = \sigma_2^2 \quad \text{(2つの母分散は等しい)} $$

$$ H_1: \sigma_1^2 \neq \sigma_2^2 \quad \text{(2つの母分散は異なる)} $$

F 統計量の導出

各群の不偏分散を $s_1^2$, $s_2^2$ とします。正規母集団の性質から、

$$ \frac{(n_1 – 1)\,s_1^2}{\sigma_1^2} \sim \chi^2(n_1 – 1), \quad \frac{(n_2 – 1)\,s_2^2}{\sigma_2^2} \sim \chi^2(n_2 – 1) $$

が成り立ちます。F 分布の定義に従い、帰無仮説 $H_0: \sigma_1^2 = \sigma_2^2$ のもとで F 統計量を構成します。

$$ \begin{align} F &= \frac{\dfrac{(n_1 – 1)\,s_1^2}{\sigma_1^2} \Big/ (n_1 – 1)}{\dfrac{(n_2 – 1)\,s_2^2}{\sigma_2^2} \Big/ (n_2 – 1)} \\ &= \frac{s_1^2 / \sigma_1^2}{s_2^2 / \sigma_2^2} \\ &= \frac{s_1^2}{s_2^2} \cdot \frac{\sigma_2^2}{\sigma_1^2} \\ &= \frac{s_1^2}{s_2^2} \quad (\because \sigma_1^2 = \sigma_2^2 \text{ under } H_0) \end{align} $$

つまり、帰無仮説のもとで F 統計量は 2つの不偏分散の比 になります。

$$ F = \frac{s_1^2}{s_2^2} \sim F(n_1 – 1,\; n_2 – 1) $$

慣例として、大きい方の分散を分子に置きます($s_1^2 \geq s_2^2$ となるように)。F 値が 1 に近ければ2つの分散は似ており、1 から大きく離れるほど分散の差が大きいことを示唆します。

具体例: 2つの製造ラインの品質比較

ある工場の2つの製造ラインで生産された部品の重量のばらつきを比較します。

ライン A ($n_1 = 8$): $\{50.2,\; 49.8,\; 51.0,\; 50.5,\; 49.6,\; 50.8,\; 50.1,\; 50.0\}$

ライン B ($n_2 = 10$): $\{51.5,\; 48.2,\; 52.0,\; 49.0,\; 50.8,\; 47.5,\; 51.8,\; 49.5,\; 52.5,\; 48.2\}$

有意水準 $\alpha = 0.05$ で等分散性を両側検定します。

ステップ1: 不偏分散の計算

ライン A の標本平均は

$$ \bar{X}_A = \frac{50.2 + 49.8 + 51.0 + 50.5 + 49.6 + 50.8 + 50.1 + 50.0}{8} = \frac{402.0}{8} = 50.25 $$

偏差平方和を計算します。

$$ \sum_{i=1}^{8}(X_i – 50.25)^2 = (-0.05)^2 + (-0.45)^2 + (0.75)^2 + (0.25)^2 + (-0.65)^2 + (0.55)^2 + (-0.15)^2 + (-0.25)^2 $$

$$ = 0.0025 + 0.2025 + 0.5625 + 0.0625 + 0.4225 + 0.3025 + 0.0225 + 0.0625 = 1.6400 $$

$$ s_A^2 = \frac{1.6400}{7} \approx 0.2343 $$

ライン B の標本平均は

$$ \bar{X}_B = \frac{51.5 + 48.2 + 52.0 + 49.0 + 50.8 + 47.5 + 51.8 + 49.5 + 52.5 + 48.2}{10} = \frac{501.0}{10} = 50.10 $$

$$ \sum_{j=1}^{10}(Y_j – 50.10)^2 = 1.96 + 3.61 + 3.61 + 1.21 + 0.49 + 6.76 + 2.89 + 0.36 + 5.76 + 3.61 = 30.26 $$

$$ s_B^2 = \frac{30.26}{9} \approx 3.3622 $$

ステップ2: F 統計量の計算

大きい方の分散を分子に置きます。

$$ F = \frac{s_B^2}{s_A^2} = \frac{3.3622}{0.2343} \approx 14.35 $$

ステップ3: 判定

自由度 $(d_1, d_2) = (9, 7)$、両側検定なので上側 $\alpha/2 = 0.025$ の臨界値を使います。$F_{0.025}(9, 7) \approx 4.82$ です。

$$ F = 14.35 > 4.82 $$

帰無仮説を棄却します。2つの製造ラインの分散は統計的に有意に異なると結論づけられます。

F 検定の注意点

F 検定は 正規性の仮定に対して敏感 です。データが正規分布から外れていると結果が信頼できなくなります。等分散性の確認には、正規性に対してよりロバストな レーベン検定(Levene’s test) を使う方が実務上は安全です。

一元配置分散分析(One-Way ANOVA)

問題設定

$k$ 個の群(処理水準)があり、$i$ 番目の群には $n_i$ 個の観測値があります。全観測数を $N = n_1 + n_2 + \cdots + n_k$ とします。

各群は正規分布に従い、分散は共通であると仮定します。

$$ X_{ij} \sim N(\mu_i, \sigma^2) \quad (i = 1, 2, \dots, k, \quad j = 1, 2, \dots, n_i) $$

仮説の設定

$$ H_0: \mu_1 = \mu_2 = \cdots = \mu_k \quad \text{(すべての群の母平均が等しい)} $$

$$ H_1: \text{少なくとも1つの } \mu_i \text{ が他と異なる} $$

注意すべき点として、対立仮説は「どの群が異なるか」は特定せず、「少なくとも1組の平均に差がある」という形であることです。

基本統計量の定義

各群の標本平均(群平均)を

$$ \bar{X}_{i\cdot} = \frac{1}{n_i} \sum_{j=1}^{n_i} X_{ij} $$

全体の標本平均(総平均)を

$$ \bar{X}_{\cdot\cdot} = \frac{1}{N} \sum_{i=1}^{k} \sum_{j=1}^{n_i} X_{ij} $$

と定義します。

変動の分解: SST = SSB + SSW

ANOVA の核心は、データの全変動を「群間変動」と「群内変動」に分解する ことです。ここではこの分解を1行ずつ丁寧に導出します。

各データ $X_{ij}$ と総平均 $\bar{X}_{\cdot\cdot}$ の偏差を、群平均 $\bar{X}_{i\cdot}$ を仲介して分解します。

$$ X_{ij} – \bar{X}_{\cdot\cdot} = (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot}) + (X_{ij} – \bar{X}_{i\cdot}) $$

右辺の第1項 $(\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})$ は「群平均と総平均の差(群間のずれ)」、第2項 $(X_{ij} – \bar{X}_{i\cdot})$ は「データと群平均の差(群内のばらつき)」を表します。

両辺を二乗して全データについて和を取ります。

$$ \sum_{i=1}^{k} \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{\cdot\cdot})^2 = \sum_{i=1}^{k} \sum_{j=1}^{n_i} \left[(\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot}) + (X_{ij} – \bar{X}_{i\cdot})\right]^2 $$

右辺を展開します。$(a + b)^2 = a^2 + 2ab + b^2$ を適用すると、

$$ \begin{align} &\sum_{i=1}^{k} \sum_{j=1}^{n_i} \left[(\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot}) + (X_{ij} – \bar{X}_{i\cdot})\right]^2 \\ &= \underbrace{\sum_{i=1}^{k} \sum_{j=1}^{n_i} (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2}_{\text{第1項}} + \underbrace{2 \sum_{i=1}^{k} \sum_{j=1}^{n_i} (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})(X_{ij} – \bar{X}_{i\cdot})}_{\text{交差項}} + \underbrace{\sum_{i=1}^{k} \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{i\cdot})^2}_{\text{第3項}} \end{align} $$

交差項がゼロになることの証明:

群 $i$ について、$j$ に関する内側の和を計算します。$(\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})$ は $j$ に依存しないので和の外に出せます。

$$ \sum_{j=1}^{n_i} (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})(X_{ij} – \bar{X}_{i\cdot}) = (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot}) \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{i\cdot}) $$

ここで、標本平均の定義から

$$ \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{i\cdot}) = \sum_{j=1}^{n_i} X_{ij} – n_i \bar{X}_{i\cdot} = n_i \bar{X}_{i\cdot} – n_i \bar{X}_{i\cdot} = 0 $$

が成り立ちます。これは「データの偏差の和はゼロ」という標本平均の基本的な性質です。したがって、すべての $i$ について交差項はゼロとなり、

$$ 2 \sum_{i=1}^{k} \sum_{j=1}^{n_i} (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})(X_{ij} – \bar{X}_{i\cdot}) = 0 $$

第1項の整理:

$(\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2$ は $j$ に依存しないので、$j$ についての和は $n_i$ 倍になります。

$$ \sum_{i=1}^{k} \sum_{j=1}^{n_i} (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2 = \sum_{i=1}^{k} n_i (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2 $$

結果: 二乗和の分解

以上をまとめると、次の分解が得られます。

$$ \boxed{\underbrace{\sum_{i=1}^{k} \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{\cdot\cdot})^2}_{\text{SST(全変動)}} = \underbrace{\sum_{i=1}^{k} n_i (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2}_{\text{SSB(群間変動)}} + \underbrace{\sum_{i=1}^{k} \sum_{j=1}^{n_i} (X_{ij} – \bar{X}_{i\cdot})^2}_{\text{SSW(群内変動)}}} $$

各記号の意味は次のとおりです。

記号 名称 意味
SST Total Sum of Squares(全変動) データ全体のばらつき
SSB Between-group Sum of Squares(群間変動) 群平均のばらつき(処理の効果を反映)
SSW Within-group Sum of Squares(群内変動) 各群内のばらつき(誤差・ノイズ)

イメージとしては、SSB が大きいほど群の間の差が大きく、SSW が大きいほど各群内のデータが散らばっています。SSB が SSW に比べて十分大きければ、群間に本質的な差があると判断できます。

F 統計量の構成と自由度

自由度

各二乗和に対応する自由度は次のとおりです。

変動 自由度 説明
SST $N – 1$ 全 $N$ 個のデータから総平均を推定するため 1 を引く
SSB $k – 1$ $k$ 個の群平均から総平均を推定するため 1 を引く
SSW $N – k$ 各群 $n_i$ 個から群平均を推定するため、全体で $k$ を引く

自由度も加法的に分解されます。

$$ (N – 1) = (k – 1) + (N – k) $$

平均平方(Mean Square)

二乗和を自由度で割ったものを 平均平方(Mean Square) と呼びます。

$$ \text{MSB} = \frac{\text{SSB}}{k – 1} \quad \text{(群間の平均平方)} $$

$$ \text{MSW} = \frac{\text{SSW}}{N – k} \quad \text{(群内の平均平方)} $$

MSW は共通分散 $\sigma^2$ の不偏推定量です。帰無仮説が成り立つとき MSB も $\sigma^2$ の不偏推定量となりますが、帰無仮説が成り立たないとき MSB は $\sigma^2$ より大きくなります。

F 統計量

$$ F = \frac{\text{MSB}}{\text{MSW}} = \frac{\text{SSB} / (k – 1)}{\text{SSW} / (N – k)} $$

帰無仮説 $H_0: \mu_1 = \mu_2 = \cdots = \mu_k$ のもとで、この F 統計量は

$$ F \sim F(k – 1,\; N – k) $$

に従います。F 値が 1 付近であれば群間に差がなく、F 値が大きいほど群間の差が群内のばらつきに比べて大きいことを意味します。

ANOVA 表

分散分析の結果は ANOVA 表 にまとめるのが慣例です。

要因 平方和 (SS) 自由度 (df) 平均平方 (MS) F値 p値
群間(Between) SSB $k – 1$ MSB = SSB / $(k-1)$ MSB / MSW $P(F > F_{\text{obs}})$
群内(Within) SSW $N – k$ MSW = SSW / $(N-k)$
全体(Total) SST $N – 1$

具体的な手計算の例

3種類の肥料 A, B, C を使って栽培した作物の収穫量(kg)を比較します。有意水準 $\alpha = 0.05$ で検定します。

肥料 A 肥料 B 肥料 C
6 8 13
8 12 9
4 9 11
5 11 8
3 6 7
4 8 12

ステップ1: 基本統計量の計算

$$ k = 3, \quad n_1 = n_2 = n_3 = 6, \quad N = 18 $$

各群の標本平均を計算します。

$$ \bar{X}_{1\cdot} = \frac{6 + 8 + 4 + 5 + 3 + 4}{6} = \frac{30}{6} = 5.0 $$

$$ \bar{X}_{2\cdot} = \frac{8 + 12 + 9 + 11 + 6 + 8}{6} = \frac{54}{6} = 9.0 $$

$$ \bar{X}_{3\cdot} = \frac{13 + 9 + 11 + 8 + 7 + 12}{6} = \frac{60}{6} = 10.0 $$

総平均を計算します。

$$ \bar{X}_{\cdot\cdot} = \frac{30 + 54 + 60}{18} = \frac{144}{18} = 8.0 $$

ステップ2: 群間変動 SSB の計算

$$ \begin{align} \text{SSB} &= \sum_{i=1}^{3} n_i (\bar{X}_{i\cdot} – \bar{X}_{\cdot\cdot})^2 \\ &= 6 \times (5.0 – 8.0)^2 + 6 \times (9.0 – 8.0)^2 + 6 \times (10.0 – 8.0)^2 \\ &= 6 \times (-3.0)^2 + 6 \times (1.0)^2 + 6 \times (2.0)^2 \\ &= 6 \times 9.0 + 6 \times 1.0 + 6 \times 4.0 \\ &= 54.0 + 6.0 + 24.0 \\ &= 84.0 \end{align} $$

ステップ3: 群内変動 SSW の計算

各群内の偏差平方和を計算します。

群 A(群平均 5.0):

$$ \begin{align} \text{SS}_A &= (6-5)^2 + (8-5)^2 + (4-5)^2 + (5-5)^2 + (3-5)^2 + (4-5)^2 \\ &= 1 + 9 + 1 + 0 + 4 + 1 = 16.0 \end{align} $$

群 B(群平均 9.0):

$$ \begin{align} \text{SS}_B &= (8-9)^2 + (12-9)^2 + (9-9)^2 + (11-9)^2 + (6-9)^2 + (8-9)^2 \\ &= 1 + 9 + 0 + 4 + 9 + 1 = 24.0 \end{align} $$

群 C(群平均 10.0):

$$ \begin{align} \text{SS}_C &= (13-10)^2 + (9-10)^2 + (11-10)^2 + (8-10)^2 + (7-10)^2 + (12-10)^2 \\ &= 9 + 1 + 1 + 4 + 9 + 4 = 28.0 \end{align} $$

$$ \text{SSW} = 16.0 + 24.0 + 28.0 = 68.0 $$

ステップ4: SST の検算

$$ \text{SST} = \text{SSB} + \text{SSW} = 84.0 + 68.0 = 152.0 $$

全データと総平均 8.0 の偏差平方和を直接計算しても 152.0 になることが確認できます。

ステップ5: 平均平方と F 統計量の計算

$$ \text{MSB} = \frac{\text{SSB}}{k – 1} = \frac{84.0}{3 – 1} = \frac{84.0}{2} = 42.0 $$

$$ \text{MSW} = \frac{\text{SSW}}{N – k} = \frac{68.0}{18 – 3} = \frac{68.0}{15} \approx 4.533 $$

$$ F = \frac{\text{MSB}}{\text{MSW}} = \frac{42.0}{4.533} \approx 9.265 $$

ステップ6: 判定

自由度 $(k-1, N-k) = (2, 15)$、有意水準 $\alpha = 0.05$ における F 分布の上側臨界値は $F_{0.05}(2, 15) \approx 3.682$ です。

$$ F = 9.265 > 3.682 $$

よって帰無仮説 $H_0$ を棄却します。3種類の肥料の間で収穫量に統計的に有意な差がある と結論づけられます。

ANOVA 表にまとめると以下のとおりです。

要因 SS df MS F p値
群間 84.0 2 42.0 9.265 0.0025
群内 68.0 15 4.533
全体 152.0 17

ただし、ANOVA は「どの群が異なるか」までは教えてくれません。どの群間に差があるかを特定するには 多重比較法(テューキーの HSD 検定など)を行う必要があります。

ANOVA の前提条件

分散分析を正しく適用するためには、以下の3つの前提条件を確認する必要があります。

1. 正規性

各群のデータが正規分布に従う(または十分近い分布である)ことが前提です。ただし、中心極限定理により、サンプルサイズが十分に大きい場合(各群 20〜30 以上)には正規性の仮定が多少崩れても ANOVA は頑健に機能します。

確認方法:

  • シャピロ-ウィルク検定scipy.stats.shapiro
  • Q-Q プロット による視覚的確認

2. 等分散性(分散の均一性)

すべての群の母分散が等しい($\sigma_1^2 = \sigma_2^2 = \cdots = \sigma_k^2$)ことが前提です。

確認方法:

  • レーベン検定scipy.stats.levene): 正規性に対してロバスト
  • バートレット検定scipy.stats.bartlett): 正規性を仮定

等分散性が満たされない場合は、ウェルチの ANOVA を検討してください。

3. 独立性

各観測値が互いに独立であることが必要です。同じ被験者に繰り返し測定を行った場合は、一元配置 ANOVA ではなく 反復測定 ANOVA を使う必要があります。

前提条件が満たされない場合

正規性や等分散性が明らかに成り立たない場合は、ノンパラメトリック検定である クラスカル-ウォリス検定(Kruskal-Wallis test) を検討してください(scipy.stats.kruskal)。

Python での実装

scipy.stats.f_oneway による ANOVA

import numpy as np
from scipy import stats

# =============================================
# 一元配置 ANOVA: 3種類の肥料の収穫量比較
# =============================================
group_a = np.array([6, 8, 4, 5, 3, 4])
group_b = np.array([8, 12, 9, 11, 6, 8])
group_c = np.array([13, 9, 11, 8, 7, 12])

# scipy.stats.f_oneway で一元配置ANOVAを実行
f_stat, p_value = stats.f_oneway(group_a, group_b, group_c)
alpha = 0.05

print("=" * 55)
print("一元配置分散分析(ANOVA): 3種類の肥料の収穫量比較")
print("=" * 55)
print(f"群 A: 平均 = {group_a.mean():.2f}, 分散 = {group_a.var(ddof=1):.2f}, n = {len(group_a)}")
print(f"群 B: 平均 = {group_b.mean():.2f}, 分散 = {group_b.var(ddof=1):.2f}, n = {len(group_b)}")
print(f"群 C: 平均 = {group_c.mean():.2f}, 分散 = {group_c.var(ddof=1):.2f}, n = {len(group_c)}")
print(f"\nF統計量: {f_stat:.4f}")
print(f"p値: {p_value:.6f}")
if p_value < alpha:
    print(f"判定: p値 ({p_value:.6f}) < α ({alpha}) → H₀を棄却")
    print("→ 少なくとも1つの群の平均が他と有意に異なる")
else:
    print(f"判定: p値 ({p_value:.6f}) ≥ α ({alpha}) → H₀を棄却しない")

手計算との比較

import numpy as np
from scipy import stats

# データ
group_a = np.array([6, 8, 4, 5, 3, 4])
group_b = np.array([8, 12, 9, 11, 6, 8])
group_c = np.array([13, 9, 11, 8, 7, 12])

groups = [group_a, group_b, group_c]
k = len(groups)
N = sum(len(g) for g in groups)
grand_mean = np.concatenate(groups).mean()

print("=" * 55)
print("手計算による ANOVA")
print("=" * 55)

# 各群の統計量
for i, (name, g) in enumerate(zip(["A", "B", "C"], groups)):
    print(f"群 {name}: 平均 = {g.mean():.2f}, 不偏分散 = {g.var(ddof=1):.2f}, n = {len(g)}")

print(f"\n総平均: {grand_mean:.2f}")
print(f"群数 k = {k}, 全観測数 N = {N}")

# SSB(群間変動)
ssb = sum(len(g) * (g.mean() - grand_mean)**2 for g in groups)
print(f"\nSSB = {ssb:.2f}")

# SSW(群内変動)
ssw = sum(np.sum((g - g.mean())**2) for g in groups)
print(f"SSW = {ssw:.2f}")

# SST(全変動)
sst = np.sum((np.concatenate(groups) - grand_mean)**2)
print(f"SST = {sst:.2f}")
print(f"SSB + SSW = {ssb + ssw:.2f}  (= SST の検算: {np.isclose(ssb + ssw, sst)})")

# 平均平方
msb = ssb / (k - 1)
msw = ssw / (N - k)
print(f"\nMSB = SSB / (k-1) = {ssb:.2f} / {k-1} = {msb:.4f}")
print(f"MSW = SSW / (N-k) = {ssw:.2f} / {N-k} = {msw:.4f}")

# F統計量
f_manual = msb / msw
print(f"\nF = MSB / MSW = {msb:.4f} / {msw:.4f} = {f_manual:.4f}")

# p値(F分布の上側確率)
p_manual = 1 - stats.f.cdf(f_manual, dfn=k-1, dfd=N-k)
print(f"p値 = {p_manual:.6f}")

# scipy との比較
f_scipy, p_scipy = stats.f_oneway(*groups)
print(f"\n--- scipy.stats.f_oneway との比較 ---")
print(f"F統計量: 手計算 = {f_manual:.4f}, scipy = {f_scipy:.4f}")
print(f"p値:     手計算 = {p_manual:.6f}, scipy = {p_scipy:.6f}")

# ANOVA表
print(f"\n{'='*60}")
print(f"{'要因':<8} {'SS':>8} {'df':>4} {'MS':>10} {'F':>8} {'p値':>10}")
print(f"{'-'*60}")
print(f"{'群間':<8} {ssb:>8.2f} {k-1:>4d} {msb:>10.4f} {f_manual:>8.4f} {p_manual:>10.6f}")
print(f"{'群内':<8} {ssw:>8.2f} {N-k:>4d} {msw:>10.4f}")
print(f"{'全体':<8} {sst:>8.2f} {N-1:>4d}")
print(f"{'='*60}")

前提条件の確認

import numpy as np
from scipy import stats

group_a = np.array([6, 8, 4, 5, 3, 4])
group_b = np.array([8, 12, 9, 11, 6, 8])
group_c = np.array([13, 9, 11, 8, 7, 12])

print("=" * 55)
print("ANOVA の前提条件の確認")
print("=" * 55)

# 1. 正規性の検定(シャピロ-ウィルク検定)
print("\n--- 正規性(シャピロ-ウィルク検定, H₀: 正規分布に従う)---")
for name, group in [("A", group_a), ("B", group_b), ("C", group_c)]:
    stat, p = stats.shapiro(group)
    result = "正規性を棄却しない" if p >= 0.05 else "正規性を棄却"
    print(f"群 {name}: W = {stat:.4f}, p = {p:.4f} → {result}")

# 2. 等分散性の検定(レーベン検定)
print("\n--- 等分散性(レーベン検定, H₀: 分散が等しい)---")
lev_stat, lev_p = stats.levene(group_a, group_b, group_c)
result = "等分散性を棄却しない" if lev_p >= 0.05 else "等分散性を棄却"
print(f"Levene's W = {lev_stat:.4f}, p = {lev_p:.4f} → {result}")

# 3. バートレット検定(正規性が仮定できる場合に使用)
print("\n--- 等分散性(バートレット検定, H₀: 分散が等しい)---")
bart_stat, bart_p = stats.bartlett(group_a, group_b, group_c)
result = "等分散性を棄却しない" if bart_p >= 0.05 else "等分散性を棄却"
print(f"Bartlett's T = {bart_stat:.4f}, p = {bart_p:.4f} → {result}")

F 分布の可視化

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

x = np.linspace(0.01, 6, 500)

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

# 左図: 自由度を変えた F 分布
ax = axes[0]
params = [(2, 15), (5, 15), (10, 15), (5, 5), (10, 30)]
colors = ["red", "blue", "green", "orange", "purple"]
for (d1, d2), color in zip(params, colors):
    ax.plot(x, stats.f.pdf(x, dfn=d1, dfd=d2), linewidth=1.8,
            color=color, label=f"$F({d1}, {d2})$")

ax.set_xlabel("$x$", fontsize=13)
ax.set_ylabel("Probability density", fontsize=13)
ax.set_title("F-distribution with various degrees of freedom", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 6)
ax.set_ylim(0, 1.0)

# 右図: ANOVA の棄却域
ax = axes[1]
d1, d2 = 2, 15  # 手計算の例の自由度
x2 = np.linspace(0.01, 12, 500)
f_critical = stats.f.ppf(0.95, dfn=d1, dfd=d2)
f_obs = 9.265

ax.plot(x2, stats.f.pdf(x2, dfn=d1, dfd=d2), "b-", linewidth=2,
        label=f"$F({d1}, {d2})$")
ax.fill_between(x2, stats.f.pdf(x2, dfn=d1, dfd=d2),
                where=(x2 >= f_critical), alpha=0.3, color="red",
                label=f"Rejection region ($\\alpha=0.05$)")
ax.axvline(f_obs, color="darkred", linestyle="--", linewidth=2,
           label=f"$F_{{obs}} = {f_obs:.3f}$")
ax.axvline(f_critical, color="gray", linestyle=":", linewidth=1.5,
           label=f"$F_{{crit}} = {f_critical:.3f}$")

ax.set_xlabel("$F$", fontsize=13)
ax.set_ylabel("Probability density", fontsize=13)
ax.set_title("ANOVA example: rejection region", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 12)
ax.set_ylim(0, 0.85)

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

まとめ

本記事では、F 検定と一元配置分散分析(ANOVA)について、理論の導出から Python 実装まで解説しました。

  • F 分布 は2つの独立なカイ二乗分布の比として定義され、分散の比を検定するための基盤となる分布です。$t^2 \sim F(1, \nu)$ の関係から、2群の ANOVA は t 検定と等価です。
  • 等分散の F 検定 は2つの母集団の分散が等しいかを $F = s_1^2 / s_2^2$ で検定します。ただし正規性に敏感なため、実務上はレーベン検定が推奨されます。
  • 一元配置 ANOVA は、全変動を群間変動(SSB)と群内変動(SSW)に分解し、$F = \text{MSB} / \text{MSW}$ で3群以上の平均差を1回の検定で判定します。
  • F 統計量が大きいほど群間の差が群内のばらつきに比べて大きいことを意味し、帰無仮説のもとでは $F \sim F(k-1, N-k)$ に従います。
  • ANOVA の前提条件として 正規性等分散性独立性 の確認が必要です。
  • ANOVA で帰無仮説が棄却された場合でも、「どの群間に差があるか」を特定するには 多重比較法(テューキーの HSD 検定など)が必要です。

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

  • 多重比較法(テューキーの HSD 検定、ボンフェローニ補正): ANOVA で有意差が検出された後のペアワイズ比較
  • カイ二乗検定: カテゴリデータの独立性・適合度の検定