// PART 02 · ARTICLE 04 / 07
DATA-PREP FEATURES L2

特徴量エンジニアリング

— 臨床知識を変数に翻訳する

11 min read· L2· 2026.05.20 update· by Editor

特徴量エンジニアリングは、生データをモデルが扱いやすい形へ変換する作業です。 リハビリ研究では、FIM、SIAS、訓練量、発症からの日数などを、そのまま入れるだけでは臨床的な意味が伝わりにくいことがあります。 この記事では、臨床知識を変数へ翻訳する考え方を整理します。

// CONTEXT

特徴量は、予測モデルの材料です。 ただし、増やせばよいものではありません。 予測時点で利用できる情報だけを使い、CV の内側で処理する設計が重要です。 基本の変数整理は 説明変数と目的変数を参照してください。

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

  • 特徴量エンジニアリングの役割を説明できます。
  • FIM、BMI、訓練量などの派生特徴量を設計できます。
  • 非線形特徴量と交互作用項を臨床的に使い分けられます。
  • 特徴量生成で起きるリーケージを避ける設計を理解できます。

// 02 · CONCLUSION先に結論

// 03 · FIGURE特徴量を作る流れ

// FEATURE ENGINEERING FLOW RAW DATA 電子カルテ・評価表 FEATURE ENGINEERING 臨床知識で変換 MODEL 予測 / 評価 年齢 / 性別 身長 / 体重 入院時 FIM (運動) 入院時 FIM (認知) SIAS / NIHSS 発症からの日数 訓練時間 day1-3 施設 ID ↓ そのままだと 関係が捉えにくい 派生 BMI = wt / ht² 運動 FIM / 認知 FIM 比 集約 3日間累積訓練量 施設内平均との差 (fold 内 fit) 交互作用 発症日数 × 認知 FIM 非線形 log(days+1) / age² restricted cubic spline ML MODEL Logistic / RF / GBM / NN ↓ Pipeline 内で 前処理 → 学習 予測 / 評価 ⚠ 退院後情報・全データ集計は使わない / 集計は fold 内で fit する
図1: 生データから特徴量へのフロー。年齢、入院時 FIM、訓練時間などの生データから、派生特徴量、交互作用、スプライン展開を経てモデルに渡す。

図1では、特徴量エンジニアリングを翻訳作業として示します。 生データは、電子カルテや評価表から得られます。 しかし、そのままではモデルが関係を捉えにくい場合があります。

たとえば、身長と体重を別々に入れるだけでなく、BMI を作ることがあります。 入院時 FIM 合計だけでなく、運動 FIM と認知 FIM の比率を見ることもあります。 これらは、臨床で見ている構造を数値化する操作です。

// FEATURE COUNT × SAMPLE SIZE 症例数 N 50 100 300 1,000 5,000 特徴量数 p 1 10 50 200 1,000 SAFE EPV 高 / p ≪ N CAUTION 正則化 / 特徴量選択を検討 DANGER 過学習・不安定推定が顕著 p ≈ N N/p ≈ 10 (古典的目安) N=100, p=8 小規模だが特徴量も少 N=300, p=80 回復期コホート(注意) N=100, p=200 画像特徴量+表(危険) N=1,000, p=60 多施設EHR(余裕) 小規模研究で特徴量を増やしすぎると、偶然の規則を拾いやすい。正則化・選択・CV と組み合わせる。
図2: 特徴量数と症例数の二次元マップ。症例数に対して特徴量が多すぎると、過学習や不安定な推定が起きやすい。

図2では、特徴量数と症例数の関係を示します。 小規模のリハ研究で特徴量を増やしすぎると、偶然の規則を拾いやすくなります。 これは、過学習と正則化で扱う問題とつながります。

// 04 · CLINICALリハ研究での具体例

// CASE 1 · 入院時 FIM の比率

回復期病棟の退院時 FIM を予測します。 入院時 FIM 合計だけでなく、運動 FIM / 認知 FIM の比率を作ります。 これにより、身体機能と認知機能の偏りを表せます。

ただし、退院時 FIM を使って利得を作ると、入院時予測では未来情報になります。 予測時点を明確にして扱います。

// CASE 2 · 訓練量の累積

PT、OT、ST の訓練時間を日ごとに記録します。 1 日単位の時間だけでなく、入院後 7 日間の累積訓練量を作ります。 時間方向の治療量を表現できます。

退院時アウトカムを入院時に予測する場合は、入院後の訓練量は使えません。 予測の目的が週次更新モデルか、入院時モデルかで扱いが変わります。

// CASE 3 · 施設内平均との差

多施設データで、施設ごとに患者背景が異なります。 施設内平均 FIM との差を作ると、同じ施設内で相対的に重いか軽いかを表せます。 施設差を考える手がかりになります。

この集計は、全データで行うとリーケージになります。 各 fold の train 側だけで平均を計算します。 test 側には、その平均を適用します。

// CASE 4 · 発症からの日数 × 認知機能

発症からの日数と認知 FIM の交互作用を作ります。 早期入院でも認知機能が低い場合、回復過程が異なるかもしれません。 このような臨床仮説を、交互作用項として表現します。

交互作用は、何でも組み合わせるものではありません。 事前に説明できる組み合わせを優先します。

// 05 · THEORY特徴量設計の考え方

特徴量エンジニアリングは、生データを臨床的な意味へ近づける作業です。 表形式データでは、深層学習より前に、この工程が性能と解釈性を左右することがあります[1]

たとえば、年齢は連続値として扱えます。 しかし、80 歳以上という層を作ると、臨床的な区分が見やすくなります。 一方で、安易な二値化は情報を減らすこともあります。 研究目的に合わせて選びます。

特徴量を作る前に決める 3 つの軸

最初に決めるのは、予測時点です。 入院時予測なら、入院時点で分かる情報だけを使います。 1 週後更新モデルなら、入院後 1 週までの情報を使えます。 この線引きが曖昧だと、特徴量設計が不安定になります。

次に、臨床仮説を決めます。 たとえば、同じ FIM 合計点でも、運動 FIM が低い患者と認知 FIM が低い患者では、介入計画や退院調整の意味が異なります。 この違いを表すために、運動 FIM と認知 FIM の比率や差を作ります。

3 つ目は、症例数とのバランスです。 N=100 の研究で数百列の特徴量を作ると、偶然の相関を拾いやすくなります。 予測性能だけでなく、再現性と解釈性も考えて候補を絞ります[3]

臨床知識を数値にする発想

臨床では、評価点そのものの値だけでなく、背景との組み合わせを見ています。 たとえば、同じ歩行速度でも、若年の頭部外傷と高齢の脳卒中では意味が異なります。 同じ入院時 FIM でも、発症からの日数が短いか長いかで解釈は変わります。

特徴量エンジニアリングは、この見方を表に落とし込む作業です。 年齢層、疾患群、発症からの日数、入院前 ADL、認知機能などを組み合わせます。 ただし、組み合わせは無制限に増やしません。 臨床的に説明できる候補を優先します。

もう 1 つ重要なのは、単位のそろえ方です。 訓練量は分、時間、単位数で記録されることがあります。 歩行距離も m、10 m 歩行、6 分間歩行で意味が違います。 特徴量を作る前に、定義と単位をデータ辞書で確認します。

派生特徴量

派生特徴量は、既存の列から新しい列を作る方法です。 BMI、FIM 利得率、入院から離床までの日数などが例です。 変数の意味が臨床的に説明できる点が利点です。

BMI = weight_kg / (height_m * height_m)
FIM_gain_rate = (FIM_discharge - FIM_admission) / FIM_admission
Days_to_mobilization = first_mobilization_date - admission_date

ここで注意が必要です。 FIM 利得率は、退院時 FIM を含みます。 そのため、退院時を予測する入院時モデルの説明変数には使えません。 これは、データリーケージの典型です。

非線形特徴量

線形モデルは、基本的に直線的な関係を仮定します。 しかし、臨床データでは曲線的な関係がよくあります。 年齢、発症からの日数、訓練量などは、その例です。

log 変換、二次項、スプライン展開を使うと、線形モデルでも曲線関係を表現できます。 restricted cubic spline は、臨床予測モデルでよく使われる考え方です[2]

log_days = log(days_since_onset + 1)
age_sq = age * age
spline(age) = basis_1(age), basis_2(age), basis_3(age), basis_4(age)

ただし、複雑な変換を増やすほど、解釈は難しくなります。 また、症例数が少ない場合は推定が不安定になります。 変換の候補は、研究計画で事前に整理します。

交互作用項

交互作用項は、2 つの変数の組み合わせ効果を表します。 たとえば、年齢の影響が重症度によって変わる場合です。 線形モデルでは、交互作用を入れないと表現できません。

y = b0 + b1*x1 + b2*x2 + b3*x1*x2 + error

example:
FIM_discharge = b0
              + b1*FIM_motor_admission
              + b2*FIM_cognition_admission
              + b3*FIM_motor_admission*FIM_cognition_admission
              + error

機械学習モデルの中には、交互作用を自動的に拾いやすいものがあります。 たとえば、木系モデルは分岐により相互作用を表現しやすいです。 しかし、線形回帰やロジスティック回帰では、明示的に入れる設計が必要です。

集約特徴量

集約特徴量は、複数行やグループを要約した列です。 施設内平均との差、過去 N 回の評価平均、リハ訓練量の累積などです。 時系列データや多施設データで使いやすい方法です。

ただし、集約特徴量はリーケージを起こしやすいです。 test 側の患者を含めて施設平均を計算すると、評価データの情報が訓練側へ混入します。 集計も fold 内で行います。

時点をまたぐ特徴量の注意

リハビリ研究では、時点の混在が起こりやすいです。 入院時評価、退院時評価、退院後追跡が同じ表に入ることがあります。 そのため、各列に「いつ測定したか」を付けて管理します。

たとえば、退院先、自宅復帰、退院時 FIM はアウトカム側の情報です。 入院時予測モデルに混ざると、モデルは未来を見てしまいます。 この問題は、性能を高く見せるだけでなく、外部検証で再現しにくい原因になります。

モデル別の意味づけ

線形モデルでは、特徴量の作り方が表現力に直結します。 曲線関係や交互作用は、明示的に列として作る必要があります。 一方で、ランダムフォレストや勾配ブースティングは、非線形性を拾いやすいです。

それでも、臨床的に意味のある特徴量は有用です。 木系モデルでも、良い特徴量は分岐を助けることがあります。 また、論文で結果を説明しやすくなります[4]

特徴量を記録する表を作る

特徴量を作ったら、コードだけに残さず、一覧表として記録します。 列名、元にした列、計算式、測定時点、欠測時の扱いを並べます。 この表は、共同研究者との確認にも役立ちます。

たとえば、BMI は身長と体重から作ります。 どちらも入院時測定であれば、入院時予測に使えます。 しかし、体重が退院時測定であれば、同じ BMI でも意味が変わります。 列名だけでは判断できないため、収集時点を明記します。

FIM 関連の特徴量では、さらに注意が必要です。 入院時 FIM、退院時 FIM、FIM 利得、FIM 効率は、互いに強く関連します。 退院時アウトカムを予測するモデルでは、退院時 FIM を含む派生特徴量を説明変数に入れません。

小規模データでの実務的な考え方

リハビリ研究では、数十例から数百例の表形式データを扱うことが多いです。 この規模では、複雑な特徴量を大量に作るより、臨床的に意味のある少数の特徴量を安定して評価する方が向いています。 モデルの性能差が小さい場合、解釈しやすさが研究価値につながります。

たとえば、FIM 運動項目を 13 項目すべて入れる方法があります。 一方で、移乗、歩行、階段など、研究目的に近い項目を事前に選ぶ方法もあります。 どちらを選ぶかは、目的が予測性能なのか、臨床的な説明なのかで変わります。

画像やセンサーデータを使う研究でも、表形式の特徴量は残ります。 年齢、発症からの日数、入院時 ADL、併存疾患などは、画像特徴や歩行特徴と組み合わせることがあります。 その場合も、どの時点の情報かをそろえておきます。

特徴量の名前も重要です。 score1new_var のような名前では、数か月後に意味が分かりにくくなります。 fim_motor_addays_since_onsetrehab_minutes_3days のように、内容と時点が分かる名前にします。

また、欠測処理や標準化と特徴量生成の順番も記録します。 比率を作ってから補完するのか、補完してから比率を作るのかで値が変わります。 解析コード、データ辞書、Methods の記載を一致させることが大切です。

実務では、最初から複雑な特徴量を作り込まないこともあります。 まず単純な特徴量で基準モデルを作ります。 その後、臨床仮説に基づく特徴量を少しずつ追加し、CV 内で変化を確認します。

追加した特徴量で性能が上がっても、外部データで同じ傾向になるとは限りません。 そのため、特徴量の意味、再現性、測定コストを合わせて評価します。 臨床で無理なく測れるかも重要な判断材料になります。

// 06 · IMPLEMENTATIONPython での実装例

以下は教育用の仮想データを想定した例です。 実データを扱う場合は、個人情報保護、倫理審査、施設ルール、利用規約を確認します。 ファイル名は example_rehab_dataset.csv とします。

派生特徴量、スプライン、集約特徴量を含めます。 重要なのは、前処理を Pipeline の中で fit することです。 CV の各 fold で、訓練側だけから処理を学習します。

# Educational example only.
# Confirm privacy rules, IRB approval, institutional policy,
# and data-use agreements before handling real clinical data.

import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, SplineTransformer, StandardScaler

RANDOM_STATE = 42
TARGET = "walking_independent_discharge"

df = pd.read_csv("example_rehab_dataset.csv")
y = df[TARGET].astype(int)
X = df.drop(columns=[TARGET])

class ClinicalFeatureMaker(BaseEstimator, TransformerMixin):
    """Create clinically interpretable features inside CV folds."""
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        X = X.copy()
        X["bmi"] = X["weight_kg"] / (X["height_m"] ** 2)
        X["age_band"] = pd.cut(
            X["age"],
            bins=[0, 64, 74, 84, 120],
            labels=["under65", "65_74", "75_84", "85plus"],
            include_lowest=True,
        ).astype(str)
        X["motor_cognition_ratio"] = (
            X["fim_motor_ad"] / X["fim_cognition_ad"].replace(0, np.nan)
        )
        dose_cols = ["rehab_minutes_day1", "rehab_minutes_day2", "rehab_minutes_day3"]
        X["rehab_minutes_3days"] = X[dose_cols].sum(axis=1, min_count=1)
        X["onset_cognition_interaction"] = (
            X["days_since_onset"] * X["fim_cognition_ad"]
        )
        return X

class FacilityMeanDiff(BaseEstimator, TransformerMixin):
    """Estimate facility means from the train fold only."""
    def __init__(self, group_col="facility_id", value_col="fim_total_ad"):
        self.group_col = group_col
        self.value_col = value_col

    def fit(self, X, y=None):
        self.global_mean_ = X[self.value_col].mean()
        self.group_mean_ = X.groupby(self.group_col)[self.value_col].mean()
        return self

    def transform(self, X):
        X = X.copy()
        m = X[self.group_col].map(self.group_mean_).fillna(self.global_mean_)
        X[f"{self.value_col}_facility_diff"] = X[self.value_col] - m
        return X

feature_pipe = Pipeline([
    ("clinical", ClinicalFeatureMaker()),
    ("facility_diff", FacilityMeanDiff()),
])

num_cols = [
    "age", "bmi", "fim_motor_ad", "fim_cognition_ad", "fim_total_ad",
    "days_since_onset", "first_mobilization_day", "rehab_minutes_3days",
    "motor_cognition_ratio", "onset_cognition_interaction",
    "fim_total_ad_facility_diff",
]
spline_cols = ["age", "days_since_onset"]
cat_cols = ["sex", "facility_id", "age_band"]

num_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])
spline_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("spline", SplineTransformer(n_knots=4, degree=3, include_bias=False)),
    ("scaler", StandardScaler()),
])
cat_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore")),
])

preprocess = ColumnTransformer([
    ("num", num_pipe, num_cols),
    ("spline", spline_pipe, spline_cols),
    ("cat", cat_pipe, cat_cols),
])

pipe = Pipeline([
    ("features", feature_pipe),
    ("preprocess", preprocess),
    ("model", LogisticRegression(max_iter=2000)),
])

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
scores = cross_val_score(pipe, X, y, cv=cv, scoring="roc_auc")
print(scores.mean().round(3), scores.std().round(3))

# NG: all-row aggregation before CV leaks validation/test information.
# df["facility_mean_all"] = df.groupby("facility_id")["fim_total_ad"].transform("mean")
# Use fit inside Pipeline instead.

この例では、施設内平均との差を独自 Transformer にしています。 fit は train fold だけで呼ばれます。 そのため、validation fold の施設平均は学習に使われません。

PolynomialFeatures は便利です。 しかし、全変数へ無制限に使うと特徴量が急増します。 臨床仮説がある変数に限定して検討します。

// 07 · MYTHSよくある誤解

誤解: 特徴量は多いほど良い
特徴量が増えると、偶然の関係を拾いやすくなります。 症例数が少ない研究では、過学習や共線性が問題になります。 事前に候補を絞る設計が重要です。
誤解: 自動特徴量生成だけで十分
PolynomialFeatures や featuretools は便利です。 しかし、特徴量が急増し、解釈が難しくなります。 リハ研究では、臨床的に説明できる特徴量を優先します。
誤解: 目的変数を使った特徴量作成は前処理なので安全
目的変数を使って特徴量を作ると、ターゲットリーケージになります。 退院時 FIM を使った FIM 利得率は、入院時予測モデルでは使えません。
誤解: 交互作用は機械学習が自動で見つける
木系モデルでは表現されやすい場合があります。 一方、線形モデルでは交互作用項を明示しないと表現できません。 モデルごとの性質を確認します。

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

特徴量エンジニアリングは、Methods で具体的に記載します。 「前処理を行った」だけでは不十分です。 どの列から何を作り、どの時点の情報を使ったかを明記します。

特に、リハビリ研究では評価時点が複数あります。 入院時、退院時、追跡時の列が混ざりやすいです。 読者が再現できるように、特徴量の由来を表で示すと理解しやすくなります。

研究計画書の段階で、主要特徴量と探索的特徴量を分ける方法もあります。 主要特徴量は臨床仮説に基づく候補です。 探索的特徴量は、追加解析として扱います。 この区別は、結果の解釈を落ち着かせます。

  • 元データの列名、定義、収集時点を記載します。
  • 派生特徴量の式を記載します。
  • log 変換、二次項、スプラインの設定を記載します。
  • 交互作用項を入れた理由を記載します。
  • 集約特徴量を fold 内で作成したかを記載します。
  • 特徴量候補を事前に決めたかを記載します。

査読では、予測時点で取れない情報が混ざっていないかを見られます。 たとえば、退院時アウトカムを予測するのに、退院後 6 か月の追跡情報が入ると問題です。 TRIPOD+AI でも、データ源、予測時点、モデル化手順の透明性が重視されます[5]

また、特徴量選択と混同しないように書き分けます。 特徴量エンジニアリングは「作る」工程です。 特徴量選択は「残す」工程です。 次の記事では、特徴量選択を扱います。

// 09 · CHECKLIST特徴量設計チェックリスト

  • 01予測時点で取得できる列だけを使っていますか。
  • 02派生特徴量の式を Methods に書けますか。
  • 03FIM 利得率など、未来情報を含む特徴量を混ぜていませんか。
  • 04交互作用項は臨床的に説明できますか。
  • 05スプラインや二次項を増やしすぎていませんか。
  • 06施設平均などの集約は train fold 内で計算していますか。
  • 07特徴量生成後の列数と症例数のバランスを確認しましたか。
  • 08Pipeline または同等の手順で再現できますか。

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

  1. Q1入院時モデルで使うとリーケージになる可能性が高い特徴量はどれですか。
    • 入院時 FIM 合計
    • 入院時 BMI
    • 退院時 FIM を含む FIM 利得率
    • 発症から入院までの日数
    SHOW ANSWER
    C. 退院時 FIM は未来情報です。
  2. Q2線形モデルで交互作用を表したい場合に適切な方法はどれですか。
    • 交互作用項を明示的に作る
    • 欠測値をすべて削除する
    • カテゴリ変数を削除する
    • 評価データを学習に含める
    SHOW ANSWER
    A. 線形モデルでは、交互作用項を入れないと表現できません。
  3. Q3施設内平均との差を作るときに重要な点はどれですか。
    • 全データで施設平均を計算する
    • test 側だけで平均を計算する
    • train fold 内で平均を計算する
    • 施設 ID を削除してから計算する
    SHOW ANSWER
    C. 全データ集計はリーケージにつながります。
  4. Q4スプライン展開の主な目的はどれですか。
    • すべての変数をカテゴリ化する
    • 曲線的な関係を表現しやすくする
    • 欠測を自動的に消す
    • 目的変数を説明変数へ入れる
    SHOW ANSWER
    B. スプラインは非線形関係の表現に使います。

// REF参考文献

  1. Kuhn M, Johnson K. Feature Engineering and Selection: A Practical Approach for Predictive Models. CRC Press; 2019.
  2. Harrell FE Jr. Regression Modeling Strategies. 2nd ed. Springer; 2015.
  3. Hastie T, Tibshirani R, Friedman J. The Elements of Statistical Learning. 2nd ed. Springer; 2009.
  4. James G, Witten D, Hastie T, Tibshirani R. An Introduction to Statistical Learning. 2nd ed. Springer; 2021.
  5. Collins GS, Moons KGM, Dhiman P, et al. TRIPOD+AI statement. BMJ 2024;385:e078378.