MENU

【無料プレゼント付き】学会発表・論文投稿に必要な統計を最短で学ぶことができる無料メルマガ

査読者(Reviewer)のツッコミを未然に防ぐ!IPTW解析における重み分布の確認と箱ひげ図(Boxplot)の描き方

研究者: 傾向スコア逆確率重み付け(IPTW)解析を行っているのですが、査読者や指導医から「重みの分布を箱ひげ図(Boxplot)で確認したか?」と聞かれました。なぜ箱ひげ図が必要で、どう描いて外れ値を処置すればいいのでしょうか……?

統計ER: IPTWはたった1〜2例の「極端に大きな重み」によって結果全体が歪むリスクがあります!箱ひげ図を描くことで重みの異常(外れ値)を一目で発見できますよ。ggplot2 を使えばエラーなく確実で美しいプロットが出力でき、summary(W) で数値を確認してから WeightIt::trim() でトリミング処置を行うのが鉄板のフローです。さっそく手順を解説しましょう!

傾向スコアを用いた逆確率重み付け(IPTW: Inverse Probability of Treatment Weighting)解析を実施する際、指導医や論文の査読者(Reviewer)から以下のような指摘を受けたことはないだろうか。

  • 「算出した重み(Weights)の分布を箱ひげ図(Boxplot)で確認したか?」
  • 「極端に大きな重み(Extreme Weights / 外れ値)を持つ症例が存在しないか?」
  • 「重み付けによって、調整前の背景因子(年齢や検査値など)の分布はどのように変化したか?」

IPTW解析は交絡調整の強力な手法だが、裏側で「重みの巨大化」が起きていると、たった1〜2例の極端な症例によってオッズ比やハザード比が大きく歪んでしまう脆さを持っている。

この問題を事前に発見し、解析の健全性を担保するために不可欠なのが「箱ひげ図によるビジュアル確認」である。

本記事では、WeightItggplot2 パッケージを活用し、重みの外れ値チェックから連続共変量のバランス確認、外れ値が見つかった際のトリミング(Trimming)処理までをRでスマートに完結させる手順を解説する。

>>もう統計で悩むのは終わりにしませんか? 

↑1万人以上の医療従事者が購読中

目次

IPTW解析で箱ひげ図を描くべき「2つの決定的な理由」

IPTW解析において箱ひげ図を使用する目的は、大きく分けて以下の2点に集約される。

1. 算出した「重み(Weights)」の分布と極端値(Extreme Weights)の確認

IPTWにおける重み $w_i$ は、傾向スコア $e_i$ の逆数($1 / e_i$ や $1 / (1 – e_i)$)として計算される。

もし傾向スコアが 0 や 1 に極めて近い症例(例: $e_i = 0.005$)が存在すると、その重みは $1 / 0.005 = 200$ と巨大化してしまう。このような「巨大な重みを持つ症例」が存在すると、その1例のデータが解析結果全体を支配してしまい、推定量の分散(標準誤差)が飛躍的に増大する。

箱ひげ図を描くことで、ひげの先から大きく飛び出した点(外れ値)の有無や、重みの中央値・分布の広がりが一目でチェックできる。

2. 調整前後(Unadjusted vs Adjusted)での「共変量バランス」の視覚的確認

一般に交絡バランスの確認には「標準化平均差(SMD)」を並べたラブプロット(Love Plot)が使われる。しかし、SMDはあくまで「平均値の差」しか見ていない。

連続変数(例: 年齢、BMI、CRP値など)について重み付け前後の箱ひげ図を描くことで、「平均値だけでなく、中央値や四分位範囲(IQR)、データの広がり全体が群間で綺麗に揃ったか」をより厳密に実証することが可能になる。

確実かつ美麗!ggplot2 で箱ひげ図を描く実践フロー

Rでエラーを起こさず、かつ論文や報告書向けに美しくカスタマイズできる手法が、ggplot2geom_boxplot() を使用する方法である。

あらかじめ必要なパッケージをインストール・読み込んでおこう。

library(tidyverse)
library(WeightIt)
library(patchwork) # グラフを左右に並べる用

Step 1: サンプルデータの作成とIPTW重みの計算

まずは動作確認用のサンプルデータを作成し、WeightIt で重みを計算する。

# サンプルデータの作成
set.seed(123)
n <- 300
df_raw <- tibble(
  treat = sample(c(0, 1), n, replace = TRUE, prob = c(0.6, 0.4)),
  age   = rnorm(n, mean = 65, sd = 10),
  bmi   = rnorm(n, mean = 24, sd = 4)
)

# IPTW重みの計算
W <- weightit(treat ~ age + bmi, data = df_raw, method = "glm", estimand = "ATE")

# 重みと治療変数を元のデータフレームに結合
df_plot <- df_raw %>% 
  mutate(
    weight = W$weights,
    treat  = factor(treat) # グラフ描画用にFactor化
  )

Step 2: 重み(Weights)の箱ひげ図チェック

作成したデータを用いて、治療群ごとの重みの分布を箱ひげ図として出力する。データの実際の散らばりも可視化するため、ドットプロット(geom_jitter)も重ねて描写する。

# 1. 重み自体の分布を箱ひげ図で確認
ggplot(df_plot, aes(x = treat, y = weight, fill = treat)) +
  geom_boxplot(alpha = 0.7, outlier.color = "red", outlier.size = 2) + 
  geom_jitter(width = 0.1, alpha = 0.2) + 
  labs(
    title = "Distribution of IPTW Weights by Treatment Group",
    x     = "Treatment Group",
    y     = "Inverse Probability Weight"
  ) +
  theme_minimal()

ひげの先から赤色で示された「外れ値」がどれほど突出しているかを視覚的に確認する。

Step 3: summary(W) による重みの数値チェック

トリミング(剪定)を行う前に、客観的な数値基準を確認する。WeightIt オブジェクトに対して summary() 関数を実行すると、重みの最小値・中央値・最大値や、上位パーセンタイルの数値を一括で確認できる。

# 重みの統計量を数値で詳細確認
summary(W)

確認のポイント:

出力内の Summary of weights: セクションにある Max(最大値)を確認する。ここが「20」や「50」など極端に大きい数値になっている場合、たった1つの症例が解析結果全体を歪めているリスクが高いと判断できる。

Step 4: 外れ値が存在する場合のトリミング処理(WeightIt::trim

summary(W) で巨大な最大値を確認した場合、重みのトリミング(Trimming / Truncation) を行う。WeightIttrim() 関数を使い、上位1%(99パーセンタイル)を超える巨大な重みを上限値へクリッピングする。

# 重みの上位 1%(99パーセンタイル)でトリミングを実行
W_trimmed <- trim(W, at = 0.99)

# トリミング後のデータを再結合
df_plot_trimmed <- df_raw %>% 
  mutate(
    weight = W_trimmed$weights,
    treat  = factor(treat)
  )

# トリミング後の重みの箱ひげ図を再描画して確認
ggplot(df_plot_trimmed, aes(x = treat, y = weight, fill = treat)) +
  geom_boxplot(alpha = 0.7, outlier.color = "red") + 
  labs(title = "Trimmed IPTW Weights", x = "Treatment Group", y = "Weights") +
  theme_minimal()

トリミングを実行することで巨大すぎた重みが抑制され、箱ひげ図上の極端な外れ値が解消される。

Step 5: 連続共変量(年齢など)の調整前後バランスを箱ひげ図で比較する

トリミングを終えた綺麗な重み(weight)を使って、特定の共変量(例: age)の分布が重み付けによってどう揃ったかを確認する。ggplot2 の箱ひげ図では weight = weight 引数を渡すことで調整後の分布を描写できる。

# 調整前(Unadjusted)の箱ひげ図
p_unadj <- ggplot(df_plot_trimmed, aes(x = treat, y = age, fill = treat)) +
  geom_boxplot(alpha = 0.6) +
  labs(title = "Unadjusted (Age)", x = "Group", y = "Age") +
  theme_minimal() + theme(legend.position = "none")

# 調整後(IPTW Adjusted)の箱ひげ図(weight引数を指定)
p_adj <- ggplot(df_plot_trimmed, aes(x = treat, y = age, fill = treat, weight = weight)) +
  geom_boxplot(alpha = 0.6) +
  labs(title = "IPTW Adjusted (Age)", x = "Group", y = "Age") +
  theme_minimal() + theme(legend.position = "none")

# patchwork パッケージで左右に並べて表示
p_unadj + p_adj

「調整前(左)の群間差」と「調整後(右)に分布の中央値や広がりが綺麗に重なり合った様子」を左右に並べることで、背景因子の調整プロセスを視覚的・説得力をもって論文の付録等でアピールできる。

>>もう統計で悩むのは終わりにしませんか? 

↑1万人以上の医療従事者が購読中

【まとめ】コピペで動く!重み確認からトリミング・箱ひげ図描画の一括コード

本記事で解説した「重み算出 ➔ 箱ひげ図確認 ➔ 数値確認 ➔ トリミング ➔ 共変量バランス確認」の一連のパイプラインコードを以下にまとめた。自身の手元のデータセット名・変数名に置き換えて活用してほしい。

# === IPTW解析における「箱ひげ図」確認・外れ値処理テンプレート ===
library(tidyverse)
library(WeightIt)
library(patchwork)

# 1. IPTW重みの計算
W <- weightit(
  treat ~ age + bmi, # 治療変数 ~ 共変量
  data     = df_raw,
  method   = "glm",
  estimand = "ATE"
)

# 2. 【可視化】重み(Weights)の箱ひげ図チェック
df_plot <- df_raw %>% mutate(weight = W$weights, treat = factor(treat))
ggplot(df_plot, aes(x = treat, y = weight, fill = treat)) +
  geom_boxplot(alpha = 0.7, outlier.color = "red") + 
  geom_jitter(width = 0.1, alpha = 0.2) +
  theme_minimal()

# 3. 【数値確認】summary(W) で最大値や上位%点のチェック
summary(W)

# 4. 【外れ値処置】上位 1% でトリミング処理
W_clean <- trim(W, at = 0.99)
df_clean <- df_raw %>% mutate(weight = W_clean$weights, treat = factor(treat))

# 5. 【効果確認】連続共変量(例: age)の調整前後バランスを箱ひげ図で左右比較
p1 <- ggplot(df_clean, aes(x = treat, y = age, fill = treat)) +
  geom_boxplot(alpha = 0.6) + labs(title = "Unadjusted") + theme_minimal()

p2 <- ggplot(df_clean, aes(x = treat, y = age, fill = treat, weight = weight)) +
  geom_boxplot(alpha = 0.6) + labs(title = "IPTW Adjusted") + theme_minimal()

p1 + p2 # 左右並べてプロット出力

IPTW解析を行う際は、単に P 値やハザード比を算出するだけでなく、事前に ggplot2 の箱ひげ図と summary(W) の数値で重みの分布と外れ値の有無を確認する習慣をつけておこう。

確実な前処理とチェックを行うことで、査読での突っ込みを防ぎ、信頼性の高い臨床研究論文を完成させることができる。

おすすめ書籍

できる! 傾向スコア分析: SPSS・Stata・R を用いた必勝マニュアル

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

リサーチクエスチョン探し?データ分析?論文投稿?、、、で、もう悩まない!

第1章臨床研究ではなぜ統計が必要なのか?計画することの重要性
  • 推定ってどんなことをしているの?
  • 臨床研究を計画するってどういうこと?
  • どうにかして標本平均を母平均に近づけられないか?
第2章:研究目的をどれだけ明確にできるのかが重要
  • データさえあれば解析でどうにかなる、という考え方は間違い
  • 何を明らかにしたいのか? という研究目的が重要
  • 研究目的は4種類に分けられる
  • 統計専門家に相談する上でも研究目的とPICOを明確化しておく
第3章:p値で結果が左右される時代は終わりました
  • アメリカ統計協会(ASA)のp値に関する声明で指摘されていること
  • そうは言っても、本当に有意差がなくてもいいの…?
  • なぜ統計専門家はp値を重要視していないのか
  • 有意差がない時に「有意な傾向があった」といってもいい?
  • 統計を放置してしまうと非常にまずい
第4章:多くの人が統計を苦手にする理由
  • 残念ながら、セミナー受講だけで統計は使えません。
  • インプットだけで統計が使えない理由
  • どうやったら統計の判断力が鍛えられるか?
  • 統計は手段なので正解がないため、最適解を判断する力が必要
第5章:統計を使えるようになるために今日から何をすれば良いか?
  • 論文を読んで統計が使えるようになるための5ステップ
第6章:統計を学ぶために重要な環境
  • 統計の3つの力をバランスよく構築する環境

以下のボタンをクリックして、画面に出てくる指示に従って、必要事項を記入してください。

この記事を書いた人

医師、医学博士(専門は疫学および統計学)。10年以上の研究歴を有し、筆頭著者として10本の英文原著論文を執筆した実績を持つ。現在、外資系製薬企業にて15年以上にわたり、臨床開発やデータ解析、承認審査、薬価交渉戦略といった実務の最前線に従事。

「統計に悩むあなたを救いたい」をコンセプトに、ブログ「統計ER」やYouTube、SNSを通じて、医療従事者や研究者向けに「実務直結の統計ノウハウ」を配信。

学術的な厳密さと、製薬業界での高度な実務経験を融合させた独自の視点から、ブラックボックス化しない統計解析の普及を目指す。オンラインショップ「TKER SHOP」での計算ツール提供や、クラウドワークス等を通じた統計コンサルティング・解析代行(主にRを使用)も幅広く展開中。

コメント

コメントする

目次