医師のための統計学再入門

【応用編】第5回:感度分析と統計解析の実践

けー@代謝内科 先生

臨床医の皆様が統計学を再学習し、臨床研究のデザインや統計学の基礎知識を身につけることを目的とした本シリーズ「医師のための統計学再入門」。

応用編の最終回となる今回は、「感度分析と統計解析の実践」について、特に統計解析結果の「頑健性(ロバスト性)」を検証するための「感度分析(sensitivity analysis)」について解説します。

統計解析の結果が出たとき、「この結論はどれくらい信頼できるのか?」「変な前提に依存していないか?」といった疑問が浮かぶことはありませんか? 感度分析とは、まさにそうした不安に対して答えを出すための方法です。

この記事では、感度分析の考え方と活用法、Pythonを用いた実践例、そして統計解析結果の批判的読解の視点について、一緒に学んでいきましょう。

前回の記事はこちら:医師のための統計学再入門【応用編】第4回:交互作用の解析

感度分析とは? なぜ必要なのか

感度分析とは、一言で言えば「統計解析の結論がどれくらい条件に依存しているか」を検証する方法です。

たとえばこんなことはないでしょうか?

  • 投入する変数を1つ変えると、p値が有意になったりならなかったりする
  • 打ち切りデータの扱い方を変えると、ハザード比が大きく変わる
  • 分析対象の集団(全体 vs 特定サブグループ)を変えると結果が逆になる

これらは、結論が「分析条件」によって左右される、つまり「ロバストでない」状況です。

感度分析の目的は上記のような状況下で、「解析条件を変えても結論が変わらないか」を検証することです。

どのように感度分析を行うか?(主なパターン)

感度分析にはさまざまな方法がありますが、代表的なものを以下に紹介します。

a.説明変数の入れ替え・除去

「年齢」「性別」「BMI」「HbA1c」を投入していた重回帰モデルから、あえて「HbA1c」を外してみて、結果の有意性や回帰係数に大きな変化があるかどうかを確認します。

b.モデルそのものを変えてみる

ロジスティック回帰を決定木に、Cox回帰を加速失敗時間モデルに変更し、モデル依存性を確認します。

c.打ち切り・欠測データの扱い方を変える

欠損値を補完する方法(平均補完、多重代入など)を変更し、打ち切りデータをどう扱うかで生存時間解析の結果がどう変わるかを確認します。

d.集団を変更してみる(PPS解析など)

全症例(ITT) vs プロトコル遵守群(PPS)での解析において、 サブグループ解析ではなく「母集団の感度分析」を検討します。

Pythonによる感度分析の実例

ここでは、第2回でご紹介したPima Indians Diabetes Dataset を用いて、変数の削除による感度分析を行ってみましょう。

このデータセットは、768名のピマ・インディアン女性の健康調査から収集されたもので、「糖尿病の有無」をアウトカムとして以下の説明変数が含まれています。

  • Pregnancies(妊娠回数)
  • Glucose(血糖値)
  • BloodPressure(血圧)
  • SkinThickness(皮膚厚)
  • Insulin(インスリン値)
  • BMI(体格指数)
  • DiabetesPedigreeFunction(家族歴)
  • Age(年齢)

Pythonで通常のロジスティック回帰モデルを作成してみます。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression


url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv"
columns = ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin',
           'BMI', 'DiabetesPedigreeFunction', 'Age', 'Outcome']
df = pd.read_csv(url, names=columns)


X = df.drop("Outcome", axis=1)
y = df["Outcome"]


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)


model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)


print("通常モデルの精度:", model.score(X_test, y_test))
 

実行すると、

通常モデルの精度: 0.7359307359307359

と表示されます。

次に、変数「Insulin」を除いて再解析してみましょう。

X_sensitivity = X.drop("Insulin", axis=1)
X_train2, X_test2, y_train2, y_test2 = train_test_split(X_sensitivity, y, test_size=0.3, random_state=42)


model2 = LogisticRegression(max_iter=1000)
model2.fit(X_train2, y_train2)


print("Insulinを除いたモデルの精度:", model2.score(X_test2, y_test2))
 

今度は、

Insulinを除いたモデルの精度: 0.7359307359307359

となりました。

つまり、項目「Insulin」の有無によらず解析結果は安定しているということになります。

また、感度分析は「変数を入れ替える」だけではありません。アウトカムの定義そのものを変更してみるという手法もあります。

たとえば:

  • 「HbA1c 7.0%以上」を糖尿病と定義 → 「6.5%以上」にしてみる
  • 「30日死亡率」→「90日死亡率」に変更してみる

このような変更で結論がどれほど揺らぐかを確認することで、アウトカムの選択によるバイアスの可能性を探ることができます。

また、統計モデルにおけるハイパーパラメータ(正則化の強さなど)を微調整しながら、結論の安定性を確認する方法も感度分析の一環です。

ここで正則化について、少し追加でご説明します。

正則化というのは、モデルが訓練データに過剰に適合する過学習を防ぐための方法です。ロジスティック回帰においては、正則化を行うことで過学習を防ぎ、モデルの汎化性能を高める役割をもっており、いわば係数が極端に大きくならないように制約を加える仕組みです。

これにより、モデルが訓練データのノイズに過剰に適合するのを防ぎ、より安定した予測を実現します。

以下のコードで、正則化パラメータCはロジスティック回帰における正則化の逆数であり、その値がモデルの複雑さにどのように影響するかを説明しています。

たとえば、以下のようにして正則化強度を少しずつ変化させて、モデルの精度を評価するのもよいでしょう。

# 正則化パラメータCを変えてモデル構築
for c in [0.01, 0.1, 1.0, 10.0]:
    model = LogisticRegression(C=c, max_iter=1000)
    model.fit(X_train, y_train)
    print(f"C={c} -> 精度: {model.score(X_test, y_test):.3f}")
  • C=0.01 -> 精度: 0.723
  • C=0.1 -> 精度: 0.727
  • C=1.0 -> 精度: 0.736
  • C=10.0 -> 精度: 0.736

欠測データの補完方法による感度分析

臨床データでは、患者の一部で採血値が欠損していたり、アンケート項目が未記入だったりと、欠測データ(missing data)が発生することは避けられません。

このような欠測データをどのように扱うかは、解析結果に大きな影響を与える可能性があるため、感度分析の重要な対象となります。

補完方法の違いによる影響

まず、よく使われる補完方法には次のようなものがあります。

補完方法 特徴と影響
平均値補完 欠損値をその変数の平均値で埋める。簡便だが、ばらつきが減少し、分布がゆがむ可能性がある。
中央値補完 外れ値の影響を抑えるが、依然として分布を平坦化する傾向がある。
多重代入(multiple imputation) 他の変数との関係性も使って複数の補完値を生成し、統合して扱う。より自然で信頼性の高い補完方法だが、計算負荷が高く実装には統計的理解が必要。

たとえば、以下のようにPythonで実装できます。

import pandas as pd
from sklearn.experimental import enable_iterative_imputer  # IterativeImputerを有効にするための設定(実験的な機能)
from sklearn.impute import IterativeImputer
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error


# 例として、欠損値を含む可能性のあるDataFrame df を作成します
data = {'A': [1, 2, None, 4, 5],
        'B': [None, 7, 8, 9, None],
        'C': [10, 11, 12, None, 14]}
df = pd.DataFrame(data)


# -------------------- 元の欠損値を含む DataFrame を表示 --------------------
print("【元の欠損値を含む DataFrame】")
print(df)
print("\n")


# -------------------- 平均値補完 --------------------
# fillna() 関数を使って、DataFrame内の欠損値(NaN)を各列の平均値で埋めます
df_filled_mean = df.fillna(df.mean())
print("【平均値補完後のDataFrame】")
print(df_filled_mean)
print("\n")


# -------------------- 多重代入(IterativeImputerを使用) --------------------
# IterativeImputerは、他の列の値を使って欠損値を予測し、補完を行う高度な手法です


# IterativeImputerのインスタンスを作成します
# max_iter: 欠損値の予測と補完の繰り返し回数の最大値。ここでは10回繰り返します
# random_state: 乱数シードを設定することで、結果の再現性を確保します
imp = IterativeImputer(max_iter=10, random_state=0)


# fit_transform() メソッドを使って、DataFrameに多重代入を適用します
# fit() でモデル(欠損値の予測器)を学習し、transform() で実際に欠損値を補完します
df_filled_mi_array = imp.fit_transform(df)


# 補完されたNumPy配列をPandasのDataFrameに変換します
# columns=df.columns で、元のDataFrameの列名を保持します
df_filled_mi = pd.DataFrame(df_filled_mi_array, columns=df.columns)
print("【多重代入後のDataFrame】")
print(df_filled_mi)
print("\n")


# -------------------- 補完後のデータを用いた回帰モデルの構築と評価(例) --------------------
# ここでは、説明変数をAとB、目的変数をCとして簡単な線形回帰モデルを想定します
# 実際には、ご自身の分析に合わせてモデルを構築してください


# 欠損値補完後のデータで学習データとテストデータに分割
X_mean = df_filled_mean[['A', 'B']]
y_mean = df_filled_mean['C']
X_train_mean, X_test_mean, y_train_mean, y_test_mean = train_test_split(X_mean, y_mean, test_size=0.3, random_state=42)


X_mi = df_filled_mi[['A', 'B']]
y_mi = df_filled_mi['C']
X_train_mi, X_test_mi, y_train_mi, y_test_mi = train_test_split(X_mi, y_mi, test_size=0.3, random_state=42)


# 平均値補完データで線形回帰モデルを学習し、評価
model_mean = LinearRegression()
model_mean.fit(X_train_mean, y_train_mean)
y_pred_mean = model_mean.predict(X_test_mean)
mse_mean = mean_squared_error(y_test_mean, y_pred_mean)
print(f"【平均値補完データで学習したモデルの平均二乗誤差】: {mse_mean:.2f}")


# 多重代入データで線形回帰モデルを学習し、評価
model_mi = LinearRegression()
model_mi.fit(X_train_mi, y_train_mi)
y_pred_mi = model_mi.predict(X_test_mi)
mse_mi = mean_squared_error(y_test_mi, y_pred_mi)
print(f"【多重代入データで学習したモデルの平均二乗誤差】: {mse_mi:.2f}")
 

実行すると、以下のように表示されます。

【元の欠損値を含む DataFrame】

【元の欠損値を含む DataFrame】
A B C
0 1.0 NaN 10.0
1 2.0 7.0 11.0
2 NaN 8.0 12.0
3 4.0 9.0 NaN
4 5.0 8.0 14.00
 

【平均値補完後のDataFrame】

【平均値補完後のDataFrame】
A B C
0 1.0 8.0 10.0
1 2.0 7.0 11.00
2 3.0 8.0 12.00
3 4.0 9.0 11.75
4 5.0 8.0 14.00
 

【多重代入後のDataFrame】

【多重代入後のDataFrame】
A B C
0 1.000000 5.844134 10.00000
1 2.000000 7.000000 11.00000
2 3.290549 8.000000 12.00000
3 4.000000 9.000000 12.81579
4 5.000000 10.467573 14.00000
 

【平均値補完データで学習したモデルの平均二乗誤差】: 0.78

【多重代入データで学習したモデルの平均二乗誤差】: 0.00

上記のように、異なる欠損値補完方法を用いることで、回帰モデルの性能(ここでは平均二乗誤差)がどのように変化するかを確認でき、補完方法によって結論が変わるかどうかも検証できます。

❖ 論文評価時のチェックポイント

  • 補完方法が明示されているか
  • 補完方法の選択理由が記載されているか
  • 感度分析として補完法の違いが検討されているか

❖ アウトカム定義の変更も感度分析が有効です

  • 例:「HbA1c 7.0%以上」→「6.5%以上」に変更
  • 例:「30日死亡」→「90日死亡」に変更

アウトカムの定義を変えても、結論が大きく変わらないなら、解析結果は頑健(ロバスト)だと考えられます。

❖ モデル設定の違いも確認対象とする

前述のとおり、統計モデルのハイパーパラメータ(ロジスティック回帰における正則化係数Cなど)を変更する感度分析も有効です。

たとえば平均補完でも多重代入でも、回帰係数の方向性や有意性が変わらないなら、解析結果は頑健(ロバスト)であると言えます。

逆に、前提を少し変えるだけで結論が揺らぐ場合、その解釈には慎重さが求められます。

論文を読むときのチェックリスト

感度分析の視点は、研究者だけでなく読者にとっても重要です。

チェック項目 内容
結果は変数選択に依存していないか 変数の除去で結果が変化していないか
欠測補完の妥当性はあるか 明示され、感度分析されているか
アウトカム定義は恣意的でないか 複数の定義でも一貫しているか
モデル前提に依存していないか 他のモデル・パラメータでも結果が一致しているか

これらの内容を意識して論文を読むことで、研究結果の信頼性や頑健性をより深く評価できるようになります。

表面的なp値や結論だけでなく、その結果が様々な条件下でも揺るがないか、特定の都合の良い設定に依存していないかを見抜く力を養い、エビデンスの質を適切に判断し、臨床現場での意思決定に活かすことができることを目指しましょう。

まとめ

今回は、感度分析を通じて統計解析結果の「信頼できるかどうか」を評価する方法を学びました。とくに重要なのは以下のポイントです。

  • 感度分析は、解析条件を変えても結論が保たれるかを検証する手法です。
  • 説明変数、アウトカム定義、補完方法、モデル設定など多角的に行うことが重要です。
  • 論文評価でも「感度分析が行われているか」は重要なポイントです。

感度分析を適切に行うことで、解析結果に対して自信を持って解釈できるようになります。統計の結論とは、「条件つきの真実」です。その条件が変わっても結果が変わらないかどうかを確認することが大切ですね。

コメントを見る / 書く

公式SNS

こちらの記事に加えキャリアや働き方に関連する
医師の皆様に役立つ情報を発信中!

けー@代謝内科
M.D., Ph.D. 糖尿病、内分泌専門医として地方の総合病院で楽しく働いています。なにか面白そうなデータを見つけたら解析してみたくなる癖があります。正確でわかりやすい情報発信を心がけています。
[X]URL:https://x.com/keimitoma

求人をみる/アンケート会員に申し込む

医師転職ドットコム 医師バイトドットコム 医師アンケート会員登録

コメントを投稿する

コメント

※ コメントはエピロギ編集部が確認後、掲載させていただきます。

投稿者名(12文字以内)

ページの先頭へ