進階已經雙重審閱,尚未人工抽查

順序型邏輯迴歸與 shift plot

結果是分級而不是有無時(mRS、NYHA、CTCAE、疼痛分數),二分法會丟掉一半的訊息。common odds ratio 是什麼、proportional odds 是一個假設而不是結果、怎麼檢查它、以及格子人數太少時估計會怎麼壞掉。

分級的結果,被二分之後剩下什麼

臨床研究有一整類結果既不是連續的、也不是有無,而是有序的分級

  • mRS(modified Rankin Scale,改良 Rankin 量表)0–6,中風試驗的標準結果
  • NYHA 心衰竭功能分級 I–IV
  • CTCAE 不良事件嚴重度 1–5 級
  • 疼痛評分 0–10、咳嗽嚴重度 0–2、內視鏡分級、病理分期

這些分級的共同性質是有順序但沒有間距:mRS 從 2 進到 3 與從 4 進到 5,都叫「差一級」, 但那兩件事對病人的意義完全不同。所以既不能當成連續變項去算平均(間距沒有意義), 也不該直接二分成「好 vs 壞」(會把大量訊息丟掉)。

最常見的處理是二分:「mRS 0–2 算 good outcome」。這麼做的代價是 從 mRS 5 進步到 mRS 3 的病人,在分析裡與完全沒有進步的病人被算成同一件事。 中風試驗因此改用 shift analysis(位移分析),問的不是「多少人達標」, 而是「整個分布有沒有往好的方向移動」。它的統計工具就是這一頁的順序型邏輯迴歸 (ordinal logistic regression),而它報出來的那個數字,就是讀者最常撞到卻最少被解釋的 common odds ratio(共同勝算比)。

這一頁的例子

沿用群集與重複測量資料那一頁的甘草含漱液試驗 (medicaldata::licorice_gargle),但換一個結果:拔管當下的咳嗽嚴重度 extubation_cough,分成 0 = 沒有咳嗽、1 = 輕度、2 = 中重度 三級。 233 位病人有紀錄(2 位缺失)。

組別0 = 沒有咳嗽1 = 輕度2 = 中重度合計
對照組71(61%)30(26%)15(13%)116
甘草含漱組88(75%)25(21%)4(3%)117
Shift plot:兩條水平堆疊長條,各代表一組在三個咳嗽等級上的百分比分布,橫軸 0 到 100%。上方是對照組(n = 116):沒有咳嗽 61%、輕度 26%、中重度 13%。下方是甘草含漱組(n = 117):沒有咳嗽 75%、輕度 21%、中重度 3%。甘草組最淺色的「沒有咳嗽」區段明顯比對照組長,而最深色的「中重度」區段在甘草組縮成一條細長方塊。兩條長條都是從 0 排到 100% 的滿格堆疊,所以移動的不是整條棒子,而是段落之間的邊界:甘草組的兩道分隔線都往右移,較輕的等級因此吃掉更多長度。
兩組在三個等級上的完整分布。位移發生在兩個切點上:無咳嗽的比例上升 14.0 個百分點,中重度的比例下降 9.5 個百分點——以百分點看是前者較大,但以相對幅度看,中重度是掉了 74%。產圖腳本 figures/scripts/B2-13-ordinal.R

這張圖就是 shift analysis 的視覺形式。 它把「分布整體往哪邊移動」直接畫出來, 而不是先選一個切點再報一個比例。注意兩件事:無咳嗽的比例上升了 14 個百分點, 而中重度的比例從 13% 掉到 3%—— 後面這個變化的相對幅度大得多,但如果你只二分成「有沒有咳嗽」,它會完全消失。

模型長什麼樣

順序型邏輯迴歸最常用的形式是比例勝算模型(proportional odds model)。 它不是對每一個等級各配一個模型,而是對每一個切點同時建模。三個等級有兩個切點: 「≥ 1 vs 0」與「≥ 2 vs ≤ 1」。MASS::polr 是從每個切點的下側寫這個模型的, 下面印出來的結果就是這個形式:

logP(Yk)P(Y>k)=ζkβx,k=0,1\log \frac{P(Y \le k)}{P(Y > k)} = \zeta_k - \beta x, \qquad k = 0, 1

關鍵在於:每個切點有自己的截距 ζk\zeta_k,但共用同一個 β\beta。 截距吸收「這個等級本來有多常見」,斜率吸收「治療把整個分布推了多遠」。

本頁模型的兩個截距是 0.417(切在 0|1) 與 2.104(切在 1|2), 治療的係數是 -0.723(標準誤 0.283,t = -2.55)。

取指數並翻成「對照組相對於甘草組」的方向,得到 common OR 2.060(95% CI 1.18–3.59)

動手跑一次

library(medicaldata)
library(MASS)

lg <- licorice_gargle
d  <- subset(data.frame(y = lg$extubation_cough, treat = lg$treat), !is.na(y))

table(d$treat, d$y)                     # 先看格子有多空

fit <- polr(factor(y, ordered = TRUE) ~ factor(treat), data = d, Hess = TRUE)
summary(fit)
exp(-coef(fit))                         # common OR:對照組 vs 甘草組
exp(-rev(confint.default(fit)))         # 95% CI

# proportional odds:每個切點各配一個二元 logistic
summary(glm(I(y >= 1) ~ factor(treat), data = d, family = binomial))
summary(glm(I(y >= 2) ~ factor(treat), data = d, family = binomial))

# 因為只有一個二元共變項,2x3 表可以直接做飽和模型的概似比檢定
tb <- table(d$treat, d$y)
ll_sat <- sum(ifelse(tb > 0, tb * log(tb / rowSums(tb)), 0))
lrt <- 2 * (ll_sat - as.numeric(logLik(fit)))
c(statistic = lrt, p = pchisq(lrt, df = 1, lower.tail = FALSE))

# 格子太空會怎樣:換成 pacu30min_cough
ds <- subset(data.frame(y = lg$pacu30min_cough, treat = lg$treat), !is.na(y))
table(ds$treat, ds$y)
summary(polr(factor(y, ordered = TRUE) ~ factor(treat), data = ds, Hess = TRUE))
summary(glm(I(y >= 2) ~ factor(treat), data = ds, family = binomial))

驗證環境:R 4.6.0 + MASS 7.3.65 + medicaldata 0.2.0。polr() 不給 p 值,只給 t value;要 p 值請自己從常態近似算,或用 confint() 的 profile 區間。注意 polr 的模型寫法是 zeta - beta*x,所以係數的符號方向與一般 glm 相反。

假設怎麼檢查

最直覺、也最容易向臨床讀者解釋的檢查方式是:把每一個切點各配一個二元 logistic 迴歸, 把得到的 log OR 並排看。如果比例勝算假設在資料裡站得住,這些數字應該接近。

切點對照組事件數甘草組事件數OR(對照 vs 甘草)95% CIlog OR
切在 ≥ 1(任何咳嗽 vs 無)45 / 11629 / 1171.921.10–3.370.654
切在 ≥ 2(中重度 vs 其餘)15 / 1164 / 1174.201.35–13.051.434
順序型模型:兩個切點一起用2.061.18–3.590.723
森林圖,對數尺度的橫軸,三條估計。最上面是二分成「任何咳嗽(≥ 1)」的 OR 1.92(1.10–3.37)。中間是二分成「中重度(≥ 2)」的 OR 4.20(1.35–13.05),點估計明顯偏右,而且信賴區間長得多。最下面用不同顏色與菱形標記畫順序型模型的 common OR 2.06(1.18–3.59),落在兩個切點估計之間但比較靠近上面那一條。三條的信賴區間都沒有跨過 OR = 1 的垂直虛線。
順序型模型不挑切點,它同時使用兩個切點。common OR 落在兩個切點各自的估計之間,比較靠近資訊量較大的那一個。產圖腳本 figures/scripts/B2-13-ordinal.R

兩個切點的 log OR 相差 0.780, 換算成 OR 是 2.18 倍。看起來差滿多的——但這個「看起來」值多少錢, 要看不確定性有多大

因為本頁只有一個二元共變項,這張 2×3 表可以直接做精確的概似比檢定: 飽和模型有四個自由參數、比例勝算模型有三個,所以檢定有 1 個自由度。 結果是 χ² = 2.411,p = 0.121。

格子太空的時候

同一份資料換一個時點:PACU 30 分鐘的咳嗽 pacu30min_cough。這個變項的分布是

組別0 = 沒有咳嗽1 = 輕度2 = 中重度
對照組88244
甘草含漱組99180

全體只有 4 位病人達到中重度, 而且這 4 位全部落在對照組,甘草組是 0 位。這一格是空的。

等級多不等於資訊多

同一份資料的喉嚨痛評分 pacu30min_throatPain 有 7 個等級(0 到 6),比咳嗽的三級多得多。但它的次數分布是:

等級0123456
人數169201814912

73% 的人是 0 分,尾端有 2 個等級 的人數少於五位,最少的只有 1 位。等級數是量表設計的性質, 有效資訊量是資料的性質,兩者無關。 這種分布配順序型模型時,尾端的切點基本上是靠假設在支撐, 所以本頁不對它配模型——實務上會先合併尾端等級,或改用不需要比例勝算假設的做法。

違反比例勝算假設時怎麼辦

檢查發現各切點的 OR 明顯不同時,選項由簡到繁:

做法它在做什麼代價
照實報告各切點的 OR放棄用一個數字概括,把兩三個切點的估計並列沒有單一的效果量;多重比較的問題浮現
部分比例勝算模型(partial proportional odds)只讓違反假設的那個變項有各切點自己的係數,其餘共用要用 VGAMordinal 套件;解讀變複雜
多項式邏輯迴歸(multinomial logistic)完全放棄順序資訊,每個等級對參考等級各一組係數參數變多、檢定力下降,而且丟掉了「有順序」這個真實訊息
連續比例模型(continuation ratio)改問「已經到了 k 級的人,會不會再進到 k+1 級」回答的是不同的臨床問題,要確認那個問題才是你要的
無母數的位移檢定(Wilcoxon / van Elteren)只檢定分布有沒有位移,不估效果量沒有可以校正共變項的效果量可報

常見誤用

誤用為什麼錯
把分級當連續變項算平均與 t 檢定等級之間的間距沒有意義,平均數不可解讀
沒查比例勝算假設就報 common OR那個「common」是假設,不是結果
假設檢定不顯著就寫「假設成立」只能寫「未偵測到偏離的證據」;小樣本下檢定力很低
只看 p 值不看各切點估計的差距與區間p 值不顯著可能只是資訊不足,區間才看得出能排除多少
先試幾個切點再挑最顯著的二分選擇後的 p 值失效,是隱藏的多重比較
報 common OR 卻不寫方向要寫清楚是哪一組相對於哪一組、往哪個方向
稀疏格出現巨大 OR 仍照樣報完全分離時該估計值不存在,見邏輯迴歸
以為順序型模型能免疫於稀疏格它靠比例勝算假設借資訊,而該假設在空格處無法驗證
等級數多就以為資訊量大尾端每級一兩位病人時,那些切點是假設在撐
shift plot 只畫二分後的比例那就失去 shift analysis 的全部意義;要畫完整分布
拿 common OR 直接與二分後的 OR 比大小兩者定義的對比不同,數值不可互換
重複測量的分級結果直接配 polr同一個人多次測量不獨立,見群集與重複測量資料

相關頁面

重跑本頁的所有數字

/opt/homebrew/bin/Rscript figures/scripts/B2-13-ordinal.R

讀讀看這張圖

答案取自產生本頁圖表的同一份統計輸出,不是另外打上去的。

檢查比例勝算假設的做法,是在每個切點各配一個 logistic。cough 大於等於一這個切點得到的是哪一個數字,它要拿來跟什麼比?

看答案與解析

正確答案: 1.92——該切點的 odds ratio,拿來跟其他切點的 odds ratio 比

這個切點的 odds ratio 是 1.92,而檢查比例勝算假設要做的,是把各個切點的 OR 排在一起看它們差多少——假設說的正是「每一個切點的 OR 都相同」。1.10 與 3.37 是同一個 OR 的信賴區間兩端,不是另外的估計。跟模型的共同 OR 比也不對:共同 OR 是在假設成立的前提下算出來的,用它當基準等於預設了要檢查的東西。差太多的時候,那個共同 OR 不代表任何一個切點,而模型仍然只會印出一個數字。

在那個稀疏的結果變項上,cough 大於等於二這個切點的 licorice 組事件數是零。模型會怎麼反應?

看答案與解析

正確答案: 有 0 個事件就是完全分離,估計被推向無限大,而模型照樣印出一個數字

有一格是零就是完全分離:那個切點的 log odds ratio 被推到十幾、信賴區間上界跑到無限大,而模型不會停下來,它照樣印出一個看起來正常的數字。4 是對照組在同一個切點的事件數——另一組有資料並不能救回來,分離的條件是任何一格為零。18 是另一個切點上 licorice 組的事件數,模型不會因為別的切點健康就改用它。看到一個大得離譜的 OR 配上一個大得離譜的信賴區間,先去數那張表的格子。

throat pain 有七個等級,其中兩個等級的人數少於五人。這對比例勝算模型有什麼影響?

看答案與解析

正確答案: 有 2 個等級人數過少,那些切點幾乎沒有資料可用

兩個等級的人數少於五人,比例勝算模型在那些切點上幾乎沒有資料可用,而它仍然會給出一個看起來很精確的共同 odds ratio——等級多不等於資訊多。7 是等級的個數,把它當成資訊量正好倒過來;1 是最小那一格的人數,合併等級確實是一種處理方式,但合併會改變模型在問的問題,不能說「就沒有問題了」。決定怎麼處理稀疏等級之前,要先知道有幾個切點是靠幾個人撐起來的。

素材來源與授權

本頁為原創內容

回報內容問題

這個站的統計內容由 AI 撰寫、AI 互審,人工只做抽查。你看得出來的錯,我們不一定看得出來。

寫得越具體越修得動,例如哪一句話跟哪本教科書/哪篇論文的說法不一致。

留了才回得了信;不留也會看。

一併送出的資訊

這些是自動帶上的,每一項都可以取消。