觀察性研究的因果推論

給臨床醫師的實戰指南
Causal Inference from Observational Studies: A Practical Guide for Clinicians

林協霆

和信治癌中心醫院腫瘤內科部

引言

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

演講主題

觀察性研究的因果推論:給臨床醫師的實戰指南

Causal Inference from Observational Studies: A Practical Guide for Clinicians

今天你會學到什麼?

  1. 為什麼:觀察性研究為何不能直接下因果結論?
  2. 怎麼想:如何設計研究問題?(Target Trial)
  3. 怎麼做:用什麼方法分析?(TMLE + Super Learner)
  4. 怎麼驗證:如何確認結果可信?(敏感度分析)

課程地圖

階段 內容 重點
1-2 問題與設計 為什麼需要?怎麼設計?
3-4 方法概念 PS → IPW → 雙重穩健 → TMLE
5-6 準備工作 選方法、備資料
7 R 實作 程式碼示範
8-9 驗證報告 E-value、論文寫作

第一部分:為什麼需要因果推論?

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

開場情境:一個「成功」的研究

某醫學中心回顧性研究報告

「新藥 DrugX 顯著提升晚期癌症病人存活率」

研究結果看起來很棒!

組別 人數 一年存活率 p-value
新藥組 500 70%
傳統治療 500 50% < 0.001

統計顯著、效果量大、樣本數夠…

可以發 paper 了嗎?

等等,讓我們看看病人特徵…

新藥組

  • 平均年齡:55 歲
  • ECOG 0-1:85%
  • 共病指數:1.2

傳統治療組

  • 平均年齡:68 歲
  • ECOG 0-1:45%
  • 共病指數:3.8

😱 兩組根本不一樣!

混淆效應視覺化:Simpson’s Paradox

觀察:整體看起來新藥好很多,但分層後差異變小!

真相大白

  • 醫師傾向開新藥給「狀況較好」的病人
  • 新藥組本來就會活比較久
  • 70% vs 50% 的差異,有多少是藥效?有多少是選擇偏誤?

核心問題

觀察到的差異 ≠ 因果效應

關聯 vs 因果

  • 描述性問題:「用新藥的人和沒用的人,結果有沒有不同?」
  • 因果性問題:「如果讓同一群人用新藥 vs 不用,結果會不會不同?」
  • 核心困難:反事實 (counterfactual) 永遠觀察不到

反事實:我們永遠只看到一半

因果推論的核心問題:我們想知道「?」是什麼,但永遠無法直接觀察

林俊傑 - 可惜沒如果

倘若那天

把該說的話好好說 該體諒的不執著

如果那天我 不受情緒挑撥 你會怎麼做

那麼多如果 可能如果我

可惜沒如果 ( Counterfact ) 只剩下結果 (Outcome)

  • Y(1):在「把話好好說、體諒但不執著、不受情緒挑撥」這種處置(treatment)發生的結果
    • (JJ 跟 Angelababay 修成正果)。
  • Y(0):在實際發生(JJ當天的真實行為與狀態)所得到的結果
    • ( 沒有跟 Aneglababy 在一起,詳見 MV )。

經典謬誤案例

觀察到的關聯 真正的原因
🍦 冰淇淋銷量 ↑ → 溺水死亡 ↑ 夏天(天氣熱)
🏥 住院病人死亡率 > 在家 病重才住院
👟 鞋子尺寸大 → 閱讀能力強 年齡(小孩長大)
🌡️ 黃色手指 → 肺癌風險 ↑ 抽菸

這些都是「混淆」造成的假象!

臨床常見陷阱

  • 用 statin 的人心血管事件少 → 但他們本來就比較注重健康
  • 做篩檢的人存活較久 → Lead-time bias + 健康者偏誤
  • ICU 用某藥的病人死亡率高 → 因為病情嚴重才用
  • 術後早期下床的病人恢復快 → 恢復好的才能下床

關鍵思考

是「治療 → 結果」,還是「某因素 → 治療 & 結果」?

混淆的問題

為什麼直接比較會有偏誤?

醫師傾向開新藥給較輕症的病人

→ 新藥組本來就會比較好

digraph {
    rankdir=TB
    node [shape=box, width=1.2, fontname="jf-openhuninn-2.1"]
    edge [fontname="jf-openhuninn-2.1"]
    C [label="病人嚴重度"]
    A [label="治療選擇"]
    Y [label="結果"]
    C -> A
    C -> Y
    A -> Y [style=dashed, label="?"]
}

小結:為什麼需要因果推論?

  1. 觀察性資料中,關聯 ≠ 因果
  2. 混淆因子會讓我們得到錯誤的結論
  3. 我們需要特殊方法來「模擬」隨機分配的效果

💭 思考時間

想一想你自己的研究

  1. 你想比較的「治療」是什麼?
  2. 你的資料是觀察性的嗎?
  3. 有哪些因素可能同時影響「誰接受治療」和「結果好壞」?

把這些因素寫下來,等一下我們會用 DAG 來整理它們

本章輸出

下一步:那要怎麼設計研究,才能回答因果問題呢?

第二部分:研究設計思維

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

在開始分析之前,你需要先想清楚:

  • 你到底想回答什麼問題?
  • 如果可以做 RCT,你會怎麼設計?
  • 哪些變數該調整,哪些不該調整?

流程位置

承接 本章目標 下一步
你已知道「混淆」讓結果不可信 把問題寫成 Target Trial,用 DAG 分清變數角色 學會用方法處理混淆(PS / IPW)

Target Trial Emulation

核心概念:「如果能做 RCT,你會怎麼設計?」

用觀察性資料模擬那個理想試驗

為什麼需要 Target Trial?

  • 很多重要問題 無法 做 RCT(倫理、成本、時間)
  • 觀察性研究常犯的錯:沒有明確的研究設計
  • Target Trial 強迫你思考:「我到底在問什麼問題?」

“If you can’t describe the trial you’re trying to emulate, you probably shouldn’t be doing the analysis.” — Miguel Hernán

RCT vs 觀察性研究

步驟 RCT Target Trial Emulation
定義資格條件 Protocol 寫好 你要自己定義
治療分配 隨機 觀察(需調整混淆)
Time zero 隨機分配時 你要明確定義
追蹤 Protocol 規定 資料庫限制
分析 ITT / Per-protocol 模擬 ITT / Per-protocol

關鍵差異:RCT 的隨機化幫你處理混淆,觀察性研究要自己處理

Target Trial 實例

研究問題:Statin 是否降低心血管死亡?

理想的 RCT

  • 納入:40-75歲、高血脂
  • 隨機分配 statin vs placebo
  • 追蹤 5 年
  • 主要結局:CV 死亡

用健保資料模擬

  • 納入:同條件、首次診斷高血脂
  • 比較:開始用 statin vs 未使用
  • Time zero:診斷日
  • 追蹤至 CV 死亡或 5 年

Target Trial 七要素

要素 你要定義的
資格條件 誰可以納入?
治療策略 新藥 vs 什麼?
治療分配 病人如何被分組(觀察性)
追蹤起點 Time zero 是什麼時候?
結果 主要 outcome
追蹤期間 追多久?
因果對比 要估計什麼?ATE?

分析語言對照(Y / A / W)

把臨床語言轉成分析語言,後面寫程式會更順:

符號 對應 臨床例子
Y 結果 死亡、住院、血壓變化
A 治療 新藥 vs 標準治療
W 混淆因子 年齡、共病、嚴重度

一句話:先定好 Y/A/W,你就知道要叫 AI 做什麼。

延伸:每次下 prompt 都用 資料 → 角色 → 方法 → 估計量 → 診斷 → 解讀 的 6 要素架構。

⚠️ 常見陷阱:Immortal Time Bias

不死時間偏差

病人必須「活到某時間點」才被歸類為治療組,導致治療組看起來比較好

錯誤設計

  • 住院第 3 天開始用藥 → 歸為治療組
  • 但前 3 天是「不死時間」
  • 治療組先天存活優勢!

正確做法

  • Time zero = 符合條件的時間點
  • 所有人從同一起點開始追蹤
  • 或用時間變動模型處理

DAG 因果圖(簡化版)

三種變數

  • 混淆因子 (Confounder):該調整
    • 年齡:影響用藥選擇,也影響預後
    • 共病:醫師考量共病決定治療,共病也影響結果
  • 中介變數 (Mediator):不該調整
    • 血壓下降:降壓藥 → 血壓↓ → 心血管事件↓
    • 血糖控制:糖尿病藥 → HbA1c↓ → 併發症↓
  • 碰撞變數 (Collider):不該調整
    • 住院:疾病嚴重 → 住院 ← 治療副作用
    • 存活到某時點:好體質 → 存活 ← 有效治療

DAG 視覺化:三種結構

關鍵:用 DAG 想清楚變數之間的關係,再決定調整什麼

小結:研究設計思維

  1. Target Trial 框架定義你的研究問題
  2. DAG 畫出變數之間的因果關係
  3. 只調整 混淆因子,不調整中介變數和碰撞變數
  4. 確認三大假設是否合理

✏️ 練習時間

試著為你的研究畫 DAG

  1. 寫出你的暴露 (A) 和結果 (Y)
  2. 列出可能的混淆因子
  3. 用箭頭連接它們
  4. 標記哪些該調整、哪些不該調整

工具:可以用紙筆,或用 DAGitty 線上畫

本章輸出

下一步:設計好了,那具體要用什麼統計方法呢?

第三部分:方法概念(上)基礎篇

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

現在我們知道要調整混淆因子,但怎麼調整

  • 傳統方法有什麼問題?
  • 什麼是「傾向分數」?
  • 為什麼推薦「雙重穩健」方法?

流程位置

承接 本章目標 下一步
你已定義研究問題與變數角色 理解 PS/IPW 如何讓兩組可比 學雙重穩健與 TMLE

傾向分數 (Propensity Score)

白話:「根據病人特徵,預測他接受治療的機率」

用途:讓兩組變得可比較

PS 是怎麼算出來的?

核心:PS 把多個共變量壓縮成一個分數,代表「這個人被治療的機率」

Demo

重疊區域:兩組可比較的範圍

傳統方法的問題

方法 做法 風險
單純迴歸調整 把混淆因子丟進模型 模型設錯就完了
PS matching 用 PS 配對 丟掉很多樣本
PS weighting (IPW) 用 PS 加權 極端權重不穩定

名詞解釋

  • PS matching:找 PS 相近的治療組與對照組病人配對比較
  • IPW (Inverse Probability Weighting):用 1/PS 當權重,讓兩組「人工平衡」

⚠️ 為什麼不推薦單獨用 IPW?

IPW 的問題

當 PS 接近 0 或 1 時:

  • 權重 = 1/PS 會爆炸
  • 少數人主導整個估計
  • 方差巨大、估計飄動

實際情況

幾百例的臨床資料:

  • 很常遇到重疊不佳
  • 某些病人 PS 很極端
  • IPW 會非常不穩定

結論

IPW 可以當敏感度分析,但不建議當主分析

IPW 權重的危險:極端值

問題:少數極端權重的人,主導了整個估計結果!

到目前為止…

我們學到什麼?

  1. PS:預測誰會接受治療
  2. IPW:用 1/PS 加權讓兩組可比
  3. 問題:IPW 權重會爆炸、不穩定

本章輸出

下一步:有沒有更穩健的方法? → 雙重穩健估計

第四部分:方法概念(下)進階篇

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

基礎方法(PS、IPW)有其限制,現在來學更穩健的方法:

  • 雙重穩健估計:兩個模型,錯一個也沒關係
  • TMLE:最推薦的雙重穩健方法
  • Super Learner:自動選最佳模型組合

流程位置

承接 本章目標 下一步
你已了解 PS/IPW 的優點與限制 掌握雙重穩健、TMLE、Super Learner 依 outcome/estimand 選方法與套件

雙重穩健估計(核心概念)

什麼是「雙重穩健」(Doubly Robust)?

同時用兩個模型來估計因果效應,只要其中一個模型正確,結果就不會有偏誤。就像買兩份保險!

  • 同時建兩個模型:PS 模型 + 結果模型
  • 好處:只要其中一個對,結果就對
  • 「買保險」的概念

TMLE vs AIPW

名詞解釋

  • TMLE (Targeted Maximum Likelihood Estimation):針對「你想估計的東西」做最佳化的雙重穩健方法
  • AIPW (Augmented IPW):在 IPW 基礎上加入結果模型修正的雙重穩健方法

共同點

  • 都是雙重穩健

TMLE 優勢

  • 多一個 targeting 步驟
  • 更穩定

實務上:選 TMLE 就對了

雙重穩健:「錯一個也沒關係」

關鍵優勢:只有當「兩個模型都錯」時才會有偏誤 → 大大降低風險

但問題來了…

雙重穩健的前提

「至少一個模型要對」— 但我們怎麼確保?

傳統做法

  1. 手動選一個模型(如 logistic)
  2. 假設函數形式正確
  3. 祈禱沒選錯

問題

  • 真實關係可能是非線性的
  • 可能有交互作用
  • 選錯了 → 兩個模型都錯 → 偏誤!

解法:讓資料自己決定用哪個模型 → Super Learner

Super Learner(一句話解釋)

什麼是 Super Learner?

一種「模型組合」技術:同時跑多個機器學習模型,用交叉驗證找出最佳加權組合。

  • 不用手動選模型,讓演算法自動找最佳組合
  • 大幅降低「兩個模型都錯」的機率
  • 這就是為什麼現代因果推論都用 TMLE + Super Learner

白話總結

Super Learner 就像「模型評審團」:讓多個模型各自預測,然後根據表現分配權重。你不用煩惱該選哪個模型,讓資料說話!

Super Learner 庫的選擇

小樣本原則

庫要小且保守,不要追求複雜!

推薦的候選模型

  • SL.glm:標準 logistic/線性
  • SL.glmnet:正則化(ridge/lasso)
  • SL.gam:平滑非線性
  • SL.ranger:(事件數夠才加)

小樣本要避免

  • 深樹 random forest
  • 大規模 boosting
  • 複雜超參數搜尋
  • 太多候選模型

Super Learner 庫:實例選擇

情境 事件數 推薦的庫
罕見疾病 RCT 30-50 SL.glm, SL.glmnet
一般臨床研究 50-200 SL.glm, SL.glmnet, SL.gam
大型健保資料庫 500+ 上述 + SL.ranger, SL.xgboost
超大資料 5000+ 可加更多彈性模型

經驗法則

事件數 ÷ 10 ≈ 可用的候選模型數上限

使用 ML 模型的隱憂

問題

Super Learner 用了彈性的機器學習模型,但這會帶來一個統計問題…

傳統迴歸

  • 模型簡單、參數少
  • 用同一筆資料擬合 + 推論 → OK

機器學習模型

  • 模型複雜、容易過擬合
  • 用同一筆資料擬合 + 推論 → 標準誤會偏小、信賴區間不可靠

白話說:模型「記住」了資料的噪音,導致我們對效果估計過度自信

PS 截斷(Truncation)

問題

PS 太接近 0 或 1 時,權重會爆炸

解法:把 PS 限制在合理範圍

# 常見截斷範圍
ps_truncated <- pmax(0.01, pmin(0.99, ps)) # 寬鬆
ps_truncated <- pmax(0.05, pmin(0.95, ps)) # 保守

重要提醒

截斷範圍要預先宣告,不要事後調參(避免 p-hacking 嫌疑)

小結:方法概念

到目前為止,我們學了:

概念 重點
傾向分數 (PS) 預測接受治療的機率,讓兩組可比
雙重穩健 兩個模型,錯一個也沒關係
TMLE 最推薦的雙重穩健方法
Super Learner 自動選最佳模型組合

本章輸出

下一步:不同類型的結果變數,要用什麼方法?

第五部分:情境選擇菜單

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

你的研究結果是什麼類型?

  • 連續變數?(血壓、HbA1c)
  • 二元變數?(死亡 Y/N)
  • 存活時間?(死亡時間)

不同類型 → 不同方法 → 不同 R 套件

流程位置

承接 本章目標 下一步
你已掌握 TMLE / Super Learner 的概念 依 outcome 與 estimand 做方法選擇 準備資料與品質檢查

一張表搞定方法選擇

Outcome 類型 範例 估計什麼 R 套件
連續 血壓、HbA1c 平均差 tmle
二元 死亡 Y/N Risk Diff / RR tmle
存活 死亡時間 Survival diff survtmle
計數 住院次數 Rate Ratio WeightIt
重複測量 多時間點追蹤 軌跡差異 geepack

Estimand 的選擇

名詞解釋

  • Estimand:你想估計的「目標量」,要先定義清楚才能選方法
  • ATE (Average Treatment Effect):如果讓「所有人」都接受治療 vs 都不接受,平均差異是多少?
  • ATT (Average Treatment Effect on the Treated):對於「實際接受治療的人」,治療效果是多少?

ATE vs ATT:什麼時候用哪個?

ATE(全體平均效果)

  • 問:「如果政策推廣給所有人」
  • 需要兩組充分重疊
  • 樣本小、重疊差時很不穩定

ATT(已治療者效果)

  • 問:「對已經用這治療的人」
  • 只需對照組能代表治療組
  • 重疊不佳時更實際可估

實務建議

幾百例的臨床資料,優先考慮 ATT,除非重疊非常好

小結:選擇方法

  1. 先確定你的 結果類型(連續/二元/存活/計數)
  2. 再決定你要估計的 目標量(ATE/ATT)
  3. 然後選擇對應的 R 套件

🎯 確認你的選擇

你的研究設定

根據剛才學的,確認一下:

  1. 你的結果變數是什麼類型?
    • 連續(血壓)/ 二元(死亡)/ 存活(時間到事件)
  2. 你要估計 ATE 還是 ATT?
    • 重疊好 → ATE / 重疊差 → ATT
  3. 對應的 R 套件是?
    • tmle / survtmle / 其他

本章輸出

下一步:方法選好了,資料要怎麼準備?

第六部分:資料準備

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

在跑分析之前,資料要先整理好:

  • 資料格式長什麼樣?
  • 哪些變數要放進去?
  • 有哪些常見的陷阱?

流程位置

承接 本章目標 下一步
你已選好方法與 estimand 把資料整理成可分析的格式並完成品質檢查 把整個流程用 R 跑一次

原始資料

你的 Excel 可能長這樣:

id 治療 年齡 性別 ECOG 共病數 追蹤時間 事件 備註
001 新藥 58 1 2 365 1
002 標準 62 0 1 420 0 轉院
003 新藥 71 2 3 180 1
004 標準 55 1 730 0 共病數遺漏
005 新藥 67 1 2 90 1 提早退出

要注意:每一列是一個病人、每一欄是一個變數、遺漏值要標示清楚

資料格式要求

觀察到什麼?

  • 治療組年齡較高、男性較多
  • 兩組 baseline 特徵不平衡

陷阱

  • 直接比較結果會有偏誤
  • 需要調整混淆因子
  • 這就是為什麼需要因果推論方法!

資料品質檢查(比模型更重要!)

在跑任何分析之前,先做這三件事

  1. 缺失值處理:不要默默刪除!
  2. 重疊檢查:兩組是否可比?
  3. 事件數評估:樣本夠不夠穩定?

缺失值處理

常見錯誤

直接刪除有缺失的個案(listwise deletion),假裝沒事

缺失機制 意思 處理方式
MCAR 完全隨機缺失 刪除還可以(但浪費資料)
MAR 可觀察變數可解釋 多重插補 (Multiple Imputation)
MNAR 缺失本身有意義 敏感度分析

建議:用 mice 套件做多重插補,在每套插補資料內分析再合併

重疊檢查(Overlap / Positivity)

  • 兩組 PS 分布大量重疊
  • 可以估計 ATE
  • 兩組 PS 幾乎不重疊
  • 應改估 ATT 或限制分析範圍

重疊不好怎麼辦?

方法 說明 適用情境
改估 ATT 只估計「治療組」的平均效果 對照組有很多極端 PS
Trimming 排除 PS < 0.1 或 > 0.9 的個案 兩端都有極端值
Overlap weights 給重疊區域較高權重 想保留所有樣本

實務建議

  • 先畫 PS 分布圖,判斷重疊程度
  • 如果對照組 PS 普遍很低 → 改估 ATT
  • 如果兩端都有極端值 → Trimming 或 Overlap weights
  • 報告時說明你選擇的理由!

事件數評估

經驗法則

二元結局時,事件數 < 50 會讓任何高自由度模型都很不穩定

事件數 建議
< 30 極度保守:只調整 2-3 個最重要混淆因子
30-50 保守:用正則化模型、減少變數
50-100 標準分析可行,但 Super Learner 庫要小
> 100 可以用較彈性的模型

常見資料問題總表

問題 解法
遺漏值 Multiple imputation(先處理)
混淆因子放錯時間點 只能用 baseline 的
Time zero 不明確 仔細定義追蹤起點
治療定義模糊 明確定義什麼算「接受治療」
重疊不佳 改 ATT、trimming、或限制分析範圍
事件數太少 減少調整變數、用正則化

存活資料格式

df_surv <- data.frame(
  id = ...,
  treatment = ...,
  time = ..., # 追蹤時間
  event = ..., # 1=事件,0=設限
  # 混淆因子...
)

本章輸出

下一步:資料準備好了,來看實際的 R 程式碼!

R 快速入門

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

給不熟 R 的人

如果你已經會 R

可以跳過這一段,直接進入實作!

這一段會快速介紹:

  • 如何安裝需要的套件
  • 基本語法
  • 讀取資料

流程位置

承接 本章目標 下一步
你已完成方法選擇與資料準備 補足 R 基礎,確保能跟上實作 用一致的 6 要素架構跑完整分析

安裝 R 和 RStudio

  1. 下載 R:https://cran.r-project.org/
  2. 下載 RStudio:https://posit.co/download/rstudio-desktop/

建議

Positron 是現代寫 R 最方便的介面,強烈建議使用

安裝套件

# 只需執行一次
install.packages(c(
  "tmle",
  "SuperLearner",
  "cobalt",
  "WeightIt",
  "ggplot2",
  "dplyr"
))

# 每次使用前載入
library(tmle)
library(SuperLearner)

提示:安裝可能需要幾分鐘,請耐心等待

R 基本語法

# 賦值(建立變數)
x <- 10
name <- "治療組"

# 向量(多個值)
ages <- c(45, 52, 67, 38)
mean(ages) # 計算平均

# Data frame(表格)
df <- data.frame(
  id = 1:4,
  age = ages,
  treatment = c(1, 1, 0, 0)
)

讀取資料

# 從 CSV 檔讀取
df <- read.csv("data/08_r-basics/patients_basic.csv")

# 查看資料結構
head(df) # 前 6 筆
str(df) # 變數類型
summary(df) # 基本統計

Pipe 運算子

# 傳統寫法(難讀)
result <- select(filter(df, age > 50), treatment, outcome)

# Pipe 寫法(易讀)
result <- df |>
  filter(age > 50) |>
  select(treatment, outcome)

白話說|> 就是「然後」的意思,把左邊的結果傳給右邊

本次會用到的套件

套件 用途
tmle TMLE 估計(連續/二元結果)
survtmle TMLE 估計(存活結果)
SuperLearner 模型組合
cobalt 平衡診斷
WeightIt 計算傾向分數權重
ggplot2 畫圖

本章輸出

準備好了?讓我們開始實作!

第七部分:R 實作示範

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

終於要寫 code 了!這一節會示範:

  • 連續結果:用 tmle 套件
  • 二元結果:用 tmle 套件
  • 存活結果:用 survtmle 套件
  • 平衡診斷:確認調整有效

流程位置

承接 本章目標 下一步
你已準備好資料與 R 基礎 用 6 要素架構把分析跑完整 檢驗未測量混淆(敏感度分析)

Prompt 結構:6 要素(比語法更重要)

每次請 AI 寫程式,先把這 6 件事講清楚:

  1. 資料描述:檔案路徑、樣本數、資料型態
  2. 角色對應Y(結果)、A(治療)、W(混淆因子)
  3. 方法選擇:TMLE / IPW / Cox
  4. 估計量:ATE / RD / RR / HR
  5. 診斷需求:SMD、重疊、權重、ESS
  6. 輸出格式:表格 + 一句臨床解讀

最小可跑 Prompt 模板

資料在 data/xxx.csv,共有 N 筆。
Y 是 ______(連續/二元/存活),A 是 ______,W 包含 ______。
請用 ______ 方法估計 ______(ATE/RD/RR/HR),並回報:
1) 主要估計值與 95% CI
2) 平衡診斷(SMD < 0.1)
3) 權重診斷(最大權重、有效樣本量)
最後用一句話解讀臨床意義。

口訣:資料 → 角色 → 方法 → 估計量 → 診斷 → 解讀

完整分析流程:5 步驟視覺化

從資料到結論:每一步都要檢查,不是跑完程式就結束!

連續結果:6 要素架構

要素 這個例子
資料 data/09_r-workflow/df_continuous.csv
角色 Y=血壓變化(連續),A=treatmentW=年齡/性別/共病/嚴重度
方法 TMLE
估計量 ATE
診斷 SMD、PS 重疊、權重、ESS
輸出 估計值+95% CI + 一句臨床解讀

Vibe Coding: 連續結果

請 AI 幫你完成這段分析,你可以這樣說:

「請讀取 data/09_r-workflow/df_continuous.csv,這是一個降血壓藥物的觀察性研究資料。治療變數是 treatment,結果是 outcome(血壓變化,mmHg),混淆因子有 age, sex, comorbidity, severity。請用 TMLE 方法估計平均治療效果(ATE),並使用 Super Learner 組合 GLM 和 GLMNet 模型。」

練習 prompt(請改成你的研究題目)

資料在 data/09_r-workflow/df_continuous.csv,共有 N 筆。
Y 是 ______(連續),A 是 ______,W 包含 ______。
請用 TMLE 估計 ATE,並回報估計值+95% CI、SMD、PS 重疊、最大權重、有效樣本量。
最後用一句話解讀臨床意義。

連續結果:程式碼

library(tmle)
library(SuperLearner)

SL.lib <- c("SL.glm", "SL.glmnet")

df <- read.csv("data/09_r-workflow/df_continuous.csv")

fit_cont <- tmle(
  Y = df$outcome,
  A = df$treatment,
  W = df %>% select(age, sex, comorbidity, severity),
  Q.SL.library = SL.lib,
  g.SL.library = SL.lib,
  family = "gaussian"
)

fit_cont$estimates$ATE

載入 tmleSuperLearner 套件,這是雙重穩健估計的核心工具。

定義 Super Learner 的候選模型庫:GLM(線性)+ GLMNet(正則化)。小樣本建議保守設定。

執行 TMLE:Y 是結果、A 是治療、W 是混淆因子。Q.SL.library 用於結果模型,g.SL.library 用於 PS 模型。

提取 ATE 估計值,包含點估計和 95% 信賴區間。

連續結果:執行結果

一句話臨床解讀(固定句型)

句型:治療組相較對照組,平均「血壓變化」差異為 ___ mmHg(95% CI ___ 到 );臨床上表示 ___。

解讀:治療組平均血壓下降比對照組多約 5.2 mmHg

Vibe Coding: 二元結果

請 AI 幫你完成這段分析,你可以這樣說:

「請讀取 data/09_r-workflow/df_binary.csv,這是一個癌症治療的觀察性研究資料。治療變數是 treatment,結果是 death(0/1),混淆因子有 age, sex, comorbidity, severity。請用 TMLE 方法估計 Risk Difference 和 Risk Ratio。」

二元結果:6 要素架構

要素 這個例子
資料 data/09_r-workflow/df_binary.csv
角色 Y=death(二元),A=treatmentW=年齡/性別/共病/嚴重度
方法 TMLE
估計量 RD, RR
診斷 SMD、PS 重疊、權重、ESS
輸出 RD/RR + 95% CI + 一句臨床解讀

練習 prompt(請改成你的研究題目)

資料在 data/09_r-workflow/df_binary.csv,共有 N 筆。
Y 是 ______(二元),A 是 ______,W 包含 ______。
請用 TMLE 估計 RD 與 RR,並回報估計值+95% CI、SMD、PS 重疊、最大權重、有效樣本量。
最後用一句話解讀臨床意義。

二元結果:程式碼

fit_bin <- tmle(
  Y = df$death,
  A = df$treatment,
  W = df %>% select(age, sex, comorbidity, severity),
  Q.SL.library = SL.lib,
  g.SL.library = SL.lib,
  family = "binomial"
)

fit_bin$estimates$ATE # Risk Difference
fit_bin$estimates$RR # Risk Ratio

二元結果只需改兩處:Y 改成 0/1 變數(如死亡),family 改成 "binomial"

二元結果可以提取 Risk Difference(風險差)和 Risk Ratio(相對風險)兩種 estimand。

二元結果:執行結果

一句話臨床解讀(固定句型)

句型:治療組相較對照組,死亡風險差為 (95% CI ),相對風險為 ;臨床上表示 ______。

解讀:治療降低死亡風險約 8.5%(RD = -0.085)

Vibe Coding: 存活結果

請 AI 幫你完成這段分析,你可以這樣說:

「請讀取 data/09_r-workflow/df_survival.csv,這是一個存活分析資料。治療變數是 treatment,追蹤時間是 time,事件指標是 event(1=死亡,0=設限),混淆因子有 age, sex, comorbidity, severity。請用 IPW 加權的 Cox 迴歸估計 Hazard Ratio。」

存活結果:6 要素架構

要素 這個例子
資料 data/09_r-workflow/df_survival.csv
角色 Y=Surv(time, event)A=treatmentW=年齡/性別/共病/嚴重度
方法 IPW + Cox(或 survtmle)
估計量 HR
診斷 SMD、PS 重疊、權重、ESS
輸出 HR + 95% CI + 一句臨床解讀

練習 prompt(請改成你的研究題目)

資料在 data/09_r-workflow/df_survival.csv,共有 N 筆。
YSurv(time, event)A 是 ______,W 包含 ______。
請用 IPW + Cox 估計 HR,並回報估計值+95% CI、SMD、PS 重疊、最大權重、有效樣本量。
最後用一句話解讀臨床意義。

存活結果:TMLE 方法

library(survtmle)

fit_surv <- survtmle(
  ftime = df$time,
  ftype = df$event,
  trt = df$treatment,
  adjustVars = df %>% select(age, sex, comorbidity, severity),
  t0 = 365,
  SL.ftime = SL.lib,
  SL.ctime = SL.lib,
  SL.trt = SL.lib,
  method = "hazard"
)

存活分析需要專用的 survtmle 套件。

ftime 是追蹤時間、ftype 是事件指標(1=事件,0=設限)、trt 是治療變數。

adjustVars 放混淆因子,t0 指定要估計的時間點(如 365 天)。

三個 Super Learner 庫分別用於:事件時間模型、設限時間模型、治療模型。

存活結果:IPW 替代方法

library(survival)
library(WeightIt)

# 計算 PS 權重
w <- weightit(
  treatment ~ age + sex + comorbidity + severity,
  data = df_surv,
  method = "ps"
)

# 加權 Cox 迴歸
fit_cox <- coxph(
  Surv(time, event) ~ treatment,
  data = df_surv,
  weights = w$weights
)

使用 survival 做存活分析,WeightIt 計算傾向分數權重。

weightit() 估計 PS 並計算 IPW 權重。

將權重帶入 coxph(),估計加權後的 Hazard Ratio。

存活結果:執行結果

一句話臨床解讀(固定句型)

句型:加權後 HR 為 (95% CI ),表示治療組的死亡風險約為對照組的 %;臨床上表示 ______。

解讀:加權後 HR = 0.72,表示治療組的死亡風險是對照組的 72%

Vibe Coding: 平衡診斷

請 AI 幫你完成這段分析,你可以這樣說:

「我已經用 WeightIt 計算了傾向分數權重。請用 cobalt 套件幫我檢查平衡:(1) 用 bal.tab() 列出所有變數的 SMD,閾值設 0.1;(2) 用 love.plot() 畫出加權前後的 SMD 比較圖。」

平衡診斷:6 要素架構

要素 這個例子
資料 已有加權物件 w
角色 A=treatmentW=欲平衡的共變數
方法 cobalt::bal.tab, love.plot
估計量 SMD、Love Plot
診斷 閾值 0.1、極端權重
輸出 SMD 表 + Love Plot + 一句解讀

練習 prompt(請改成你的研究題目)

我已經計算好權重物件 w
請用 cobalt 顯示 SMD 表(閾值 0.1)並畫 Love Plot;同時回報最大權重與有效樣本量。
最後用一句話說明平衡是否足夠。

平衡診斷:程式碼

library(cobalt)
library(WeightIt)

w <- weightit(
  treatment ~ age + sex + comorbidity + severity,
  data = df,
  method = "ps"
)

# SMD 檢查
bal.tab(w, thresholds = c(m = 0.1))

# 視覺化
love.plot(w, thresholds = c(m = 0.1))

cobalt 用於平衡診斷,WeightIt 用於計算傾向分數權重。

weightit() 計算權重:公式指定混淆因子,method = "ps" 使用 logistic regression 估計 PS。

bal.tab() 檢查 SMD,設定閾值 0.1,超過表示平衡不佳。

love.plot() 畫出 Love Plot,視覺化加權前後的 SMD 變化。

平衡診斷:執行結果

一句話臨床解讀(固定句型)

句型:加權後 SMD 皆 < 0.1(或指出未通過變數),表示兩組基線大致平衡/仍需調整 ______。

怎麼看這張表?

  • SMD(Standardized Mean Difference):衡量兩組差異的指標
  • 加權前:原始資料中兩組的差異(通常不平衡)
  • 加權後:IPW 調整後的差異(目標 < 0.1)
  • ✓ 通過:SMD < 0.1,表示該變數已平衡

平衡診斷:Love Plot

怎麼看 Love Plot?

  • 圓點:加權前的 SMD(通常偏右,不平衡)
  • 三角形:加權後的 SMD(目標在虛線左側)
  • 紅色虛線:SMD = 0.1 的閾值
  • 好的結果:所有三角形都在虛線左側

Love Plot:更多變數範例

目標:加權後所有 SMD < 0.1

PS 重疊檢查

ggplot(df, aes(x = ps, fill = factor(treatment))) +
  geom_density(alpha = 0.5) +
  labs(title = "Propensity Score Overlap")

權重診斷(很重要!)

為什麼要檢查權重?

極端權重 = 少數人主導估計 = 結果不可信

# 檢查權重分布
summary(w$weights)

# 最大權重(不要太大)
max(w$weights)

# 有效樣本量(不要掉太多)
sum(w$weights)^2 / sum(w$weights^2)

有效樣本量 (Effective Sample Size)

原始樣本量:500 人

加權後有效樣本量

\[ n_{eff} = \frac{(\sum w_i)^2}{\sum w_i^2} \]

解讀

  • \(n_{eff}\) ≈ 原始 N → 權重均勻,好
  • \(n_{eff}\) << 原始 N → 少數人主導,警告!

經驗法則

有效樣本量掉到原始的 50% 以下,要小心解讀結果

診斷清單(審稿者會看!)

診斷項目 目標 R 函數
SMD 全部 < 0.1 cobalt::bal.tab()
PS 重疊 充分重疊 密度圖
最大權重 不要太極端 max(weights)
有效樣本量 不要掉太多 公式計算

報告時務必呈現這四項!

小結:R 實作

完成分析後,你應該有:

項目 確認
TMLE 估計值 ✅ ATE 和 95% CI
平衡診斷 ✅ SMD < 0.1
PS 重疊 ✅ 兩組有足夠重疊

但是…就算一切都做對了,還有一個問題:未測量的混淆因子

✅ 檢查清單

你的分析有沒有漏掉這些?

在進入敏感度分析之前,確認:

如果有任何一項不通過,回去檢查資料或模型!

本章輸出

第八部分:敏感度分析

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

你可能會問:「如果有我沒測量到的混淆因子怎麼辦?」

這就是為什麼我們需要 敏感度分析

  • 量化:需要多強的未測量混淆才能推翻結論?
  • 工具:E-value(最簡單實用)

流程位置

承接 本章目標 下一步
你已完成主要估計與診斷 量化未測量混淆的影響 把結果寫成論文

為什麼需要敏感度分析?

  • 再好的方法都無法處理「未測量混淆」
  • 敏感度分析:量化「需要多強的未測量混淆才能推翻結論」

E-value(最簡單實用)

什麼是 E-value?

E-value 告訴你:「需要多強的未測量混淆因子,才能讓你的結果變成沒有效果」。數字越大,結果越穩健。

library(EValue)

# 二元結果(RR)
evalues.RR(1.5, lo = 1.2, hi = 1.9, true = 1)

# 存活結果(HR)
evalues.HR(0.7, lo = 0.5, hi = 0.9, true = 1)

# 連續結果(先轉換)
d <- abs(ate) / sd(Y)
rr <- exp(0.91 * d)
evalues.RR(rr, lo = rr_lo, hi = rr_hi, true = 1)

如何解讀 E-value

E-value = 2.5 表示:

  • 需要一個 RR ≥ 2.5(對 treatment)且 RR ≥ 2.5(對 outcome)的未測量混淆
  • 才能完全解釋掉觀察到的效果
  • 越大 = 越穩健
  • 經驗法則:E-value > 2 通常算不錯

E-value 視覺化

實心點 = E-value(點估計),空心點 = E-value(信賴區間下界)

小結:敏感度分析

  1. 敏感度分析是因果推論的必要步驟
  2. E-value 告訴你結果有多「穩健」
  3. E-value > 2 通常是合理的門檻

本章輸出

下一步:怎麼把這些結果寫成論文?

第九部分:結果報告

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

本節重點

分析做完了,怎麼寫進論文?

  • Methods 段落怎麼寫?
  • 需要哪些圖表?
  • Results 怎麼報告?

流程位置

承接 本章目標 下一步
你已完成主分析與敏感度分析 把分析轉成可投稿的 Methods/Results 總結與行動清單

Methods 怎麼寫

We estimated the average treatment effect using targeted maximum likelihood estimation (TMLE) with Super Learner for both propensity score and outcome models. The ensemble included logistic regression, random forest, and gradient boosting. Covariate balance was assessed using standardized mean differences (threshold < 0.1). Sensitivity to unmeasured confounding was evaluated using E-values.

必備圖表

圖表 內容
Table 1 Baseline characteristics(原始 + 加權後)
Figure 1 PS overlap plot
Figure 2 Love plot(SMD 平衡圖)
Figure 3 結果圖(forest plot / KM curve)
Table 2 主結果 + 95% CI + E-value

結果報告範例

The estimated risk difference was −5.2% (95% CI: −8.1% to −2.3%, p = 0.001), indicating the new drug reduced mortality by approximately 5 percentage points. All standardized mean differences were below 0.1 after weighting. The E-value was 2.8, suggesting that substantial unmeasured confounding would be required to explain away the observed effect.

本章輸出

總結

Agenda

  1. 引言

  2. 第一部分:為什麼需要因果推論?

  3. 第二部分:研究設計思維

  4. 第三部分:方法概念(上)基礎篇

  5. 第四部分:方法概念(下)進階篇

  6. 第五部分:情境選擇菜單

  7. 第六部分:資料準備

  8. R 快速入門

  9. 第七部分:R 實作示範

  10. 第八部分:敏感度分析

  11. 第九部分:結果報告

  12. 總結

流程位置

承接 本章目標 下一步
你已會報告 Methods/Results 總結重點,給出可直接執行的清單 帶回臨床決策的實務原則

小樣本的實務判準(不要踩雷!)

幾百例臨床資料的優先順序

穩定性 > 可估性 > 模型華麗程度

  1. 不要把「模型越能分出誰被治療」當優點 → 分太開 = 重疊差 = 推論不穩
  2. 不要把處置後變項塞進調整集合 → 除非你明確在估直接效果
  3. 不要只報一個方法 → 至少要有平衡、重疊、權重、敏感度分析
  4. 預先宣告 estimand 和截斷規則 → 避免 p-hacking 嫌疑

穩健性分析建議

做 2-3 個替代分析,確認結果穩定:

項目 替代設定
PS 模型 簡單(線性)vs 彈性(GAM/正則化)
截斷範圍 0.01/0.99 vs 0.05/0.95
方法 TMLE vs AIPW vs Matching
Estimand ATE vs ATT

如果結論只在某特定設定成立,必須在討論中承認

完整分析計畫範本

可以照抄的 checklist

  1. 定義 index time、處置 A、結局 Y、追蹤窗
  2. 用 DAG 決定混淆因子集合(全部 baseline)
  3. 多重插補處理缺失值
  4. 主 estimand:ATT(重疊充分才改 ATE)
  5. 主方法:TMLE + cross-fitting (2-fold)
  6. Nuisance:保守 Super Learner 庫 + PS 截斷 [0.01, 0.99]
  7. 報告:效應 + 95% CI、SMD、權重、重疊圖、E-value

觀察性研究因果推論流程

  1. Target Trial 設計:定義研究問題
  2. DAG 畫出因果假設:確定要調整的變數
  3. 資料準備:收集 baseline 混淆因子
  4. TMLE + Super Learner:雙重穩健估計
  5. 檢查:PS overlap + SMD < 0.1
  6. E-value 敏感度分析:評估結果穩健性

方法選擇速查

類型 方法
連續 tmle(..., gaussian)
二元 tmle(..., binomial)
存活 survtmle()
計數 IPW + Poisson

常見問題

問題 回答
樣本數要多少? 沒有絕對標準,但每組 > 100 較穩定
混淆因子要放幾個? 根據 DAG 決定,不是越多越好
Reviewer 不熟 TMLE? 附 PS matching 當 sensitivity analysis
PS 重疊很差怎麼辦? 考慮 trimming 或 overlap weights

延伸資源

  • Hernán & Robins “Causal Inference: What If”(免費線上)
  • tlverse 教學網站
  • Miguel Hernan 的 edX 課程

本章輸出

謝謝!