臨床データでは、欠測は例外ではありません。 FIM、SIAS、握力、認知機能検査、追跡転帰は、 病状、施設、時期、評価者の影響を受けます。 欠測値処理では、単に空欄を埋めるだけでなく、 なぜ欠けたのかを考える必要があります。 その判断が、予測モデルの性能と解釈を左右します。
この記事では、MCAR / MAR / MNAR の違いを整理します。 そのうえで、列削除、complete-case、単純補完、 多重代入(MICE)、機械学習による補完を比較します。 補完器を全データで fit しないことも重視します。 関連する基本用語は 02·01 説明変数と目的変数と、 09·02 データリーケージとは何かも参照してください。
// 01 · LEARN OUTCOMESこの記事でわかること
- MCAR / MAR / MNAR の違いを、臨床データで説明できます。
- complete-case、単純補完、多重代入の限界を整理できます。
- missing indicator を使う場面を判断できます。
- 補完を Pipeline 内で fit する理由を説明できます。
// 02 · CONCLUSION最初に結論
// 03 · FIGURE欠測の型と補完の考え方
MCAR は、評価表が偶然紛失したような状態です。 欠測が患者背景にも転帰にも関係しない場合です。 しかし、臨床データではこの仮定は強めです。 欠測には理由があることが多いからです。
MAR は、欠測が観測済み変数で説明できる状態です。 たとえば、重症例ほど SIAS が未評価になりやすい場合です。 重症度、年齢、入院時 FIM などが記録されていれば、 それらを補完モデルに入れる余地があります。
MNAR は、欠測の起こりやすさが未観測量に依存します。 例として、転帰不良例ほど追跡不能になる状況があります。 この場合、観測済み変数だけでは説明しにくいです。 多重代入を用いても、仮定の影響が残ります。
// 04 · CLINICALリハ研究で起きやすい欠測
回復期病棟 N=300 の研究を考えます。 SIAS 下肢項目の欠測が 8% あります。 欠測は重症例に多い傾向です。 この場合、完全なランダム欠測とは考えにくいです。 年齢、入院時 FIM、NIHSS などで説明できるなら、 MAR 仮定のもとで MICE を検討できます。
N=150 の頭部外傷研究を想定します。 6 か月後 FIM の追跡欠測が 22% あります。 転帰不良例ほど連絡不能になりやすいなら、 MNAR の可能性があります。 このときは、補完値だけに依存せず、 complete-case 解析との比較を併記します。
多施設データでは、施設によって測定機器が異なります。 ある施設では握力計がなく、測定値が欠けることがあります。 この欠測は、患者の状態だけでなく施設に関係します。 施設 ID を補完モデルの共変量に入れると、 欠測の構造をより反映しやすくなります。
電子カルテから自動抽出したデータでは、 認知機能スコアが未入力のことがあります。 未入力は、評価不能、時間不足、実施対象外など、 複数の意味を持ちます。 補完値に加えて欠測フラグを作ると、 「欠けていること」自体を特徴量にできます。
// 05 · THEORY欠測の型を確率で見る
欠測を考えるときは、観測された値だけを見ません。 「どの値が欠けたか」を示す変数 R を考えます。 R は、観測済みなら 1、欠測なら 0 のように表せます。 X は説明変数、Y は目的変数です。
MCAR : P(R | X, Y) = P(R)
欠測が完全にランダム
MAR : P(R | X, Y) = P(R | X_obs)
欠測が観測済み変数で説明可能
MNAR : P(R | X, Y) != P(R | X_obs)
欠測がアウトカムまたは未観測量に依存
MCAR では、欠測した症例を除いても、 推定の偏りは比較的小さくなります。 ただし、症例数は減ります。 信頼区間は広くなりやすいです。
MAR では、complete-case だけにすると偏りが入ります。 たとえば、重症例で SIAS が欠けやすい場合です。 欠測例を落とすと、軽症例に寄ったデータになります。 このときは、観測済み変数を使った補完が候補になります。
MNAR では、観測済み情報だけでは不十分です。 追跡不能の理由が、転帰不良そのものに関係する場合です。 欠測の仮定を変えた感度分析が重要になります。 解析結果の頑健性を確認するためです。
MICE の基本発想
MICE は、欠測列を 1 つずつ目的変数にします。 他の列を説明変数として回帰モデルを作ります。 これを繰り返して、複数の補完データセットを作ります。 最後に、各データセットの解析結果を統合します。
各欠測列について:
1. 暫定値で初期化
2. その列を目的とした回帰モデルを当てはめる
3. 予測値で欠測を更新
4. ループして M セットの補完データを得る
5. 各データセットで分析
6. Rubin's rules で統合
重要なのは、不確実性を残すことです。 欠測値を 1 つの値に固定すると、 データが本来よりそろって見えます。 その結果、分散や信頼区間が小さく見えることがあります。 多重代入は、この問題を軽減するための枠組みです [1][2]。
// 06 · IMPLEMENTATIONPipeline で補完する実装例
実装で最も避けたいのは、全データで補完器を fit する流れです。 補完器は、中央値、分布、近傍情報を学習します。 test や validation の値が入ると、評価が楽観的になります。 scikit-learn では Pipeline に入れると、 CV の各 fold 内で fit されます。
# 教育用の仮想データを想定した例です。
# 実データで実行する前に、個人情報保護、倫理審査、
# 施設ルール、共同研究契約、利用規約を確認してください。
# 予測時点で取得できない列は、説明変数から外します。
import numpy as np
import pandas as pd
from sklearn.experimental import enable_iterative_imputer # noqa: F401
from sklearn.impute import SimpleImputer, KNNImputer, IterativeImputer
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (
train_test_split,
StratifiedKFold,
GroupKFold,
cross_validate,
)
from sklearn.metrics import roc_auc_score
RANDOM_STATE = 42
# 1. 読み込み
df = pd.read_csv("example_rehab_dataset.csv")
# 2. 欠測率の確認
missing_rate = (
df.isna()
.mean()
.sort_values(ascending=False)
.rename("missing_rate")
)
print(missing_rate.head(20))
# 3. 予測時点を確認して列を決める
# 例: 入院時点で退院時歩行自立を予測する想定です。
target = "walk_independent_discharge"
group_col = "patient_id"
numeric_features = [
"age",
"days_from_onset",
"fim_motor_admission",
"fim_cognition_admission",
"sias_lower_admission",
"grip_strength_admission",
]
categorical_features = [
"sex",
"stroke_type",
"facility_id",
]
X = df[numeric_features + categorical_features].copy()
y = df[target].astype(int)
groups = df[group_col] if group_col in df.columns else None
# 4. 単純補完の比較用パイプライン
numeric_simple = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median", add_indicator=True)),
("scaler", StandardScaler()),
]
)
categorical_simple = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore")),
]
)
preprocess_simple = ColumnTransformer(
transformers=[
("num", numeric_simple, numeric_features),
("cat", categorical_simple, categorical_features),
]
)
pipe_simple = Pipeline(
steps=[
("preprocess", preprocess_simple),
(
"model",
LogisticRegression(max_iter=2000, class_weight="balanced"),
),
]
)
# 5. MICE 風の IterativeImputer
numeric_mice_like = Pipeline(
steps=[
(
"imputer",
IterativeImputer(
random_state=RANDOM_STATE,
max_iter=20,
sample_posterior=True,
add_indicator=True,
),
),
("scaler", StandardScaler()),
]
)
preprocess_mice_like = ColumnTransformer(
transformers=[
("num", numeric_mice_like, numeric_features),
("cat", categorical_simple, categorical_features),
]
)
pipe_mice_like = Pipeline(
steps=[
("preprocess", preprocess_mice_like),
(
"model",
LogisticRegression(max_iter=2000, class_weight="balanced"),
),
]
)
# 6. KNNImputer の小例
numeric_knn = Pipeline(
steps=[
("imputer", KNNImputer(n_neighbors=5, weights="distance")),
("scaler", StandardScaler()),
]
)
preprocess_knn = ColumnTransformer(
transformers=[
("num", numeric_knn, numeric_features),
("cat", categorical_simple, categorical_features),
]
)
pipe_knn = Pipeline(
steps=[
("preprocess", preprocess_knn),
(
"model",
LogisticRegression(max_iter=2000, class_weight="balanced"),
),
]
)
# 7. train/test 分割後に fit する例
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
stratify=y,
random_state=RANDOM_STATE,
)
pipe_mice_like.fit(X_train, y_train)
proba = pipe_mice_like.predict_proba(X_test)[:, 1]
print("test_auc", roc_auc_score(y_test, proba))
# 8. CV では fold 内で前処理が fit されます。
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
cv_result = cross_validate(
pipe_mice_like,
X,
y,
cv=cv,
scoring=["roc_auc", "accuracy"],
return_train_score=False,
)
print(pd.DataFrame(cv_result).mean())
# 9. 同一患者の反復評価がある場合は GroupKFold も検討します。
if groups is not None and groups.nunique() >= 5:
gcv = GroupKFold(n_splits=5)
group_result = cross_validate(
pipe_simple,
X,
y,
cv=gcv,
groups=groups,
scoring="roc_auc",
)
print("group_auc", group_result["test_score"].mean())
# 10. NG 例: 全データで先に fit しない
# bad_imputer = SimpleImputer(strategy="median")
# X_bad = bad_imputer.fit_transform(X[numeric_features])
# X_train, X_test, y_train, y_test = train_test_split(X_bad, y)
# 上の流れでは test 側の分布も補完器に入ります。
この例では、数値列とカテゴリ列を分けています。 数値列は補完後に標準化します。 カテゴリ列は最頻値で補完し、One-Hot Encoding を行います。 どの処理も ColumnTransformer 内に置いています。
IterativeImputer は MICE 風の補完です。 ただし、古典的な多重代入と同一ではありません。 複数回の補完データを作り、結果を統合する設計が必要です。 実際の論文では、使用した回数、乱数、統合方法を記載します。
KNNImputer は近い症例から値を推定します。 直感的ですが、スケールや外れ値の影響を受けます。 高次元では距離が不安定になることもあります。 そのため、補完方法の選択は性能だけで決めません。 欠測の理由と臨床的妥当性も確認します。
// 07 · MYTHSよくある誤解
- 誤解: 欠測は全部削除すれば安全です。
- complete-case は単純で説明しやすい方法です。 しかし、MAR や MNAR では偏りが入ります。 重症例が落ちると、モデルは軽症例中心に学習します。
- 誤解: 平均値補完をすれば問題ありません。
- 平均値補完は、欠測値を同じ値に集めます。 分散を小さく見せます。 p 値や信頼区間が過小評価されることがあります。
- 誤解: MICE を使えば欠測の問題は消えます。
- MICE は補完モデルの仮定に依存します。 MAR に近い状況では有用です。 MNAR の偏りを単独で扱い切れない場合があります。
- 誤解: 欠測フラグを足せば十分です。
- 欠測フラグは有用な特徴量になることがあります。 一方で、欠測の因果構造は説明しません。 補完値、欠測理由、感度分析を組み合わせます。
// 08 · WRITING論文 Methods で書くこと
欠損値処理は、Methods の短い一文で済ませると、 査読で指摘されやすい部分です。 特に予測モデル研究では、補完と評価分割の順序が重要です。 TRIPOD+AI でも、データ処理の透明性が重視されています [6]。
- 各変数の欠測率を、表または補足表で示します。
- 欠測の型について、MCAR / MAR / MNAR の観点で説明します。
- 除外した列や症例がある場合、基準を明記します。
- 補完方法、補完に使った変数、補完回数を記載します。
- 補完器を train fold 内で fit したことを明記します。
- complete-case 解析などの感度分析を示します。
- 目的変数を補完モデルに入れたかを記載します。
目的変数を MICE の共変量に入れるかは、 研究目的によって扱いが変わります。 予測モデル開発では、評価データの目的変数情報が 前処理に混ざるとリーケージになります。 一方、説明目的の統計解析では、効率を上げるために 目的変数を含める設計もあります。 どちらの場合も、解析計画に明記します。
査読者が見たいのは、補完方法の名前だけではありません。 欠測がどのように発生したかという臨床的説明です。 たとえば、SIAS の欠測が重症度に関係するなら、 その仮定を Methods と Limitations に書きます。
// 09 · CHECKLIST欠測値処理チェックリスト
- 01各列の欠測率を確認しましたか。
- 02欠測の理由を、臨床・施設・時点から考えましたか。
- 03MCAR / MAR / MNAR の仮定を記載できますか。
- 04complete-case の偏りを確認しましたか。
- 05平均値補完だけに依存していませんか。
- 06補完器を Pipeline 内に入れましたか。
- 07test / validation で補完器を fit していませんか。
- 08感度分析を計画しましたか。
// 10 · QUIZ確認クイズ
-
Q1重症例ほど SIAS が欠測しやすい場合、最も近い考え方はどれですか。
- MCAR
- MAR の可能性
- 欠測ではない
- 標準化の問題
SHOW ANSWER
B. 観測済みの重症度や FIM で説明できるなら、MAR として扱える可能性があります。 -
Q2平均値補完の主な弱点はどれですか。
- すべての外れ値を増やす
- 分散を小さく見せやすい
- カテゴリ変数だけに使える
- CV が使えなくなる
SHOW ANSWER
B. 同じ平均値に集めるため、ばらつきが小さく見えることがあります。 -
Q3予測モデルの評価で避けたい実装はどれですか。
- Pipeline 内で補完する
- fold 内で imputer を fit する
- 全データで imputer を fit してから分割する
- 欠測率を表に示す
SHOW ANSWER
C. test 側の分布が補完器に混ざるため、リーケージになります。 -
Q4MNAR が疑われるときに重要な対応はどれですか。
- 補完後の結果だけを示す
- 欠測理由と感度分析を示す
- すべて平均値で埋める
- 目的変数を削除する
SHOW ANSWER
B. MNAR では仮定に依存するため、感度分析が重要になります。
// REF参考文献
- Rubin DB. Inference and missing data. Biometrika 1976;63(3):581-592.
- Little RJA, Rubin DB. Statistical Analysis with Missing Data. 3rd ed. Wiley; 2019.
- van Buuren S. Flexible Imputation of Missing Data. 2nd ed. CRC Press; 2018.
- Sterne JAC, White IR, Carlin JB, et al. Multiple imputation for missing data in epidemiological and clinical research: potential and pitfalls. BMJ 2009;338:b2393.
- Azur MJ, Stuart EA, Frangakis C, Leaf PJ. Multiple imputation by chained equations: what is it and how does it work? International Journal of Methods in Psychiatric Research 2011;20(1):40-49.
- Collins GS, Moons KGM, Dhiman P, et al. TRIPOD+AI statement. BMJ 2024;385:e078378.