本ページは 欠損メカニズム(Missing Data Mechanism)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。
欠損メカニズム(Missing Data Mechanism)は、 統計学・データサイエンスの 基礎理論です。 アンケート、 医療データ、 センサー測定値など 欠損のないデータはまずないのが現実。 ところが「とりあえず平均で埋める」「行ごと削除」といった素朴な対応は、 メカニズムによっては 結果を大きく歪めることが知られています。
3 つのメカニズムを具体例で:
| 分類 | 意味 | 例 | 対処 |
|---|---|---|---|
| MCAR(完全ランダム) | 欠損が完全に偶然 | 機器故障で一部測定不能 | 削除 or 平均補完 OK |
| MAR(観測値依存) | 他の観測値で欠損確率が決まる | 男性が体重欄を空欄にしがち | 多重代入法 推奨 |
| MNAR(欠損値自身依存) | 欠損値自身の値で欠損が決まる | 高所得者ほど所得欄を空欄に | モデル化が必要・困難 |
例:健康診断データで 「肥満気味の人ほど体重欄を空欄」にすると、 これは MNAR。 単純に「他の人の体重平均」で埋めると、 全体の平均体重を 過小評価してしまいます。
3 メカニズムの数学的定義($Y$ = 観測したい値、 $M$ = 欠損指示子、 $X$ = 他の観測変数):
例:所得アンケートで「年収 1000万以上の人ほど無回答」(MNAR)の場合:
| 処理方法 | 推定平均年収 | 真値からの偏り |
|---|---|---|
| 真の平均(理論値) | 650 万円 | 0 |
| 欠損行を削除(CCA) | 520 万円 | −130 万(過小評価) |
| 観測値の平均で補完 | 520 万円 | −130 万(過小評価) |
| 多重代入法(MICE) | 540 万円 | −110 万(改善するが不十分) |
| 選択モデルで対処 | 620 万円 | −30 万(仮定を入れて補正) |
MNAR では どの方法も完全な不偏推定にならない。 ベストエフォートとして選択モデルや感度分析が使われます。
最小コードで動かしてみる例:
1 2 3 4 5 6 7 8 9 10 | import pandas as pd from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) print('欠損率:', df.isnull().mean()) # 多重代入法(MICE) — MAR を仮定 imputer = IterativeImputer(max_iter=10, random_state=0) df_filled = imputer.fit_transform(df.select_dtypes(include='number')) |
本ページは 欠損メカニズム(Missing Data Mechanism)を 12+ セクションで多角的に解説します。
📂 分野:データ処理 / 関連語:MCAR / MAR / MNAR / 多重代入法 / Heckman
Rubin (1976) で定式化された統計理論。 「なぜ欠損したのか」によって、 削除・補完・モデル化のいずれが正当化されるかが決まります。
SSDSE-B(47都道府県統計)でも欠損は普通で、 MAR/MNAR の見極めが結論を左右します。 scikit-learn の Imputer 群、 R の mice、 SAS の PROC MI — どれもこの 3 分類が前提。
典型場面:(a) Methods 節で「12% 欠損を多重代入で対処」、 (b) 因果推論でサンプル選択バイアスを議論、 (c) 機械学習データパイプライン設計、 (d) アンケート設計時の必須設問判断。
「データが穴あきになる原因」を 3 つに分類します。
| メカニズム | 定義 | 具体例 | 削除はOK? |
|---|---|---|---|
| MCAR | 欠損確率が全変数と独立 | 印刷ミスでランダム欠落 | ○ バイアスなし |
| MAR | 欠損確率が観測値に依存 | 農村県で IT 指標欠損 | △ MICE |
| MNAR | 欠損確率が欠損値自身に依存 | 高所得世帯が回答拒否 | × 補正困難 |
1000 通配って 200 通無回答のとき:
仮定の強さ:MCAR ⊂ MAR ⊂ MNAR。 強い仮定ほど分析は楽だが、 現実は MNAR 寄り。
$Y$ = 観測したい変数、 $M$ = 欠損指示子、 $X$ = 補助変数。 各メカニズムは $P(M\mid Y,X)$ の依存性で定義:
MICE が正当化されるのは MAR まで。 MNAR では Heckman 選択モデルが必要。
完全データを $(Y, M)$ とすると、 $p(Y_{obs}, M \mid \theta, \psi) = \int p(Y \mid \theta) p(M \mid Y, \psi) dY_{mis}$。 MAR では $\psi$ を無視できる(ignorable)。
所得アンケート(真平均 650万、 高所得 MNAR):
| 方法 | 推定平均 | ズレ | 備考 |
|---|---|---|---|
| 真の平均 | 650 | ±0 | 参照 |
| Listwise | 520 | −130 | 高所得が抜けたまま |
| 平均補完 | 520 | −130 | 分散も過小 |
| KNN (k=5) | 535 | −115 | 近傍依存 |
| MICE (m=20) | 545 | −105 | MAR 仮定 |
| Heckman | 620 | −30 ✓ | 仮定要 |
$\hat\theta_1=540, \hat\theta_2=555, \hat\theta_3=548$、 各分散 $U_k=900,950,920$。
$\bar\theta = 547.67$、 $\bar U = 923.3$、 $B = 56.3$、 $T = \bar U + (1+1/m)B = 998.4$、 $SE \approx 31.6$、 95% CI = $(485.7, 609.6)$
1 2 3 4 5 6 7 8 9 | import pandas as pd import numpy as np import missingno as msno import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) print('欠損率:', df.isnull().mean().sort_values(ascending=False).head(10)) msno.matrix(df); plt.savefig('figures/missing_matrix.png', dpi=120) msno.heatmap(df); plt.savefig('figures/missing_heatmap.png', dpi=120) |
1 2 3 4 5 6 7 | from sklearn.linear_model import LogisticRegression num = df.select_dtypes(include='number') miss_flag = num.isnull().astype(int) for col in num.columns[num.isnull().any()][:5]: others = num.drop(columns=col).fillna(num.mean()) lr = LogisticRegression(max_iter=500).fit(others, miss_flag[col]) print(col, '説明力:', lr.score(others, miss_flag[col])) |
1 2 3 4 5 6 7 | from sklearn.experimental import enable_iterative_imputer from sklearn.impute import SimpleImputer, KNNImputer, IterativeImputer imp_mean = SimpleImputer(strategy='mean').fit_transform(num) imp_knn = KNNImputer(n_neighbors=5).fit_transform(num) imp_mice = IterativeImputer(max_iter=10, random_state=0).fit_transform(num) result = pd.DataFrame({'元':num.mean(),'平均':pd.DataFrame(imp_mean,columns=num.columns).mean(),'KNN':pd.DataFrame(imp_knn,columns=num.columns).mean(),'MICE':pd.DataFrame(imp_mice,columns=num.columns).mean()}) print(result.head(10)) |
1 2 3 4 5 | import statsmodels.api as sm from statsmodels.imputation.mice import MICE, MICEData mice_data = MICEData(num) mice = MICE('death_rate ~ aging_rate + medical_cost', sm.OLS, mice_data) print(mice.fit(n_burnin=5, n_imputations=20).summary()) |
1 2 3 | imp = SimpleImputer(strategy='median', add_indicator=True) X = imp.fit_transform(num) print('元の列数:', num.shape[1], '/ 補完後:', X.shape[1]) |
Donald B. Rubin (1976) "Inference and Missing Data" で体系化。 それ以前は「削除か平均補完」が標準。 Rubin は MCAR/MAR/MNAR の 3 分類と ignorable 概念を導入。 1987 年の著書で多重代入法が確立。
2000 年代に Schafer・van Buuren が MICE を実装し、 R の mice パッケージで普及。 scikit-learn の IterativeImputer もこの系譜。 GAIN, MIWAE, MissForest など深層補完も活発。
主要文献:Rubin (1976) Biometrika; Little & Rubin (2019) 第 3 版; van Buuren (2018) Flexible Imputation; Heckman (1979) Econometrica; Schafer & Graham (2002) Psychological Methods。
SSDSE-B-2026 では一部指標(小規模県の専門医療、 離島部の通信指標)が欠損。 単純削除では 8 県が消え n=39 に。 MAR と見て多重代入すれば 47 県全部を活用可能。
MIMIC-III 集中治療データベース例:患者症状が重いほど検査値未記録 → 典型的 MNAR。 単純補完では推定が楽観的すぎる。 indicator 変数の追加や重症度モデルとの同時推定が必要。
Methods:「データの 12.3% に欠損があり、 Little の MCAR 検定で p < 0.001 となったため MCAR は棄却。 観測共変量で説明可能と判断し、 MICE(m=20)を適用した(van Buuren, 2018)。 全推定は Rubin's rules で統合した。」
Results:「補完前後で主係数の符号と統計的有意性は不変だったが、 SE は 12% 増加した(補完の不確実性を適切に反映)。」
Limitations:「MAR 仮定は検証不能であり、 MNAR の可能性を排除できない。 感度分析として欠損値に ±20% の系統的ズレを仮定した場合でも結論は不変。」
「欠損」の一般化が サンプル選択バイアス。 「観測される事例自体が母集団から偏る」現象。
| シナリオ | 典型例 | 欠損分類 |
|---|---|---|
| 追跡調査の脱落 | 10 年後再調査で連絡不通 | 引っ越し頻度依存 → MAR/MNAR |
| 労働市場参加 | 働く人の賃金しか観測不能 | 留保賃金依存 → MNAR |
| 医療レジストリ | 重症者は転院・死亡で追跡不能 | 結果依存 → MNAR |
| 顧客満足度 | 極端な人だけ回答 | 結果自体に依存 → MNAR |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import numpy as np # 学習用に分布パラメータで生成(本番では公的データを使用) N = 200 np.random.seed(0) income = np.random.lognormal(6.0, 0.7, N) age = np.random.normal(45, 12, N) # (1) MCAR y1 = income.copy(); y1[np.random.rand(N) < 0.3] = np.nan # (2) MAR prob = 1 / (1 + np.exp((age - 50)/5)) y2 = income.copy(); y2[np.random.rand(N) < prob] = np.nan # (3) MNAR prob3 = (income - income.min()) / (income.max() - income.min()) y3 = income.copy(); y3[np.random.rand(N) < prob3] = np.nan print('真の平均:', income.mean()) print('MCAR後 :', np.nanmean(y1), '(不偏)') print('MAR後 :', np.nanmean(y2), '(やや偏る)') print('MNAR後 :', np.nanmean(y3), '(過小評価)') |
1 2 3 4 5 6 7 8 9 10 11 12 | from sklearn.linear_model import LinearRegression from sklearn.impute import SimpleImputer X = np.column_stack([age, np.ones(N)]) y_full = income; y_obs = y2 # MAR シナリオ mask = ~np.isnan(y_obs) print('真の係数 :', LinearRegression().fit(X, y_full).coef_) print('Listwise:', LinearRegression().fit(X[mask], y_obs[mask]).coef_) y_mean = SimpleImputer(strategy='mean').fit_transform(y_obs.reshape(-1,1)).ravel() print('平均補完 :', LinearRegression().fit(X, y_mean).coef_) # 観察: Listwise は MAR でもバイアスあり / 平均補完は係数を 0 寄りに |
完全データの尤度は $p(Y, M\mid\theta,\psi) = p(Y\mid\theta) p(M\mid Y,\psi)$。 観測データの尤度は $\int p(Y\mid\theta) p(M\mid Y,\psi) dY_{mis}$。 MAR では $p(M\mid Y,\psi) = p(M\mid Y_{obs},\psi)$ なので $\psi$ が $\theta$ と分離可能 → 欠損モデルを無視できる。 MNAR ではこれが成立せず、 同時推定が必要。
| パッケージ | 得意分野 | 備考 |
|---|---|---|
| mice | 連続・カテゴリ混在 MICE | 業界標準 |
| Amelia | EM アルゴリズム + bootstrap | 時系列対応 |
| mi | ベイズ的多重代入 | 収束診断が豊富 |
| missForest | RF による非線形補完 | tuning 少 |
| VIM | 欠損パターン可視化 | 診断特化 |
| ステップ | やること | 使うツール例 | 成果物 |
|---|---|---|---|
| 1. 目的設定 | 分析の問いを 1 文で書く。 「欠損メカニズム」をどう使うか、 仮説と判断基準を決める。 | ホワイトボード、 issue tracker | 1 ページ計画書 |
| 2. データ取得 | SSDSE / e-Stat / 政府統計から、 該当指標を取得。 ライセンス・出典を確認。 | pandas, requests, e-Stat API | data/raw/ の CSV |
| 3. 前処理 | 型変換、 欠損対処、 単位統一、 外れ値検査。 「欠損メカニズム」に必要な形に整える。 | pandas, scikit-learn | data/processed/ の DataFrame |
| 4. 分析・実装 | 「欠損メカニズム」を適用。 パラメータ・前提条件を文書化。 結果を保存。 | pandas, sklearn, scipy, statsmodels | results/ のテーブル・図 |
| 5. 報告 | 図表 + 解釈 + 限界を整理。 再現性のためコードと環境(requirements.txt)を添付。 | Jupyter, Quarto, Markdown | レポート(HTML/PDF) |
project/ ├── data/ │ ├── raw/ # 取得した生データ(git にコミットして再現性確保) │ └── processed/ # 前処理後の中間ファイル ├── notebooks/ # 探索的分析の Jupyter ├── src/ # 本番コード(モジュール化) │ ├── preprocess.py │ ├── analyze.py # ← 欠損メカニズム の実装 │ └── visualize.py ├── results/ # 出力(CSV, PNG, モデルファイル) ├── docs/ # レポート、 図表説明 ├── requirements.txt └── README.md # 「目的・データ・手順・結論」を 1 枚で
「欠損メカニズム」と並んで現場で使われる代替・関連手段を整理。 「どれが正解」ではなく「状況に応じて使い分ける」のが実務。
| 選択肢 | 強み | 弱み | 推奨シーン |
|---|---|---|---|
| 本命:欠損メカニズム | 標準的・実装が豊富・解釈しやすい | 適用条件あり | 仮説が明確で、 条件を満たすデータ |
| 古典的代替案 | 計算が軽い・教科書通り | 柔軟性に欠ける | 小規模データ、 教育用 |
| ベイズ的代替案 | 不確実性を確率分布で表現 | 事前分布の選択が議論を呼ぶ | サンプル小、 事前知識あり |
| 機械学習的代替案 | 非線形・高次元に対応 | 解釈性が低い、 過学習リスク | 予測重視、 大規模データ |
| ノンパラ・ロバスト代替案 | 分布仮定に頼らない | パワーが下がる | 分布が不明、 外れ値多い |
| データ規模 | 「欠損メカニズム」の典型計算時間 | 必要メモリ | 推奨ツール |
|---|---|---|---|
| 小(〜1 万行) | 1 秒未満 | 〜100 MB | pandas, numpy のみ |
| 中(〜100 万行) | 数秒〜数分 | 1〜4 GB | pandas + 適切なベクトル化 |
| 大(〜1 億行) | 分〜時間 | 16 GB+ | Polars / Dask / DuckDB |
| 超大(〜100 億行) | 時間〜日 | 分散環境 | Spark / BigQuery / Snowflake |
pd.read_csv(..., usecols=[...]) でメモリ削減chunksize=10000 で逐次処理| テスト種別 | 内容 | ツール例 |
|---|---|---|
| ユニットテスト | 個別関数が期待通りの値を返すか | pytest, unittest |
| スキーマテスト | DataFrame の列・型・値域が想定通りか | pandera, great_expectations |
| 回帰テスト | 既知の入力に対する出力が変わっていないか | pytest + 固定 seed |
| 性能テスト | 計算時間・メモリが許容範囲か | pytest-benchmark, memory_profiler |
| 再現性テスト | 同じ入力で必ず同じ出力か(seed 固定) | numpy.random.seed (※学習目的のみ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pytest import pandas as pd from src.analyze import missing_mechanism_function # 仮の関数名 def test_basic_output(): df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) result = missing_mechanism_function(df) assert result is not None assert len(result) > 0 def test_handles_missing(): df = pd.DataFrame({'x': [1, 2, None, 4]}) result = missing_mechanism_function(df) assert result.isnull().sum() == 0 # 仕様: NaN を除いて返す def test_reproducible(): df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1) r1 = missing_mechanism_function(df) r2 = missing_mechanism_function(df) pd.testing.assert_frame_equal(r1, r2) |
| レベル | 推奨書籍/論文 | 著者 | 備考 |
|---|---|---|---|
| 入門 | Pythonによるデータ分析入門 (第3版) | Wes McKinney | pandas 公式バイブル |
| 入門 | データサイエンス 100 本ノック | The Quest 創業者 | 実装演習 |
| 中級 | Python Data Science Handbook | Jake VanderPlas | NumPy/pandas/sklearn/matplotlib 一気通貫 |
| 中級 | R for Data Science (2e) | Hadley Wickham | tidy 思想の理論 |
| 中級 | Statistical Rethinking (2e) | Richard McElreath | ベイズ統計の名著 |
| 上級 | Elements of Statistical Learning | Hastie, Tibshirani, Friedman | 機械学習の数理 |
| 上級 | Causal Inference: The Mixtape | Scott Cunningham | 因果推論 |
| 論文 | 「ggplot2: Elegant Graphics for Data Analysis」 | Wickham | 可視化哲学 |
「本研究では、 47 都道府県の社会経済指標(SSDSE-B-2026)について、 欠損メカニズム(Missing Data Mechanism)を用いて [目的] を分析した。 適用条件として [前提条件] を確認し、 [パラメータ設定] を採用した。 計算には Python 3.11、 pandas 2.2.0、 [当該ライブラリ] を使用した。 再現性のためコードは GitHub に公開している。」
「欠損メカニズムの結果、 [指標] = [値](95% CI: [下限–上限])が得られた。 表 X に [比較対象] との対比を示す。 [予想と一致/予想と異なる] 結果であり、 [解釈] と考えられる。 ロバストネスチェックとして [代替手法] でも同様の結論を確認した。」
「本研究の限界として、 (1) 観察データに基づくため因果関係を厳密には主張できない、 (2) 欠損メカニズムの前提である [前提] が完全には満たされない可能性、 (3) サンプルが 47 都道府県に限定される、 が挙げられる。 今後は [追加データ・追加手法] による頑健性検証が望まれる。」