// PART 02 · ARTICLE 05 / 07
DATA-PREP FEATURE-SELECTION L2

特徴量選択

— どの変数を残すかをデータと臨床知識で決める

11 min read· L2· 2026.05.20 update· by Editor

リハビリテーション研究では、FIM、SIAS、NIHSS、握力、歩行速度、画像所見、薬剤情報など、 多くの候補変数が集まります。変数が多いほど情報は増えますが、症例数に対して多すぎると、 モデルは偶然の規則まで拾いやすくなります。特徴量選択は、予測性能だけでなく、解釈性、 測定コスト、再現性を整えるための前処理です。

// CONTEXT

ここでは「使える変数を全部入れる」発想から一歩進みます。 どの変数を残すかを、データと臨床知識の両方で検討します。 特徴量選択も前処理の一部なので、評価データを見てから選び直すと データリーケージにつながります。

// 01 · LEARN OUTCOMESこの記事で学ぶこと

  • 特徴量を絞る理由を、過学習、解釈性、測定コスト、EPV の観点から説明できるようになります。
  • フィルタ法、ラッパー法、埋め込み法の違いを、臨床研究の文脈で整理できます。
  • 相関係数、VIF、Lasso、RFE、Boruta の使いどころと限界を理解できます。
  • 特徴量選択を Pipeline と CV の内側で行い、test leakage を避ける実装を確認できます。

// 02 · CONCLUSION結論

重要なのは、特徴量選択を「自動で正解を出す手続き」と考えないことです。 予測モデルでは、選ばれた変数は予測に役立った変数です。 それは、原因を示す変数とは限りません。

たとえば、退院時 FIM を予測するモデルで、入院時 FIM、年齢、発症から入院までの日数、 SIAS、認知 FIM、栄養指標が候補になるとします。Lasso で 12 変数が残っても、 その 12 変数だけが臨床的に重要という意味ではありません。共線性により、似た情報を持つ変数の中から、 代表として 1 つだけ残る場合があります。

// 03 · FIGURE図で見る特徴量選択

// FEATURE SELECTION METHODS FILTER 変数ごとに統計指標で評価 x1 x2 x3 x4 score1 score2 score3 score4 相関 / χ² / ANOVA / MI → 上位 K 個を選ぶ 軽い / 組合せ見えにくい WRAPPER モデル性能で変数セットを探す 変数セット {x1, x3, x4} モデル AUC / CV score 変数を入替 RFE / Forward / Backward → 性能を見て変数を選ぶ 探索回数多い / 過学習注意 EMBEDDED 学習中に選択を行う MODEL L1 罰則 / 重要度 x1 x3 x4 x2 x5 Lasso / Elastic Net / RF imp → 学習と選択を一体で 候補多いときの第一段階 いずれの方法でも、選択は CV の train fold 内で fit する。test を見て選び直さない。
Fig 1. フィルタ法・ラッパー法・埋め込み法の比較。フィルタ法は変数ごとに粗く絞り、 ラッパー法はモデル性能を見ながら変数セットを探し、埋め込み法は学習の中で選択を行う。

図1では、3 つの方法を「変数の見方」で分けます。 フィルタ法は、モデルを作る前に変数を評価します。 相関係数、χ² 検定、ANOVA、相互情報量などが含まれます。 計算は軽い一方で、変数同士の組み合わせは見えにくいです。

ラッパー法は、候補変数を入れたり外したりしながら、 実際のモデル性能で評価します。RFE、Forward selection、Backward selection が代表です。 ただし、探索回数が増えるため、過学習しやすくなります。

埋め込み法は、モデルの学習過程に選択を組み込みます。 Lasso、Elastic Net、Tree feature importance、Boruta などが含まれます。 医療データでは、候補変数が多いときの第一段階として使いやすい方法です。

// LASSO COEFFICIENT PATH log(lambda) 小 ← 罰則 → 大 係数 β 0 入院時 FIM SIAS 下肢 認知 FIM 栄養指標 薬剤数 CV で選ばれた lambda* 残る変数 入院時 FIM, SIAS 下肢 係数 0 (除外された変数) 認知 FIM, 栄養, 薬剤数 …
Fig 2. Lasso の係数パス図。正則化強度 lambda が大きくなるほど、 一部の係数が 0 に近づき、最終的にモデルから除外される。

図2では、Lasso の考え方を示します。 lambda が小さいと、多くの変数がモデルに残ります。 lambda が大きくなると、係数が 0 になる変数が増えます。 これは、説明変数を減らしながらモデルを作る仕組みです。 詳しくは Lasso / Ridge / Elastic Net と合わせて読むと理解しやすくなります。

// 04 · CLINICALリハ研究で起きる場面

// CASE 1 · 入院時 50 変数から退院時 FIM を予測

回復期病棟 N=300 の仮想データを考えます。 入院時 FIM、SIAS、NIHSS、握力、嚥下評価、栄養指標など、50 変数があります。 すべて入れると解釈が難しくなります。 Lasso を使い、CV 内で 12 変数に絞る設計にします。 ただし、残った変数は臨床的に再確認します。

// CASE 2 · 薬剤フラグ 200 列があるデータ

電子カルテ由来のデータでは、薬剤名から作ったフラグ列が増えやすいです。 200 列をそのまま入れると、偶然の関連を拾う可能性があります。 Boruta や木系モデルの重要度で候補を絞り[3]、 その後に薬剤の臨床的意味を確認します。 同じ薬効群をまとめる処理も検討対象です。

// CASE 3 · 歩行特徴量 100 種から転倒を予測

加速度計や歩行解析から、歩幅、立脚時間、左右差、変動係数などが得られます。 これらは互いに強く相関することがあります。 まず相関行列と VIF で冗長な特徴量を確認します。 次に RFE で候補を絞り、残った 8 特徴量の安定性を評価します。

// CASE 4 · 多施設データで施設差が大きい

多施設研究では、施設ごとに評価の頻度や測定機器が異なる場合があります。 施設 A でだけ選ばれる変数は、疾患の特徴ではなく施設差を反映している可能性があります。 施設別 GroupKFold を使い、施設をまたいでも安定して選ばれる変数を確認します。

これらの例に共通するのは、候補変数が多いほど、解析者の判断が増える点です。 どの特徴量を作るか、どれを残すか、どの単位でまとめるかにより結果は変わります。 そのため、特徴量選択は「データが決める作業」ではなく、 研究目的と臨床知識を反映する作業として扱います。

// 05 · THEORY理論の最小限

特徴量選択の目的は、変数を少なくすることだけではありません。 予測に必要な情報を残しながら、偶然の関連を減らすことです。 医療研究では、症例数が限られます。 そのため、変数数とのバランスが重要です。

EPV とサンプルサイズの考え方

EPV は events per variable の略です。 ロジスティック回帰で、イベント数に対して変数が多すぎないかを見る古典的な目安です。 EPV ≥ 10 はよく知られた基準ですが、十分条件ではありません。 近年は、目的、モデル、アウトカム頻度、期待される性能に応じた設計が重視されます。 臨床予測モデルのサンプルサイズ設計については、Riley らの考え方が参考になります [4]

Lasso と Elastic Net

Lasso は、係数の大きさに罰則をかけます。 その結果、一部の係数が 0 になります。 これにより、モデルに入る変数を自然に減らします [2]

Lasso:
min_beta  sum((y - X beta)^2) + lambda * sum(abs(beta))

Elastic Net:
min_beta  loss(y, X beta)
          + lambda1 * sum(abs(beta))
          + lambda2 * sum(beta^2)

Elastic Net は、Lasso と Ridge の中間です。 強く相関した変数があるとき、Lasso は代表の 1 つだけを残すことがあります。 Elastic Net は、似た変数群をまとめて残しやすい場合があります。

VIF による多重共線性の確認

多重共線性は、説明変数同士が強く関連している状態です。 たとえば、FIM 運動、FIM 合計、移乗項目、歩行項目は互いに重なります。 予測性能だけなら問題が小さい場合もあります。 しかし、係数の解釈は不安定になります。

VIF_j = 1 / (1 - R_j^2)

R_j^2:
  変数 j を、他の説明変数で回帰したときの決定係数

VIF が高い変数は、他の変数からかなり説明できる変数です。 ただし、VIF の閾値だけで機械的に削ると、臨床的に重要な変数を失うことがあります。 変数の定義、測定時点、臨床的意味を合わせて見ます。

相互情報量

相互情報量は、変数 X がアウトカム Y の情報をどれだけ持つかを見る指標です。 線形関係だけでなく、非線形の関係にも反応します。 一方で、単変量の評価なので、他の変数との組み合わせまでは十分に見えません。

Mutual Information:
MI(X, Y) = sum_x sum_y p(x, y) * log( p(x, y) / (p(x) * p(y)) )

特徴量選択の基本文献として、Guyon と Elisseeff の総説は現在でも重要です [1]。 ただし、医療データでは統計的な選択だけで完結しません。 予測時点で取得できる変数か、測定コストは現実的か、解釈できるかを確認します。

選択の安定性を見る

1 回の分割だけで選ばれた変数は、たまたま残った可能性があります。 そのため、fold ごとに選ばれた変数を記録します。 毎回残る変数、半分程度で残る変数、ほとんど残らない変数を分けて見ます。 入院時 FIM や SIAS のように毎回残る変数は、予測に安定して関わる可能性があります。

一方で、施設 ID、測定曜日、特定の薬剤名などが不安定に残る場合があります。 その変数が臨床状態を表しているのか、運用の違いを表しているのかを考えます。 ここを確認しないと、外部検証で性能が落ちる原因になります。

説明目的と予測目的を分ける

予測モデルでは、アウトカムを当てることが主目的です。 そのため、選ばれた変数は予測に役立つ変数です。 しかし、論文では「予後因子」と書きたくなる場面があります。 予後因子という言葉を使う場合は、因果的な解釈に近づきます。 その場合は、研究デザイン、交絡、測定時点をより慎重に扱います。

// 06 · IMPLEMENTATIONPython 実装例

ここでは、教育用の仮想データ example_rehab_dataset.csv を使います。 実データを扱う場合は、個人情報保護、倫理審査、施設ルール、データ利用規約を確認します。 変数選択も前処理なので、CV の各 fold 内で fit します。

# 教育用コードです。実患者データではありません。
# 個人情報保護、倫理審査、施設ルール、利用規約を確認します。
# 特徴量選択は Pipeline の中で行い、CV の各 fold 内で fit します。

import numpy as np
import pandas as pd

from sklearn.compose import ColumnTransformer
from sklearn.feature_selection import SelectKBest, mutual_info_classif, RFE, SelectFromModel
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression, LassoCV
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold, GroupKFold, cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import RandomForestClassifier

from statsmodels.stats.outliers_influence import variance_inflation_factor

RANDOM_STATE = 42
np.random.seed(RANDOM_STATE)

df = pd.read_csv("example_rehab_dataset.csv")
target = "walk_independent_discharge"

# 予測時点より後の情報は説明変数から外します。
future_cols = ["fim_total_discharge", "fim_gain", "length_of_stay"]
X = df.drop(columns=[target] + future_cols, errors="ignore")
y = df[target].astype(int)

numeric_cols = [
    "age", "days_from_onset", "fim_motor_admission",
    "fim_cognition_admission", "sias_lower_admission", "grip_strength",
]
categorical_cols = ["sex", "stroke_type", "facility_id"]
numeric_cols = [c for c in numeric_cols if c in X.columns]
categorical_cols = [c for c in categorical_cols if c in X.columns]

# VIF は探索的確認です。ここでは train 側だけで確認します。
X_train_raw, X_test_raw, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=RANDOM_STATE
)

vif_source = X_train_raw[numeric_cols].copy()
vif_source = vif_source.fillna(vif_source.median(numeric_only=True))
vif_source = pd.DataFrame(StandardScaler().fit_transform(vif_source), columns=numeric_cols)

vif_table = pd.DataFrame({
    "feature": numeric_cols,
    "vif": [variance_inflation_factor(vif_source.values, i)
            for i in range(len(numeric_cols))]
})
print(vif_table.sort_values("vif", ascending=False))

numeric_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

categorical_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore")),
])

preprocess = ColumnTransformer([
    ("num", numeric_pipe, numeric_cols),
    ("cat", categorical_pipe, categorical_cols),
])

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
base_model = LogisticRegression(max_iter=2000, solver="liblinear")

def evaluate(pipe, name):
    auc = cross_val_score(pipe, X, y, cv=cv, scoring="roc_auc")
    print(name, auc.mean(), auc.std())

filter_pipe = Pipeline([
    ("preprocess", preprocess),
    ("select", SelectKBest(mutual_info_classif, k=12)),
    ("model", base_model),
])
evaluate(filter_pipe, "SelectKBest")

rfe_pipe = Pipeline([
    ("preprocess", preprocess),
    ("select", RFE(estimator=base_model, n_features_to_select=12)),
    ("model", base_model),
])
evaluate(rfe_pipe, "RFE")

lasso_pipe = Pipeline([
    ("preprocess", preprocess),
    ("select", SelectFromModel(LassoCV(cv=5, random_state=RANDOM_STATE), threshold="median")),
    ("model", base_model),
])
evaluate(lasso_pipe, "Lasso selection")

rf_selector = SelectFromModel(
    RandomForestClassifier(n_estimators=300, random_state=RANDOM_STATE),
    threshold="median"
)
rf_pipe = Pipeline([("preprocess", preprocess), ("select", rf_selector), ("model", base_model)])
evaluate(rf_pipe, "RandomForest selection")

# 多施設データでは GroupKFold で安定性を確認します。
if "facility_id" in df.columns:
    groups = df["facility_id"]
    group_auc = cross_val_score(lasso_pipe, X, y, cv=GroupKFold(5), groups=groups, scoring="roc_auc")
    print("GroupKFold", group_auc.mean(), group_auc.std())

# NG 例: 全データで選択してから分割する処理は避けます。
# X_all = preprocess.fit_transform(X)
# X_selected = SelectKBest(mutual_info_classif, k=12).fit_transform(X_all, y)
# X_train, X_test, y_train, y_test = train_test_split(X_selected, y)

final_pipe = lasso_pipe
final_pipe.fit(X_train_raw, y_train)
proba = final_pipe.predict_proba(X_test_raw)[:, 1]
print("Hold-out AUC", roc_auc_score(y_test, proba))

このコードでは、SelectKBest、RFE、SelectFromModel をすべて Pipeline に入れています。 そのため、cross_val_score の各 fold で、前処理、変数選択、モデル学習が分けて実行されます。 test 側の情報を使って変数を選ばないことが重要です。

Boruta を使う場合も考え方は同じです。 boruta_py などを使う際は、全データで先に Boruta を fit しないようにします。 Pipeline に組み込みにくい場合は、外側 CV と内側 CV を分け、評価データを変数選択に使わない設計にします。

// 07 · MYTHSよくある誤解

誤解:相関係数で順位付けすれば最良の変数が選べる
相関係数は単変量の関係を見ます。 他の変数を同時に入れたときの役割は分かりません。 FIM 合計と FIM 運動のように、似た情報を持つ変数では特に注意が必要です。
誤解:特徴量重要度が高ければ因果関係がある
Tree importance や SHAP は予測への寄与を見ます。 原因を示す指標ではありません。 施設差や測定頻度が重要度として現れることもあります。
誤解:Lasso で 0 になった変数は臨床的に不要
Lasso は相関する変数の中から代表を残すことがあります。 0 になった変数が臨床的に無意味とは限りません。 変数群としての意味を確認する必要があります。
誤解:ステップワイズ法はよく使われるので安全
Forward や Backward selection は、多くの候補を試す方法です。 小規模データでは、偶然に合った変数セットを選びやすくなります。 使用する場合は、探索的解析として位置づけを明記します。

// 08 · WRITING論文 Methods で書くこと

特徴量選択は、査読で指摘されやすい部分です。 「変数をどのように選んだか」が曖昧だと、過大評価や解析後選択の疑いが出ます。 TRIPOD+AI の流れでも、予測因子の定義、選択、モデル構築の透明性が重視されます。

// METHODS に含める項目
  • 候補変数を決めた根拠
  • 候補変数の測定時点
  • 除外した変数とその理由
  • フィルタ法、ラッパー法、埋め込み法のどれを用いたか
  • 閾値やハイパーパラメータ
  • 特徴量選択を CV の内側で fit したか
  • 最終モデルに残った変数の一覧
  • complete-case や別手法との感度分析

たとえば、退院時歩行自立を予測する研究では、 入院時に取得できる変数だけを候補にしたことを明記します。 退院時 FIM、FIM 利得、入院期間などは、予測時点では得られないため除外します。 これは データリーケージ を避けるためです。

査読者が気にするのは、選択方法そのものだけではありません。 「test データを見てから変数を選び直していないか」も重要です。 したがって、CV の各 fold 内で特徴量選択を行ったことを明記します。

また、統計的に選ばれた変数が臨床的に妥当かを説明します。 たとえば、年齢、入院時 FIM、SIAS 下肢、認知 FIM は説明しやすい変数です。 一方で、特定施設の ID や測定曜日が残った場合、施設差や運用差の可能性を検討します。

Results では、選択された変数名だけを並べるのでは不十分です。 選択頻度、係数の方向、臨床的な解釈を合わせて示すと読みやすくなります。 たとえば、入院時 FIM が低いほど非自立になりやすいという方向は自然です。 反対に、臨床的に説明しにくい方向なら、コーディング、欠測、施設差を確認します。

Discussion では、選ばれなかった変数にも触れます。 たとえば、握力が残らなかった場合でも、測定の欠測、対象者の偏り、FIM との重複が理由かもしれません。 「重要でない」と断定せず、研究条件の中での結果として書きます。

もう一つ大切なのは、解析の再現性です。 どの seed、どの fold、どの閾値で選んだかを記録します。 同じデータから同じ変数が選ばれる状態にしておくと、共同研究者や査読者が確認しやすくなります。あとから解析を修正する場合も、変更点を追いやすくなり、研究記録としても残しやすくなります。

// 書き方の例

Candidate predictors were restricted to variables available at admission. Feature selection was performed within each cross-validation fold using Lasso regularization. Variables selected in the final model were reviewed for clinical plausibility. Variables measured after the prediction time point were excluded to avoid data leakage.

変数を減らすことの副作用

特徴量選択には利点がありますが、副作用もあります。 測定コストを下げ、モデルを読みやすくする一方で、 重要な臨床情報を落とす可能性があります。 特に、少数例にしか現れない症状や合併症は、統計的には選ばれにくいことがあります。 しかし、リハビリテーションでは、その少数例が臨床的に重要なことがあります。

そのため、最終候補は自動選択の結果だけで決めません。 変数の測定しやすさ、説明しやすさ、施設を変えても取れるかを確認します。 実装上は Pipeline に入れますが、解釈上は人間の確認が残ります。

// 09 · CHECKLIST特徴量選択チェックリスト

  • 01候補変数は研究計画の段階で定義しましたか。
  • 02予測時点で取得できない変数を除外しましたか。
  • 03相関行列や VIF で冗長な変数を確認しましたか。
  • 04フィルタ法だけでなく、多変量での役割も見ましたか。
  • 05特徴量選択を CV の fold 内で fit しましたか。
  • 06test データを見て変数を選び直していませんか。
  • 07最終的に残った変数を臨床的に説明できますか。
  • 08Methods に選択方法、閾値、残った変数を書きましたか。

// 10 · QUIZ理解度チェック

  1. Q1特徴量選択を test データを見た後に行うと、何が問題になりますか。
    • モデルの計算時間だけが増えます。
    • test データの情報がモデル構築に混入します。
    • 欠測値が増えます。
    • カテゴリ変数が使えなくなります。
    SHOW ANSWER
    B. test leakage になり、性能が過大評価される可能性があります。
  2. Q2Lasso で係数が 0 になった変数について、最も適切な理解はどれですか。
    • 臨床的に意味がない変数です。
    • 予測に使われなかった可能性がありますが、臨床的価値は別に検討します。
    • 必ず測定ミスです。
    • 因果関係が否定されます。
    SHOW ANSWER
    B. 共線性により代表変数だけが残ることがあります。
  3. Q3VIF が高いときに疑うことは何ですか。
    • 目的変数が二値であること。
    • 説明変数同士が強く関連していること。
    • 欠測が完全にランダムであること。
    • AUC が必ず低いこと。
    SHOW ANSWER
    B. 多重共線性の確認に使います。
  4. Q4多施設データで特徴量選択を評価するとき、特に有用な分割はどれですか。
    • GroupKFold
    • すべての行をランダムに混ぜるだけの分割
    • test データを使った変数選択
    • 目的変数で並べ替えた分割
    SHOW ANSWER
    A. 施設や患者単位のまとまりを保った評価に使えます。

// REF参考文献

  1. Guyon I, Elisseeff A. An introduction to variable and feature selection. Journal of Machine Learning Research 2003;3:1157-1182.
  2. Tibshirani R. Regression shrinkage and selection via the Lasso. Journal of the Royal Statistical Society Series B 1996;58(1):267-288.
  3. Kursa MB, Rudnicki WR. Feature selection with the Boruta package. Journal of Statistical Software 2010;36(11):1-13.
  4. Riley RD, Ensor J, Snell KIE, et al. Calculating the sample size required for developing a clinical prediction model. BMJ 2020;368:m441.