論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
ノンパラメトリック検定
Nonparametric Test
分布の形を仮定しない検定法(順位ベース等)。外れ値や非正規分布データに強い。
仮説検定ノンパラ順位検定

🔖 キーワード索引(拡張)

ノンパラメトリック検定の周辺概念をクイックアクセス:

Mann-Whitney U Wilcoxon符号順位 Kruskal-Wallis Friedman 検定 符号検定 順位和統計量 ランク変換 タイ(同順位)処理 検出力の低下 中央値の検定 ≠ 平均の検定 scipy.stats.mannwhitneyu scipy.stats.wilcoxon scikit_posthocs pingouin

🔖 キーワード索引

このページを高速ナビゲートするための索引チップです。クリックで該当セクションへ。

索引30秒結論文脈直感数式記号→意味実値計算Python実装落とし穴関連手法関連用語グループ教材

💡 30秒で分かる結論

🍰 まずはやさしく

データの形を決めない自由な検定です。

どんな分布のデータでも分析するために使います。

極端に高い買い物などの外れ値がある時に便利です。

この章ではノンパラメトリック検定の結論を学びます。

👁️ 直感 — ノンパラメトリック検定は「分布の仮定なし」

ノンパラメトリック検定は、 母集団分布について強い仮定をしない検定群。 「データが正規分布に従う」必要がない。

使い時

🎯 主要なノンパラ検定

パラメトリック ノンパラメトリック 用途
1標本 t検定Wilcoxon符号付き順位中央値の検定
対応のあるt検定Wilcoxon符号付き順位前後比較
独立2標本 t検定Mann-Whitney U(Wilcoxon順位和)2群比較
一元配置 ANOVAKruskal-Wallis3群以上比較
反復測定 ANOVAFriedman 検定3条件以上
Pearson相関Spearman, Kendall順位相関

💡 30秒で分かる結論

📍 あなたが今見ているもの

🍰 まずはやさしく

分布に縛られない統計の手法です。

データの形が分からない時に正しく判断するために使います。

部活のアンケートなど、順位だけのデータに役立ちます。

このページでは用語の意味と使い方を解説します。

論文中に 「ノンパラメトリック検定」として登場する用語。

ノンパラメトリック検定 とは:分布の形を仮定しない検定法(順位ベース等)。外れ値や非正規分布データに強い。

📍 あなたが今見ているもの(文脈ボックス)

このページは ノンパラメトリック手法 を解説する用語ページです。
カテゴリ:統計的手法
ジャストインタイム型データサイエンス教育の一環として、必要な時に参照し、関連概念とともに学べる構成になっています。
基準ページ:correlation.html(149KB、12セクション、SSDSE-B 実値計算)と同等以上の品質を目指しています。

🎨 直感で掴む

🍰 まずはやさしく

データの「順位」に注目する考え方です。

平均ではなく中央値で差を比べるために使います。

東京のように人口が飛び抜けて多い地域の分析に便利です。

ここでは直感的なイメージと選び方を学びます。

母集団の分布形状を仮定しない統計手法群。正規性に依存しないため外れ値や歪んだ分布に頑健。順位(ランク)に変換するか、リサンプリング(ブートストラップ等)で推論する。

場面ノンパラ手法の選び方
SSDSE-B-2026 の都道府県人口(東京が外れ値)平均ではなく中央値で要約し、 2 群比較は Mann-Whitney U 検定で順位差を見る
所得・物価などの歪んだ分布Wilcoxon 符号順位検定で対応のあるデータを正規性に依存せず比較
3 都市圏×食料消費の比較Kruskal-Wallis 検定で 3 群以上の中央値差を検定(ANOVA の代替)
非線形な単調関係(教育費 vs 進学率)Pearson の代わりに Spearman 順位相関で単調関係を捉える

🎨 概念図で押さえる

ノンパラメトリック検定は「正規性などの分布仮定を置かない」検定群。 ここでは「パラ vs ノンパラ」「順位ベースの考え方」「外れ値耐性」を概念図で押さえる。

パラメトリックとノンパラメトリックの仮定比較パラメトリック (左) vs ノンパラ (右) の仮定正規分布を仮定μ, σ で完全記述分布形を仮定せず中央値・順位だけ'>
図 A. パラメトリックは「正規分布」など特定の分布形を仮定して、 平均 μ・分散 σ² など少数のパラメータで全てを表す。 ノンパラは分布形を仮定せず、 中央値・順位など「形に依存しない指標」だけを使う。
外れ値に対する頑健性概念図外れ値が入った時の影響中央値=本来位置平均=外れ値で右ずれ外れ値t 検定 (平均使用) → 外れ値で平均値が動く → 結論が変わるMann-Whitney (順位使用) → 1 個の外れ値も「1 つの順位」 → 結論安定'>
図 B. 外れ値 1 個で平均は大きく動くが、 中央値・順位はほぼ動かない。 だから Mann-Whitney など順位ベース検定は外れ値に頑健。 SSDSE-B-2026 の東京のような巨大値があるデータでは特に効果的。
パラ vs ノンパラ検定の対応関係マップパラメトリック ⇔ ノンパラの対応パラメトリックノンパラ (順位)対応 2 群: 対応 t 検定Wilcoxon 符号順位検定独立 2 群: 独立 t 検定Mann-Whitney U 検定3 群以上: One-way ANOVAKruskal-Wallis 検定Pearson 相関Spearman 順位相関'>
図 C. 主要パラメトリック検定とノンパラ検定の 1 対 1 対応。 仮定が崩れたら矢印で右側へ移行。 順位ベース検定はどれも「中央値・順位での差」を見る点で一貫している。

📐 ノンパラ検定の仕組み

🍰 まずはやさしく

数値ではなく順位を使う仕組みです。

外れ値の影響を小さくして分析するために使います。

スマホの利用時間のランキングのようなデータに有効です。

ここでは具体的な計算の仕組みについて読みます。

📐 ノンパラ検定の仕組み

多くのノンパラ検定は、 値そのものではなく順位(rank)に基づきます。 これにより:

ノンパラメトリック手法 の中心となる数式・定義は次の通りです。

$$ \hat{F}_n(x) = \frac{1}{n}\sum_{i=1}^n \mathbf{1}(X_i \leq x) $$

🔬 「ノンパラ検定」を深く理解する

🔬 「ノンパラ検定」を深く理解する

パラメトリック vs ノンパラ — 検出力の差

パラメトリック検定は仮定が正しいときに最大の検出力を持つ。 仮定が崩れるとノンパラの方が頑健で高い検出力。 一般則:

順位検定の歴史

📝 練習問題 — 理解度チェック

  1. この用語の基本定義を、 自分の言葉で説明できますか?
  2. この手法が使われる典型的なシナリオを3つ挙げられますか?
  3. この手法の前提条件・仮定を確認できますか?
  4. 結果を解釈する際の注意点は何ですか?
  5. 類似手法との違いを説明できますか?
  6. Python(または他言語)で実装できますか?
  7. SSDSE データで応用例を作成できますか?

🧮 SSDSE-B 実値計算 — 8 地方ブロックの「年平均気温」を比較

2023 年度・47 都道府県の「年平均気温(列 B4101)」を 8 地方ブロックに分け、 Kruskal-Wallis 検定で分布差を調べます。 併せて一元配置 ANOVA を走らせ、 順位ベース検定とパラメトリック検定の結論を見比べます(気温は北ほど低く南ほど高いので、 地方差がはっきり出る題材)。

▼ コード解説(Kruskal-Wallis 検定(3 群以上))
🎯 解説: scipy.stats.kruskal で 3 群以上の分布差を順位ベースで検定。 一元配置 ANOVA のノンパラ版。
📥 入力例: 2023 年度・8 地方ブロック × 年平均気温(列 B4101)
📤 実行例: H = 31.65, p < 0.001(ANOVA も F = 13.38, p < 0.001) → 地方間で気温分布が異なる
💬 読み方: Kruskal-Wallis は正規性を仮定しないので、 サンプルの少ない地方ブロック比較でも安心。 ここでは 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
27
import pandas as pd
from scipy import stats

# 2 行目(日本語見出し行)を飛ばし、英字コードを列名として読む
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年度の 47 都道府県だけ抽出

def region(code):                            # 地域コード R0X000 -> 8 地方ブロック
    n = int(str(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 '九州沖縄'
df['region'] = df['Code'].apply(region)

# B4101 = 年平均気温(℃)を 8 地方ブロックで比較
groups = [df.loc[df['region'] == r, 'B4101'].values
          for r in df['region'].unique()]

H, p_kw  = stats.kruskal(*groups)
F, p_aov = stats.f_oneway(*groups)
print(f'Kruskal-Wallis : H = {H:.2f}, p = {p_kw:.4f}')
print(f'One-way ANOVA  : F = {F:.2f}, p = {p_aov:.4f}')

実行結果(2023 年度の実データ): H ≈ 31.65, p_kw < 0.001、 F ≈ 13.38, p_aov < 0.001。 どちらも 1% 水準で有意で、 「地方ブロックによって年平均気温の分布が異なる」と結論できる(北の北海道・東北が低く、 南の九州沖縄が高い)。 8 群のうち北海道は 1 県だけなど群サイズが小さく正規性も怪しいので、 主結果には順位ベースの Kruskal-Wallis を採るのが無難。

ポストホック:Mann-Whitney U で 2 群ずつ比較(Bonferroni 補正)

▼ コード解説(Mann-Whitney U によるポストホック(全ペア比較))
🎯 解説: Kruskal-Wallis が有意なら、 どの地方どうしが違うかを Mann-Whitney U で総当たり比較し、 Bonferroni で多重比較を補正。
📥 入力例: 8 地方ブロックの全ペア(8 群 → 28 ペア)× 年平均気温
📤 実行例(実測) Kruskal-Wallis : H = 31.65, p = 0.0000 One-way ANOVA : F = 13.38, p = 0.0000
💬 読み方: ペア数が増えるほど補正は厳しくなる。 「どこが違うか」を見る事後検定では補正が必須。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from itertools import combinations
regions = df['region'].unique()
k = len(regions)
m = k*(k-1)//2          # 比較数
print(f'比較数 = {m}, Bonferroni 補正後の α = {0.05/m:.4f}')
for r1, r2 in combinations(regions, 2):
    a = df.loc[df.region==r1, 'B4101']
    b = df.loc[df.region==r2, 'B4101']
    U, p = stats.mannwhitneyu(a, b, alternative='two-sided')
    mark = '*' if p < 0.05/m else ' '
    print(f'{mark} {r1:<5s} vs {r2:<5s}  U={U:6.1f}  p={p:.4f}')

scikit_posthocs で Dunn 検定(Holm 補正)

▼ コード解説(Dunn 検定(Kruskal 後の多重比較))
🎯 解説: scikit_posthocs.posthoc_dunn は Kruskal-Wallis 専用の事後検定。 順位平均の差を使い、 Holm 補正込みで全ペアの p 値行列を返す。
📥 入力例: val_col='B4101'(年平均気温), group_col='region', p_adjust='holm'
📤 実行例: 8×8 の p 値行列 気温差の大きい地方ペアが有意
💬 読み方: Dunn 検定は Kruskal-Wallis 純正の事後検定。 手書きの Mann-Whitney 総当たりより推奨。
1
2
3
4
import scikit_posthocs as sp
pvals = sp.posthoc_dunn(df, val_col='B4101',
                        group_col='region', p_adjust='holm')
print(pvals.round(3))

🧮 実値で計算してみる(SSDSE-B-2026)

政府統計の総合窓口 e-Stat が公開する SSDSE-B-2026.csv(47都道府県×項目)を用いた具体的計算例を示します。

例として 2023 年度の「年平均気温(B4101)」と「消費支出(L3221、 二人以上の世帯)」の Spearman 順位相関を取ると ρ ≈ −0.305(p ≈ 0.037、 Pearson の r ≈ −0.23 よりやや強めに検出)。 気温の低い(寒い)地方ほど消費支出がやや大きい、 という弱い単調傾向が読める。

項目値・指標
データ件数47 都道府県
対象指標年平均気温(B4101)・消費支出(L3221)など
計算結果上記説明参照

🧮 数式に値を入れて手で計算する: Mann-Whitney U 検定

合成 2 群でランクに基づく U 統計量を計算する。

Step 1: 2 群とランク

ランク
A3,5,72,4,5
B2,4,81,3,6

Step 2: U 計算

R_A = 2+4+5 = 11 n_A·(n_A+1)/2 = 3·4/2 = 6 U_A = R_A - n_A(n_A+1)/2 = 11 - 6 = 5 n_A·n_B = 9, U_min = min(U_A, n_A·n_B-U_A) = min(5, 4) = 4

🐍 Python で再現

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) A4103(合計特殊出生率) B4101(年平均気温) 北海道 5,092,000 24,430 1.06 11.0 東京都 14,086,000 86,348 0.99 17.6 沖縄県 1,468,000 12,549 1.6 23.8 …(全 47 行)
 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
# ── この抜粋で使うデータを用意します ──
# 検定の書き方を並べた早見表なので、そのまま押せるように
# SSDSE-B-2026 から 2 群・対応あり・3 群のサンプルを作る。
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
temp = df['B4101'].astype(float).values          # 年平均気温
group1 = g1 = temp[:24]                          # 北海道〜静岡
group2 = g2 = temp[24:]                          # 愛知〜沖縄
group3 = g3 = df['A4103'].astype(float).values   # 合計特殊出生率
data = temp                                      # 1 標本検定用
mu0 = float(temp.mean())
# 対応のあるデータ(同じ 47 県の 2012 年と 2023 年)
_all = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
before = _all[_all['SSDSE-B-2026'] == 2012]['A1101'].astype(float).values
after = _all[_all['SSDSE-B-2026'] == 2023]['A1101'].astype(float).values
cond1, cond2, cond3 = before, after, (before + after) / 2
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values
observed = np.array([[20, 15], [12, 18]])        # クロス集計の例

from scipy import stats
A = [3, 5, 7]
B = [2, 4, 8]
U, p = stats.mannwhitneyu(A, B)
print(f"U = {U}")
print(f"p = {p:.3f}")

📤 実行結果

U = 5.0 p = 1.000

💬 手計算 (Step 2) U=5 と Python 出力が完全一致。

🐍 Python でのノンパラ検定

▼ コード解説(scipy.stats で主要ノンパ検定を一望)
🎯 解説: scipy.stats だけで 2 群・対応・多群・相関の主要ノンパ検定を一通り呼べる早見。
📥 入力例: group1/group2(独立), before/after(対応), x/y(相関)
📤 実行例: 各関数が(統計量, p 値)を返す
💬 読み方: まず scipy で当たりを付け、 効果量や事後検定が要るときに pingouin / scikit_posthocs へ。
 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
36
37
38
39
40
41
42
43
44
45
46
47
# ── この抜粋で使うデータを用意します ──
# 検定の書き方を並べた早見表なので、そのまま押せるように
# SSDSE-B-2026 から 2 群・対応あり・3 群のサンプルを作る。
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
temp = df['B4101'].astype(float).values          # 年平均気温
group1 = g1 = temp[:24]                          # 北海道〜静岡
group2 = g2 = temp[24:]                          # 愛知〜沖縄
group3 = g3 = df['A4103'].astype(float).values   # 合計特殊出生率
data = temp                                      # 1 標本検定用
mu0 = float(temp.mean())
# 対応のあるデータ(同じ 47 県の 2012 年と 2023 年)
_all = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
before = _all[_all['SSDSE-B-2026'] == 2012]['A1101'].astype(float).values
after = _all[_all['SSDSE-B-2026'] == 2023]['A1101'].astype(float).values
cond1, cond2, cond3 = before, after, (before + after) / 2
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values
observed = np.array([[20, 15], [12, 18]])        # クロス集計の例

from scipy import stats

a, b = group1, group2
g4 = df['L3221'].astype(float).values
data_per_condition = [cond1, cond2, cond3]
diff = after - before

# Mann-Whitney U(独立2標本)
u, p = stats.mannwhitneyu(group1, group2, alternative='two-sided')

# Wilcoxon 符号付き順位(対応のあるデータ)
w, p = stats.wilcoxon(before, after)

# Kruskal-Wallis(3群以上)
h, p = stats.kruskal(group1, group2, group3)

# Friedman 検定(反復測定)
f, p = stats.friedmanchisquare(cond1, cond2, cond3)

# Spearman 相関
rho, p = stats.spearmanr(x, y)

# Kendall's tau
tau, p = stats.kendalltau(x, y)

🚧 落とし穴と注意点

🐍 Python 実装バリエーション — scipy / pingouin / scikit_posthocs

1. scipy.stats — 主要 5 検定の最短コード

▼ コード解説(scipy.stats のノンパ 5 検定(最短コード))
🎯 解説: 2 群独立・2 群対応・k 群独立・k 群対応・符号検定の最短コード集。
📥 入力例: a,b(独立)/ x,y(対応)/ g1..g4(多群)/ diff(符号)
📤 実行例: それぞれ検定統計量と p 値を返す
💬 読み方: method='exact' や correction=True でタイ・小標本に対応。 データ構造(独立/対応、 2群/多群)で関数を選ぶ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from scipy import stats
# 2 群独立:Mann-Whitney U
U, p = stats.mannwhitneyu(a, b, alternative='two-sided', method='auto')
# 2 群対応:Wilcoxon 符号順位
W, p = stats.wilcoxon(x, y, zero_method='wilcox', correction=True)
# k 群独立:Kruskal-Wallis
H, p = stats.kruskal(g1, g2, g3, g4)
# k 群対応:Friedman
chi2, p = stats.friedmanchisquare(*data_per_condition)
# 符号検定(scipy 1.10+)
res = stats.binomtest(np.sum(diff > 0), n=np.sum(diff != 0), p=0.5)

2. pingouin — 高機能で効果量も自動

▼ コード解説(pingouin(効果量も自動))
🎯 解説: pingouin は検定と同時に効果量・信頼区間まで返す高機能ライブラリ。
📥 入力例: pg.mwu(a,b), pg.kruskal(data=df, dv='B4101', between='region')
📤 実行例: U 統計量・p 値・効果量 (RBC/CLES) が 1 つの表に
💬 読み方: 論文用に効果量まで欲しいときは pingouin が便利。 scipy より出力が親切。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ── この抜粋で使うデータを用意します ──
import numpy as np
import pandas as pd
import pingouin as pg

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
df = df.copy()
_no = df['Code'].str[1:3].astype(int)
df['region'] = np.select(
    [_no <= 7, _no <= 14, _no <= 23, _no <= 30, _no <= 35, _no <= 39],
    ['北海道東北', '関東', '中部', '近畿', '中国', '四国'], default='九州沖縄')
_t = df['B4101'].astype(float).values
a, b = _t[:24], _t[24:]
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values

# Mann-Whitney + 効果量 r + CI
print(pg.mwu(a, b))
# Kruskal-Wallis + η²
print(pg.kruskal(data=df, dv='B4101', between='region'))
# Wilcoxon + 効果量
print(pg.wilcoxon(x, y))

3. scikit_posthocs — Kruskal 後のペアワイズ Dunn / Conover

▼ コード解説(scikit_posthocs(Kruskal / Friedman 後の多重比較))
🎯 解説: Kruskal-Wallis / Friedman 後のペアワイズ多重比較専用ライブラリ。
📥 入力例: posthoc_dunn / posthoc_conover / posthoc_nemenyi_friedman
📤 実行例: 補正済み p 値の対称行列
💬 読み方: p_adjust で Holm・BH-FDR などを選択。 Kruskal 後は Dunn、 Friedman 後は Nemenyi が定番。
 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
import scikit_posthocs as sp

# この抜粋だけで動くように、群 g と値 y を持つ長形式データを作る
_np = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
_latest = _np[_np['SSDSE-B-2026'] == 2023].copy()
_latest['g'] = pd.cut(_latest['Code'].str[1:3].astype(int),
                      bins=[0, 13, 23, 35, 47],
                      labels=['東日本', '中部', '西日本', '九州沖縄'])
_latest['y'] = _latest['A1303'] / _latest['A1101'] * 100      # 高齢化率(%)
df = _latest[['g', 'y']].dropna()

# 対応のない k 群の多重比較
print('--- Dunn (holm) ---')
print(sp.posthoc_dunn(df, val_col='y', group_col='g', p_adjust='holm').round(3))
print('--- Conover (fdr_bh) ---')
print(sp.posthoc_conover(df, val_col='y', group_col='g', p_adjust='fdr_bh').round(3))

# Nemenyi (Friedman 後) は「行=同じ対象 × 列=条件」の行列を渡す。
# ここでは 47 都道府県 × 4 時点(繰り返し測定)で作る
_np['y'] = _np['A1303'] / _np['A1101'] * 100
data_matrix = (_np.pivot_table(index='Prefecture', columns='SSDSE-B-2026', values='y')
                  [[2012, 2016, 2020, 2023]].dropna())
print('--- Nemenyi (Friedman 後, 47 県 × 4 時点) ---')
print(f'行列の形: {data_matrix.shape}')
print(sp.posthoc_nemenyi_friedman(data_matrix.to_numpy()).round(4))

4. statsmodels で並べ替え検定(permutation test)

▼ コード解説(並べ替え検定(permutation test))
🎯 解説: scipy.stats.permutation_test で「中央値差」をラベル並べ替えで検定。 分布仮定が最小の厳密検定。
📥 入力例: 2 群 a,b、 統計量=中央値差、 n_resamples=20000
📤 実行例: observed diff と並べ替え分布から得た p 値
💬 読み方: 任意の統計量で検定を組めるのが強み。 計算は重いが小標本で最も信頼できる。
1
2
3
4
5
6
7
from scipy.stats import permutation_test
def diff_median(x, y, axis=0):
    return np.median(x, axis=axis) - np.median(y, axis=axis)

res = permutation_test((a, b), diff_median, n_resamples=20000,
                       alternative='two-sided', random_state=0)
print(f'observed diff = {res.statistic:.3f}, p = {res.pvalue:.4f}')

5. 効果量と信頼区間をブートストラップで

▼ コード解説(ブートストラップで効果量の CI)
🎯 解説: scipy.stats.bootstrap で効果量(Cliff's δ)の 95% 信頼区間をリサンプリングで構成。
📥 入力例: 2 群 a,b、 統計量=Cliff's δ、 n_resamples=5000
📤 実行例(実測) observed diff = -1.650, p = 0.0002
💬 読み方: p 値だけでなく効果量の CI を併記するのが現代的。 分布仮定なしで区間が得られる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from scipy.stats import bootstrap
import numpy as np

def cliffs_delta(x, y):
    n = len(x) * len(y)
    return (np.sum(x[:,None] > y[None,:]) - np.sum(x[:,None] < y[None,:])) / n

res = bootstrap((a, b), cliffs_delta, paired=False,
                n_resamples=5000, confidence_level=0.95)
print('Cliffs δ 95% CI =', res.confidence_interval)

🐍 Python 実装

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 B4101(年平均気温) L3221(消費支出(二人以上の世帯)) 北海道 11.0 296,888 東京都 17.6 341,320 沖縄県 23.8 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pandas as pd
from scipy.stats import spearmanr, mannwhitneyu

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]           # 2023 年度の 47 都道府県
temp = df['B4101']            # 年平均気温
cons = df['L3221']            # 消費支出(二人以上の世帯)

rho, p_s = spearmanr(temp, cons)              # 順位相関 ρ ≈ -0.305
u, p_u = mannwhitneyu(temp[:24], temp[24:])   # 前半/後半の県で気温を比較
print(f'Spearman rho={rho:.3f}, p={p_s:.4g}')
print(f'Mann-Whitney U={u:.1f}, p={p_u:.4g}')

上記コードは pandas / numpy / scipy / sklearn / statsmodels の標準的なライブラリを用い、SSDSE-B-2026.csv を直接読み込んで計算します(合成データ不使用)。

🎮 触って理解する — 外れ値を 1 点ドラッグして「順位の頑健性」を体感

下の図は説明用の架空データ(A店・B店の 1 日あたり売上〔万円〕、各 n = 8)です。上の帯が実際の値、下の帯が順位(1〜16 位)で、灰色の対応線が「どの値がどの順位に落ちるか」を示します。B店の赤い点を左右にドラッグして極端な外れ値にしてみてください。平均ベースの t 検定(Welch)の p 値は大きく揺れるのに、順位ベースの Mann-Whitney U の p 値はほとんど動かないことがリアルタイムで確認できます。スライダーまたは再生ボタンで「値 → 順位」への変換過程もアニメーション表示できます。

※ データは説明用の架空値(SSDSE 実測値ではありません)。p 値はページ内 JavaScript で厳密に計算しています:t 検定は Welch 法(自由度は Welch–Satterthwaite 近似、p 値は t 分布の正則化不完全ベータ関数)、Mann-Whitney U はタイ補正+連続性補正つき正規近似。いずれも scipy.stats.ttest_ind(equal_var=False) / mannwhitneyu(method='asymptotic') の出力と一致することを検証済みです。

🧪 実験ガイド — 3 つの操作で 3 つの発見

  1. 右へ引っ張る(24 → 60):B店の平均は 19.5 → 24.0 万円へ跳ね上がるのに、t 検定の p 値は 0.056 → 0.197 へ悪化する。外れ値は平均を押し上げると同時に分散を爆発させるので、シグナル(平均差)よりノイズ(標準誤差)の方が速く増えるからだ。一方、赤い点はもともと最大値(順位 16 位)で、どこまで右に引っ張っても「16 位のまま」。だから Mann-Whitney の p 値は 0.0731 から 1 桁も動かない
  2. 左へ引っ張る(24 → 5):今度は赤い点が全 16 個の最小値(順位 1 位)まで転落する。順位が変わったので Mann-Whitney の p 値も 0.0731 → 0.343 へ変化する。つまり順位検定は「値の大小関係(順序)が変わったときだけ」反応する。頑健=鈍感ではなく、順序情報には正しく敏感である。
  3. スライダーで「値 → 順位」を再生:値軸では点の間隔がバラバラ(外れ値は遠くポツン)だが、順位軸では全点が等間隔に整列する。これが「順位変換=間隔情報を捨てて順序だけ残す」操作の正体。外れ値の「極端さ」が変換の瞬間に消えるのが見える。

💭 直感の核心 — 順位は「スケール」を捨てて「順序」だけ残す

順位変換のもうひとつの重要な帰結が単調変換不変性だ。データ全体に log や平方根などの単調増加変換をかけても、大小関係は保存されるので順位は 1 ミリも変わらない。したがって Mann-Whitney U や Spearman ρ の値・p 値は対数変換の前後で完全に同一になる。t 検定や Pearson 相関は変換のたびに結果が変わるから、「対数を取るべきか否か」で悩む場面では、順位ベース手法が「変換の選択に依存しない結論」を与えてくれる。上のウィジェットで赤い点をどれだけ右に引っ張っても p 値が変わらないのは、この不変性の特殊ケース(最大値をさらに大きくする変換)を体感していることに他ならない。

⚠️ 体感とセットで覚える 3 つの注意

🚀 発展 — 「順位」の先にあるノンパラ道具箱

ノンパラメトリック=順位検定、ではない。分布形を仮定しない道具は大きく 3 系統ある。①順位系(本ページ:Mann-Whitney・Kruskal-Wallis・Spearman 順位相関)、②リサンプリング系ブートストラップは再標本化で任意の統計量の信頼区間を、並べ替え検定はラベルのシャッフルで厳密な p 値を、分布仮定なしに構成する。③ノンパラ推定系カーネル密度推定は「分布そのものの形」をデータから直接推定する(ヒストグラムの滑らか版)。外れ値への対処という文脈では外れ値ロバスト統計のページ、パラメトリック側との使い分けはt 検定のページの使い分けナビが隣接する。

⚠️ ノンパラメトリック検定の落とし穴 — 6 つの典型ミス

① 「ノンパラ=中央値の検定」と誤解する

Mann-Whitney U や Kruskal-Wallis は、 厳密には「中央値の検定」ではなく「分布が確率的に大きいか小さいか(stochastic dominance)」を検定している。 分布の形が大きく違う(一方が右に裾を引く等)と、 中央値が等しくても有意になりうる。 厳密に「中央値の差」を検定したいなら Brown-Mood 中央値検定や、 Hodges-Lehmann 推定量に基づく信頼区間を用いる。 教科書通りに「中央値の比較」と書いて投稿査読で指摘されることが多い。

② 正規性が成り立つのにノンパラを使い検出力を失う

「外れ値が怖いから何でもノンパラ」というのは過剰な保守。 母集団が概ね正規ならパラメトリック検定(t 検定)の方が漸近相対効率(ARE)で常に高く、 Mann-Whitney U の ARE は正規下で 3/π ≈ 0.955 にしかならない。 つまり同じ検出力を得るのに 5% 多くサンプルが必要。 まず正規性プロット(Q-Q)・Shapiro 検定で確認し、 明らかに正規でない場合のみノンパラに切り替える、 という順序が標準。

③ タイ(同順位)が多いのに正規近似 p 値で済ます

順序データやカウントデータでは同値が頻発し、 Mann-Whitney U の正規近似 p 値が不正確になる。 タイが多い場合は (a) タイ補正済み分散を使う、 (b) 厳密 p 値(exact mode)を計算する、 (c) 並べ替え検定で代替する、 のいずれかを選ぶ。 scipy.stats.mannwhitneyu は method='exact' でタイがあると警告を出す。 N が大きく exact が計算困難なら permutation test に切り替える。

④ 多重比較補正なしでポストホックを乱発する

Kruskal-Wallis で有意になったあと、 ペアワイズ Mann-Whitney U を 28 ペア(8 群)打って、 補正なしで「ここが有意!」と報告する事故が多い。 これでは family-wise error rate が α = 1 - (1-0.05)^28 ≈ 76% にまで膨らむ。 Bonferroni(保守的)、 Holm(手堅い)、 Benjamini-Hochberg(FDR)のいずれかを必ず適用。 scikit_posthocs.posthoc_dunn は p_adjust 引数で指定可能。

⑤ 効果量を報告しない

p 値だけ書いて効果量を出さないのは現代統計の悪習。 Mann-Whitney U なら r = Z/√N、 rank-biserial 相関 r_rb、 もしくは Cliff's δ を報告するのが標準。 Wilcoxon 符号順位なら r = Z/√N、 もしくは Hedges' g に変換。 サンプルが少ない時は信頼区間(ブートストラップ)を併記すると説得力が増す。 査読者は p 値より効果量と CI を重視する傾向が強い。

⑥ Wilcoxon 符号順位検定で対称性仮定を忘れる

Wilcoxon signed-rank test は「差の分布が中央値ゼロを中心に対称」という仮定を置く。 対称性が崩れる(強く歪んだ差分分布)と、 検定の解釈は「中央値ゼロ」ではなく「分布のシフト」になる。 対称性が怪しい場合は符号検定(sign test、 差の符号だけを見る)の方が頑健だが、 検出力は落ちる。 ヒストグラムと QQ プロットで対称性を必ず確認すること。

⚠️ 落とし穴

🗺️ 概念マップ — 3つの視点で体系を理解する

ノンパラメトリック検定 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。

📍 体系階層のパス

🌐 統計・データサイエンス › 推測統計 › 検定 › ノンパラメトリック検定

① 🔗 関係マップ — 「他の手法とどう繋がっているか」

中心に ノンパラメトリック検定 を置き、 そこから t検定・Kruskal-Wallis・中央値・p値・F検定・χ²検定 など 計 11 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先

② ⭕ 包含マップ — 「どのカテゴリに含まれているか」

大きな円が小さな円を包含する Circle Packing 図。 「ノンパラメトリック検定」は緑色でハイライト

📍現在地:統計・データサイエンス

③ 🌳 ツリーマップ — 「面積で見るボリューム比較」

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「ノンパラメトリック検定」は緑色でハイライト

🎯 3つのマップの使い分け

マップ 分かること こんな時に見る
🔗 関係マップ手法間の横の関係(前提→発展→応用)「次に何を学べばよい?」 学習順序の判断
⭕ 包含マップ分類体系の入れ子構造(上位⊃下位)「この手法はどんなジャンルに属する?」
🌳 ツリーマップ分野の規模比較(面積=ボリューム)「データサイエンス全体の俯瞰像」

💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは ノンパラメトリック検定隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス検定 → ノンパラメトリック検定 という入れ子の位置を示します。 「検定には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。

🔗 隣接手法への橋渡し

「ノンパラメトリック検定」は単独で完結せず、 前後の手法と組み合わさって価値が発揮される。 入力データの準備 (上流)・同目的の代替手法との比較 (並列)・結果の活用 (下流) という 3 軸で隣接領域を整理する。

この上流・並列・下流の対応を地図化することで、 「ノンパラメトリック検定」を中核に据えた分析パイプライン (データ準備 → 手法選択 → 結果の検証と展開) の全体像が見えてくる。

🌳 手法選択フロー

「nonparametric」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。

典型シナリオ別の手法選択

シナリオ重視する観点候補手法
データが大規模 (n が多い、 高次元)計算コスト / メモリ / 並列化が必要ビジネス理解
外れ値が多い / ノイズあり頑健性 / 外れ値除去 / 中央値ベースデータ理解
解釈性を重視する線形 / 木構造 / ルールベースデータ準備
予測精度を最優先するアンサンブル / ハイパラ調整 / 交差検証モデリング
データが少ない正則化 / ベイズ / 転移学習 / 簡素なモデルデータアナリスト
リアルタイム/オンライン処理ストリーミング / 軽量モデル / 逐次更新データサイエンティスト

選んだ後の検証ステップ

  1. 前処理確認: 標準化 / 欠損処理 / カテゴリ変数のエンコード が適切か
  2. ハイパラ調整: 交差検証で安定する値を選ぶ
  3. 性能評価: タスクに応じた指標 (RMSE / Accuracy / F1 / AUC / シルエット等) を複数併用
  4. 頑健性チェック: 別手法 / 別シードで結果が大きく違わないか確認
  5. 解釈: 結果を分野知識と照らし合わせ、 意味のある発見になっているか検討

👁️ 直感をさらに深める — 「順位」という共通通貨

パラメトリック検定は「母集団は正規分布 N(μ, σ²)」のように分布の形をパラメータ(μ, σ)で完全に指定し、そのパラメータについて推論する。ノンパラメトリック検定はこの一歩を踏まない。「分布の形は分からない・仮定したくない」という前提から出発し、形に依存しない情報=順位(rank)や符号(sign)だけを取り出して推論する。値そのものを捨てて順位に置き換えることは、いわば各国通貨を「順位」という共通通貨に両替する操作にあたる。両替すれば、元が円でもドルでも(正規でも対数正規でも)同じ土俵で比較でき、しかも 1 兆円のような極端値も「最上位という 1 つの順位」に丸め込まれる。

なぜ「分布の仮定」を外せるのか — 帰無分布が組合せで決まる

順位検定の核心は、帰無仮説が正しいとき、順位の並びは母集団分布の形と無関係に決まる点にある。たとえば Mann-Whitney U 検定では「2 群は同じ分布から来た」という帰無仮説のもとで、全データを混ぜて順位を振ると、どの順位がどちらの群に来るかは純粋な組合せ(何通りの割り当てがあるか)だけで決まる。母集団が正規でも歪んでいても、この組合せ構造は変わらない。だから帰無分布(p 値を測る物差し)を分布の形を知らなくても計算できる。これが「分布フリー(distribution-free)」の正体であり、正規性という仮定を丸ごと不要にする仕組みである。パラメトリック側の対応表・使い分けは t 検定ANOVA のページと往復すると理解が深まる。

パラメトリックとの対比 — 3 つの軸で整理

観点パラメトリック(例:t 検定)ノンパラ(例:Mann-Whitney U)
何を仮定するか分布形(正規性)+独立性+(等分散)分布形は仮定せず。独立性・連続性等は残る
何を使うか実測値そのもの(間隔情報)順位・符号(順序情報のみ)
得意な場面仮定が成り立つ・大標本・高い検出力が欲しい小標本・外れ値・歪み・順序尺度

要は「仮定を強くして情報を全部使う」か「仮定を弱くして順序だけ使う」かのトレードオフである。どちらが上位というものではなく、データの素性で選ぶ。中央値の直感は 中央値、順位の下地は 四分位箱ひげ図 のページが補完する。

⚠️ 落とし穴をさらに深める — SSDSE 実データで「ノンパラは万能ではない」を体感

上の「6 つの典型ミス」に加え、実データで最も誤解されやすい 2 点を SSDSE-B-2026(2023 年度・47 都道府県)の実測で確認する。いずれも合成データではなく、下のコードをそのまま実行すれば再現できる。

落とし穴 A:「順位化すれば必ず良い」わけではない — 検出力を失う実例

2023 年の 高齢化率(65 歳以上人口 A1303 ÷ 総人口 A1101 × 100)と 年平均気温(B4101)の関連を、Pearson・Spearman・Kendall で測ると次のようになる(実測値)。

指標係数p 値5% で有意?
Pearson r(線形・パラメトリック)−0.4450.0017✅ 有意
Spearman ρ(順位)−0.2640.073❌ 非有意
Kendall τ(順位)−0.1850.069❌ 非有意

「寒い地方ほど高齢化が進む」という負の関連はどの指標も向きは同じだが、Pearson は 1% 水準で有意なのに、順位ベースの Spearman・Kendall は 5% でも有意にならない。順位化で間隔情報(沖縄が飛び抜けて暖かい、など)を捨てた結果、検出力が落ちて有意性を取りこぼしている。これは落とし穴②「正規性が成り立つのにノンパラを使い検出力を失う」の実例であり、「ノンパラの方が安全=常に良い」ではないことを示す。順位化はタダではなく、情報損失=検出力の代償を伴う。

落とし穴 B:外れ値・てこ点があるときは逆に順位が「効く」— 表裏一体

ただし A の話には裏がある。上の Pearson の強さは、沖縄(高齢化率 23.8%・年平均気温 23.8℃)という「若くて非常に暖かい」てこ点(leverage point)に支えられている面がある。沖縄 1 県を除くと Pearson r は −0.445 → −0.333(p=0.024)へ大きく縮む一方、Spearman ρ は −0.264 → −0.217(p=0.148)とほとんど動かない。つまり Pearson は 1 点の極端値に敏感、順位ベースは頑健、というお馴染みのトレードオフがここでも成り立つ。「Pearson が強い=本当に強い関連」とは限らず、てこ点が押し上げているだけかもしれない。順位相関との食い違いは、外れ値・非線形を疑うシグナルとして使う(外れ値ロバスト統計 のページ参照)。

落とし穴 C:群を分けても差が出ないこともある — 「有意でない」を正しく読む

高齢化率を 東日本(都道府県コード 1〜23)西日本(24〜47)で Mann-Whitney U 検定にかけると、中央値は東 31.23%・西 32.57% だが U = 231.0, p = 0.344 で有意差なし(Welch の t 検定でも p = 0.426 で一致)。ノンパラ検定で有意にならないのは「差がない証拠」ではなく「差を検出できるだけの情報がなかった」だけ、という点に注意(p 値検出力分析)。加えて、ノンパラでも独立性の仮定は外せない:47 都道府県を独立標本として扱えるかは別問題で、地理的な空間相関があれば前提が崩れる。「分布フリー=仮定ゼロ」ではないことを、この点は改めて思い出させてくれる。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) B4101(年平均気温) 北海道 5,092,000 1,681,000 11.0 東京都 14,086,000 3,205,000 17.6 沖縄県 1,468,000 350,000 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd
import 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].copy()          # 2023 年度・47 都道府県
df['aging'] = df['A1303'] / df['A1101'] * 100         # 高齢化率(65歳以上人口比率)

# A: 高齢化率 vs 年平均気温 — Pearson は有意・順位相関は非有意
print('Pearson :', stats.pearsonr(df['aging'], df['B4101']))   # r=-0.445, p=0.0017
print('Spearman:', stats.spearmanr(df['aging'], df['B4101']))  # rho=-0.264, p=0.073
print('Kendall :', stats.kendalltau(df['aging'], df['B4101'])) # tau=-0.185, p=0.069

# B: 沖縄(てこ点)を除くと Pearson は縮み Spearman は不動
d2 = df[~df['Prefecture'].str.contains('沖縄')]
print('w/o沖縄 Pearson :', stats.pearsonr(d2['aging'], d2['B4101']))   # r=-0.333, p=0.024
print('w/o沖縄 Spearman:', stats.spearmanr(d2['aging'], d2['B4101']))  # rho=-0.217, p=0.148

# C: 東日本 vs 西日本の高齢化率 — Mann-Whitney U
df['pref_no'] = df['Code'].apply(lambda c: int(str(c)[1:3]))
east = df.loc[df['pref_no'] <= 23, 'aging']
west = df.loc[df['pref_no'] >= 24, 'aging']
print('東 median=%.2f, 西 median=%.2f' % (east.median(), west.median()))  # 31.23 / 32.57
print(stats.mannwhitneyu(east, west, alternative='two-sided'))            # U=231.0, p=0.344

※ 数値はいずれも SSDSE-B-2026.csv(2023 年度・47 都道府県)の実測。合成データは使用していません。丸めは小数第 3〜4 位。

まとめ — 「ノンパラ=万能・安全」という思い込みを捨てる

🚀 発展をさらに深める — 順位検定の原理と 3 系統の道具箱

① 主要な順位検定は「順位和」という一本の背骨で繋がる

Mann-Whitney U・Wilcoxon 符号順位・Kruskal-Wallis は別々の検定に見えるが、いずれも「順位に置き換えてから、群ごとの順位和(または順位平均)が偏っていないか」を見る点で一貫している。

検定データ構造統計量の骨子パラメトリック対応
Wilcoxon 順位和 = Mann-Whitney U独立 2 群一方の群の順位和 R から U=Rn(n+1)/2独立 t 検定
Wilcoxon 符号順位対応 2 群差の絶対値を順位化し、正の差の順位和 W⁺対応 t 検定
Kruskal-Wallis独立 k 群各群の順位平均のばらつき H(χ² 近似)一元配置 ANOVA
Friedman対応 k 群ブロック内順位の群間ばらつき反復測定 ANOVA
Spearman ρ2 変量の関連順位に変換してから Pearson 相関を計算Pearson 相関

帰無仮説のもとでは順位和の期待値・分散が組合せだけで閉じた式になる(例:Mann-Whitney U の平均 n₁n₂/2、分散 n₁n₂(N+1)/12)。小標本では全組合せから厳密分布を、大標本では正規(または χ²)近似を使う。同順位があると分散を縮めるタイ補正が入る。

② リサンプリング系 — 順位に頼らず「シャッフルで物差しを作る」

順位検定は「順序」に情報を落とすが、並べ替え検定(permutation test)は値をそのまま使いながら分布仮定を外す。群ラベルを何度もシャッフルして「もし群に差がなければ得られたはずの統計量の分布」を実際に作り、観測値がその裾にあるかで p 値を測る。中央値差でも平均差でも任意の統計量で検定を組めるのが強みで、並べ替え検定 のページが詳しい。もう一方の ブートストラップは、標本から復元抽出を繰り返して効果量(Cliff の δ、Hodges-Lehmann 推定量など)の信頼区間を分布仮定なしに構成する。p 値中心になりがちなノンパラ検定に、効果量と区間推定を補うのが現代的な作法である。

③ ノンパラ推定系 — 「分布そのもの」をデータから描く

検定だけがノンパラではない。経験分布関数 ₙ(このページ冒頭の数式)は母集団分布を階段関数で近似し、カーネル密度推定(KDE)はそれを滑らかにして「分布の形」を直接描く(ヒストグラムの連続版)。2 標本が同じ分布かを形ごと比べる Kolmogorov-Smirnov 検定ₙ の最大差を統計量にする。回帰では、外れ値・非線形に強い 分位点回帰や LOWESS がノンパラ推定の代表格である。

④ セミパラメトリック — パラとノンパラの「良いとこ取り」

分布全体は仮定しないが、関心のあるパラメータ(回帰係数など)だけはパラメトリックに扱うセミパラメトリックな手法群もある。代表は生存時間解析の Cox 比例ハザードモデル:ベースラインハザードの形は自由(ノンパラ)にしつつ、共変量の効果は係数(パラメトリック)で推定する。「分布形の仮定は避けたいが、係数の解釈は欲しい」という実務ニーズに応える中間路線であり、ノンパラ(仮定最小・解釈は弱め)とパラメトリック(仮定強め・解釈は明快)のスペクトル上に位置づけると全体像が掴める。