「selection bias」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「selection bias」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「selection bias の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
データの選び方が偏る現象です。
正しい結論を出すために使います。
成功した人だけへの調査が例です。
結果がズレる理由について読みます。
標本の選び方が偏っているために結果が歪む。
🍰 まずはやさしく
データの限界を知るための道具です。
分析のミスを防ぐために使います。
SNSのアンケートなどが例です。
概念や使い方の基本を読みます。
アンケート・観察データ・SNS分析 — 公的統計以外のほとんどのデータは何らかの選択バイアスを持っています。 「データから言えること」の限界を画定するための概念。
本ページでは「selection bias」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 12 年 × 100 超列) の実データを使った再現可能な学習を目指す。
「selection bias」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。
🍰 まずはやさしく
見えているものだけを信じる罠です。
隠れたデータに気づくために使います。
生き残った機体だけの集計が例です。
直感的に理解するための例を読みます。
戦時中の話:撃墜されずに帰還した戦闘機の被弾箇所を集計 → 「ここを補強すべき」と結論。
実は逆。 帰還できなかった機(被弾箇所が致命的)はデータに入っていない。 帰還機が無事だった部位は そこを撃たれても帰れる安全な場所。 補強すべきは「帰還機が被弾していない場所」。
これがサバイバルバイアスの古典例(Wald の戦闘機)。
🍰 まずはやさしく
選び方の偏りを表す言葉です。
正しく定義して計算するために使います。
一部の人だけが集まる状況が例です。
定義や数式について詳しく読みます。
選択バイアス(Selection Bias):標本の選び方が偏っているために結果が歪む。
同義・関連語:選抜バイアス, 抽出バイアス
| 記号 | 意味 |
|---|---|
| 選択メカニズム | 個体が標本に入る確率の構造 |
| MAR | Missing At Random(観測値で説明可能) |
| MNAR | Missing Not At Random(観測値でも説明できない) |
| IPW | Inverse Probability Weighting(逆確率重み付け) |
選択バイアス(Selection Bias)は、 単に用語の定義を覚えるだけでは本当には理解できません。 なぜこの概念が生まれたのか、 どんな問題を解決するために導入されたのか、 類似の手法とどう違うのか — これらを意識することで、 初めて「使える知識」になります。
数式や Python コードはあくまで 道具。 道具の使い方を覚える前に、 その道具で何をしたいか(目的) を明確にすることが、 データサイエンス学習の鉄則です。
この用語は、 単独で存在するわけではなく、 多くの関連概念とネットワークを形成しています。 上の「関連用語」セクションに挙げたリンク先を1つずつ辿ると、 全体像が見えてきます。 特に:
理論を学ぶことと、 実務で使えることは別物です。 公的統計(SSDSE、 e-Stat 等)の実データで実装・実験することで、 教科書だけでは見えない罠 に気付けます。 たとえば:
これらは 選択バイアス に限った話ではなく、 データサイエンス全般に共通する作法です。 「落とし穴」セクションの内容と合わせて、 自分なりのチェックリストを作るとよいでしょう。
選択バイアス を使った分析の 正しさを担保する ためには、 以下の観点で検証するのが定番です。
| 確認する点 | 選択バイアス で何を見るか |
|---|---|
| 「ビッグデータなら大丈夫」誤解 | n が大きくても偏っていれば結論は歪む。 |
| 生存者しか見ない | 倒産企業・退会ユーザーの除外で過大評価。 |
| ノンレスポンス無視 | 回答率50%なら、 残り50%の特徴を推定する必要。 |
| 自己選抜の見落とし | プログラム参加者と非参加者の比較は因果ではない。 |
| 再現性 | 同じデータ・同じコードで同じ結果が出るか。このページの ▶ 実行ボタンで確かめられます |
選択バイアス は分野横断で活躍する概念です。 業界別に見ると以下のような使われ方があります。
選択バイアス を実際のデータで学ぶときは、 SSDSE(教育用標準データセット、 総務省統計局)が便利です。
これらは 統計センターの SSDSE ページ から CSV で直接ダウンロードできます。 上の Python コード例で data/raw/SSDSE-B-2026.csv としているのが、 まさにこれです。
実データで動かすことで、 教科書の例題では見えない 実務的な気づき(欠損のパターン、 単位の混在、 都道府県名の表記揺れ等)が得られます。
pip install pandas numpy scikit-learn matplotlib で揃います。utf-8 ではなく shift_jis や cp932 の場合がある(古い日本の公的統計に多い)。 encoding='cp932' を試してください。%matplotlib inline、 スクリプト実行なら plt.show() を忘れずに。 日本語フォントは matplotlib 用に別途設定(japanize-matplotlib 等)が必要。選択バイアス (Selection Bias) は、 推定したい母集団 $\mathcal{P}$ と、 実際にデータが得られたサンプル $\mathcal{S}$ の分布が系統的に異なる状況を指します。 形式化するには、 個体 $i$ が標本に入る指示変数 $S_i \in \{0, 1\}$ を導入し、 $P(S=1 \mid X, Y)$ が $X$ や $Y$ に依存するかどうかが争点となります。 もし $S \perp\!\!\!\perp (X, Y)$ ならランダムサンプリングで偏りなし。 一方、 $P(S=1 \mid Y) \neq P(S=1)$ なら結果依存サンプリング、 $P(S=1 \mid X) \neq P(S=1)$ なら共変量依存サンプリングです。 後者は IPW (Inverse Probability Weighting) で補正可能ですが、 前者は結果を観察しない限り重み付けできず、 強い仮定が必要になります。 因果推論文脈での選択バイアスは Berkson のパラドックス、 collider bias、 survivorship bias、 self-selection bias、 nonresponse bias、 truncation、 censoring など多様な形をとり、 DAG (Directed Acyclic Graph) で見ると「条件付けてはいけない変数に条件付けた」結果として現れます。 SSDSE-B-2026 のような全数調査 (47 都道府県 × 全年) ではこの種のバイアスは原理的に発生しませんが、 標本データに切り替えた瞬間に発生し得ます。 たとえば「自殺率 → 失業率」を分析するとき、 「自殺者は研究対象から除外」というデザインを取ると collider に条件付けるバイアスが入ります。 数学的に補正したい場合は、 $P(S=1 \mid X)$ の傾向スコア $\pi(X)$ を推定して逆数重み $1/\pi(X)$ をかける ── ただし $\pi(X) \to 0$ のサンプルで重みが爆発する不安定性に注意。 結局、 選択バイアスの最大の対処法は事前のサンプリングデザインであり、 統計手法による事後補正は最終手段だと心得てください。
第二次世界大戦中、 帰還した爆撃機の弾痕分布から「補強すべき箇所」を決めようとした軍。 統計学者 Wald は「帰還しなかった機体の被弾箇所こそ補強すべき」と指摘 ── 帰還機だけを見るのは選択バイアスの典型。 「見えているデータ」がすべてではない、 という生存者バイアスは現代のスタートアップ成功本にも蔓延しています。
$E[Y \mid X, S=1] \neq E[Y \mid X]$ がバイアスの本質。 サンプルに入る確率が結果や共変量に依存すると、 サンプル内での条件付き期待値が母集団のそれとずれます。 補正には傾向スコア $\pi(X) = P(S=1 \mid X)$ を推定し、 $w_i = 1/\pi(X_i)$ で重み付け平均を取る ── これが IPW です。
SSDSE-B-2026 は全 47 都道府県 × 全年の全数データなので原理的にバイアスフリー。 しかし「人口 100 万人以上の県だけ採用」と切り出すと選択バイアスを擬似的に作り出せます。 この場合、 「都市部の医療事情」を全国に外挿することはできません。 サンプリングフィルタが既知なら、 真の母平均との差を比較できます。
sklearn.linear_model.LogisticRegression で傾向スコアを推定、 statsmodels で重み付き回帰 (wls) を実行。 簡易な補正としては df.sample(weights=w) で再標本化することも可。 ただし重みの極端値はバリアンスを膨らませるため、 weight trimming も検討。
ここから先は、 これまでの定義・数式・補正手法を 実際の SSDSE-B-2026 公的データ (47 都道府県 × 2012-2023 年) を題材に、 「人口総数 200 万人以上の県だけを抽出してしまった」 状況をシミュレーションし、 全国平均推定がどこまで歪み、 どの補正法がどこまで戻せるか を 3 つの図 (散布図・ヒストグラム・箱ひげ図) と 6 つの表でていねいに見ていきます。 「選択バイアスは数式の話」 ではなく 「公的統計を使う実務すべてに常駐する問題」 であることが直感できるはずです。
下の散布図は SSDSE-B-2026 から取得した 「総人口 (x 軸) × 出生数 (y 軸)」 の関係です。 全 47 都道府県の真の関係 (青) に対して、 「人口 200 万以上を強くサンプリング」 した擬似バイアス標本 (赤) を重ねてあります。 グラフから読み取れるのは、 散布図の 左下 (小規模県) がスカスカに抜け落ち、 右上の都市部だけが残ること。 すなわち回帰直線の傾きはほとんど変わらなくても、 切片と平均は大きく上振れ し、 「全国の出生数はこんなに多い」 という誤った印象を与えるのです。
図 1. SSDSE-B-2026 の総人口 × 出生数。 200 万人未満の小規模県を脱落させると、 選択後標本の平均出生数が大きく上振れする。
ヒストグラムは、 全 47 都道府県の出生数 (人) の分布を 全数 (青) と 選択後 (赤) で重ねたものです。 全数では左右に長い裾を持つ右歪分布ですが、 選択後は左裾がばっさり消え、 分布の平均が右に大きく移動 しています。 つまり、 全国平均出生数は約 1.5 万人 (15,474 人) ですが、 選択後の標本平均では 3.2 万人 (31,974 人) と 2 倍以上に誇張 される。 これが選択バイアスが「点推定をどれだけ歪めるか」 を最もわかりやすく示す図です。
図 2. 出生数 (人) のヒストグラム。 選択後 (赤) は左裾 (小規模県) が抜け、 平均が右へ大きく移動する。
最後の図は、 「無補正・IPW・層別化・Heckman・DR・MI (multiple imputation)」 という 6 つの推定値を箱ひげ図で並べたものです。 真値 (全数平均) は約 1.5 万人 (15,474 人) で、 無補正の選択標本は約 3.2 万人とほぼ 2 倍に誇張されますが、 IPW で 2.8 万人、 層別化で 1.3 万人、 Heckman で 1.5 万人と、 補正法によってどこまで真値に近づくかが直感的に比較できます。 重要なのは「どの補正法も真値ピッタリには戻らない」 こと。 補正は緩和策であり完全治癒ではない ことを箱ひげ図のばらつきが物語っています。
図 3. 推定法別の全国平均出生数の比較。 真値は約 1.5 万人。 無補正は約 3.2 万人、 Heckman・DR で約 1.5 万人まで回復する。
| DGP (データ生成過程) | 発生する選択バイアス | 典型的な実務シーン | 第一選択の補正法 |
|---|---|---|---|
| $X \to S$ のみ | covariate-dependent selection | 所得階層別の調査回収率差 | IPW / post-stratification |
| $Y \to S$ のみ | outcome-dependent selection | 病院に行く患者だけを観測 | Heckman 2 段階 |
| $X \to S \leftarrow Y$ | collider conditioning | 入院患者の合併症研究 | DAG + IPW |
| $U_1 \to S \leftarrow U_2$ | M-bias | 未観測因子の同時関与 | Sensitivity 分析 |
| 時系列依存 $S_t \to S_{t+1}$ | attrition / dropout | 長期コホート研究の脱落 | IPCW (逆確率打切重み) |
| RCT + 自発的参加 | non-compliance bias | 医療試験の同意者偏り | ITT 解析 / IV 推定 |
| 対面 vs オンライン混合 | mode effect | 調査モード切替の影響 | モード変数を共変量化 |
| 推定法 | 点推定 (人) | 真値からのズレ | ズレ率 (%) | 必要前提 |
|---|---|---|---|---|
| 真値 (全数平均) | 15474 | ±0 | 0.0 | 全数取得 |
| 無補正 | 31974 | +16500 | +106.6 | 無前提 (誤推定) |
| IPW | 27948 | +12474 | +80.6 | 傾向モデル正 |
| post-stratification | 12681 | -2793 | -18.0 | 層分布既知 |
| Heckman 2 段階 | 14510 | -964 | -6.2 | 正規性 |
| DR (Doubly Robust) | 14524 | -949 | -6.1 | 傾向 OR 結果モデル |
→ 補正前は +107% も誇張されますが、 Heckman や DR で 6% 程度に縮小。 一方で「真値ピッタリ」 には到達せず、 残差は 未観測変数とモデル誤特定 に由来します。 だから sensitivity 分析が必須なのです。
| 手法 | 計算コスト | 解釈性 | 頑健性 | 適用範囲 | 推奨シーン |
|---|---|---|---|---|---|
| IPW | 低 | 高 | 中 | 広い | 最初に試す標準手法 |
| 層別化 | 低 | 高 | 中 | 限定 | 層分布が完全に既知 |
| Heckman | 中 | 中 | 低 | 限定 | 経済学・結果依存型 |
| DR | 中 | 中 | 高 | 広い | 学術論文の標準 |
| MI | 高 | 中 | 中 | 広い | 不確実性も評価したい |
| Sensitivity | 低 | 高 | ― | 全般 | 補正法と必ず併用 |
| 概念 | 本質 | 選択バイアスとの違い |
|---|---|---|
| サンプリングバイアス | 標本抽出時の偏り | 選択バイアスのサブクラス (抽出段階限定) |
| 回答バイアス | 回答内容の歪み | 測定バイアス。 標本選択とは別物 |
| 交絡 (confounding) | 共通原因が関係性を歪める | 構造的には別だが補正法は近い |
| covariate shift | train/test 分布のズレ | ML 流の選択バイアス。 ほぼ同義 |
| survivorship bias | 「生き残り」 のみ観測 | 時系列特化の選択バイアス |
| publication bias | 有意な結果のみ出版 | 論文の「選択」 に対する選択バイアス |
SSDSE-B-2026 (47 都道府県 × 2012-2023 年) は全数データなのでバイアスがありません。 ここで「人口総数が 200 万人以上の県だけを取得した」と仮定し、 全国推定が歪む様子を実演します。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd, numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023] # 47 都道府県・2023 年
# 1. 真の全国平均出生数 (全 47 県)
true_mean = df23['A4101'].mean()
print('真の全国平均出生数 (バイアス無し):', round(true_mean))
# 2. 選択バイアス: 人口 200 万人以上の県だけ採用
biased = df23[df23['A1101'] >= 2_000_000]
print('採用された県数:', len(biased), '/', len(df23))
print('バイアス標本での平均:', round(biased['A4101'].mean()))
print('バイアス量:', round(biased['A4101'].mean() - true_mean)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 3. 傾向スコアで補正 (IPW)
from sklearn.linear_model import LogisticRegression
df23 = df23.copy()
df23['S'] = (df23['A1101'] >= 2_000_000).astype(int)
df23['aging'] = df23['A1303'] / df23['A1101'] # 高齢人口比率 (共変量)
lr = LogisticRegression().fit(df23[['aging']], df23['S'])
df23['pi'] = lr.predict_proba(df23[['aging']])[:, 1]
# 4. IPW 平均: バイアス標本に重みをかけて全国を復元
df_s = df23[df23['S'] == 1].copy()
df_s['w'] = 1 / df_s['pi']
ipw_mean = (df_s['A4101'] * df_s['w']).sum() / df_s['w'].sum()
print('IPW 補正後の推定:', round(ipw_mean))
print('真値との差:', round(ipw_mean - df23['A4101'].mean())) |
📝 補足(より正確な分析): このコードを実際に実行すると IPW 補正後の推定は約 31,846 人となり、 無補正 (31,974 人) からほとんど改善しません。 高齢化率だけでは「人口 200 万人以上」という選択メカニズムをほぼ説明できないためで、 IPW の性能は傾向スコアモデルが選択メカニズムを捉えているかに完全に依存する ことを示す好例です。 表 B の IPW (27,948 人) は、 選択を直接駆動する総人口ベースの選択確率を使った場合の値です。 また $\pi(X) \to 0$ のサンプル(極端に人口の少ない県)で重みが爆発するため、 weight trimming (上限 10〜100 程度) が実務的に必要です。
合成データで母集団と標本の平均差を計算する。
| 群 | 真 | 偏った標本 |
|---|---|---|
| 男性 | 50%, 平均 600 | 30%, 平均 600 |
| 女性 | 50%, 平均 400 | 70%, 平均 400 |
1 2 3 4 5 6 | true_avg = 0.5*600 + 0.5*400 biased_avg = 0.3*600 + 0.7*400 bias = biased_avg - true_avg print(f"真平均: {true_avg}") print(f"標本平均: {biased_avg}") print(f"バイアス: {bias}") |
💬 手計算 (Step 2) -40 と Python 出力が完全一致。
SSDSE-B-2026 などの実データを使った最小コード(10行):
1 2 3 4 5 6 7 8 9 10 | import pandas as pd import numpy as np rng = np.random.default_rng(0) # 母集団: 平均5、 SD2 pop = rng.normal(5, 2, 10000) print('母集団平均:', pop.mean().round(2)) # 選択バイアスのある標本: 値が高い人ほど回答率高 prob = 1 / (1 + np.exp(-(pop - 5))) selected = pop[rng.uniform(size=len(pop)) < prob] print('観察平均:', selected.mean().round(2), 'n=', len(selected)) |
※ data/raw/SSDSE-B-2026.csv は e-Stat SSDSE から取得した実データを想定。
このコードでやること:SSDSE-B-2026 の 47 都道府県データを読み込み、 「総人口 200 万人以上を強くサンプリング」 した擬似バイアス標本を作り、 無補正・IPW・post-stratification の 3 推定値を比較する。 真値 (全数平均) からのズレで補正効果が定量化できる。
📥 入力データ (SSDSE-B-2026 抜粋。 列:Code 地域コード、 Prefecture 都道府県名、 A1101 総人口 [人]、 A4101 出生数 [人]):
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 | import pandas as pd
import numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df_latest = df[df['SSDSE-B-2026'] == 2023][['Prefecture', 'A1101', 'A4101']].copy()
# 真値:全 47 都道府県の単純平均出生数
truth = df_latest['A4101'].mean()
# バイアス選択:総人口 >= 200 万のみ抽出
selected = df_latest[df_latest['A1101'] >= 2_000_000]
naive_est = selected['A4101'].mean()
# IPW:選択確率 = 1 / (1 + exp(-(pop-200万)/100万))
df_latest['prob_select'] = 1 / (1 + np.exp(-(df_latest['A1101'] - 2e6) / 1e6))
df_latest['weight'] = 1 / df_latest['prob_select']
selected_w = df_latest[df_latest['A1101'] >= 2_000_000]
ipw_est = (selected_w['A4101'] * selected_w['weight']).sum() / selected_w['weight'].sum()
# Post-stratification:人口 4 分位で層別補正
df_latest['stratum'] = pd.qcut(df_latest['A1101'], 4, labels=False)
strat_weights = df_latest['stratum'].value_counts(normalize=True)
sel = df_latest[df_latest['A1101'] >= 2_000_000]
strat_means = sel.groupby('stratum', observed=False)['A4101'].mean().fillna(0)
ps_est = (strat_means * strat_weights).sum()
print(f'真値 (全数平均) : {truth:.0f} 人')
print(f'無補正 (バイアス標本平均) : {naive_est:.0f} 人')
print(f'IPW 補正後 : {ipw_est:.0f} 人')
print(f'Post-stratification 補正後 : {ps_est:.0f} 人') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:無補正は真値の 約 2 倍 に誇張されており、 「全国の平均出生数は約 3.2 万人」 と政策レポートに書けば致命的な誤解になります。 IPW で 27,948 人、 post-stratification で 12,681 人と真値 15,474 人へ寄るものの、 完全には一致しません。 これは「人口以外の未観測因子 (医療体制、 若年層比率など)」 が残るためで、 ここまで来てなお sensitivity 分析が必要な理由が見えます。
このコードでやること:上の IPW にさらに「結果モデル (出生数を population で線形回帰)」 を組み合わせ、 傾向モデルか結果モデルのどちらかが正しければ一致推定 となる Doubly Robust (DR) 推定を実装する。
📥 入力:上で計算した df_latest (population, 出生数, prob_select, weight)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from sklearn.linear_model import LinearRegression
# Step 1: 結果モデル (総人口 → 出生数) を選択標本で学習
X_sel = selected[['A1101']].values
y_sel = selected['A4101'].values
model = LinearRegression().fit(X_sel, y_sel)
# Step 2: 全標本に予測値を当てる (μ̂(X))
df_latest['mu_hat'] = model.predict(df_latest[['A1101']].values)
# Step 3: DR 推定量を計算
S = (df_latest['A1101'] >= 2_000_000).astype(float)
e = df_latest['prob_select']
Y = df_latest['A4101']
mu = df_latest['mu_hat']
dr_est = (S * Y / e - (S - e) / e * mu).mean()
print(f'DR 推定値: {dr_est:.0f} 人')
print(f'真値からのズレ: {dr_est - truth:+.0f} 人') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:DR は IPW (27,948) より真値 15,474 人に近い 14,524 人を返します。 ズレは −949 人 (約 6%) と無補正 (+107%) に比べ大幅に縮小。 これが「傾向 OR 結果モデルのどちらかが正しければ一致」 という DR の頑健性の実証です。
$X \to Y$、 $X \to S$、 $Y \to S$ の構造で $S$ に条件付ける (=サンプリングする) と、 $X$ と $Y$ の間に「collider 経由のスプリアスパス」が開きます。 これが選択バイアスの代数的本質です。 Pearl の do-calculus で形式的に診断できます。
| 手法 | 想定 | 長所 | 短所 |
|---|---|---|---|
| IPW (傾向スコア) | 共変量依存サンプリング | 直感的 | 重み爆発 |
| Heckman 2 段階 | 結果依存 + 正規性 | 同時推定 | 正規性仮定強い |
| Sample reweighting | 既知サンプリング比 | 単純 | 比未知だと不可 |
| Sensitivity analysis | 未知バイアスの幅推定 | 頑健性評価 | 点推定不可 |
| Multiple imputation | 欠損データ的扱い | 不確実性反映 | 計算重 |
| Doubly robust | 傾向 OR 結果モデル | 頑健性高 | 実装複雑 |
訓練データと運用環境のサンプリング分布がずれる「共変量シフト」「ラベルシフト」「コンセプトシフト」は、 選択バイアスの一種です。 ドメイン適応、 重要度重み付け、 不変表現学習などが対処手段。 LLM の RLHF データも「アノテーター選択バイアス」を含み得ます。
| ステップ | 内容 |
|---|---|
| 1. データ生成過程の明文化 | どんな経路で標本になったか |
| 2. DAG の描画 | $S$ と他変数の関係を矢印で |
| 3. d-separation 判定 | バックドアパスが残るか |
| 4. 補正戦略選定 | IPW / 層別化 / 操作変数等 |
| 5. Sensitivity analysis | 未観測バイアスへの頑健性 |
選択バイアスは知識として知っていても、 「実装段階で気づかぬまま再混入する」 ことが極めて多い概念です。 ここでは、 過去の経済学・疫学・機械学習の事例から繰り返し報告されている 10 個の典型ミス を、 「何が起き、 どう検出し、 どう直すか」 の 3 ステップで整理します。 1 つでも未確認の項目があれば、 あなたの分析にも同じバイアスが潜んでいる可能性があります。
SSDSE-B-2026 は確かに 47 都道府県の全数ですが、 「全数 = バイアスゼロ」 ではありません。 公的統計に乗らない経済活動 (地下経済・自家消費・無申告所得) が漏れていれば measurement bias が残ります。 さらに、 「年度途中で発表された推計値の改訂」 が反映されているか否かで 同じ年・同じ県でも値が変わる ケースがあり、 「全数だから安心」 とは言えません。 対処: 公開資料の改訂履歴を必ず確認し、 「どの版を使ったか」 を分析メモに残す。
IPW は選択確率 $e(X)$ が 0 に近いサンプルで重みが無限大に発散します。 結果、 1 つのデータ点が推定値を支配 してしまい、 信頼区間が異常に広がります。 対処: 重みの上限を裾でクリップ (例: 99 パーセンタイル超は丸める) する stabilized IPW を使う。 重みの最大/最小比 (effective sample size) を報告するのが学術論文の標準。
IPW・DR は 傾向モデル $e(X)$ が正しい 前提に立っています。 「ロジスティック回帰で線形項のみ」 という素朴な仕様だと、 非線形・交互作用が無視され、 残差バイアスが残ります。 対処: スプライン・ランダムフォレスト・XGBoost 等で傾向モデルの柔軟化、 そして balance check (補正後に共変量分布が selected と全体で揃うか) を必ず実施。
「treatment と outcome の両方の影響を受ける変数」 を条件付けすると、 本来独立な 2 変数に偽の相関が生まれる (Berkson's paradox)。 例:入院患者だけを観測して「糖尿病と心臓病は無関係」 と結論する誤解は典型例。 対処: 共変量を機械的に投入するのではなく、 必ず DAG を描いて collider をマーク する。 統計ソフトの「全変数調整」 ボタンは collider を呼び込みやすい。
長期コホート研究で「途中で連絡が取れなくなった人」 を単に除外すると、 健康状態の悪い人が脱落しやすい場合に 「治療効果を過大評価」 する。 対処: 各時点の打切り確率 $C_t$ をモデル化し、 逆確率打切重み (IPCW) を用いて補正。 ITT (intention-to-treat) 解析と併用する。
オンライン調査は 「インターネット利用者・回答に同意した人」 という二重選択を経た標本。 国勢調査の年齢・性別・地域分布と比べないと、 補正の必要性自体が見えません。 対処: post-stratification キャリブレーションで「年齢×性別×地域」 分布を国勢調査に合わせる。 「未補正値」 と「補正値」 を必ず両方報告。
医療データで「同じ患者の異なる時点」 を train/test に分けると、 個人固有情報がリークし、 covariate shift が見かけ上ゼロ になります。 これは「分布シフトに頑健なモデル」 という誤った結論を生みます。 対処: 「患者単位 (group split)」 で分割。 sklearn の GroupKFold を使う。 さらに 「test 分布が将来の運用分布と一致するか」 を本番投入前に検証。
Heckman 2 段階推定は 「誤差項の二変量正規性」 を前提とします。 これが破れると、 補正後の係数が 無補正値より歪む ことすらあります。 対処: 残差の Mardia 検定や Shapiro-Wilk で正規性をチェック。 違反するならノンパラ Heckman (Newey, Klein-Spady) や semi-parametric な方法に切り替える。
Sensitivity 分析は 「未観測バイアスがどれだけあれば結論が覆るか」 を可視化するもので、 補正の代わりにはなりません。 「sensitivity でロバストだから補正不要」 という主張は誤り。 対処: 必ず (a) 主分析の補正 (IPW/DR 等) と (b) sensitivity 分析を セット で実施し、 「補正後の点推定 + sensitivity 範囲」 を 1 枚の表にまとめる。
どんなに丁寧に補正しても 「未観測バイアスはゼロにならない」。 にもかかわらず、 多くの論文・レポートで「補正済みなので信頼できる」 と書いて終わるのが現状です。 査読者・読者は「補正のロジック」 を見たいので、 選択メカニズムの仮定・補正法・残るバイアスの方向 を 1 段落で明記する習慣をつけてください。 対処: Limitations セクションに最低 3 行:「① 選択の構造、 ② 補正法とその仮定、 ③ 補正後も残るバイアスの方向と大きさ」。
| フェーズ | 確認項目 | 完了? |
|---|---|---|
| 設計 | 選択メカニズムを 1 段落で明文化したか | ☐ |
| 設計 | DAG で collider/confounder/mediator を描き分けたか | ☐ |
| 補正 | IPW の重み分布をヒストグラムで確認したか | ☐ |
| 補正 | balance check (補正前後の共変量分布) を実施したか | ☐ |
| 補正 | 2 つ以上の補正法 (IPW + post-stratification 等) を併用したか | ☐ |
| 検証 | Sensitivity 分析 (E-value 等) を実施したか | ☐ |
| 報告 | 「補正後も残るバイアスの方向」 を Limitations に書いたか | ☐ |
標本をどう選ぶかで、母集団と選択標本の推定値がどれだけズレるかを体感します。
下の図を横方向にドラッグ(=選択の強さ)するか、スライダーを動かすと、
灰色=母集団の全点、青=選ばれた標本がリアルタイムに変わり、
母集団の回帰直線(灰の破線)と選択標本の回帰直線(青の実線)、および相関・平均が正確に再計算されます。
2 つのモードを切り替えて、範囲制限で相関が消える例と、コライダー選択で偽の相関が生まれる例を見比べてください。
※ 図は概念理解のための模式データ(乱数シード固定で再現可能)であり、実測値ではありません。SSDSE-B-2026 は 47 都道府県の全数データなので、この種のサンプリング選択は起きません。
💡 試してみよう: モード①で「選択の強さ」を上げると、選ばれた青点の X 範囲が狭まり、母集団では強い相関(r≈0.60)が選択標本ではほぼ消える(範囲制限=range restriction)。モード②では母集団は無相関(r≈0)なのに、S=X+Y の上位だけを残すと青点に右下がりの偽相関が現れる(コライダー選択=Berkson の逆説)。どちらも X・Y の平均が母集団からズレていることにも注目。
選択バイアスの核心は「だれを標本に入れるか」という一手で結論が動く点です。上のモード①では、テストの高得点者だけを合格させると、合格者の中では点数の幅が狭くなり、点数と別指標(例:入学後成績)の相関が実際より弱く見えます。母集団では明確な関係があっても、選抜という切り取りで見えなくなる。モード②では、才能 X と美貌 Y が無関係な母集団から「才能か美貌のどちらかが高い人」だけを俳優として選ぶと、俳優の中では「才能が高い人ほど美貌は平凡」という実在しない負の相関が生じます。共通の結果(選抜 S)で条件付けたことが、独立な 2 変数を結びつけたのです。
この概念は単独で存在するものではなく、 周辺の用語と包含・対比・派生の関係で結ばれています。 中心に置いて、 矢印が伸びる先のページをリンクから辿ってみてください。
因果ダイアグラム (DAG) を使うと、 選択バイアスの構造を視覚的に診断できます。 ノードは変数、 矢印は因果関係、 ボックスは「サンプリングの条件 (= 観測される)」を表します。
$X \to Y$ かつ $S$ がランダムなら、 標本でも母集団でも因果効果は不偏。 これが理想的なランダムサンプリング。
$X$ がサンプリングに影響するが $Y$ には依存しない。 IPW で $1/P(S|X)$ 重み付けすれば不偏化可能。 「都市部だけ調査」のような場合。
$Y$ がサンプリングに影響。 IPW では $Y$ を知らないと補正不可。 Heckman 2 段階や明示的モデル化が必要。 「死亡者を除く臨床研究」がこの形。
$X$ と $Y$ が共に $S$ に流入し、 $S$ で条件付けると見かけ上 $X$ と $Y$ に相関が出る。 Berkson のパラドックスの構造。
未観測共通原因が $S$ を介して接続。 一見独立だが $S$ で条件付けると $X$-$Y$ がリンク。 観察研究で頻発し、 検出が困難。
IPW (Inverse Probability Weighting) の理論的性質を、 SSDSE-B-2026 全数データから人為的に偏ったサブセットを作り、 補正効果を測定して確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd, numpy as np
from sklearn.linear_model import LogisticRegression
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
df23['aging'] = df23['A1303'] / df23['A1101'] # 高齢人口比率
# 真の母集団平均 (全 47 県)
true_mean = df23['A4101'].mean()
print(f'真の平均出生数: {true_mean:.0f} 人')
# 選択メカニズム: 高齢化率が高い県ほど回答しやすい (確率的)
rng = np.random.default_rng(42)
df23['prob'] = 1 / (1 + np.exp(-(df23['aging'] - df23['aging'].median()) / 0.02))
df23['S'] = (rng.uniform(size=len(df23)) < df23['prob']).astype(int)
biased = df23.loc[df23['S'] == 1, 'A4101'].mean()
print(f'高齢化率で偏った標本 ({int(df23.S.sum())} 県): {biased:.0f} 人, バイアス {biased-true_mean:+.0f}') |
1 2 3 4 5 6 7 8 9 10 11 | # IPW 補正: 既知の選択確率で重み付け
selected = df23[df23['S'] == 1].copy()
selected['w'] = 1 / selected['prob']
ipw_mean = (selected['A4101'] * selected['w']).sum() / selected['w'].sum()
print(f'IPW 補正後: {ipw_mean:.0f} 人, 残バイアス {ipw_mean-true_mean:+.0f}')
# 重み統計
print(f'重みの分布: min={selected["w"].min():.2f}, max={selected["w"].max():.2f}')
# Weight trimming
selected['w_trim'] = selected['w'].clip(upper=10)
ipw_trim = (selected['A4101'] * selected['w_trim']).sum() / selected['w_trim'].sum()
print(f'Trimming 後: {ipw_trim:.0f} 人') |
バイアス標本の単純平均は真値 15,474 人を大きく下回り (8,807 人)、 IPW 補正で 10,995 人まで戻りますが、 高齢化率の低い県が完全に欠落しているため完全復元はできません。 重みの最大値は約 3.8 で、 weight trimming (上限 10) の効果は本例では出ません ── 上限値は cross-validation で決めるのが実務的。
IPW は共変量がすべて観測されていると仮定します。 未観測共変量があると補正が不完全。 Rosenbaum (2002) の Sensitivity Analysis では、 「未観測共変量がもたらすバイアス比 Γ」を変化させて結論がいつ覆るかを評価します。
1 2 3 4 5 6 7 8 9 10 11 | import numpy as np
# 観測した因果効果 (例:政策介入群と統制群の差)
tau_observed = 1500 # 円
# Γ を変化させたときの推定区間
for gamma in [1.0, 1.5, 2.0, 3.0]:
# Γ = 1 はバイアス無し、 大きいほど未観測共変量の影響大
bias_max = (gamma - 1) / (gamma + 1) * abs(tau_observed)
lower = tau_observed - bias_max
upper = tau_observed + bias_max
print(f'Γ={gamma:.1f}: τ ∈ [{lower:.0f}, {upper:.0f}] 円')
# Γ=1.5 でも結論 (τ > 0) が維持されるなら頑健 |
Γ=2 でも結論が同じなら「未観測共変量が処置確率を 2 倍歪めても、 結論は変わらない」と言える ── 強い頑健性。 観察研究の論文ではこの Sensitivity Analysis を併記するのが標準的なベストプラクティスです。
機械学習の文脈では、 選択バイアスは「訓練分布 ≠ テスト分布」として現れます。 主要な 3 種類を整理します。
| シフト種別 | $P(X)$ | $P(Y\|X)$ | 対処 |
|---|---|---|---|
| Covariate Shift | 変化 | 不変 | Importance Weighting |
| Label Shift | 不変 | 変化 (周辺) | BBSE, EM |
| Concept Shift | 不変 | 変化 | Online Learning |
| Combined Shift | 変化 | 変化 | Domain Adaptation |
| Selection Bias | 一部欠落 | 同上 | IPW + DA |
SSDSE-B-2026 で「2012-2017 年データで訓練 → 2018-2023 年で予測」とすると、 高齢化率の上昇による Covariate Shift が発生します。 対処として、 訓練時に最新年データを upweight するか、 ドメイン適応 (DANN, MMD) を適用します。
論文・社内レポートで選択バイアスを誠実に扱うためのテンプレートを示します。 「無いことにする」「言及しない」のは現代的な研究倫理に反します。
既存セクションでは IPW・Heckman・DAG までを扱いました。 本追補では SSDSE-B-2026 の 都道府県別 47 行 × 12 年 = 564 行 全数データを使い、 「もし特定の県が回答拒否したら / もし観測が市町村単位でランダムに欠落したら」 という仮想シナリオを 5 通り作成し、 IPW / 層別化 / Heckman / Sensitivity Analysis / Doubly Robust の 5 補正法を比較します。 単一補正法の限界、 補正後の標準誤差爆発、 効果量の感度幅 ── 教科書では端折られがちな 「補正して終わりではない」 という実務的厳しさをハンズオンで体感してもらうのが本追補の目的です。
標本は母集団を映す鏡です。 鏡が 傾いている (=サンプリング確率が均一でない) と、 顔は写るのに比率が歪みます。 鏡が 欠けている (=一部の母集団が完全に除外) と、 そもそも顔の一部が見えません。 IPW は「傾いた鏡を計算で立て直す」 補正で、 欠けた部分は計算で復元できないため、 推定対象を「観測されうる母集団 (=サンプリング確率 > 0 の領域)」 に限定する必要があります。 この positivity (overlap) 仮定 が満たされない領域への外挿は、 どれだけ補正しても保証されません。 「強い仮定の上にしか強い結論は乗らない」 ── 統計学の根本ルールです。
欠測メカニズムは Rubin (1976) の三分類で語られます。 数式と日本語訳を併記します。
$R$ は「観測された (1) か否か (0)」 の指示変数、 $X$ は観測共変量、 $Y$ は (時に欠測する) 目的変数です。 MCAR は「コインを投げて消す」 ── 完全にランダム。 完全ケース分析でも不偏。 MAR は「観測されている $X$ で条件付ければランダム」 ── $X$ を使った補正 (IPW, multiple imputation) で不偏推定可能。 MNAR は「観測できない $Y$ 自体が欠測理由」 ── 補正不可能、 sensitivity analysis で頑健性チェックのみ。 SSDSE-B-2026 のような全数データは「もし MNAR なら何が起こるか」 をシミュレーションする実験台として理想的です。
このコードでやること: SSDSE-B-2026 の 47 都道府県 2023 年データに「人口 200 万人以上の県のみ回答」 という仮想 selection mechanism を適用、 真値 (全 47 県平均) との乖離を補正前 / IPW / 層別化 / Heckman / DR (Doubly Robust) / 単純平均で比較。
📥 入力データ (SSDSE-B-2026 2023 年抜粋):
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 | import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression, LinearRegression
from scipy.stats import norm
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023].copy()
true_mean = df23['A4101'].mean() # 全国平均出生数
# selection mechanism: 人口 200 万人以上の県のみ観測
df23['S'] = (df23['A1101'] >= 2_000_000).astype(int)
naive = df23.loc[df23['S'] == 1, 'A4101'].mean()
# 既知の平滑な選択確率 e(X)
df23['e'] = 1 / (1 + np.exp(-(df23['A1101'] - 2e6) / 1e6))
df23['w'] = 1 / df23['e']
sel = df23['S'] == 1
# IPW
ipw_mean = (df23.loc[sel, 'A4101'] * df23.loc[sel, 'w']).sum() / df23.loc[sel, 'w'].sum()
# 層別化: 総人口 4 分位で層ごとに平均 → 加重
df23['stratum'] = pd.qcut(df23['A1101'], 4, labels=False)
strat_mean = sum(
df23[(df23['stratum']==k) & sel]['A4101'].mean() * (df23['stratum']==k).mean()
for k in range(4) if df23[(df23['stratum']==k) & sel].shape[0] > 0)
# Heckman 2 段階: Mills 比を共変量に追加
xb = (df23['A1101'] - 2e6) / 1e6
df23['mills'] = norm.pdf(xb) / np.clip(norm.cdf(xb), 1e-6, 1)
heck = LinearRegression().fit(df23.loc[sel, ['A1101', 'mills']], df23.loc[sel, 'A4101'])
heck_pred = heck.predict(df23[['A1101', 'mills']]).mean()
# Doubly Robust
out_model = LinearRegression().fit(df23.loc[sel, ['A1101']], df23.loc[sel, 'A4101'])
mu = out_model.predict(df23[['A1101']])
dr = (mu + df23['S'] * df23['w'] * (df23['A4101'] - mu)).mean()
print(f'真値={true_mean:.0f}\n単純={naive:.0f}\nIPW={ipw_mean:.0f}\n層別={strat_mean:.0f}\nHeckman={heck_pred:.0f}\nDR={dr:.0f}') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:単純平均は真値から +106.6% も乖離する一方、 補正後は IPW (+80.6%) → 層別 (−18.0%) → Heckman (−6.2%) → DR (−6.1%) と精度が改善します。 Heckman・DR が真値に最も近いのは 「傾向スコア OR 結果モデルのいずれかが正しければ不偏」 という二重保証のおかげ。 ただし全補正法とも positivity 仮定 ── 人口の少ない県 (鳥取県・島根県など) は選択標本に現れず外挿になる ── から逃れられない。 補正の限界を理解した上で結論を述べるのが実務の正解。
選択バイアスを取り巻く手法群を、 仮定の強さの順に並べると以下になります。 強い仮定ほど推定の効率は高いが、 仮定が崩れた時の頑健性は低くなる ── 「仮定とロバスト性のトレードオフ」 を体系的に整理してください。
| 手法 | 主な仮定 | 推定対象 | 頑健性 | SSDSE-B 適用例 |
|---|---|---|---|---|
| 完全ケース分析 | MCAR | 全母集団平均 | 低 | 回答拒否がランダム時のみ |
| IPW (Horvitz-Thompson) | MAR + positivity | 全母集団平均 | 中 | 総人口による回答率歪み |
| 層別化 | 層内 MCAR | 層加重平均 | 中 | 人口規模別 4 分位 |
| Heckman 2 段階 | 二変量正規性 + 排除制約 | 条件付き期待値 | 低 | 所得 → 回答 → 賃金の同時推定 |
| Doubly Robust | 傾向 OR 結果モデル正しい | 全母集団平均 | 高 | 推奨デフォルト |
| Multiple Imputation | MAR + 補完モデル正しい | 全母集団 (含分散) | 中 | 欠測値補完 + Rubin's rule |
| Sensitivity (Rosenbaum) | 未観測 Γ の上限 | 結論の頑健性 | 最高 | MNAR 下での Γ 探索 |
| DA (Domain Adaptation) | 共変量シフト | target 領域予測 | 中 | 県間移転学習 |
このコードでやること: SSDSE-B-2026 で「県別の出生数」 を IPW 補正した推定値が、 未観測共変量によってどれだけ歪み得るかを Rosenbaum の感度パラメータ $\Gamma$ で評価。 $\Gamma=1$ は「未観測歪み無し」、 $\Gamma=2$ は「処置確率が 2 倍歪んでも結論が同じ」 という頑健性の上限を意味します。
📥 入力データ: 上記 IPW スクリプトの $\pi(X)$, $w_i$, 推定値 $\hat{\mu}_{\rm IPW}$ を再利用。
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 numpy as np
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023].copy()
df23['S'] = (df23['A1101'] >= 2_000_000).astype(int)
sel = df23['S'] == 1
# 既知の平滑な選択確率 e(X)
pi = (1 / (1 + np.exp(-(df23['A1101'] - 2e6) / 1e6))).values
# Rosenbaum's Γ で歪み上限・下限を探索
true_mean = df23['A4101'].mean()
y = df23.loc[sel, 'A4101'].values
results = []
for gamma in [1.0, 1.2, 1.5, 2.0, 3.0]:
pi_low = pi / (gamma - (gamma - 1) * pi)
pi_high = gamma * pi / (1 + (gamma - 1) * pi)
w_low = 1 / np.clip(pi_low, 0.02, 1)
w_high = 1 / np.clip(pi_high, 0.02, 1)
est_low = (y * w_low[sel.values]).sum() / w_low[sel.values].sum()
est_high = (y * w_high[sel.values]).sum() / w_high[sel.values].sum()
results.append((gamma, est_low, est_high))
for g, lo, hi in results:
print(f'Γ={g}: 推定範囲 [{lo:.0f}, {hi:.0f}] 真値={true_mean:.0f}') |
📤 実行すると次の出力が得られる:
💬 結果の読み方: IPW 推定値は $\Gamma$ を 3 まで上げても 23,600〜30,400 人の範囲に留まり、 未観測交絡に対しては頑健です。 ただしどの $\Gamma$ でも真値 15,474 人には届きません ── これは positivity 違反 (人口 200 万未満県の完全欠落) による偏りで、 感度分析では救えない種類の問題です。 論文では 主分析の補正値と sensitivity 範囲を併記 するのが標準的ベストプラクティスです。
| 区分 | 用語 | 関係性 |
|---|---|---|
| 前提 (prerequisite) | 確率, 標本抽出, 条件付き確率 | $P(R \mid X, Y)$ を理解するには確率論の基礎が必要 |
| 並列 (cognate) | 交絡, 測定バイアス, サバイバーシップ | 三大バイアスの兄弟。 因果推論の必修 |
| 発展 (derivative) | 傾向スコア, DAG, Heckman 推定, ドメイン適応 | 補正手法・診断ツール・ML への拡張 |
理論を頭で理解しても、 実データで「補正したらどう変わるか」 を手で動かさないと身につきません。 本ラボでは SSDSE-B-2026 を素材に、 5 つの典型的な selection mechanism を順に再現し、 補正前後の推定値を比較します。 各シナリオは 「現実世界で何に対応するか」 を明示し、 単なる数値演習に終わらせない構成にしています。
大都市圏ほど自治体の統計部署が大きく、 調査回答率が高い ── 現実によくある状況です。 SSDSE-B-2026 で人口 300 万人以上の 10 県のみが回答したと仮定し、 「県別の平均出生数」 の推定が真値からどれだけずれるかを観察します。
このコードでやること: 人口 300 万人以上の県のみで「出生数」 の平均を取り、 全 47 県の真値と比較。 IPW で補正。
📥 入力例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023].copy()
true_birth_mean = df23['A4101'].mean()
df23['S'] = (df23['A1101'] >= 3_000_000).astype(int)
biased_mean = df23.loc[df23['S'] == 1, 'A4101'].mean()
# IPW(既知の平滑な選択確率)
df23['e'] = 1 / (1 + np.exp(-(df23['A1101'] - 3e6) / 1e6))
w = 1 / df23['e']
ipw_mean = (df23.loc[df23['S']==1, 'A4101'] * w[df23['S']==1]).sum() / w[df23['S']==1].sum()
print(f'真値: {true_birth_mean:.0f} 人')
print(f'バイアス標本平均: {biased_mean:.0f} 人 (+{(biased_mean/true_birth_mean-1)*100:.1f}%)')
print(f'IPW 補正: {ipw_mean:.0f} 人 ({(ipw_mean/true_birth_mean-1)*100:+.1f}%)') |
📤 実行結果:
💬 単純平均では真値の 約 2.8 倍 に膨れ上がります。 IPW でも +168% までしか縮まないのは、 人口 300 万未満県が選択標本に 1 つも無く (positivity 違反) 外挿になるためで、 完全補正は不可能。 教訓: 「対象母集団に含まれない県を結論に含めるな」。
「成長している地域に共通の特徴は何か」 という問いを、 2012→2023 年で人口が増えた県 (=生き残った県) のみで調べると、 人口が減った県が母集団から除外され、 「正しく見える間違った一般化」 に陥ります。 SSDSE-B-2026 では人口増加県のみで「出生数」 の平均を見て、 真値との乖離を確認します。
このコードでやること: 2012→2023 年で人口が増えた県のみで出生数の平均を計算し、 全 47 県平均と比較。
📥 入力例: SSDSE-B-2026 から 2023 年・47 県を抽出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd
import numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
pop12 = df[df['SSDSE-B-2026'] == 2012].set_index('Prefecture')['A1101']
pop23 = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture')['A1101']
# 「生き残った (人口が増えた) 県」だけを見る
growing = pop23.index[pop23 > pop12]
df23 = df[df['SSDSE-B-2026'] == 2023]
true_mean = df23['A4101'].mean()
surv_mean = df23[df23['Prefecture'].isin(growing)]['A4101'].mean()
print(f'全47県の平均出生数: {true_mean:.0f}')
print(f'人口増加県 ({len(growing)} 県) のみ: {surv_mean:.0f}')
print(f'歪み: {surv_mean/true_mean:.2f} 倍') |
📤 実行結果:
💬 人口が増えた 7 県のみを見ると、 平均出生数が全国の 2.89 倍にも見えます。 「生き残った (人口が増えた) 県は出生数が多い」 という当たり前の発見を全国一般化してしまうのが survivorship bias の罠。
医療体制の手厚い (一般診療所が多い) 都市部の県ほど各種調査に協力しやすい ── という状況を模します。 SSDSE-B-2026 の「一般診療所数 (I5102)」 が中央値以上の県と未満の県で「出生数」 を比較し、 「協力しやすい県に偏るバイアス」 を可視化します。
このコードでやること: 一般診療所数の上位 50% 県と下位 50% 県で出生数を比較し、 「医療の充実度」 で標本を選ぶと結果がどう歪むかを確認。
📥 入力例: SSDSE-B-2026 から 2023 年・47 県・一般診療所数 (I5102) と出生数 (A4101) を抽出。
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023]
# 一般診療所数 (I5102) が中央値以上の「医療充実県」だけを見る
med = df23['I5102'].median()
high = df23[df23['I5102'] >= med]
low = df23[df23['I5102'] < med]
print(f'医療充実県 ({len(high)} 県) 出生数平均: {high["A4101"].mean():.0f}')
print(f'医療希薄県 ({len(low)} 県) 出生数平均: {low["A4101"].mean():.0f}')
print(f'全47県 出生数平均: {df23["A4101"].mean():.0f}')
print(f'歪み: 医療充実県のみで {high["A4101"].mean() / df23["A4101"].mean():.2f} 倍') |
📤 実行結果:
💬 医療充実県は人口も多い県が多いため、 「一般診療所の多い県でのみ調査」 すると平均出生数が全国の 1.6 倍に見え、 人口効果と医療効果が混ざる ── 典型的な志願者バイアス。
合計特殊出生率 (TFR) で「1.40 以上の県のみ観測」 と切断すると、 平均値は当然押し上げられます。 SSDSE-B-2026 の「合計特殊出生率 (A4103)」 を 1.40 以上で切断し、 Tobit モデルで補正を試みます。
このコードでやること: SSDSE-B-2026 の合計特殊出生率 (A4103) を用い、 1.40 以上の県のみで平均を取り、 Tobit (打ち切り回帰) で補正。
📥 入力例: SSDSE-B-2026 から 2023 年・47 県・合計特殊出生率 (A4103) を抽出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd
import numpy as np
from scipy.stats import norm
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df23 = df[df['SSDSE-B-2026'] == 2023]
tfr = df23['A4103'] # 合計特殊出生率
true_mean = tfr.mean()
threshold = 1.40
trunc = df23[df23['A4103'] >= threshold] # 閾値以上のみ観測 (切断)
naive_mean = trunc['A4103'].mean()
# Tobit 補正の近似: 切断正規分布の平均補正
sigma = tfr.std()
z = (threshold - naive_mean) / sigma
lambda_hat = norm.pdf(z) / (1 - norm.cdf(z))
tobit_corrected = naive_mean - sigma * lambda_hat
print(f'真値: {true_mean:.3f}')
print(f'切断後単純平均 ({len(trunc)} 県): {naive_mean:.3f} (+{naive_mean-true_mean:.3f})')
print(f'Tobit 補正: {tobit_corrected:.3f}') |
📤 実行結果:
💬 切断 (truncation) は欠損 (censoring) と異なり、 閾値未満は完全に観測されません。 Tobit モデルは正規性を仮定して未観測領域を「平均回帰量」 で補正。 単純平均 1.468 → Tobit 1.401 と真値 1.293 へ近づきますが、 完全復元は不可能。
経時調査で対象者が時間と共に脱落する場合、 脱落率がグループ間で異なれば差別的脱落 (differential attrition) です。 SSDSE-B-2026 の 2012-2023 年パネルで、 ある年から人口減少県のデータが欠落したと仮定し、 全国推定の歪みを確認します。
このコードでやること: SSDSE-B-2026 の 2018 年以降、 人口減少県のデータが欠落したと仮定し、 残った県だけから全国人口を補外推定するとどれだけ歪むかを確認。
📥 入力例: SSDSE-B-2026 から 2012-2023 年・47 県・総人口 (A1101) の時系列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import pandas as pd
import numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
# 2012 年と 2023 年の人口を比較し、減少県を特定
pop_2012 = df[df['SSDSE-B-2026']==2012].set_index('Prefecture')['A1101']
pop_2023 = df[df['SSDSE-B-2026']==2023].set_index('Prefecture')['A1101']
declining = pop_2023.index[pop_2023 < pop_2012]
print(f'人口減少県数 (2012→2023): {len(declining)}')
# 2018 年以降の差別的脱落をシミュレート
panel = df[df['SSDSE-B-2026'] >= 2012].copy()
true_total_2020 = panel[panel['SSDSE-B-2026']==2020]['A1101'].sum()
panel_biased = panel[~((panel['SSDSE-B-2026'] >= 2018) & (panel['Prefecture'].isin(declining)))]
obs = panel_biased[panel_biased['SSDSE-B-2026']==2020]
naive_total_2020 = obs['A1101'].sum() * (47 / obs['Prefecture'].nunique())
print(f'真値 (2020 年全47県): {true_total_2020/1e6:.2f} 百万人')
print(f'補外推定: {naive_total_2020/1e6:.2f} 百万人')
print(f'歪み: {(naive_total_2020/true_total_2020-1)*100:+.2f}%') |
📤 実行結果:
💬 減少県が脱落した後、 残った (大都市中心の) 県だけを 47 県ぶんに引き伸ばすと全国人口を約 +172% も過大推定してしまいます。 補正には panel data 専用の IPCW (逆確率打切重み) や random effects model が必要です。
| # | シナリオ | SSDSE-B-2026 での再現 | 補正法 | 完全補正可能? |
|---|---|---|---|---|
| 1 | Self-Selection | 人口300万以上の県のみ | IPW | 部分的 (positivity 内のみ) |
| 2 | Survivorship | 人口増加県のみ | 層別化+逆確率 | 部分的 |
| 3 | Volunteer | 医療充実県のみ | 傾向スコア+DR | 部分的 |
| 4 | Truncation | 合計特殊出生率1.40超 | Tobit | 分布仮定下のみ |
| 5 | Differential Attrition | 減少県の年次欠落 | IPCW | 脱落モデル次第 |
選択バイアス対策はチェックリスト化して運用するのが実務的です。 以下は論文投稿前 / モデル本番投入前に確認すべき項目です。 すべての項目が「Yes」 か「N/A」 と回答できなければ、 補正は不完全な可能性が高い。
| 段階 | 確認項目 | 回答 |
|---|---|---|
| 設計 | サンプリング mechanism を文書化したか (誰が・なぜ・どう選ばれたか) | Y / N |
| 設計 | 対象母集団 (target population) と推論母集団 (study population) の差を明示したか | Y / N |
| 設計 | DAG で sampling node を描いたか | Y / N |
| 診断 | 欠測パターン (MCAR/MAR/MNAR) を Little's test 等で検定したか | Y / N |
| 診断 | positivity 違反の領域を傾向スコア分布で確認したか | Y / N |
| 補正 | 傾向スコアモデルの仕様 (機能形・共変量選択) を明示したか | Y / N |
| 補正 | stabilized weight・trimming・truncation の閾値を事前登録したか | Y / N |
| 補正 | DR (Doubly Robust) など複数法で結果一致を確認したか | Y / N |
| 頑健性 | Rosenbaum's Γ 等の sensitivity analysis を実施したか | Y / N |
| 頑健性 | 標準誤差の bootstrap 推定 (sandwich 推定量) を併記したか | Y / N |
| 報告 | バイアスの方向・規模・結論への影響を考察に明記したか | Y / N |
| 報告 | 推論対象集団を明示し外挿の限界を述べたか | Y / N |
通常の IPW $w = 1/P(S=1|X)$ では極端な低確率サンプルで重み爆発しますが、 stabilized weight は分子に marginal probability $P(S=1)$ を掛けることで重みの期待値を 1 に保ち、 分散を大幅に下げます。 「観測されにくいサンプルの寄与を抑え、 観測されやすいサンプルの寄与は抑えすぎない」 ── stabilizer の役目です。 推定の不偏性は保たれ、 効率性が改善 ── Robins-Hernan 推奨の標準形。
本ページでは、 選択バイアスを (1) 直感、 (2) 数式 MAR/MCAR/MNAR、 (3) DAG での collider 同定、 (4) IPW / 層別化 / Heckman / DR / Sensitivity の 5 補正法比較、 (5) 5 シナリオの SSDSE-B-2026 実値ハンズオン、 (6) チェックリスト形式の実装ガイドの 6 軸で扱いました。 「補正したから問題なし」 ではなく 「補正の限界を明示しつつ、 結論の頑健性を sensitivity で示す」 という、 因果推論実務の本筋を体得してもらうのが本ページの最終目的です。
特に強調したいのは 「positivity 違反領域への外挿は不可能」 という事実。 SSDSE-B-2026 で人口 200 万以上の県のみを観測した場合、 沖縄県・鳥取県のような小規模県への外挿は どの補正法を使っても保証されません。 これは選択バイアスというより推論対象集団の限定問題で、 IPW が「魔法の杖」 ではないことを意味します。 統計学の謙虚さ ── 「分かることだけを分かる範囲で言う」 が本記事の核心メッセージです。
最後に、 SSDSE-B-2026 のような 全数データ は選択バイアスを学ぶ上で最高の教材です。 「もし XX が欠落したら」 をシミュレートできるからです。 一般のサンプル調査データではこの「真値との比較」 が不可能。 教育目的では本データを使い、 5 シナリオすべてをハンズオンで触ってから実プロジェクトに進むのを強く推奨します。
「選択バイアス」は 標本がそもそも母集団を代表していない時点で生じる歪み であり、 サンプリング設計の段階で予防策を講じ、 残ったバイアスは IPW や Heckman 2 段階推定で補正、 最後に感度分析で頑健性を確認するという上→下流の一貫対応が求められる。
選択バイアスは「データが取れた時点」で生じるため、 サンプリング設計の段階で予防し、 残った歪みを IPW や Heckman で補正、 最後に感度分析で頑健性を示す、 という上→下流の一貫対応が必要である。
「選択バイアス」をどう扱うかは、 サンプリング方式と欠測メカニズムで判断する。
SSDSE-B-2026 は全 47 都道府県の悉皆データなので選択バイアスが起きにくいが、 任意回答の Web アンケートを統合する際は IPW 補正を必ず検討する。
この追補は、 上の共有図キャプションに置いた 📝 補足(図が汎用図である旨) とは別の角度から、 選択バイアスの核心である「選ばれ方が結果と手を組む」構造を、 SSDSE-B-2026 の実測でもう一例だけ、 コンパクトに追体験するためのものです。 既存本文で扱った「人口規模による選択」とは選択変数を変え、 「若い県だけ観測してしまう」という新しい擬似メカニズムで、 補正がどこまで効くか/効かないかを実測値で確かめます。
選択バイアスの本質は、 標本が母集団をランダムに抜き取ったものではなく、 「選ばれる確率が、 見たい結果 $Y$ そのものと相関している」点にあります。 だから標本平均は母平均から方向をもってずれる(=系統誤差)。 代表的な「選ばれ方」は 3 つ:生存者バイアス(帰還した戦闘機・生き残った成功企業だけが観測される)、 志願者バイアス(調査に協力的な人=関心の高い層だけが答える)、 脱落(attrition)(元気な人ほど追跡に残り、 体調の悪い人が抜ける=健康労働者効果に通じる)。 いずれも「観測されていること自体」が選択条件になっている点が共通で、 相関や平均といった要約統計量を静かに歪めます。
高齢化率(=A1303 老年人口 ÷ A1101 総人口)が中央値未満の「若い」23 県だけを観測できたと仮定します。 高齢化率と出生数(A4101)は実測で負の相関 r = −0.737(総人口と出生数はほぼ完全な r = 0.995)なので、 若い県に偏ると出生数は上振れします。 下は CSV から再現できる実測値です(架空の数値ではありません)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd, numpy as np
from sklearn.linear_model import LogisticRegression
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()
d['aging'] = d['A1303'] / d['A1101'] # 高齢化率
true = d['A4101'].mean() # 真値 (全 47 県)
d['S'] = (d['aging'] < d['aging'].median()).astype(int) # 若い県だけ観測
naive = d.loc[d['S'] == 1, 'A4101'].mean()
# IPW: 選択を駆動した高齢化率で傾向スコアを推定
lr = LogisticRegression().fit(d[['aging']], d['S'])
d['pi'] = lr.predict_proba(d[['aging']])[:, 1]
sel = d[d['S'] == 1].copy(); sel['w'] = 1 / sel['pi']
ipw = (sel['A4101'] * sel['w']).sum() / sel['w'].sum()
print(round(true), round(naive), round(ipw)) |
💬 読みどころ:ここでは選択を駆動した変数(高齢化率)を IPW にそのまま使っているのに、 補正後も +55% とほとんど改善しません。 理由は、 選択が「中央値で真っ二つ」という決定的な閾値なので、 傾向スコア $\pi(X)$ が選択群では 1、 非選択群では 0 に張り付き、 positivity(重なり)が成立しないから。 選ばれた 23 県の中で重みが均一になり、 「重み付け直し」が働かないのです。 これは既存本文の「人口 200 万以上」例(IPW が中途半端に効いた)とは逆のパターンで、 「補正が効くかは選択メカニズムの重なり次第」という一点を実測で示しています。 一方 post-stratification は層構造を仮定に持ち込むぶん動きますが、 今度は行き過ぎて −22% に振れる ── どの補正も万能薬ではない。
深掘りの動線として、 本用語集に実在する隣接ページのみを挙げます(未整備の項目 ── サバイバーシップ/傾向スコア/Heckman/DAG/Berkson 等 ── はテキストで示しました)。