F 検定(F-test)を確実に理解するための関連キーワードを、難易度別に整理しました。
🍰 まずはやさしく
ばらつきを比べるための道具です。
グループごとの差があるか調べます。
部活の練習量に差があるか測るようなものです。
ここではF検定の結論を短くまとめます。
F検定は2つの分散の比を検定する手法。 もっとも頻繁に使う2つの応用:
🍰 まずはやさしく
データ分析でよく使う手法です。
モデル全体に意味があるか判断します。
都道府県のデータ分析などで活用します。
ここではF検定の全体像を詳しく説明します。
論文中に 「F検定」として登場する用語。
F検定 とは:回帰モデルの全体の有意性や、複数群の分散の差を検定。回帰のF統計量は「モデル全体が意味あるか」。
本ページでは F 検定 を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 12 年 × 112 列) の実データを使った再現可能な学習を目指す。
F 検定は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。
🍰 まずはやさしく
ばらつきを比べる天秤のようなものです。
2つのグループの差が本物か確かめます。
スマホカメラのブレ具合を比べるイメージです。
ここではF検定の仕組みを直感的に学びます。
F 検定の本質は 「2 つのグループのばらつきを天秤に乗せる」 ことです。 片方の皿に第 1 群の分散、 もう片方に第 2 群の分散を乗せ、 比 $F = s_1^2/s_2^2$ がどれだけ 1 から離れるかを測ります。 比が 1 のあたりなら釣り合っている(等分散)、 大きく外れていれば「両者は違うばらつきを持つ」 という判断です。
もう一つの直感は 「カメラのブレ比較」。 同じ被写体を 2 台のカメラで撮ったとき、 一方の写真の方が大きくブレていれば(分散が大きければ)「カメラの性能差がある」 と疑う。 ただし手ぶれの量は撮影ごとに違うので、 何枚も撮って平均的なブレを比較する必要がある。 これがサンプルサイズ $n_1, n_2$ と自由度の役割です。
3 群以上に話を広げると、 F 検定は ANOVA の心臓部に成長します。 「群間の平均がバラついている度合い」÷「群内の個体間ばらつき」 — これも結局は分散の比です。 SSDSE-B-2026 で 47 都道府県を 8 地方ブロックに分けて F 検定をかけると、 「地方間で差があるかどうか」 が分散の天秤で見えます。
F検定は「2つの分散の比」が偶然どこまで起こりうるかを問う。 比が1から大きく離れるほど分散が違うと判断する。
→ 山の幅が分散。 同じ幅なら F≈1、 違えば F が大きくなる。
→ F値が臨界値 F_α を超えれば「分散が違う」と判定。
→ ANOVA は「群間分散 ÷ 群内分散」で平均差を検定する F検定の応用。
🍰 まずはやさしく
分散(データのばらつき)の比のことです。
3つ以上のグループに差があるか判定します。
地方ごとの食費の平均に差があるか調べます。
ここではF統計量の計算方法と定義を読みます。
$$ F = \frac{s_1^2}{s_2^2} \sim F(df_1, df_2) $$
2つの分散の比。 F分布は非対称で 0 から始まり右側に裾を引きます。
$$ F = \frac{\text{群間平均平方}}{\text{群内平均平方}} = \frac{MS_{\text{between}}}{MS_{\text{within}}} $$
F が大きいほど「群間差が大きい」=「群差は本物」の証拠。
H₀: μ₁ = μ₂ = μ₃ = ... = μ_k(全群同じ)
H₁: 少なくとも1つの群が異なる
例:「東日本・中部・西日本で食料費の平均に差があるか?」
$$ \text{全分散} = \text{群間分散} + \text{群内分散} $$
群間分散が群内分散より十分大きければ「差あり」と判定。
ANOVA で「差がある」と分かっても「どこに差があるか」は別の検定が必要:
Ronald A. Fisher(1925)が分散分析(ANOVA)で導入。 「F」は Fisher の F。 もとは George W. Snedecor が命名(1934)。
$$ \eta^2 = \frac{SS_{\text{between}}}{SS_{\text{total}}} $$
ANOVA の説明力。 0.01 小、 0.06 中、 0.14 大。
F 検定の中心式は、 2 つの独立な標本分散の比です。 帰無仮説 $H_0: \sigma_1^2 = \sigma_2^2$ のもとで、 統計量は自由度 $(df_1, df_2) = (n_1-1, n_2-1)$ の F 分布に従います。
$$F = \frac{s_1^2}{s_2^2} \sim F(n_1-1,\ n_2-1)$$
この式が言うのは、 「2 つのグループのばらつき(分散)の比 が、 もし両方が同じ母分散から来ているなら、 1 のあたりに集まる」 という直感です。 比が 1 から大きく離れる(例: 3.5 とか 0.2)と、 「両グループの分散は同じ」 という仮定を疑う根拠になります。 たとえば SSDSE-B-2026 で東日本 vs 西日本の総人口の分散比が 2.3 なら、 「東日本の方が県間ばらつきが大きい」 と言えるかを F 分布の確率で判定します。
| 記号 | 意味 | SSDSE 例 |
|---|---|---|
| $s_1^2$ | 第 1 群の不偏分散 | 関東 1 都 6 県の総人口分散 |
| $s_2^2$ | 第 2 群の不偏分散 | 関西 2 府 4 県の総人口分散 |
| $df_1$ | 分子の自由度 | $n_1 - 1 = 6$ |
| $df_2$ | 分母の自由度 | $n_2 - 1 = 5$ |
| $F$ | 分散比(検定統計量) | 2.3, 0.5 などの数値 |
ANOVA で使う F 統計量は分散の比ですが、 中身は「群間ばらつき」÷「群内ばらつき」 です。 これを式で書くと:
$$F = \frac{\text{MS}_{between}}{\text{MS}_{within}} = \frac{SS_B/(k-1)}{SS_W/(N-k)}$$
分子の $\text{MS}_{between}$ は「群の平均が全体の平均からどれくらい離れているか」、 分母の $\text{MS}_{within}$ は「同じ群の中での個体間ばらつき」 です。 もし群ごとの平均が同じなら(つまり地方別に人口平均は変わらないなら)分子は 0 に近づき F も 0 に近づきます。 逆に「関東は大きい、 東北は小さい」 という地方差があると分子が膨らみ F が大きくなる。 SSDSE-B-2026 で 8 地方ブロックの人口を ANOVA にかけると F は 10 を超えることが多く、 「地方間で人口に差がある」 と統計的に判定されます。
このコードでやること: SSDSE-B-2026 から関東 1 都 6 県と関西 2 府 4 県の総人口を抜き出し、 等分散性を F 検定で判定する。
📥 入力データ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023年のみ(各県1行) kanto_names = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県'] kansai_names = ['三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県'] kanto = df[df['Prefecture'].isin(kanto_names)]['A1101'].astype(float) # A1101=総人口 kansai = df[df['Prefecture'].isin(kansai_names)]['A1101'].astype(float) s1_sq = kanto.var(ddof=1) s2_sq = kansai.var(ddof=1) F = s1_sq / s2_sq df1, df2 = len(kanto)-1, len(kansai)-1 p = 2 * min(stats.f.cdf(F, df1, df2), 1 - stats.f.cdf(F, df1, df2)) print(f"関東分散 s1^2 = {s1_sq:,.0f}") print(f"関西分散 s2^2 = {s2_sq:,.0f}") print(f"F 統計量 = {F:.3f}") print(f"自由度 = ({df1}, {df2})") print(f"両側 p 値 = {p:.4f}") |
📤 実行結果:
💬 結果の読み方: F = 2.40(関東の方が分散が大きい)ですが、 サンプルが各 7 県と少ないため p = 0.31 で 有意ではない。 「東京の存在で関東のばらつきは大きそう」 という直感は、 統計的には 5% 水準では確証されません。 サンプル数の制約が結論に効いていることが分かる典型例です。
| 状況 | 推奨手法 | Python 関数 | F 検定が不向きな理由 |
|---|---|---|---|
| 2 群、 正規性 OK、 等分散性確認 | F 検定(古典) | scipy.stats.f.sf | 最も基本、 ここでは適用可 |
| 2 群、 正規性怪しい | Levene 検定 | scipy.stats.levene | F 検定は正規性に過敏 |
| 2 群、 厳密に等分散性検定 | Bartlett 検定 | scipy.stats.bartlett | F 検定より検出力高いが正規性要件強い |
| 3 群以上、 等分散性 | Bartlett / Levene | scipy.stats.bartlett | F は 2 群限定 |
| 3 群以上、 平均の差 | one-way ANOVA | scipy.stats.f_oneway | ANOVA の F は分散比ではなく平均差 |
| 回帰モデルの当てはまり | regression F-test | statsmodels OLS.f_test | 複数係数の同時検定 |
| 不等分散・小標本 | Welch ANOVA / Brown-Forsythe | pingouin.welch_anova | 古典 F は等分散性が前提 |
このコードでやること: SSDSE-B-2026 の関東 vs 関西の人口に対し、 古典 F 検定と Levene 検定を併用し、 結論の安定性を確認する。
📥 入力データ: 関東 7 県 + 関西 7 県の総人口(前のセクションと同じ)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023年のみ(各県1行) kanto = df[df['Prefecture'].isin(['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県'])]['A1101'].astype(float) kansai = df[df['Prefecture'].isin(['三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県'])]['A1101'].astype(float) # 古典 F 検定(自前実装) F = kanto.var(ddof=1) / kansai.var(ddof=1) p_F = 2 * min(stats.f.cdf(F, 6, 6), 1 - stats.f.cdf(F, 6, 6)) # Levene 検定(中央値ベース=Brown–Forsythe) W, p_lev = stats.levene(kanto, kansai, center='median') # Bartlett 検定 chi2, p_bar = stats.bartlett(kanto, kansai) print(f"古典 F : F={F:.3f}, p={p_F:.4f}") print(f"Levene 検定 : W={W:.3f}, p={p_lev:.4f}") print(f"Bartlett : chi2={chi2:.3f}, p={p_bar:.4f}") |
📤 実行結果:
💬 結果の読み方: 3 つの検定とも p > 0.05 で「等分散性は棄却できない」 と一致しました(古典 F=2.40, Levene, Bartlett いずれも p≒0.28〜0.31)。 検定によって p 値は少しずつ違いますが、 このデータでは結論は同じです。 「どの検定を使うかで結論が反転するケース」 では、 まず正規性を Shapiro-Wilk で確認し、 崩れていれば正規性に頑健な Levene(中央値ベース=Brown–Forsythe)を採用する手順が安全です。
合成データではなく公的統計(SSDSE-B-2026, 2023 年)で F 検定の具体的な計算手順を、 実際に走るコードと実測出力で見せます。
2023 年の 47 都道府県を、 都市圏(東京・神奈川・大阪・愛知・埼玉・千葉・兵庫・福岡)8 都府県 と それ以外 39 道県に分けて、 総人口(列 A1101)の分散を比較します。 東京・大阪など巨大都市を含む都市圏は分散が桁違いに大きくなるはずです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 都市圏 8 都府県 vs それ以外 39 道県、指標は A1101=総人口 metro = ['東京都','神奈川県','大阪府','愛知県','埼玉県','千葉県','兵庫県','福岡県'] m = df[df['Prefecture'].isin(metro)]['A1101'].astype(float) r = df[~df['Prefecture'].isin(metro)]['A1101'].astype(float) F = m.var(ddof=1) / r.var(ddof=1) # 大きい方 / 小さい方 df1, df2 = len(m)-1, len(r)-1 crit = stats.f.ppf(0.95, df1, df2) # 右側 5% 臨界値 p = 1 - stats.f.cdf(F, df1, df2) print(f"都市圏 s1^2 = {m.var(ddof=1):,.0f}") print(f"地方 s2^2 = {r.var(ddof=1):,.0f}") print(f"F = {F:.2f}, df = ({df1}, {df2})") print(f"F(0.05, {df1}, {df2}) = {crit:.2f}") print(f"p 値 = {p:.2e} -> F >> 臨界値 なので等分散を強く棄却") |
📤 実行結果:
💬 結果の読み方: 都市圏の総人口分散は地方の約 10 倍(F=10.40)で、 臨界値 2.26 を大きく上回り p<0.001。 巨大都市を含む都市圏は「ばらつき自体が桁違い」 と統計的に確認できます。 この 2 群を平均比較する場合は等分散を仮定せず Welch 補正版の t 検定を使うべき、 という判断につながります。
2023 年の 47 都道府県を地理的に 4 ブロック(北日本 7・東日本 13・中日本 10・西日本 17)に分け、 総人口(列 A1101)の平均差を一元配置 ANOVA で検定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import pandas as pd, numpy as np from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] blocks = { '北日本': ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県'], '東日本': ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県', '新潟県','富山県','石川県','福井県','山梨県','長野県'], '中日本': ['岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府', '兵庫県','奈良県','和歌山県'], '西日本': ['鳥取県','島根県','岡山県','広島県','山口県','徳島県','香川県', '愛媛県','高知県','福岡県','佐賀県','長崎県','熊本県','大分県', '宮崎県','鹿児島県','沖縄県'], } groups = [df[df['Prefecture'].isin(v)]['A1101'].astype(float) for v in blocks.values()] F, p = stats.f_oneway(*groups) # 一元配置 ANOVA(A1101=総人口) df1, df2 = len(groups)-1, sum(len(g) for g in groups)-len(groups) crit = stats.f.ppf(0.95, df1, df2) for name, g in zip(blocks, groups): print(f"{name}: n={len(g):>2} 平均={g.mean():,.0f}") print(f"F = {F:.3f}, df = ({df1}, {df2}), p = {p:.4f}") print(f"F(0.05, {df1}, {df2}) = {crit:.3f} -> F < 臨界値 なので有意でない") |
📤 実行結果:
💬 結果の読み方: 東日本・中日本の平均人口が北・西より大きく見えますが、 F=2.708 は臨界値 2.822 をわずかに下回り、 p=0.057 で 5% 水準では有意でない(東京を含む東日本内のばらつきが大きく、 群内分散が大きいため)。 「平均は違いそう」 という印象が有意性に届かない典型例です。 次の ③ で効果量を確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import pandas as pd, numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] blocks = { '北日本': ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県'], '東日本': ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県', '新潟県','富山県','石川県','福井県','山梨県','長野県'], '中日本': ['岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府', '兵庫県','奈良県','和歌山県'], '西日本': ['鳥取県','島根県','岡山県','広島県','山口県','徳島県','香川県', '愛媛県','高知県','福岡県','佐賀県','長崎県','熊本県','大分県', '宮崎県','鹿児島県','沖縄県'], } groups = [df[df['Prefecture'].isin(v)]['A1101'].astype(float) for v in blocks.values()] y = np.concatenate([g.values for g in groups]) grand = y.mean() ss_between = sum(len(g) * (g.mean() - grand)**2 for g in groups) ss_within = sum(((g - g.mean())**2).sum() for g in groups) eta2 = ss_between / (ss_between + ss_within) print(f"SS_between = {ss_between:.3e}") print(f"SS_within = {ss_within:.3e}") print(f"eta^2 = SS_between / (SS_between + SS_within) = {eta2:.3f}") print("Cohen の目安: 0.01=小, 0.06=中, 0.14=大 -> 効果量は『大』") |
📤 実行結果:
💬 結果の読み方: η²=0.159 は Cohen の目安で「大」に相当し、 地方ブロックは総人口の全変動の約 16% を説明します。 効果量は大きいのに ② の ANOVA が有意でなかったのは、 群数 4・標本 47 と小さく検出力が不足しているため。 「効果量は大きいが有意でない」=サンプル設計の問題、 という読み方ができます(p 値と効果量は別物)。
合成 2 標本で分散比 F 統計量を計算する。
| 群 | n | 標本分散 s² |
|---|---|---|
| A | 10 | 20 |
| B | 8 | 5 |
1 2 3 4 5 6 7 8 | from scipy import stats F = 20 / 5 df1, df2 = 9, 7 crit = stats.f.ppf(0.95, df1, df2) p = 1 - stats.f.cdf(F, df1, df2) print(f"F = {F}") print(f"臨界値: {crit:.3f}") print(f"p = {p:.4f}") |
💬 手計算 (Step 2) F=4.0 と Python 出力が完全一致。 p<0.05 で分散異なる。
🎯 このコードでやること:SSDSE-B-2026 を data/raw/SSDSE-B-2026.csv から読み込み、 2023 年の地域 3 ブロック (北海道・東北、 関東、 関西) の 総人口 (列 A1101) について (1) 分散比 F 検定 (関東 vs 北海道・東北) と (2) 3 群一元配置 ANOVA を実施し、 Tukey HSD で群間差を確認する。
📥 入力データ (SSDSE-B-2026, 2023 年の関連列、 5 行抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import pandas as pd import numpy as np from scipy import stats # SSDSE-B-2026 を読み込み、2023 年の 3 地域に分割(指標 A1101=総人口) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] tohoku = df[df['Prefecture'].isin(['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県'])]['A1101'].astype(float) kanto = df[df['Prefecture'].isin(['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県'])]['A1101'].astype(float) kansai = df[df['Prefecture'].isin(['三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県'])]['A1101'].astype(float) # (1) 分散比 F 検定: 関東 vs 北海道・東北(関東の方が分散が大きいと予想) s1, s2 = kanto.var(ddof=1), tohoku.var(ddof=1) F = s1 / s2 df1, df2 = len(kanto)-1, len(tohoku)-1 p_var = 2 * min(stats.f.cdf(F, df1, df2), 1-stats.f.cdf(F, df1, df2)) print(f'[F 検定] 関東/東北 分散比 F = {F:.3f}, df=({df1},{df2}), p={p_var:.4f}') # (2) 3 群一元配置 ANOVA F_anova, p_anova = stats.f_oneway(tohoku, kanto, kansai) print(f'[ANOVA] F = {F_anova:.3f}, p = {p_anova:.4f}') # (3) Tukey HSD 事後検定で群間差を特定 from statsmodels.stats.multicomp import pairwise_tukeyhsd y = np.concatenate([tohoku, kanto, kansai]) group = ['東北']*len(tohoku) + ['関東']*len(kanto) + ['関西']*len(kansai) print(pairwise_tukeyhsd(y, group, alpha=0.05)) |
📤 実行結果:
💬 結果の読み方:(1) 関東/東北の総人口分散比 F=9.22, p=0.016 → 関東の人口分散の方が有意に大きい (東京都が外れ値的に大きいため)。 (2) 3 群 ANOVA では F=3.35, p=0.058 で 5% 水準では有意に届かず、 平均差は「あるとは言い切れない」。 (3) Tukey HSD でも全ペアが reject=False(東北 vs 関東でさえ p=0.054 と僅差で非有意)→ 関東の人口水準は高そうだが、 関東内のばらつき(群内分散)が大きく、 群数 3・各 7 県の小標本では統計的な確証には届かない。 (1) の等分散が棄却された時点で、 本来は Welch の ANOVA に切り替えるのが安全という教訓も得られる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023].copy() # 47 都道府県を 4 地方ブロックに区分(region 列を作る) north = ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県'] east = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県', '新潟県','富山県','石川県','福井県','山梨県','長野県'] center= ['岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府', '兵庫県','奈良県','和歌山県'] df['region'] = df['Prefecture'].map( lambda x: '北' if x in north else '東' if x in east else '中' if x in center else '西') # A1101=総人口 を地方ブロックで比較する一元配置 ANOVA groups = [g['A1101'].astype(float).values for _, g in df.groupby('region')] F, p = stats.f_oneway(*groups) print(f'F = {F:.3f}, p = {p:.4f}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import numpy as np from scipy import stats # region 列は①で作成済み。「東」ブロック vs 「北」ブロックの総人口分散比 x1 = df[df['region'] == '東']['A1101'].astype(float).values x2 = df[df['region'] == '北']['A1101'].astype(float).values var1 = np.var(x1, ddof=1) var2 = np.var(x2, ddof=1) F = var1 / var2 if var1 > var2 else var2 / var1 # 大きい方 / 小さい方 df1 = (len(x1) if var1 > var2 else len(x2)) - 1 df2 = (len(x2) if var1 > var2 else len(x1)) - 1 p = 2 * (1 - stats.f.cdf(F, df1, df2)) # 両側 print(f'F = {F:.3f}, df = ({df1}, {df2}), p = {p:.4f}') |
1 2 3 4 5 6 7 | import statsmodels.api as sm from statsmodels.formula.api import ols # A1101=総人口 を region で説明する ANOVA テーブル model = ols('A1101 ~ C(region)', data=df).fit() anova_table = sm.stats.anova_lm(model, typ=2) print(anova_table) |
1 2 3 4 5 | import pingouin as pg result = pg.anova(data=df, dv='A1101', between='region', detailed=True) print(result) # 出力:F値、 p値、 partial η² まで自動計算 |
1 2 3 4 | import pingouin as pg result_welch = pg.welch_anova(data=df, dv='A1101', between='region') print(result_welch) |
1 2 3 4 | from statsmodels.stats.multicomp import pairwise_tukeyhsd tukey = pairwise_tukeyhsd(endog=df['A1101'], groups=df['region'], alpha=0.05) print(tukey.summary()) |
1 2 3 4 5 6 | from scipy import stats groups = [g['A1101'].astype(float).values for _, g in df.groupby('region')] stat, p = stats.levene(*groups, center='median') print(f'Levene W = {stat:.3f}, p = {p:.4f}') # center='median' は Brown–Forsythe 法。 正規性に頑健。 |
t 検定は中心極限定理で正規性の崩れに比較的頑健ですが、 F 検定(特に等分散性検定)は正規性の崩れに敏感です。 裾が重い・歪んだ分布では F 検定の Type I エラー率が大きく狂います。 47 都道府県の人口や所得のような分布では、 等分散性検定として F 検定よりも Levene 検定や Brown–Forsythe 検定を使う方が頑健です。 正規性は QQ プロットや Shapiro–Wilk で必ず確認しましょう。
ANOVA の F 検定は「少なくとも 1 つの群が他と違う」を示すだけで、 どのペアが違うかは別途多重比較が必要です。 単純に各ペアで t 検定を 3 回(3 群なら)行うと、 全体の Type I エラーが 5% を超え(約 14%)、 偽陽性が増えます。 Tukey HSD や Bonferroni、 Holm 法など、 群数に応じた補正を必ず適用してください。
F 検定はサンプル数が大きくなるほど検出力が上がるため、 実質的に無視できる差でも統計的に有意と判定されます。 例えば s₁² = 100、 s₂² = 105 でも N = 1000 ずつあれば有意になりますが、 実務的にこの差は無意味です。 必ず効果量(η², 分散比そのもの)と組み合わせて報告し、 統計的有意性と実質的有意性を区別しましょう。
かつては「F 検定で等分散を確認してから Student の t 検定、 等分散でなければ Welch の t 検定」という流れが推奨されました。 しかし現代の統計学では、 初めから Welch の t 検定(または Welch の ANOVA)を使う方が安全とされています。 二段階手続きは Type I エラーが累積し、 等分散性の検定自体の検出力も問題になります。
正規分布や t 分布と違い、 F 分布は左右対称ではなく、 値は常に正です。 分散比を計算するときは慣例的に「大きい分散 / 小さい分散」として F ≥ 1 とし、 右側のみを棄却域として α/2 で見ます(両側検定の場合)。 単純に「F = s₁²/s₂² が 0.5 だから棄却しない」と判断してはいけません。
同一被験者を複数時点で測定したデータに通常の one-way ANOVA を適用すると、 観測の独立性が壊れ F 統計量が過大 / 過小評価されます。 反復測定 ANOVA、 混合効果モデル(lme4 / statsmodels.MixedLM)、 GEE などの相関構造を扱える手法を使う必要があります。 球面性の仮定(Mauchly 検定)にも注意。
「都市圏 8 県 vs 地方 39 県」のように群サイズが極端に偏ると、 F 検定は分散の同等性を強く要求するようになり、 仮定の崩れに敏感になります。 また Type III 平方和(不均衡 ANOVA で必要)の選択も問題になります。 サンプルサイズが揃わない場合は、 Welch の ANOVA、 混合モデル、 もしくはノンパラメトリック(Kruskal–Wallis)を検討してください。
F 検定を一言でいえば 「2 つの分散を割り算し、 その比 F が 1 からどれだけ離れているかを問う」 検定です。 F 分布は 0 から始まり右へ長い裾を引く非対称分布で、 帰無仮説(2 つの分散は同じ母分散から来る)が正しければ比は 1 の周辺に集まります。 比が 1 から大きく離れるほど、 F 分布の右の裾に飛び出して p 値が小さくなり「分散が違う/群差がある」と判定されます。 大事なのは、 まったく同じ「分散比」という発想が、 使う場面によって三つの顔を持つことです。
| 顔 | 分子(大きくしたい方) | 分母(誤差) | 自由度 | 問い |
|---|---|---|---|---|
| ① 分散の等質性検定 | $s_1^2$ | $s_2^2$ | $(n_1-1,\ n_2-1)$ | 2 群のばらつきは等しいか |
| ② ANOVA | 群間平均平方 $MS_B$ | 群内平均平方 $MS_W$ | $(k-1,\ N-k)$ | $k$ 群の平均は等しいか |
| ③ 回帰の全体検定 | 回帰平均平方 $MSR$ | 残差平均平方 $MSE$ | $(p,\ n-p-1)$ | 全係数 = 0 を棄却できるか |
この統一像があると「なぜ回帰の当てはまりも分散分析も同じ F で測れるのか」がすっきりします。 いずれも 「説明された変動 ÷ 説明できない変動」 を自由度で調整した比だからです。
群が 2 つだけのとき、 ANOVA の F 統計量は t 検定の統計量の 2 乗にぴったり一致します($F(1,\nu) = t_\nu^2$)。 「平均差の t 検定」と「分散比としての F 検定」は、 2 群では同じ検定の別表現なのです。 SSDSE-B-2026(cp932, skiprows=[1], 2023 年 47 都道府県の実測)で 東日本 23 県 vs 西日本 24 県の食料費 L322101(二人以上世帯の平均、 円)を比べると、 $t = 2.7722$ に対し $t^2 = 7.6849$、 一元配置 ANOVA の $F = 7.6849$ と一致します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd
from scipy import stats
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].copy()
df['pref_no'] = df['Code'].str[1:3].astype(int) # 都道府県番号 01..47
# 東日本(1..23) vs 西日本(24..47) の食料費 L322101(円)
e = df[df['pref_no'] <= 23]['L322101'].astype(float)
w = df[df['pref_no'] >= 24]['L322101'].astype(float)
# (1) 分散比検定(両側): H0 は 2 群の分散が等しい
F = e.var(ddof=1) / w.var(ddof=1)
d1, d2 = len(e) - 1, len(w) - 1
p = 2 * min(stats.f.cdf(F, d1, d2), stats.f.sf(F, d1, d2))
print(f'分散比 F = {F:.3f} df = ({d1}, {d2}) 両側 p = {p:.4f}')
# (2) F = t^2 の確認(2 群の平均差)
t, _ = stats.ttest_ind(e, w, equal_var=True)
Fa, _ = stats.f_oneway(e, w)
print(f't = {t:.4f} t^2 = {t*t:.4f} ANOVA F = {Fa:.4f}')
|
📤 実行結果:
💬 読み方: 食料費の ばらつき は東西で有意差なし(分散比 F = 1.75, p = 0.19)。 一方 平均 は東(約 82,853 円)が西(約 78,437 円)より高く、 t 検定で有意(p ≈ 0.008)。 そしてその F は t の 2 乗に一致します。 このように「分散を比べる F」と「平均差の t」は別の問いですが、 2 群の平均差検定に限っては F と t² が同一物である、 という関係を実データで確認できます。
2 群の分散比 F 検定は、 正規性の崩れに極めて敏感です。 裾が重い・歪んだ分布では、 分散が本当は等しくても F 検定が「差あり」と誤判定しやすくなります(第一種の過誤の膨張)。 47 都道府県の人口・所得・地価のように東京や大阪が突出する分布では特に危険。 等分散性を調べたいなら、 分散比 F ではなく Levene 検定(中央値まわりの絶対偏差を使う Brown–Forsythe 版が頑健)や Bartlett 検定を第一選択にしてください(scipy.stats.levene / scipy.stats.bartlett)。 Bartlett は検出力が高い代わりに正規性要件が強く、 Levene は正規性の崩れに頑健、 という使い分けです。
ANOVA や回帰の全体 F は本質的に 片側(右側)検定です(分子が大きいときだけ棄却)。 一方、 2 群の分散比検定で「どちらが大きいか未定」なら 両側にすべきで、 上のコードのように $2\times\min(\text{cdf},\ \text{sf})$ で p 値を出します。 慣例で「大きい分散 / 小さい分散」として F ≥ 1 に固定し、 右側確率を 2 倍する流儀もあります。 scipy.stats.f.sf(F, d1, d2) をそのまま片側 p として使うと、 分散比検定では p を半分に見積もってしまうので注意。
重回帰では、 全体 F 検定が有意でも 個々の係数が有意とは限らず、 逆に全体 F は有意なのにどの t も有意でない、 という現象も起こります(多重共線性の典型症状)。 全体 F は「モデル全体が偶然以上か」、 個別 t は「他の変数を固定したときその 1 変数が効くか」を問う別物です。 「$R^2$ が高い」ことと「全体 F が有意」も別概念で、 標本が小さいと $R^2$ が高くても全体 F が有意にならないことがあります。
F 分布は $(df_1, df_2)$ の 2 つの自由度で形が決まり、 順番を入れ替えると臨界値が変わります。 ANOVA は $(k-1,\ N-k)$、 回帰の全体検定は $(p,\ n-p-1)$($p$ は定数項を除く説明変数の数)、 偏 F 検定は $(q,\ n-k)$($q$ は追加した係数の数)。 分子・分母を取り違えると p 値が大きく狂います。 同じ観測 F でも自由度が違えば有意にも非有意にもなり得ます。
これは既出の落とし穴②の再確認ですが重要です。 ANOVA の F が有意でも「どのペアが違うか」は言えません。 有意になった後の総当たり t 検定は 多重比較問題(多重検定)を引き起こし、 全体の第一種過誤が膨らみます。 Tukey HSD、 Bonferroni、 Holm、 あるいは偽発見率(FDR)制御を必ず併用してください。
F 検定の分子・分母はどちらも 分散(偏差の 2 乗和)なので、 外れ値の影響を強く受けます。 たった 1 つの極端値で分散比が数倍になり、 有意/非有意が反転することも。 検定前に箱ひげ図や散布図で外れ値を確認し、 必要なら頑健統計(中央値ベースの Brown–Forsythe など)へ切り替えましょう。 除外する場合はドメイン的根拠を明示すること。
F 値と p 値だけでは「差の大きさ」が伝わりません。 サンプルが大きいと実質的に無視できる差でも有意になります(既出③)。 ANOVA なら $\eta^2$・$\omega^2$、 回帰なら $R^2$・調整 $R^2$、 2 群なら分散比そのものを必ず併記し、 統計的有意性と実質的有意性を切り分けてください。
最も素朴な F 検定。 帰無仮説 $H_0:\sigma_1^2=\sigma_2^2$ のもと $F=s_1^2/s_2^2 \sim F(n_1-1,\ n_2-1)$。 上の実測(食料費 東西)では $F=1.751$, $df=(22,23)$, 両側 $p=0.19$ で等分散を棄却できませんでした。 ただし前述のとおり、 実務では正規性に頑健な Levene 検定(中央値版 = Brown–Forsythe)や、 検出力の高い Bartlett 検定を代替に使うのが定石です。 これらは 3 群以上の等分散性も一度に検定できます。
全変動を「群間」と「群内」に分解します。$SS_T = SS_B + SS_W$、 それぞれを自由度で割って平均平方にし、
$$F = \frac{MS_B}{MS_W} = \frac{SS_B/(k-1)}{SS_W/(N-k)} \sim F(k-1,\ N-k)$$
群間の平均が離れているほど $SS_B$ が膨らみ F が大きくなります。 scipy.stats.f_oneway や statsmodels の anova_lm で計算できます。
全体 F 検定は「すべての回帰係数 = 0」を一度に検定します:
$$F = \frac{SSR/p}{SSE/(n-p-1)} = \frac{R^2/p}{(1-R^2)/(n-p-1)}$$
部分 F 検定(入れ子モデル比較)は、 縮小モデルに変数群($q$ 個)を足したとき、 残差平方和の減り $SSE_{reduced}-SSE_{full}$ が偶然以上かを問います:
$$F = \frac{(SSE_{r}-SSE_{f})/q}{SSE_{f}/(n-k)} \sim F(q,\ n-k)$$
SSDSE-B-2026(2023 年 47 県の実測)で、 大学学生数 E6302 を予測します。 縮小モデルは「総人口 A1101 のみ」、 完全モデルは「+ 大学数 E6102」。 大学数を足すと $R^2$ が 0.789 → 0.964 に上がり、 その改善が有意か(部分 F)を anova_lm で検定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd
import statsmodels.api as sm
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
y = df['E6302'].astype(float) # 大学学生数(被説明変数)
x1 = df['A1101'].astype(float) # 総人口
x2 = df['E6102'].astype(float) # 大学数
# 縮小モデル:大学学生数 ~ 総人口
m_r = sm.OLS(y, sm.add_constant(pd.DataFrame({'x1': x1}))).fit()
# 完全モデル:+ 大学数
m_f = sm.OLS(y, sm.add_constant(pd.DataFrame({'x1': x1, 'x2': x2}))).fit()
print(f'縮小 R2={m_r.rsquared:.3f} 全体F={m_r.fvalue:.1f} p={m_r.f_pvalue:.1e}')
print(f'完全 R2={m_f.rsquared:.3f} 全体F={m_f.fvalue:.1f} p={m_f.f_pvalue:.1e}')
print(sm.stats.anova_lm(m_r, m_f)) # 入れ子モデル比較 = 部分F検定
|
📤 実行結果(要点):
💬 読み方: 総人口だけでも全体 F は極めて有意(モデルは偶然でない)。 そこに大学数を足すと部分 F = 212.55 が示すとおり当てはまりが有意に向上し、 大学数はモデルに残す価値があると判断できます。 部分 F は「変数選択」の統計的な根拠になります。
F 分布は独立な 2 つの χ² 変数の比(各々を自由度で割ったもの)として定義されます:
$$F = \frac{\chi^2_{d_1}/d_1}{\chi^2_{d_2}/d_2} \sim F(d_1,\ d_2)$$
ここから兄弟関係が見えます。(a) 分子の自由度が 1 なら $F(1,\nu)=t_\nu^2$(t の 2 乗)。 (b) 分母の自由度を無限大にすると $d_1 F \to \chi^2_{d_1}$(χ² に収束)。 つまり t・χ²・F は同じ正規理論から派生した一族で、 分布としても検定としても地続きです。
p 値は標本サイズに依存するため、 効果の大きさは別途報告します。 ANOVA では:
効果量の詳細は 効果量 のページを参照。 回帰では $R^2$・調整 $R^2$ が対応する効果量です。
※ Levene 検定・Bartlett 検定・Welch の ANOVA は本教材に独立ページがないため、 リンクではなく scipy.stats.levene / scipy.stats.bartlett / pingouin.welch_anova として実装名で示しています。
F検定 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 推測統計 › 検定 › F検定
中心に F検定 を置き、 そこから t検定・χ²検定・分散・p値・重回帰・ノンパラ検定 など 計 14 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語とあとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。
大きな円が小さな円を包含する Circle Packing 図。 「F検定」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「F検定」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは F検定 の隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス → 検定 → F検定 という入れ子の位置を示します。 「検定には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。
「F検定」は単独で完結せず、 隣接する手法と接続することで分析パイプラインの一部として機能する。 以下に具体的な接続関係を示す。
F 検定は分散比 F = s₁²/s₂² が F(df₁, df₂) 分布に従うことを利用するため、 (1) 各群が正規分布 (2) 観測の独立性 という 2 仮定が成立しないと p 値が歪む。 ANOVA でグループ間平均差を検定する前に Levene 検定 (1960) で分散均一性確認 + Shapiro-Wilk で正規性確認、 違反時は Welch's ANOVA か Kruskal-Wallis に切り替える。
「f test」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。
| シナリオ | 重視する観点 | 候補手法 |
|---|---|---|
| 2 群間の分散比較 | 等分散性検定 (Welch t 検定の前段) | F 検定 (Fisher 1925) / Levene 検定 |
| 3 群以上の平均差検定 | 群間 / 群内分散の比 F = MS_b/MS_w | 一元配置 ANOVA / 事後検定 Tukey HSD |
| 回帰モデル全体の有意性 | 全係数=0 の帰無仮説 (overall F) | 重回帰の F 統計量 |
| 入れ子モデルの比較 | 削減モデル vs 完全モデル | 偏 F 検定 / 尤度比検定 |
| 正規性が疑わしい | ノンパラメトリック代替 | Kruskal-Wallis 検定 / 順列検定 |
| 等分散性が破れている | Welch 補正 / ロバスト分散推定 | Welch ANOVA (Welch 1951) / Brown-Forsythe 検定 |
下のスライダー(またはグラフを直接ドラッグ)で、 3 つの群の 平均の広がり Δ(群間分散)・群内のばらつき σ(群内分散)・各群の標本サイズ n を動かしてみよう。 上のパネルは 3 群のデータ点と群平均・総平均、 下のパネルは自由度 $(df_1, df_2)$ の F 分布・棄却域(赤・α=0.05)・観測された $F$・p 値をリアルタイムに描く。 群間のばらつきが群内より大きくなるほど $F$ が右へ動き、 p 値が小さくなる 様子を体感できる。 F 分布の pdf / cdf は正則化不完全ベータ関数で正確に計算している。
F 検定の心臓は割り算ひとつ。 分子に「群と群の間で平均がどれだけ離れているか」(群間分散)、 分母に「同じ群の中で個体がどれだけ散らばっているか」(群内分散)を置く。 スライダーで Δ を大きくすると群の位置が縦に離れて分子が膨らみ、 σ を小さくすると点が締まって分母が縮む。 どちらも $F$ を押し上げ、 F 分布の右の裾に統計量が飛び出して p 値が小さくなる。 逆に Δ≈0 なら群の違いは誤差に埋もれ、 $F$ は 1 付近に落ち着く。 標本サイズ n を増やすと同じ Δ でも分子の自由度あたりの寄与が安定し、 わずかな差でも検出しやすくなる(検出力の上昇)。
このデモの p 値が正しいのは、 各群が正規分布し、 群間で分散が等しい(等分散) という前提が成り立つときだけ。 外れ値や歪んだ分布があると $F$ は前提を裏切り、 p 値は信用できなくなる。 特に群ごとに標本数が大きく違い、 かつ分散も違う場合、 通常の ANOVA は第一種の過誤(本当は差がないのに「差あり」と誤判定)を膨らませる。 実務では正規性を Shapiro–Wilk 検定、 等分散性を Levene / Bartlett 検定で事前確認し、 崩れていれば Welch の ANOVA や Kruskal–Wallis 検定 に切り替える。 また F 検定が有意でも「どの群が違うか」は分からない — 多重比較(Tukey HSD / Bonferroni)が別途必要。
同じ分散比の発想は、 二つの主役に化ける。 (1) 一元配置 ANOVA — 上のデモそのもので、 $F = MS_{between}/MS_{within}$、 $df=(k-1,\ N-k)$。 (2) 回帰の全体検定 — 重回帰で「すべての係数が 0」という帰無仮説を、 説明された分散 ÷ 残差分散の比 $F=\dfrac{SSR/p}{SSE/(n-p-1)}$ で一気に検定する。 $R^2$ が高くても、 この全体 F が有意でなければモデルは偶然の当てはまりかもしれない。 ネストしたモデルの比較(偏 F 検定)も同じ枠組みで、 「追加した変数群が分散を有意に説明したか」 を問う。 このように F 検定は「複数のパラメータをまとめて一つの数値で検定する」 統一言語なのだ。