Kruskal-Wallis 検定に関する用語を、 仮定・検定統計量・事後検定 別に索引化します。
| カテゴリ | キーワード(日本語) | キーワード(英語) |
|---|---|---|
| 基本概念 | ノンパラメトリック検定、 順位和、 H統計量、 自由度(k−1) | non-parametric, rank sum, H statistic, df |
| 理論基盤 | 順位(ランク)、 タイの補正、 カイ二乗近似、 漸近分布 | rank, tie correction, chi-square approximation |
| 仮説 | 帰無仮説(全群同じ分布)、 対立仮説、 中央値の差 | H0, H1, median difference, location shift |
| 事後検定 | Dunn検定、 ペアワイズMann-Whitney、 Bonferroni補正、 Conover-Iman | Dunn's test, pairwise Mann-Whitney, Bonferroni, Conover |
| 関連検定 | Mann-Whitney U、 ANOVA、 Friedman検定、 Welch ANOVA | Mann-Whitney U, ANOVA, Friedman, Welch ANOVA |
| 効果量 | イプシロン二乗、 イータ二乗、 ベイズファクター | epsilon², eta², Bayes factor |
🍰 まずはやさしく
3つ以上のグループを比べる道具です。
中央値(真ん中の値)に差があるか調べます。
部活ごとのスマホ利用時間を比べる時に使います。
この検定の結論を短くまとめます。
Kruskal-Wallis 検定は、 3つ以上の独立群に差があるかをノンパラメトリックに判定。 ANOVA のロバスト版。
🍰 まずはやさしく
順位を使って差を判定する方法です。
データの分布がバラバラな時に使います。
地域ごとの人口の差を調べる時に役立ちます。
この検定がどう使われるかを説明します。
論文中に 「Kruskal-Wallis検定」として登場する用語。
Kruskal-Wallis検定 とは:3群以上の中央値に差があるかを順位ベースで検定。ANOVAのノンパラ版。正規性を仮定しない。
本ページでは Kruskal-Wallis 検定を扱う。 ANOVA (一元配置分散分析) のノンパラメトリック版で、 3 群以上の代表値 (中央値) に差があるかを順位ベースで検定する。 SSDSE-B-2026 で地域ブロック別 (北海道・東北・関東・中部・近畿・中国・四国・九州沖縄) の合計特殊出生率や人口指標に差があるかを検定する例で具体化する。
Kruskal-Wallis 検定の流れは「全データを混ぜて順位付け → 群ごとの順位和を計算 → H 統計量を計算 → カイ二乗分布で p 値判定」。 有意なら事後検定 (Dunn 法など) で「どの群とどの群が違うか」を特定する。 正規性を仮定しないので、 ANOVA より頑健だが、 検出力はやや劣る。
🍰 まずはやさしく
順位の重心(平均的な位置)を比べるイメージです。
外れ値に振り回されずに差を見つけます。
テストの点数を順位にしてグループで比べます。
直感的に仕組みを理解するための解説です。
Kruskal-Wallis を一言で表すと「データを全部 1 列に並べて順位を付け、 群ごとに順位の重心 (平均順位) を計算し、 重心が群間でバラついていれば有意」。 物理で例えるなら、 3 つの異なる重さの球を秤に載せて、 「重心が偏っているか?」を見るのと同じ発想です。
「ANOVA は平均の比較、 KW は順位の重心の比較」と覚えると、 両者の使い分けが明確になります。
SSDSE-B-2026 で 47 都道府県の人口 (連続値) を Kruskal-Wallis にかける場合、 同じ値はほぼ発生しません。 ところが、 死亡率や合計特殊出生率など 小数点 1 桁で打ち切られた値では「1.30」「1.30」のような同順位 (tie) が頻発します。 タイがあると平均順位の分散が小さくなり、 H 統計量を 過小評価するため、 タイ補正 (correction for ties) を必ず行います。
$$H_\text{corrected} = \frac{H}{1 - \frac{\sum_{j=1}^{g}(t_j^3 - t_j)}{N^3 - N}}$$
このコードでやること: SSDSE-B-2026 の合計特殊出生率 (TFR) を 8 地方ブロックに分け、 タイ補正の効果を可視化する。 TFR は小数点 2 桁打ち切りでタイが発生しやすい。
📥 入力例 (SSDSE-B-2026 抜粋):
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 28 29 30 31 32 33 34 35 | # SSDSE-B-2026 で TFR の Kruskal-Wallis (タイ補正の効果検証) import pandas as pd from scipy import stats import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) # 最新年 (2023) の 47 都道府県を抽出 latest = df[df['SSDSE-B-2026'] == 2023] # 8 地方ブロック割当 (都道府県コード R01000..R47000) def region(code): n = int(code[1:3]) if n == 1: return '北海道' if 2 <= n <= 7: return '東北' if 8 <= n <= 14: return '関東' if 15 <= n <= 23: return '中部' if 24 <= n <= 30: return '近畿' if 31 <= n <= 35: return '中国' if 36 <= n <= 39: return '四国' return '九州沖縄' latest = latest.assign(地方=latest['Code'].apply(region)) # A4103 = 合計特殊出生率 (TFR) groups = [g['A4103'].dropna().values for _, g in latest.groupby('地方')] H, p = stats.kruskal(*groups) print(f'H 統計量 = {H:.3f}') print(f'p 値 = {p:.4f}') # タイ率を計算 all_vals = np.concatenate(groups) from collections import Counter counts = Counter(all_vals) ties = [c for c in counts.values() if c > 1] print(f'タイの個数 = {sum(ties) - len(ties)}/{len(all_vals)} = {(sum(ties)-len(ties))/len(all_vals)*100:.1f}%') |
📤 実行例:
💬 タイが 34% もあるため、 scipy.stats.kruskal は内部でタイ補正済みの H (33.48) を返している。 補正なしで手計算すると H は 33.44 とわずかに小さくなる。 本ケースは p が極めて小さく (p=0.000022) 判定は変わらないが、 境界ケースではタイ補正の有無で有意判定が逆転しうる。 タイ率 30% 超は要警戒。
「H 検定で p<0.05 → どの群とどの群に差があるのか?」を特定する標準手段が Dunn 検定です。 ペアごとに平均順位差を SE で割った Z 値を計算し、 多重比較補正 (Bonferroni / Holm) を適用します。
$$Z_{ij} = \frac{\bar R_i - \bar R_j}{\sqrt{\frac{N(N+1)}{12}\left(\frac{1}{n_i}+\frac{1}{n_j}\right)}}$$
このコードでやること: 関東 (7 県)・近畿 (7 県)・九州沖縄 (8 県)・東北 (6 県) の人口について Kruskal-Wallis → Dunn 事後で「どのペアに差があるか」を特定。
📥 入力例:
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 | # SSDSE-B-2026 4 地方ブロック人口の Kruskal-Wallis + Dunn 事後検定 import pandas as pd from scipy import stats import scikit_posthocs as sp df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) def region4(code): n = int(code[1:3]) if 2 <= n <= 7: return '東北' if 8 <= n <= 14: return '関東' if 24 <= n <= 30: return '近畿' if 40 <= n <= 47: return '九州沖縄' return 'その他' latest['地方'] = latest['Code'].apply(region4) sub = latest[latest['地方'] != 'その他'] # A1101 = 総人口 H, p = stats.kruskal(*[g['A1101'].values for _, g in sub.groupby('地方')]) print(f'KW: H={H:.2f}, p={p:.4f}') # Dunn 事後検定 (Bonferroni 補正) dunn = sp.posthoc_dunn(sub, val_col='A1101', group_col='地方', p_adjust='bonferroni') print(dunn.round(3)) |
📤 実行例:
💬 「関東 vs 九州沖縄」(p=0.018) と「関東 vs 東北」(p=0.021) のみが p<0.05。 つまり関東が他地方に比べ突出して人口が多く、 近畿・九州・東北の 3 ブロックの間には統計的な差が見えない。 これは 関東一極集中の事後検定的証拠になる。
「正規分布なら ANOVA / 非正規なら Kruskal-Wallis」と機械的に覚えるのは危険です。 実際には: (1) 分布が正規に近いなら ANOVA の方が検出力が高い (約 5%)、 (2) 裾の重い分布や外れ値があれば KW の方が検出力が高い (時に 30% 以上)、 (3) サンプル数が小さいときの両者の差は小さい、 という事実があります。
このコードでやること: SSDSE 47 県人口を群間効果サイズ別に bootstrap で再抽出し、 ANOVA と KW の検出力 (p<0.05 になる割合) を比較する。
📥 入力例: SSDSE 47 県人口を母集団とし、 3 群 (各 n=10) を効果サイズ δ で平行移動して抽出
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 | # SSDSE 47 県人口を母集団とした検出力シミュレーション 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]) latest = df[df['SSDSE-B-2026'] == 2023] # 最新年 (2023) pop = latest['A1101'].values / 1000 # 47 県の実分布 (千人、右に裾長い) rng = np.random.default_rng(42) n_per_group = 10 n_iter = 2000 sd_pop = pop.std() for delta in [0, 0.3, 0.5, 0.8]: n_anova_sig = 0 n_kw_sig = 0 for _ in range(n_iter): g1 = rng.choice(pop, n_per_group, replace=True) g2 = rng.choice(pop, n_per_group, replace=True) + delta * sd_pop g3 = rng.choice(pop, n_per_group, replace=True) + 2 * delta * sd_pop _, p_anova = stats.f_oneway(g1, g2, g3) _, p_kw = stats.kruskal(g1, g2, g3) if p_anova < 0.05: n_anova_sig += 1 if p_kw < 0.05: n_kw_sig += 1 print(f'δ={delta}: ANOVA power={n_anova_sig/n_iter:.3f}, KW power={n_kw_sig/n_iter:.3f}') |
📤 実行例 (n_iter=2000):
💬 SSDSE 人口は東京・神奈川など右裾が極端に長い分布。 結果として KW が ANOVA を大きく上回る検出力 (δ=0.3 で +45.0pp、 δ=0.5 で +34.3pp、 δ=0.8 で +11.1pp) を示した。 δ=0 ではどちらも α=0.05 近傍を保つ (第一種の過誤を保持)。 「非正規データには KW」が経験的に正当化される瞬間。
| 効果サイズ δ | ANOVA | Kruskal-Wallis | 差 (KW − ANOVA) |
|---|---|---|---|
| 0.0 (帰無) | 0.040 | 0.048 | +0.008 |
| 0.3 (小) | 0.220 | 0.670 | +0.450 |
| 0.5 (中) | 0.517 | 0.860 | +0.343 |
| 0.8 (大) | 0.850 | 0.961 | +0.111 |
「p<0.05 で有意」だけでは情報不足です。 効果量 (effect size) を併記しないと、 「サンプルが大きすぎてどんな小さな差でも有意」になる罠を見抜けません。 Kruskal-Wallis では イータ二乗 (eta-squared, η²) と イプシロン二乗 (ε²) が標準指標です。
$$\eta^2_H = \frac{H - k + 1}{N - k}, \quad \varepsilon^2_H = \frac{H}{(N^2 - 1)/(N+1)}$$
このコードでやること: 先の Dunn 検定で使った 4 地方ブロック人口比較について効果量 η²・ε² を算出する。
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 28 | # Kruskal-Wallis の効果量 η² と ε² を計算 import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) def region4(code): n = int(code[1:3]) if 2 <= n <= 7: return '東北' if 8 <= n <= 14: return '関東' if 24 <= n <= 30: return '近畿' if 40 <= n <= 47: return '九州沖縄' return 'その他' latest['地方'] = latest['Code'].apply(region4) sub = latest[latest['地方'] != 'その他'] groups = [g['A1101'].values for _, g in sub.groupby('地方')] # A1101 = 総人口 H, p = stats.kruskal(*groups) N = sum(len(g) for g in groups) k = len(groups) eta2 = (H - k + 1) / (N - k) eps2 = H / ((N**2 - 1) / (N + 1)) print(f'H={H:.2f}, p={p:.4f}') print(f'η² = {eta2:.3f} (Cohen 目安: 0.01 小 / 0.06 中 / 0.14 大)') print(f'ε² = {eps2:.3f}') |
📤 実行例:
💬 η²=0.360 は「順位分散の約 36% が地方の違いで説明される」を意味し、 Cohen 目安では 非常に大きな効果。 サンプルサイズ N=28 と小さいにもかかわらず p<0.01 で有意になったのは、 効果が大きいから (検出力不足ではない)。
| η² 目安 | 3 群比較 (power=0.8) | 4 群比較 | 5 群比較 |
|---|---|---|---|
| 0.01 (小) | 956 | 1099 | 1212 |
| 0.06 (中) | 158 | 181 | 200 |
| 0.14 (大) | 66 | 76 | 84 |
kruskal.test()、 Python の scipy.stats.kruskal で広く普及1. 「KW で有意 → 中央値の差」と短絡する。 KW は「分布の確率的優越」を検定しており、 中央値の差そのものを検定しているのではありません。 群間で分布の形 (歪み・分散) が大きく異なる場合、 「中央値は同じだが KW で有意」もあり得ます。 群比較で本当に中央値の差を見たいなら Brown-Mood の中央値検定を併用しましょう。
2. 「サンプルサイズが少ない群」を理由にデータを捨てる。 KW は群サイズが不均衡でも適用可能です。 4-5 サンプルしかない群でも、 順位ベースなので外れ値の影響を受けにくく、 むしろ ANOVA より頑健。 「n が小さいから比較を諦める」前に KW を検討しましょう。
3. 事前に決めずに事後検定を選ぶ (cherry-picking)。 KW で有意になった後、 「どのペアが有意になりやすいか」を見て事後検定を選ぶと、 第一種の過誤が膨張します。 解析計画書 (Statistical Analysis Plan) に Dunn / Conover-Iman / Tukey-Nemenyi のどれを使うかを事前に明記しましょう。
4. 多重比較補正を忘れる。 5 群比較ならペア数は 10 件。 α=0.05 で各ペアを補正なく検定すると、 全体の第一種の過誤率は 1−(0.95)^10 ≈ 40% に膨張。 Bonferroni (保守的) / Holm (中庸) / Benjamini-Hochberg FDR (発見重視) を使い分けましょう。
5. 順序尺度の Likert に強引に KW を使う。 5 段階 Likert は順位として扱えますが、 値が 5 種類しかないのでタイ率が極めて高くなります。 タイ補正を必ず確認し、 場合によっては順序ロジスティック回帰の方が情報量が多くなります。
6. 群間で観察の独立性が崩れている。 同じ個体を 3 時点で測定した「対応あり 3 群比較」に KW を使うのは誤り (Friedman 検定が正解)。 兄弟ペア・隣接県のようにクラスター構造がある場合は混合モデルやクラスター頑健 SE が必要。
7. p 値だけ報告して効果量を書かない。 学会発表で「KW で p=0.03」とだけ書くと、 「実用的に意味のある差なのか不明」とコメントされます。 η² / ε² を必ず併記し、 「小・中・大」の解釈を添えましょう。
8. KW で「有意でない」を「群間に差がない」と結論する。 検出力不足 (β エラー) の可能性があります。 事前に statsmodels.stats.power で必要 N を計算し、 不足していたなら「現在のサンプルでは検出できなかった」と正確に記述します。
Kruskal-Wallis 検定は「3 群以上の中央位置に差があるか」をノンパラに検定する。 ここでは「順位への変換」「H 統計量の構造」「ANOVA との対応関係」を概念図で押さえる。
下の図は 3 群それぞれ 5 個ずつのデータ点(丸)を横軸(値 0〜100)上に並べたものです。 丸を ドラッグ(スマホは指でなぞる)すると、全データを通しで順位付けし直し、 各群の 平均順位・H 統計量・近似 p 値(χ² 自由度 k−1)が リアルタイムで再計算されます。順位ベースなので、値がいくら極端でも順位は 1〜N の範囲に収まる ——この「外れ値への頑健さ」を、平均ベースの ANOVA の F 値と並べて体感してください。
丸の上の数字はその点の「全体順位」。同じ整数値の点は タイ(同順位)となり、平均順位+タイ補正が自動適用されます。
関連ページ: ANOVA(分散分析) / ノンパラメトリック手法 / カイ二乗検定 / p 値 / 外れ値 / 順序尺度 / 効果量
🍰 まずはやさしく
計算式を使って差を数値にする方法です。
統計量(計算結果の数字)を出して判定します。
買い物した金額を順位にして計算に使いましょう。
具体的な計算の手順とルールを学びます。
全データに通し順位 R_i を付けて:
$$ H = \frac{12}{N(N+1)} \sum_{g=1}^{k} \frac{R_g^2}{n_g} - 3(N+1) $$
H は近似的に df = k-1 の χ² 分布に従う。
Kruskal-Wallis で「群間に差あり」と分かった後の、 「どの群間に差があるか」の検定:
William Kruskal(米統計学者)と W. Allen Wallis(経済学者)が1952年に提案。 ANOVAの一般化として位置づけられる。
SSDSE-B-2026(2023 年、 47 都道府県)から、 「3つの地域ブロック(東日本=北海道〜神奈川/中部=新潟〜三重/西日本=滋賀〜沖縄)の合計特殊出生率(A4103)の差」を Kruskal-Wallis で検定します。
| 地域 | n | 中央値(TFR) | 平均順位 |
|---|---|---|---|
| 東日本 | 14 | 1.15 | 9.1 |
| 中部 | 10 | 1.32 | 26.9 |
| 西日本 | 23 | 1.38 | 31.8 |
H = (12 / N(N+1)) × Σ (Rᵢ² / nᵢ) − 3(N+1)
N = 47、 各群の R̄ᵢ から計算 → H ≈ 24.6
自由度 df = k − 1 = 2、 χ²(2) でp値 ≈ 0.000005
結論:有意水準5%で帰無仮説を棄却。 地域間で合計特殊出生率に差がある。
東日本 vs 中部:p = 0.005(有意)
東日本 vs 西日本:p < 0.001(有意)
中部 vs 西日本:p = 1.00(n.s.、 Bonferroni補正後)
合成データで 3 群の中央値差をランクで評価する。
| 群 | 値 | ランク |
|---|---|---|
| A | 2,4,6 | 1,2,4 |
| B | 5,7,9 | 3,5,7 |
| C | 8,10,11 | 6,8,9 |
1 2 3 4 5 6 7 | from scipy import stats A = [2, 4, 6] B = [5, 7, 9] C = [8, 10, 11] H, p = stats.kruskal(A, B, C) print(f"H = {H:.2f}") print(f"p = {p:.4f}") |
💬 手計算 (Step 2) H≈5.69 と Python 出力が完全一致。
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 28 29 30 31 32 33 34 | import pandas as pd # ── 比べる 4 群を SSDSE-B-2026 から作る(地方ブロック別の消費支出)── _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] def _blk(code): n = int(str(code).lstrip('R')) // 1000 if 2 <= n <= 7: return '東北' if 8 <= n <= 14: return '関東' if 15 <= n <= 23: return '中部' if 24 <= n <= 30: return '近畿' return 'その他' _g = _d.assign(blk=_d['Code'].apply(_blk)).groupby('blk')['L3221'] group1 = _g.get_group('東北').values group2 = _g.get_group('関東').values group3 = _g.get_group('中部').values group4 = _g.get_group('近畿').values from scipy import stats import scikit_posthocs as sp # Kruskal-Wallis h, p = stats.kruskal(group1, group2, group3, group4) print(f'H = {h:.3f}, p = {p:.4f}') # Dunn の事後検定 import pandas as pd df_long = pd.DataFrame({ 'value': list(group1) + list(group2) + list(group3), 'group': ['A']*len(group1) + ['B']*len(group2) + ['C']*len(group3) }) dunn = sp.posthoc_dunn(df_long, val_col='value', group_col='group', p_adjust='bonferroni') print(dunn) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd from scipy.stats import kruskal df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) # 都道府県コードで東日本/中部/西日本の3ブロックに分割 n = latest['Code'].str[1:3].astype(int) latest['地域'] = pd.cut(n, [0, 14, 24, 47], labels=['東日本', '中部', '西日本']) # A4103 = 合計特殊出生率 (TFR) east = latest[latest['地域'] == '東日本']['A4103'] central = latest[latest['地域'] == '中部']['A4103'] west = latest[latest['地域'] == '西日本']['A4103'] H, p = kruskal(east, central, west) print(f'H = {H:.3f}, p = {p:.4f}') |
1 2 3 4 5 6 7 8 9 | import pingouin as pg import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) n = latest['Code'].str[1:3].astype(int) latest['地域'] = pd.cut(n, [0, 14, 24, 47], labels=['東日本', '中部', '西日本']) res = pg.kruskal(data=latest, dv='A4103', between='地域') print(res) # H, p, eps² が一括で表示 |
1 2 3 4 5 6 7 8 9 10 11 | import scikit_posthocs as sp import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) n = latest['Code'].str[1:3].astype(int) latest['地域'] = pd.cut(n, [0, 14, 24, 47], labels=['東日本', '中部', '西日本']) # Bonferroni 補正付きの Dunn検定 posthoc = sp.posthoc_dunn(latest, val_col='A4103', group_col='地域', p_adjust='bonferroni') print(posthoc) # ペアごとの調整済みp値の行列 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from scipy.stats import mannwhitneyu from itertools import combinations from statsmodels.stats.multitest import multipletests import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) n = latest['Code'].str[1:3].astype(int) latest['地域'] = pd.cut(n, [0, 14, 24, 47], labels=['東日本', '中部', '西日本']) groups = {name: g['A4103'].values for name, g in latest.groupby('地域')} pvals, pairs = [], [] for a, b in combinations(groups, 2): _, p = mannwhitneyu(groups[a], groups[b]) pvals.append(p); pairs.append(f'{a} vs {b}') reject, padj, *_ = multipletests(pvals, method='bonferroni') for pair, p, pa, r in zip(pairs, pvals, padj, reject): print(f'{pair}: raw p={p:.3f}, adj p={pa:.3f}, reject={r}') |
1 2 3 4 5 6 7 | import seaborn as sns import matplotlib.pyplot as plt sns.boxplot(data=latest, x='地域', y='A4103', palette='Set2') sns.stripplot(data=latest, x='地域', y='A4103', color='black', alpha=0.5) plt.title(f'地域別 合計特殊出生率(Kruskal-Wallis H={H:.2f}, p={p:.3f})') plt.show() |
1 2 3 4 5 6 7 8 9 | from scipy.stats import permutation_test def statistic(*samples): return kruskal(*samples).statistic res = permutation_test((east, central, west), statistic, permutation_type='independent', n_resamples=9999, random_state=0) print(f'統計量={res.statistic:.3f}, 順列p={res.pvalue:.4f}') |
Kruskal-Wallis (KW) 検定は、 一言でいえば 「3 群以上の分布の位置(中央的傾向)に差があるか」を順位だけで判定するノンパラ版 ANOVA です。 ANOVA が「群平均のばらつき ÷ 群内のばらつき」を F 値で測るのに対し、 KW は値を全体の順位に置き換えてから同じ発想を適用します。 順位に変換することで、 外れ値や非正規性の影響を受けにくくなる一方、 「値の間隔」の情報は捨てるため、 データが本当に正規なら ANOVA に検出力でわずかに劣ります。
KW は「頑健で気軽」に見えますが、 解釈でつまずく点が集中しています。 特に「中央値の差」と読み替える段階に最大の罠があります。
scipy.stats.kruskal は内部補正済みですが、 手計算やタイ率 30% 超では要警戒。scipy.stats.permutation_test で実装できる。既存例(3 地域・4 地域)に加え、 SSDSE-B-2026(2023 年、 47 都道府県)の総人口 A1101 を 8 地方ブロックに分けた Kruskal-Wallis を実測しました。 本節の数値はすべて実データからの計算値です(合成・架空値は含みません)。
| 地方 | n(県数) | 中央値人口(千人) | 平均順位 |
|---|---|---|---|
| 北海道 | 1 | 5,092 | 39.0 |
| 東北 | 6 | 1,174 | 19.2 |
| 関東 | 7 | 6,257 | 39.1 |
| 中部 | 9 | 1,931 | 24.0 |
| 近畿 | 7 | 1,727 | 28.1 |
| 中国 | 5 | 1,298 | 17.6 |
| 四国 | 4 | 810 | 9.0 |
| 九州沖縄 | 8 | 1,368 | 20.4 |
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 28 | # SSDSE-B-2026 8地方ブロックの総人口 A1101 を Kruskal-Wallis import pandas as pd from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) latest = df[df['SSDSE-B-2026'] == 2023].copy() # 最新年 (2023) def region8(code): n = int(code[1:3]) if n == 1: return '北海道' if 2 <= n <= 7: return '東北' if 8 <= n <= 14: return '関東' if 15 <= n <= 23: return '中部' if 24 <= n <= 30: return '近畿' if 31 <= n <= 35: return '中国' if 36 <= n <= 39: return '四国' return '九州沖縄' latest['地方'] = latest['Code'].apply(region8) groups = [g['A1101'].dropna().values for _, g in latest.groupby('地方')] H, p = stats.kruskal(*groups) N = sum(len(g) for g in groups) k = len(groups) eta2 = (H - k + 1) / (N - k) eps2 = H / ((N**2 - 1) / (N + 1)) print(f'H={H:.3f}, p={p:.6f}, k={k}, N={N}') print(f'η²={eta2:.3f}, ε²={eps2:.3f}') |
📤 実行例:
💬 8 地方ブロックの総人口には有意差あり(H=17.56、 自由度 k−1=7、 p=0.014)。 効果量 η²=0.271 は Cohen 目安で「大」に相当し、 順位分散の約 27% が地方の違いで説明されることを示す。 平均順位は関東 39.1・北海道 39.0 が突出し、 四国 9.0・中国 17.6 が低い —— 人口の東西・大都市圏偏在が順位に表れている。 ただし北海道は n=1 の 1 県ブロックで、 群サイズが極端に不均衡な点は解釈上の注意(この 1 群を除く感度分析が望ましい)。 有意なので、 どの地方ペアが違うかは Dunn / Conover-Iman の事後検定で特定する。
ANOVA(分散分析) — パラメトリック対応。 正規・等分散なら第一候補。
ノンパラメトリック手法 — 分布を仮定しない検定群の全体像。
仮説検定 — 帰無/対立仮説と p 値判定の基礎。
効果量 — ε² / η² で「差の大きさ」を報告する。
中央値 — KW が比較する(分布が相似なら)代表値。
順位(ランキング) — 順位化という KW の中核操作。
並べ替え検定 — χ² 近似に頼らない厳密な p 値の求め方。
相関 / カイ二乗検定 / 順序尺度 / 有意水準(関連)。
※ Mann-Whitney U 検定・Friedman 検定・Wilcoxon 検定・順位相関は本用語集に個別ページが未収録のため、 リンクなしのテキスト参照にとどめています。
Kruskal-Wallis検定 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 推測統計 › 検定 › Kruskal-Wallis検定(一元配置分散分析の分布フリー版)
中心に Kruskal-Wallis検定 を置き、 そこから ノンパラ検定・t検定・F検定・χ²検定・重回帰 計 5 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語とあとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。
大きな円が小さな円を包含する Circle Packing 図。 「Kruskal-Wallis検定」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「Kruskal-Wallis検定」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは Kruskal-Wallis検定 の隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス → 検定 → Kruskal-Wallis検定 という入れ子の位置を示します。 「検定には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。
Kruskal-Wallis 検定は順位和に基づく 3 群以上の中央値比較であり、 前段の正規性検定・等分散性確認と、 後段の事後比較 (Dunn 等) を組み合わせて結論を導く。
上流の正規性検定 (Shapiro-Wilk) と等分散検定で one-way ANOVA の前提が崩れているか確認し、 並列の Mann-Whitney U (2 群版) や Friedman 検定 (対応あり) と区別して用い、 下流の Dunn 多重比較で群間ペアの有意差を特定する流れで「分布フリーな群間比較」が完結する。
「kruskal wallis」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。
| シナリオ | 重視する観点 | 候補手法 |
|---|---|---|
| データが大規模 (n が多い、 高次元) | 計算コスト / メモリ / 並列化が必要 | ビジネス理解 等 |
| 外れ値が多い / ノイズあり | 頑健性 / 外れ値除去 / 中央値ベース | データ理解 等 |
| 解釈性を重視する | 線形 / 木構造 / ルールベース | データ準備 等 |
| 予測精度を最優先する | アンサンブル / ハイパラ調整 / 交差検証 | モデリング 等 |
| データが少ない | 正則化 / ベイズ / 転移学習 / 簡素なモデル | データアナリスト 等 |
| リアルタイム/オンライン処理 | ストリーミング / 軽量モデル / 逐次更新 | データサイエンティスト 等 |