多重比較 は、 複数の検定を同時に行うときに 偽陽性が増えすぎないように する技術です。 SSDSE-B-2026 で「47 都道府県の平均年収はすべて等しいか?」を調べるには $\binom{47}{2}=1081$ 通りのペア検定が必要。 すべて $\alpha=0.05$ で行うと約 54 回の偽陽性が予想されます。 そこで Bonferroni / Holm / BH 補正の出番です。
前提知識: 仮説検定、 p 値。 次に学ぶ: 分散分析、 Tukey の HSD、 FDR 制御。
イメージ 1: コイン投げ。 イカサマでないコインを 20 回投げて表が連続する回数を数える。 各回 5% の確率で「偶然の連続」が起きるとすると、 20 回繰り返せばどこかで起きる確率は $1-(1-0.05)^{20}=0.64$。 「20 個のうち少なくとも 1 個有意」は普通に起こる。
イメージ 2: 47 都道府県の比較。 同じ平均所得の架空データで全ペア検定すると、 約 54 ペアが「有意差あり」と出る。 偽陽性の山。 これを防ぐのが多重比較補正。
イメージ 3: 検定の網。 検定を「魚を獲る網」と考えると、 網目($\alpha$)が広いと小魚(偽陽性)も大量に獲れる。 補正は網目を細かくすること。
$m$ 個の検定 $H_{01}, \ldots, H_{0m}$ について、 観測される p 値を $p_1, \ldots, p_m$ とする。 家族全体の誤り率(FWER):
$$ \mathrm{FWER} = P(\text{1 つ以上偽陽性}) $$偽発見率(FDR):
$$ \mathrm{FDR} = E\left[\frac{\#\text{偽陽性}}{\max(\#\text{陽性},1)}\right] $$主要な補正法:
| 記号 | 意味 |
|---|---|
| $m$ | 検定の総数 |
| $\alpha$ | 名目有意水準(通常 0.05) |
| $\alpha/m$ | Bonferroni 補正後の閾値 |
| $p_{(i)}$ | 昇順で $i$ 番目の p 値 |
| FWER | 1 つ以上偽陽性が出る確率 |
| FDR | 陽性のうち偽陽性の期待比率 |
| $V$ | 偽陽性の数 |
| $R$ | 陽性総数 |
「8 地方ブロック(北海道・東北・関東・中部・近畿・中国・四国・九州沖縄)の平均所得に差があるか?」をペアで検定すると、 $\binom{8}{2}=28$ 通り。 仮に各検定で p 値が以下のように出たとします。
| ペア | p 値 | $\alpha=0.05$ で有意? | Bonferroni ($\alpha/28=0.00179$) | BH (FDR=0.05) |
|---|---|---|---|---|
| 関東-東北 | 0.0003 | ○ | ○ | ○ |
| 関東-中国 | 0.0012 | ○ | ○ | ○ |
| 関東-四国 | 0.018 | ○ | × | ○ |
| 近畿-九州沖縄 | 0.034 | ○ | × | × |
| 中部-中国 | 0.041 | ○ | × | × |
Bonferroni は厳しく 2 ペアのみ通過。 BH は 3 ペア。 「関東 vs 東北・中国・四国」だけが堅実な差として残ります。
例 1: 都道府県を地方ブロックに分けて全ペア比較。
import pandas as pd
import numpy as np
from itertools import combinations
from scipy.stats import ttest_ind
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
region_map = {
'北海道':'北海道', '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
'茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
'新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
'三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
'鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
'徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
'福岡県':'九州','佐賀県':'九州','長崎県':'九州','熊本県':'九州','大分県':'九州','宮崎県':'九州','鹿児島県':'九州','沖縄県':'九州'
}
df['地方'] = df['都道府県'].map(region_map)
col = '可処分所得(二人以上の世帯)' # 例:適切な指標列を選ぶ
groups = df.groupby('地方')[col].apply(list)
regions = list(groups.index)
results = []
for a, b in combinations(regions, 2):
t, p = ttest_ind(groups[a], groups[b], equal_var=False)
results.append((a, b, p))
res = pd.DataFrame(results, columns=['地方A','地方B','p'])
print(res.sort_values('p').head())
例 2: Bonferroni 補正と Holm 補正。
from statsmodels.stats.multitest import multipletests reject, p_adj, _, _ = multipletests(res['p'].values, alpha=0.05, method='bonferroni') res['p_bonf'] = p_adj res['有意_bonf'] = reject reject, p_adj, _, _ = multipletests(res['p'].values, alpha=0.05, method='holm') res['p_holm'] = p_adj res['有意_holm'] = reject
例 3: Benjamini-Hochberg(FDR)補正。
reject, p_adj, _, _ = multipletests(res['p'].values, alpha=0.05, method='fdr_bh')
res['p_bh'] = p_adj
res['有意_bh'] = reject
print('生 0.05 で有意:', (res['p'] < 0.05).sum())
print('Bonferroni:', res['有意_bonf'].sum())
print('Holm :', res['有意_holm'].sum())
print('BH (FDR) :', res['有意_bh'].sum())
例 4: 分散分析 → Tukey HSD。
from statsmodels.stats.multicomp import pairwise_tukeyhsd tukey = pairwise_tukeyhsd(df[col].values, df['地方'].values, alpha=0.05) print(tukey.summary())
| 方法 | 制御対象 | 厳しさ | 向く場面 |
|---|---|---|---|
| Bonferroni | FWER | ★★★★★ | 少数検定、 確証的 |
| Holm | FWER | ★★★★ | Bonferroni の改良 |
| Hochberg | FWER | ★★★ | 独立検定のとき高検出力 |
| BH (FDR) | FDR | ★★ | 大量検定、 探索的 |
| BY (FDR) | FDR | ★★★ | 相関のある検定 |
| Tukey HSD | FWER | ★★★★ | 全ペア比較(ANOVA 後) |
| Dunnett | FWER | ★★★ | 対照群との比較 |
まず全 8 地方で ANOVA を実行。 F 検定が有意なら「少なくとも 1 ペアに差がある」。 ここで止まらず、 ペア比較で どの 地方かを特定する流れ。
$\binom{8}{2}=28$ ペア。 補正なしなら 5% × 28 ≒ 1.4 ペアが偶然有意になる期待。 これでは「関東 vs 東北の差は真か?」と聞かれて答えられない。
確証目的(白書で「関東は東北より所得が高い」と報告する)なら Tukey HSD か Bonferroni。 探索目的(差がありそうなペアを次の研究に回す)なら BH。
「8 地方ブロック間の比較を Tukey HSD(FWER=0.05)で実施。 関東は他の 6 ブロックすべてに対し有意に高所得(補正後 p < 0.01)。 他の地方間に有意差なし」 — このように、 手法・α・結果を明示する。
薬剤の複数用量を比較。 Dunnett 検定で対照群と各用量を比較し、 FWER を 5% に保つ。
数万遺伝子で差次発現。 BH 補正で FDR 5-10% を狙うのが標準。
複数バリアントの効果比較。 Bonferroni か逐次検定で多重性を制御。
47 都道府県の政策効果比較。 SSDSE 系で「対照県 vs 介入県」を Dunnett 風に。
CV スコアを複数モデルで比較。 Wilcoxon + BH 補正で「真に良いモデル」を選ぶ。
| 用語 | 意味 |
|---|---|
| 第 1 種過誤 | 帰無仮説が正しいのに棄却する誤り(偽陽性) |
| 第 2 種過誤 | 帰無仮説が偽なのに棄却しない誤り(偽陰性) |
| FWER | Family-Wise Error Rate。 1 つ以上偽陽性が出る確率 |
| FDR | False Discovery Rate。 陽性中の偽陽性比率の期待値 |
| 補正済み p 値 | 同じ閾値 $\alpha$ で比較できるよう変換した p 値 |
| step-down | 小さい p から順に判定(Holm) |
| step-up | 大きい p から順に判定(BH) |
| post-hoc | ANOVA 後の事後検定 |
| プレレジ | 事前登録。 検定計画を公開して fishing を防ぐ |
import pandas as pd
import numpy as np
from itertools import combinations
from scipy.stats import ttest_ind
from statsmodels.stats.multitest import multipletests
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
# 地方ブロック割り当ては上記同様
col = '世帯主収入(二人以上の世帯)'
groups = df.groupby('地方')[col].apply(list)
pairs = list(combinations(groups.index, 2))
ps = [ttest_ind(groups[a], groups[b], equal_var=False).pvalue for a,b in pairs]
bonf = multipletests(ps, alpha=0.05, method='bonferroni')
holm = multipletests(ps, alpha=0.05, method='holm')
bh = multipletests(ps, alpha=0.05, method='fdr_bh')
out = pd.DataFrame({
'pair': [f'{a}-{b}' for a,b in pairs],
'p' : ps,
'bonf': bonf[0].astype(int),
'holm': holm[0].astype(int),
'bh' : bh[0].astype(int),
}).sort_values('p')
print(out.head(15))
結果から、 検定数 28 でも Bonferroni と BH で残るペア数が異なることが分かります。 関東 vs 他のほとんどのブロックは堅実に有意、 隣接ブロック間は補正後に消えがちです。
補正は偽陽性を抑える代わりに、 真の差を見逃す確率(第 2 種過誤)を増やします。 検出力 = $1-\beta$ と多重補正のトレードオフを意識しましょう。
| 補正 | 偽陽性抑制 | 偽陰性(見逃し) | 推奨用途 |
|---|---|---|---|
| なし | なし | 最小 | 単一検定のみ |
| BH (FDR) | 中 | 小 | 大量検定の探索 |
| Holm | 強 | 中 | FWER、 中検定数 |
| Bonferroni | 最強 | 大 | 少数検定、 厳密 |
「真の差がある」シミュレーションで、 Bonferroni では 30% しか検出できなくても、 BH では 70% 検出できる、 というケースは珍しくない。 目的に応じた選択を。
論文・レポートで多重比較を報告する標準的な書き方の例(SSDSE-B 風):
47 都道府県を 8 地方ブロックに分類し、 各ブロック間の世帯所得平均差を Welch の t 検定(両側)で 28 ペアについて比較した。 多重性の調整には Benjamini-Hochberg 法(FDR=0.05)を用いた。 補正前に p<0.05 となったペアは 6 組、 BH 補正後は 4 組(関東-東北、 関東-中国、 関東-四国、 関東-九州沖縄)であった。
この書き方で「検定回数」「補正法」「目標 α」「補正前後の結果」がすべて明記され、 査読・追試に耐える形になります。
Bonferroni:すべての $i$ について $p_i \le \alpha/m$ で棄却。 補正済み p 値は $\tilde p_i = \min(m\, p_i, 1)$。
Holm:昇順に並べた $p_{(1)} \le p_{(2)} \le \cdots$ に対し、 順に $p_{(i)} \le \alpha/(m-i+1)$ をチェック。 最初に通らなくなった瞬間以降を非棄却。
Benjamini-Hochberg:$p_{(i)} \le \frac{i}{m}\alpha$ となる最大 $i^*$ を見つけ、 $i \le i^*$ をすべて棄却。 補正済み p 値は $\tilde p_{(i)} = \min_{k\ge i} \frac{m}{k} p_{(k)}$。
Tukey HSD:スチューデント化範囲分布 $q$ に基づき、 群平均差を $\frac{|\bar X_a - \bar X_b|}{\sqrt{MSE/n}}$ で評価。 ANOVA の MSE を直接使う。
Dunnett:対照群との比較に最適化。 多変量正規分布のテーブルを使うため、 単純な Bonferroni より検出力が高い。
複数群(例:47 都道府県を 8 地方)の平均比較は、 まず ANOVA で「全体に差があるか?」を 1 つの検定で確認。 全体 F 検定が有意なら、 ペアワイズ多重比較で「どの ペアか?」に進むのが標準的な手順です。
この「2 段階手順」は FWER 制御を比較的緩く保ちつつ実用的。 SSDSE での地方比較ではこの流れが最も自然です。
同じ 5 つの p 値で BH 法($\alpha=0.05$、 $m=5$)。 BH は step-up:大きい p から順に閾値 $i\alpha/m$ と比較し、 一度通れば以下すべて棄却。
| 順位 $i$ | $p_{(i)}$ | 閾値 $i\alpha/m$ | 通る? |
|---|---|---|---|
| 5 | 0.052 | 0.050 | × |
| 4 | 0.039 | 0.040 | ○ |
$i=4$ で初めて閾値以下になったので、 上位 4 個($p \le 0.039$)すべて棄却。 Holm では 2 個、 BH では 4 個 — FDR は明らかに緩い。
5 つの p 値を $\{0.001, 0.012, 0.025, 0.039, 0.052\}$、 $\alpha=0.05$、 $m=5$ で考えます。 Holm は step-down:小さい p から順に、 「残りの数」で割って比較。
| 順位 $i$ | $p_{(i)}$ | 閾値 $\alpha/(m-i+1)$ | 判定 |
|---|---|---|---|
| 1 | 0.001 | 0.05 / 5 = 0.010 | ○ 棄却 |
| 2 | 0.012 | 0.05 / 4 = 0.0125 | ○ 棄却 |
| 3 | 0.025 | 0.05 / 3 = 0.0167 | × 停止 |
| 4-5 | — | — | ×(すべて非棄却) |
Bonferroni なら閾値は一律 0.01 だったので、 1 個目だけ棄却。 Holm は 2 個まで通せる分、 検出力が高い。
独立な検定を $m$ 回行い、 各検定で $\alpha=0.05$ なら、 1 つでも誤って棄却する確率は $1-(0.95)^m$。 具体的に:
| 検定数 $m$ | 補正なし FWER | Bonferroni 後の単発 $\alpha$ |
|---|---|---|
| 1 | 5.0% | 0.0500 |
| 5 | 22.6% | 0.0100 |
| 10 | 40.1% | 0.0050 |
| 20 | 64.2% | 0.0025 |
| 50 | 92.3% | 0.0010 |
| 100 | 99.4% | 0.0005 |
| 1081(47 県全ペア) | ≈100% | 0.000046 |
SSDSE で 47 都道府県をペア比較するなら、 補正なしでは偽陽性をほぼ確実に拾います。 ANOVA か Tukey HSD で群として比較してから個別差を見るのが定石。
Q1. Bonferroni と Holm のどちらを使うべき?
Holm が常に Bonferroni 以上の検出力を持つので、 FWER 制御では Holm が推奨。 Bonferroni は説明のしやすさで使われる。
Q2. FDR は p 値の何 % なら採用?
FDR=0.05 は「陽性の 5% は偽陽性かもしれない」という意味。 ゲノミクスでは 0.1 が一般的。 確証的なら 0.05、 探索的なら 0.1-0.2 が許容。
Q3. 検定が相関している場合は?
Bonferroni は保守的(過剰補正)になる。 並べ替え検定(ベースが同じ群を共有する場合)や Westfall-Young 法が推奨。
Q4. ベイズ的アプローチは多重比較問題を回避できる?
階層モデルで「縮約」が自動的に起きるため、 多重比較の問題が緩和される。 ただし事前分布の選び方で結果が変わる点に注意。
Q5. 「事前計画」と「探索的」の境界は?
プレレジ(pre-registration)で「これとこれを検定する」と宣言したものは事前計画。 後から「気になった」検定はすべて探索的とカウントすべき。
multipletests を活用できる0 や 1 に丸まることがある。 raw 値も残す。[仮説検定] → [多重検定] → [補正法]
├─ FWER 制御
│ ├─ Bonferroni
│ ├─ Holm
│ └─ Tukey HSD
└─ FDR 制御
├─ Benjamini-Hochberg
└─ Benjamini-Yekutieli
学習順序:仮説検定 → t/ANOVA → 多重比較 → 効果量 → ベイズ的代替手段。
stats.multitest の各種補正。