// PART 02 · ARTICLE 07 / 07
DATA-PREP PRACTICAL L1

表形式データの実践Tips

— リハ研究で 80 点を取るための前処理チェックリスト

9 min read· L1· 2026.05.20 update· by Editor

表形式データの前処理は、派手ではありません。 しかし、研究の信頼性を大きく左右します。 欠測、外れ値、表記揺れ、時点の混入を見逃すと、 モデル性能は見かけ上よく見えることがあります。 本記事では、第2部の総まとめとして、 リハ研究でまず確認したい実践的な手順を整理します。

// CONTEXT

ここで扱うのは、FIM、SIAS、NIHSS、握力、歩行速度、 施設 ID、退院先などを含む表形式データです。 画像や動画より小さく見えるデータでも、 時点・定義・欠測理由をそろえないと、 予測モデルの解釈は不安定になります。

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

  • データ受領後 30 分で確認する項目を説明できます。
  • データ辞書に入れるべき列名、単位、時点、欠測理由を整理できます。
  • 施設別・時期別の分布差を、前処理段階で確認できます。
  • 第2部の内容を 1 つの Pipeline にまとめる考え方を理解できます。

// 02 · CONCLUSION結論

// 03 · FIGURE図で理解する表形式データ前処理

// FIRST 30 MINUTES CHECKLIST 0 min 30 min 01 行数 / 列数 df.shape 想定どおりか 02 型 (dtype) df.dtypes 数値が文字列化? 03 欠測率 df.isna().mean() 列ごとに一覧化 04 ユニーク値 df.nunique() 表記揺れの種 05 基本統計 df.describe() 臨床的範囲との照合 06 重複 ID duplicated() 同一患者の複数行 07 時点の分離 列名 + データ辞書 入院 / 退院 / 追跡 08 施設差 groupby('facility_id') 背景・転帰の偏り → 受領 30 分でここまで見れば、後段の前処理設計が安定する
図1: データ受領後 30 分で見る項目。行数、列数、型、欠測率、ユニーク値、重複、時点、施設差を順番に確認します。
// PART 02 INTEGRATION MAP Pipeline + ColumnTransformer CV 内 fit / transform 02·01 X と y 予測時点・変数の定義 → 全体の出発点 02·02 欠損値処理 MCAR / MAR / MNAR 補完器を fold 内で fit 02·03 外れ値 + Scale IQR / One-Hot / Scaler 数値化の三本柱 02·04 特徴量生成 BMI / 比率 / spline 臨床知識を数値に 02·05 特徴量選択 Lasso / RFE / VIF どれを残すか 02·06 リーケージ防止 train で fit のみ 全工程に貫く原則 ML MODEL → 評価
図2: 第2部全体の関係マップ。欠測、外れ値、カテゴリ変数、標準化、特徴量生成、特徴量選択、リーケージ防止を 1 つの Pipeline に統合します。

前処理は、個別のテクニックの集まりではありません。 欠測補完だけを丁寧に行っても、 時点が混ざればリーケージが起こります。 標準化だけを Pipeline に入れても、 特徴量選択を全データで行えば評価は楽観的になります。

実務では、まずデータを理解する段階があります。 その次に、モデルへ渡すための整形があります。 この 2 段階を分けて考えると、 ミスを見つけやすくなります。

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

// CASE 1 · 新規受領データ

回復期病棟のデータを受け取りました。 行数は N=287、列数は 50 です。 SIAS 下肢項目の欠測率が 22% でした。 患者 ID の重複はありませんでした。

ここで重要なのは、欠測率だけではありません。 欠測が重症例に偏るかを確認します。 欠測理由が「評価不能」なのか、 「入力漏れ」なのかも分けて記録します。

// CASE 2 · 多施設データの罠

施設 A だけ追跡期間が長いデータがあります。 施設 B と C では、6 か月後 FIM が少ない状況です。 施設 ID は単なる管理番号ではありません。 収集体制や患者背景を反映することがあります。

施設別に欠測率、年齢、入院時 FIM、 アウトカム分布を確認します。 モデル評価では、施設単位の分割も検討します。 これは データリーケージの予防にも関係します。

// CASE 3 · 古い記録の表記揺れ

診断名に「脳梗塞」「脳こうそく」 「Cerebral infarction」が混在しています。 これを別カテゴリとして扱うと、 同じ病態が複数の変数に分かれます。

まず正規化辞書を作ります。 その後に One-Hot Encoding や Frequency Encoding を行います。 表記揺れの処理は、 カテゴリ変数の前処理の前段階です。

// CASE 4 · 追跡データの混入

入院時に退院時歩行自立を予測する研究です。 しかし、退院後 6 か月の外来 FIM が表に入っています。 この列を説明変数に入れると、 予測時点では得られない情報を使うことになります。

入院時、退院時、追跡時を列名だけで判断しないことが重要です。 データ辞書に収集時点を入れることで、 未来情報の混入を避けやすくなります。

// 05 · THEORY実務で使う考え方

1. Tidy data の原則

表形式データを扱う基本に、Tidy data があります。 1 行は 1 観測、1 列は 1 変数、1 セルは 1 値です。 この形に近いほど、集計や Pipeline 化が容易になります[1]

1 row  = 1 observation
1 col  = 1 variable
1 cell = 1 value

例:
patient_id | timepoint | fim_motor | fim_cognition | facility_id
0001       | admission | 48        | 23            | A
0001       | discharge | 72        | 28            | A

ただし、リハ研究では縦持ちと横持ちの両方を使います。 予測モデルでは、入院時変数だけを横持ちにすることもあります。 大切なのは、観測単位を明確にすることです。

2. データ辞書は研究計画の一部

データ辞書は、列名の説明表ではありません。 変数の定義、単位、収集時点、欠測理由、 予測時点で利用可能かを整理する表です。 これは 説明変数と目的変数を決める作業と直結します。

column_name        : fim_motor_admission
meaning            : 入院時 FIM 運動項目合計
unit               : points
range              : 13-91
collection_time    : admission
missing_reason     : not assessed / entry error / unknown
available_at_model : yes
source             : electronic chart
notes              : PT/OT assessment record

「FIM_motor」という列名だけでは不十分です。 入院時なのか、退院時なのかで意味が変わります。 退院時 FIM を入院時予測に使うと、 モデルは未来の情報を見ています。

3. データの型は 3 種に分ける

前処理では、データ型だけを見ると不十分です。 数値型やカテゴリ型に加えて、 観測単位、時点、ソースを確認します。

type 1: observation unit
  patient-level / admission-level / assessment-level

type 2: timepoint
  baseline / discharge / follow-up / after outcome

type 3: source
  chart / sensor / questionnaire / claims / manual entry

同じ患者の複数評価がある場合、 行単位でランダム分割すると情報が漏れます。 この場合は患者 ID を group として扱います。 前処理のリーケージ防止でも重要な視点です。

4. 第2部の総整理

第2部では、欠測、外れ値、カテゴリ変数、標準化、 特徴量生成、特徴量選択、リーケージ防止を扱いました。 これらは順番に行う作業に見えますが、 実際には相互に関係します。 たとえば、欠測フラグは特徴量になります。 外れ値処理は標準化の選択に影響します。 特徴量選択は CV 内で行わないと、 test の情報を使った選び直しになります。

そのため、前処理はノートブック上の手作業で終わらせず、 Pipeline として保存できる形にします。 scikit-learn の Pipeline は、この目的に向いた実装です[6]。 臨床的な変数選択やサンプルサイズ設計では、 Harrell や Steyerberg の考え方も参考になります[3][4]。 特徴量設計の実践的な整理には、Kuhn と Johnson の書籍が有用です[2]

// 06 · IMPLEMENTATIONPython 実装例

以下は教育用の仮想データを想定した例です。 実データを扱う場合は、個人情報保護、倫理審査、施設ルール、 データ利用規約を確認したうえで解析環境を整えます。 個人を特定できる情報は、解析用ファイルに含めない設計にします。

# 02·07 表形式データの実践Tips
# 教育用の仮想データ: example_rehab_dataset.csv
# 実データでは個人情報保護・倫理審査・施設ルールを確認します。

import json, random, sys
from pathlib import Path

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

SEED = 42
random.seed(SEED)
np.random.seed(SEED)

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

# 1. データ受領後 30 分チェック
print("shape:", df.shape)
print("dtypes:")
print(df.dtypes)
print("missing rate:")
print(df.isna().mean().sort_values(ascending=False).head(20))
print("summary:")
print(df.describe(include="all").T)

# 2. 患者 ID とユニーク値
if "patient_id" in df.columns:
    n_dup = df.loc[df.duplicated("patient_id", keep=False), "patient_id"].nunique()
    print("duplicated patient_id:", n_dup)

for col in df.columns:
    n_unique = df[col].nunique(dropna=False)
    if n_unique <= 20:
        print(f"\n{col}: {n_unique} unique")
        print(df[col].value_counts(dropna=False).head(20))

# 3. 施設別分布
if "facility_id" in df.columns:
    facility_summary = df.groupby("facility_id").agg({
        "age": ["count", "mean", "std"],
        "fim_motor_admission": ["mean", "std"],
        "walking_independent_discharge": ["mean", "count"],
    })
    print(facility_summary)

# 4. 予測時点で使えない列を除外
outcome_col = "walking_independent_discharge"
time_leak_cols = [
    "fim_motor_discharge",
    "fim_total_discharge",
    "fim_6month_followup",
    "discharge_destination",
]
leak_cols = [c for c in time_leak_cols if c in df.columns]
X = df.drop(columns=[outcome_col, "patient_id"] + leak_cols, errors="ignore")
y = df[outcome_col].astype(int)

numeric_cols = X.select_dtypes(include=["int64", "float64"]).columns.tolist()
categorical_cols = X.select_dtypes(include=["object", "category", "bool"]).columns.tolist()

numeric_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])
categorical_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore")),
])
preprocess = ColumnTransformer([
    ("num", numeric_pipe, numeric_cols),
    ("cat", categorical_pipe, categorical_cols),
])

model = LogisticRegression(
    penalty="l1", solver="liblinear", C=0.5,
    max_iter=2000, random_state=SEED,
)
pipe = Pipeline([
    ("preprocess", preprocess),
    ("model", model),
])

# 5. CV 内で前処理も fit される
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=SEED)
scores = cross_val_score(pipe, X, y, cv=cv, scoring="roc_auc")
print("AUC mean:", scores.mean())
print("AUC sd:", scores.std())

# 6. 同一患者の複数評価がある場合
if "patient_id" in df.columns and df["patient_id"].duplicated().any():
    groups = df["patient_id"]
    group_cv = GroupKFold(n_splits=5)
    group_scores = cross_val_score(pipe, X, y, cv=group_cv, groups=groups, scoring="roc_auc")
    print("Group AUC mean:", group_scores.mean())

# 7. データ辞書ひな形
schema = []
for col in df.columns:
    schema.append({
        "column_name": col,
        "dtype": str(df[col].dtype),
        "missing_rate": float(df[col].isna().mean()),
        "collection_time": "TODO: admission/discharge/follow-up",
        "unit": "TODO",
        "definition": "TODO",
        "available_at_model": "TODO: yes/no",
    })

Path("data_dictionary_template.json").write_text(
    json.dumps(schema, ensure_ascii=False, indent=2), encoding="utf-8"
)
Path("python_version.txt").write_text(sys.version, encoding="utf-8")
# pip freeze > requirements.txt で環境も記録します。

この例では、欠測補完、標準化、One-Hot、L1 正則化を 1 つの Pipeline に入れています。 CV の各 fold では、前処理も学習側だけで fit されます。 これは リーケージを防ぐ前処理の基本です。

pandas で全データに対して補完や標準化をしてから分割すると、 validation や test の情報が前処理に混ざります。 そのため、探索的確認とモデル用前処理は分けて扱います。

// 07 · MYTHSよくある誤解

誤解: Excel で目視確認すれば十分
数千行を超えると、目視では欠測や表記揺れを見落とします。 pandas で型、欠測率、ユニーク値、範囲を確認します。
誤解: 列名さえあれば意味は明らか
FIM_motor だけでは、入院時か退院時か分かりません。 単位、定義、収集時点をデータ辞書で管理します。
誤解: データが整っていれば前処理は短くできる
医療データは整って見えても、時点や施設差が混ざることがあります。 形式の整備と意味の確認は別の作業です。
誤解: Tidy data を意識しなくても結果は同じ
解析結果だけなら動く場合があります。 しかし、再利用性、監査性、Pipeline 化で差が出ます。

// 08 · WRITINGMethods に書くこと

表形式データの前処理は、Methods で省略されがちです。 しかし、予測モデル研究では再現性に関わります。 TRIPOD+AI でも、データ、予測因子、欠測、 モデル開発過程の透明性が重視されます[5]

Methods に含めたい項目

  • 01解析対象の観測単位を明記します。
  • 02予測時点を明記します。
  • 03説明変数に入れた時点を明記します。
  • 04欠測率と欠測処理の方法を書きます。
  • 05カテゴリ変数のエンコード方法を書きます。
  • 06標準化や変換を行った列を示します。
  • 07特徴量選択を CV 内で行ったかを示します。
  • 08ソフトウェアとバージョンを記録します。

書き方の例

All preprocessing steps were implemented within a scikit-learn Pipeline.
Continuous variables were imputed using the median and standardized.
Categorical variables were imputed using the most frequent category
and encoded using one-hot encoding.
The preprocessing parameters were fitted only on the training folds
within cross-validation.

論文では「前処理した」とだけ書くと、 どのデータで fit したかが分かりません。 train fold 内で fit したか、 外部検証データには transform のみ行ったかを記載します。

査読では、未来情報の混入、同一患者の重複、 施設差、欠測処理の妥当性が問われやすいです。 特に、退院時・追跡時の情報が入院時予測に入っていないかは、 研究計画段階で確認します。

// 09 · CHECKLIST30 分チェックリスト

  • 01行数、列数、患者数を確認しましたか。
  • 02各列の型とユニーク値を確認しましたか。
  • 03欠測率を列ごとに一覧化しましたか。
  • 04基本統計と臨床的範囲を確認しましたか。
  • 05患者 ID の重複と複数評価を確認しましたか。
  • 06入院時、退院時、追跡時の列を分けましたか。
  • 07施設別・時期別の分布差を確認しましたか。
  • 08データ辞書を作り、単位と定義を記録しましたか。

このチェックリストは、モデル作成前の点検です。 点検で見つかった問題は、 欠測値処理外れ値・カテゴリ変数・標準化特徴量エンジニアリング特徴量選択で個別に扱います。

// 10 · QUIZ確認クイズ

  1. Q1入院時予測モデルで、退院後 6 か月 FIM を説明変数に入れると何が問題ですか。
    • 欠測がなくなる
    • 未来情報が混入する
    • 標準化が不要になる
    • 施設差が消える
    SHOW ANSWER
    B. 予測時点で取得できない情報を使うため、リーケージになります。
  2. Q2データ辞書に含める項目として、特に重要なものはどれですか。
    • 列名、単位、定義、収集時点
    • ファイル名だけ
    • モデル名だけ
    • 論文タイトルだけ
    SHOW ANSWER
    A. 列名だけでは、時点や単位が分からないことがあります。
  3. Q3同一患者の複数評価がある場合、評価で注意する分割はどれですか。
    • 行単位の完全ランダム分割
    • 患者 ID を考慮した GroupKFold
    • 欠測列だけを削除する分割
    • 目的変数で並べ替える分割
    SHOW ANSWER
    B. 同一患者の情報が train と test に分かれると、評価が甘くなります。
  4. Q4Pipeline に入れる目的として正しいものはどれですか。
    • 前処理の fit を fold 内に閉じ込める
    • 外れ値を自動で医学的に判定する
    • 欠測の理由を自動で確定する
    • 研究倫理の確認を省略する
    SHOW ANSWER
    A. Pipeline は前処理とモデルを一体化し、CV 内 fit を保ちやすくします。

// REF参考文献

  1. Wickham H. Tidy data. Journal of Statistical Software 2014;59(10):1-23.
  2. Kuhn M, Johnson K. Feature Engineering and Selection: A Practical Approach for Predictive Models. CRC Press, 2019.
  3. Harrell FE Jr. Regression Modeling Strategies. 2nd ed. Springer, 2015.
  4. Steyerberg EW. Clinical Prediction Models. 2nd ed. Springer, 2019.
  5. Collins GS, Moons KGM, Dhiman P, et al. TRIPOD+AI statement. BMJ 2024;385:e078378.
  6. Pedregosa F, Varoquaux G, Gramfort A, et al. Scikit-learn: Machine learning in Python. Journal of Machine Learning Research 2011;12:2825-2830.