ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

逆概率加权IPW:观察性研究因果推断的R实现指南

逆概率加权IPW:观察性研究因果推断的R实现指南 我常和做医学数据分析的朋友说观察性研究里最让人头疼的不是建模而是“混杂”。你费尽力气收集了上千例随访数据简单比较治疗组和对照组结果出来永远被人质疑“是不是两组基线不一样”逆概率加权Inverse Probability WeightingIPW就是现在最常用的回应方式之一。它通过倾向得分给每个样本重新加权让处理组和对照组在可比的基础上再去做效应估计。这篇文章我会从原理讲到R实现包含倾向得分建模、权重计算、平衡性检查和常见坑适合公卫、医学、经济学以及任何做观察性数据分析的人。如果你刚接触因果推断不用怕我会尽量用具体的人和数字把概念讲清楚。如果你已经有倾向得分匹配PSM的经验那你很快会发现IPW本质上是一套思路更直接、不需要“丢弃样本”的处理方法。1. 逆概率加权的基本原理1.1 观察性研究里的混杂问题我们想回答一个很直白的问题处理treatment对结局outcome到底有没有因果效应理想状态当然是做随机对照试验随机化让处理分配和所有协变量独立比较组间均值就能得到无偏的平均处理效应。但观察性研究没有这个便利。以“降糖药是否降低心血管事件风险”为例用了药的人可能本身病情更重、年龄更大、合并症更多。如果直接在两组之间比较事件率得到的差值里既有药物效应也有基线风险差异带来的偏倚。这些同时影响“是否用药”和“结局”的变量就是混杂变量。传统做法是把混杂变量放进回归模型里“调整”但调整的结论依赖于函数形式而且当我们关心的结局是罕见事件时过度调整会带来效率损失。IPW换了一个角度我们不把所有协变量都塞进模型而是先用它们去预测每个样本接受处理的概率也就是倾向得分然后通过加权构造一个所有人都可能接受处理的“伪总体”。在这个伪总体里处理分配与协变量不再相关因果效应就可以直接比较了。1.2 从倾向得分到权重倾向得分定义为给定协变量X的条件下样本接受处理T1的概率e(X) P(T 1 | X)这个概率通常用logistic回归估计。得到倾向得分后IPW给每个样本赋一个权重处理组T1权重 w 1 / e(X)对照组T0权重 w 1 / (1 - e(X))所以综合起来w T / e(X) (1 - T) / (1 - e(X))为什么这样加权有用你可以这么理解处理组里倾向得分很高的人本来有很多类似特征的人都会被治疗他的“代表人数”其实很多而那些倾向得分很高却没有被治疗的人在现实中非常少见所以要用很大的权重把他们“放大”否则对照组里就没人能代表这类人群了。加权之后两组在协变量分布上会趋向一致。实际分析我更推荐使用稳定权重stabilized weight即在分子上乘以处理在研究人群中的总体概率w T * P(T1) / e(X) (1-T) * P(T0) / (1-e(X))比如处理组共有40%的人那处理组权重是0.4 / e(X)对照组权重是0.6 / (1-e(X))。稳定权的优势是权重均值为1极端值没那么夸张标准误也更稳定尤其是在处理率不均衡的时候。1.3 IPW和倾向得分匹配的取舍很多人会拿IPW和倾向得分匹配PSM比较。PSM的逻辑是把处理组和对照组样本“配对”只保留能匹配上的样本。直观、好解释但有几点代价首先配对会丢弃无匹配样本损失样本量其次配对质量严重依赖卡钳值的选择最后在报告里讲清楚“如何配对”往往比单纯加权更繁琐。IPW则不丢弃任何样本所有样本都带着权重进入分析统计分析过程中也不需要逐对解释。它的代价是权重极端时方差会变大而且对倾向得分模型的正确性更敏感。对我个人来说在样本量充足的情况下IPW越来越成为首选。2. R实现IPW的完整流程2.1 准备工作先装好这几个包我常用的包有四个WeightIt统一处理倾向得分和权重、survey加权分析、cobalt平衡性诊断、boot如果要做bootstrap。你如果只想走通流程只需要前三个。install.packages(c(WeightIt, survey, cobalt, boot))WeightIt是一个很省心的包它封装了多种倾向得分估计方法包括logistic回归、GBM、SuperLearner等。下面我既会演示手写计算权重也会演示WeightIt的用法这样你能理解背后发生了什么。2.2 生成一份模拟数据为了让整个过程可复现我用R生成一份带混杂的观察性数据。设定一个真实的因果效应处理对结局的效应是0.8。协变量X1会影响处理分配和结局所以如果直接比较均值估计值会明显偏离0.8。library(tidyverse) set.seed(2024) n - 1500 X1 - rnorm(n) X2 - rnorm(n) # 处理分配受X1、X2影响 ps - plogis(-0.5 0.8 * X1 0.6 * X2) T - rbinom(n, size 1, prob ps) # 结局截距1处理真实效应0.8两个混杂都有影响 Y - 1 0.8 * T 0.7 * X1 0.5 * X2 rnorm(n) dat - data.frame(X1, X2, T, Y)在这个模拟世界里如果我们不知道真相会以为T的系数就是因果效应。实际上由于X1、X2同时影响处理和结局T的组间均值差是有偏的。2.3 手写倾向得分和权重先按照经典流程手写一遍。用logistic回归估计倾向得分ps_model - glm(T ~ X1 X2, data dat, family binomial) dat$ps - predict(ps_model, type response) # 计算标准IPW权重和稳定权重 dat$w_raw - ifelse(dat$T 1, 1 / dat$ps, 1 / (1 - dat$ps)) # 稳定权重的分母用倾向得分分子用处理组/对照组总比例 dat$w_stab - ifelse(dat$T 1, mean(dat$T) / dat$ps, (1 - mean(dat$T)) / (1 - dat$ps))你可以打印一下权重的分布看看有没有异常大的值summary(dat$w_raw) summary(dat$w_stab)正常情况下稳定权重的最大值会明显小于原始权重。这也是我为什么要先手写一遍否则你根本不知道包替你做了什么。2.4 用加权回归估计处理效应简单点直接做加权最小二乘# 未加权的朴素比较 unadj - lm(Y ~ T, data dat) # IPW加权 ipw_fit - lm(Y ~ T, data dat, weights w_stab)看结果之前先说明不加权的模型得到的T系数会偏离0.8因为X1、X2的效应混在了处理差异里。加权后T系数会向0.8靠拢。但你可能会问只做Y ~ T为什么权重就够了因为IPW的目的是让处理组和对照组的协变量分布有可比性加权后的两组就像随机分组一样于是可以直接比较。更规范的做法是用survey包它会给出正确的加权标准误library(survey) design - svydesign(ids ~1, weights ~w_stab, data dat) fit_ipw - svyglm(Y ~ T, design design) summary(fit_ipw)svyglm出来的标准误是鲁棒的比普通lm更贴近实际抽样不确定性。做医学报告时多数审稿人也希望看到这种稳健标准误。2.5 用WeightIt包简化流程手写逻辑清楚后直接用WeightIt效率更高。它的好处是不仅支持logistic回归还支持GBM、IPW、熵平衡等多种方法并且能直接输出权重稍微检查一下就行。library(WeightIt) w_out - weightit(T ~ X1 X2, data dat, method glm, estimand ATE, stabilize TRUE) dat$weight - w_out$weights design2 - svydesign(ids ~1, weights ~weight, data dat) fit_ipw2 - svyglm(Y ~ T, design design2) summary(fit_ipw2)这里estimand ATE表示我们估计的是全体人群的平均处理效应。如果你想估计处理组人群中的平均效应ATT权重公式会变具体要用estimand ATT。初学者经常忽略这个参数一定要想清楚你的研究问题到底关心谁。2.6 二分类结局怎么办如果结局是二分类比如是否发生心血管事件svyglm里加family binomial()就可以估计加权后的风险差值或比值。不过要注意的是svyglm默认给出的是对数比值比log OR而且是条件效应不是边际效应。要得到边际风险差值可以用marginaleffects包配合survey对象或者在简单的场景里直接对加权后的频率做比较。不论哪种结局核心步骤都一样估计倾向得分、算权重、加权分析。模型形式不同但思路完全通用。3. 实操中必须注意的细节3.1 正性假设与倾向得分重叠检查IPW第一个要满足的假设是正性positivity在任意协变量水平下处理组和对照组都要存在样本。用大白话说不能出现某些特征的人“一定被治疗”或“一定不被治疗”。如果倾向得分趋近于0或1权重就会爆炸。检验方式很直接画出处理组和对照组的倾向得分分布图。如果两组分布几乎没有重叠说明正性有问题。library(ggplot2) ggplot(dat, aes(x ps, fill factor(T))) geom_density(alpha 0.5) labs(x Propensity score, fill Treatment)如果你看到某些区域完全被一种颜色的曲线占据分析结论就非常脆弱。这种情况可以考虑限制样本到重叠区间或者改用修剪trimming方法后再做IPW。3.2 权重截断什么时候做、怎么做尽管稳定权重已经很好了仍可能出现个别样本权重特别大。一个权重是3多数权重在0.5附近那个样本会对结果产生不成比例的影响。截断truncation或修剪trimming是最常用的办法。简单做法是把权重上下界设置在某个百分位数dat$w_trim - pmax(pmin(dat$weight, quantile(dat$weight, 0.99)), quantile(dat$weight, 0.01))但这会导致权重的总和变小最好重新归一化。一个实用技巧是先把权重的均值调整为1再做截断这样至少保持权重总和≈样本量。截断阈值没有绝对标准。有人用1%和99%分位数有人直接在倾向得分0.1到0.9之间截断。我的经验是先看summary(weight)如果最大权重大于10大概率要处理在样本量超过1000时1%截断的尺度通常够用样本量小时选择更保守的5%截断往往更稳。3.3 平衡性检验别只看p值加权做完后必须检查处理组和对照组的协变量是否平衡。很多新人直接对每个变量做t检验看p值是否大于0.05这是不对的。t检验会受到样本量影响在大样本里哪怕标准化均数差SMD只有0.02也会“显著”在小样本里不平衡得很严重也可能不显著。因此推荐看SMD。cobalt包是干这个事的。它能方便地比较加权前后各协变量的标准化均数差并画出love plotlibrary(cobalt) bal.tab(w_out, threshold 0.1, un TRUE) love.plot(w_out, threshold 0.1, var.order unadjusted, abs TRUE)判断标准通常取SMD绝对值小于0.1有些更严格的场景取0.05。如果加权后某个关键混杂变量仍不平衡说明倾向得分模型可能漏掉了交互项或非线性项需要回头调整模型。平衡性报告是论文里最有力的部分之一我强烈建议做必做步骤。3.4 给权重加上“双重稳健”保险IPW的一个短板是如果倾向得分模型设定错了权重也会错。制衡思路是在加权后的回归中继续把主要混杂变量作为协变量放进去这就是双重稳健估计。所谓双重稳健意思是只要倾向得分模型或结局模型有一个正确估计就是一致的。这个性质让它在实际分析里很实用。R里实现非常容易就是在svyglm的公式里加上协变量fit_dr - svyglm(Y ~ T X1 X2, design design2)加进去以后默认情况下你会看到T的系数略微改变同时标准误会变小。注意如果量表不同还需要考虑协变量是否中心化但这不影响代码逻辑。我个人除非样本量非常小否则都会做双重稳健版本然后把它作为敏感性分析或者主分析。3.5 缺失数据和权重标准化真实数据一般都有缺失值我见过不少人直接na.omit把不完整样本丢掉。在IPW分析里这样做特别危险因为缺失机制很可能和协变量相关丢掉样本相当于隐式改变了目标人群。更好的办法是先用多重插补mice包补齐协变量再在每个插补数据集中分别估计倾向得分、计算权重、做加权回归最后用Rubin法则合并结果。这个过程有点繁琐但现在WeightIt无法自动完成需要自己写循环。如果你愿意用完整样本分析至少要报告样本量与原始人群的差异。另外要注意当权重加起来不等于样本量时svyglm会以权重总和进行标准化一般不影响效应估计但会影响标准误。所以稳妥起见我会在分析前把权重除以权重均值保证权重均值为1。4. 常见问题与排查技巧实录4.1 加权后效应反而“更不像”真实值这个场景我遇到过好几次。如果加权后估计值与直觉差别很大或者与未加权结果方向不同先别急。优先检查倾向得分模型是否包括了所有已知混杂变量以及是否漏了非线性项。模拟数据里的X1和结局的关系是线性所以logistic回归够用真实数据往往有交互、非线性倾向得分模型不完善加权结果自然会偏。另一个常见原因是“逆处理”样本。也就是说有些低倾向得分的人被治疗了而高分的人反而没治疗。权重会把这些样本放到极大导致估计不稳定。此时需要看权重分布和倾向得分重叠图考虑截断权重或改用重叠权重overlap weight。最后在所有诊断都正常的情况下要接受结果可能与你的预期相反。因果推断不是用来证明你的预设的它只是让比较更公平。4.2 标准误偏小或被低估很多人在用lm加weights后直接用输出的标准误做推断这在权重和观察性研究里不太稳妥。权重的估计本身带有不确定性但标准误并不会自动计入这一点。标准做法是使用survey包的svyglm得到稳健标准误或者对整个过程做bootstrap把估计倾向得分和计算权重都放进bootstrap循环里。survey包处理加权标准误已经能满足绝大多数场景。如果数据是分层或整群抽样还需要在svydesign里指定cluster变量和strata变量。面板数据或重复测量数据则要用聚类标准误比如svyglm配合familyquasi或者用sandwich包手动调整。原则是权重的来源每一步都要反映到不确定性里否则你看似的p值可能过于幸运。4.3 正性不好但又不舍得删样本正性不好不代表研究完全不能做。一个办法是设置重叠区间利用倾向得分的最小和最大范围只保留倾向得分落在处理组和对照组共同覆盖范围内的样本再接IPW。另一个办法是加权时使用“重叠权重”overlap weight它对倾向得分趋近0或1的样本给的权重天然很小能避免极端权重。但要注意这两种方法都改变了目标人群。用重叠权重的估计更接近“临床交界地带人群”的效应而不是整个研究人群的ATE。所以在报告里一定要清楚说明你评估的人群是谁。审稿人比你想象的更在意这个问题。4.4 倾向得分模型选logistic还是机器学习传统上大家都用logistic回归因为它简单、透明写论文好解释。但真实关系复杂时logistic回归容易欠拟合这时可以用GBM梯度提升机或SuperLearner。R里用WeightIt切换方法很简单w_out_gbm - weightit(T ~ X1 X2, data dat, method gbm, estimand ATE)GBM理论上能捕捉更多交互和非线性。但代价是黑箱、不容易描述而且小样本情况下容易过拟合。我的建议是样本量几千以上、协变量十几甚至几十个别担心解释不了可以直接用GBM或者SuperLearner样本量几百、协变量不超过5个老老实实用logistic回归然后把更多精力放在平衡性检查上。4.5 论文或报告里应该写什么我在实际写研究报告时会固定包含这几项一是倾向得分模型的变量清单和估计方法二是加权前后协变量SMD的表格三是权重分布的描述最小值、最大值、均值四是主分析和敏感性分析结果包括截断权重、双重稳健估计五是如果用了任何因正性不好而做的样本限制必须写明限制标准。这看起来内容多但其实每部分都不复杂。cobalt的bal.tab能输出SMD表summary(w_out)能直接给出权重描述代码上10分钟就能搞定。真正花时间的反而是想清楚“为什么要选ATE而不是ATT”“为什么不把某个变量放进模型”这类问题。这些内容写在报告里比堆一堆p值有说服力得多。说实话IPW的理论门槛不高难的是每一步都做得规范。我从自己的项目里最深刻的体会是不要迷信某一个包的输出结果每一层权重都要亲眼看一看分布每个关键混杂变量都要在SMD表里找到名字。如果只能留一个检查步骤我会选加权前后的love plot它几乎能一眼看出你的倾向得分模型是否站得住脚。
返回列表