☰
目标模拟试验TTE:观察性数据因果推断实战指南
2026/10/28 5:01:31 网站建设 项目流程

“手里有几十万条随访记录,想回答一个特别朴素的问题——同样的病,早用药和晚用药,结局到底差多少”,这大概是每个碰观察性数据的人都绕不过去的场景。真跑起来你就知道有多难受:普通回归跑完,系数看着挺像回事,可是一旦有人追问“你怎么排除适应证混杂”“那些一开始就没吃药的人,凭什么算进对照组”“中途换药的人你怎么处理”,答案往往就变得含糊。目标模拟试验(Target Trial Emulation,下面统一叫 TTE)这套框架,本质上就是给这类问题准备的一份施工图:它不发明新的统计模型,而是要求你先把一个理想中的随机对照试验该怎么设计、一条一条写在纸上,再拿着观察性数据去逐环节复刻。观察性数据做因果推断最怕的不是算不出来,而是算出来的东西根本不对应任何一个真实可回答的问题,TTE 解决的正是这个“问题定义”层面的窟窿。这篇东西适合三类人看:手上已经有队列数据、想做出能站住脚的疗效对比的分析人员;被审稿意见反复打回、需要一套完整回应逻辑的研究者;以及刚开始接触因果推断、想知道除了倾向评分匹配之外还有哪些正经路子的新手。下面我按“想清楚—写方案—建估计量—写代码—排坑—报结果”的顺序,把自己踩过的坑和能直接抄的骨架都摊开讲。

1. 先搞清楚 TTE 到底在解决什么问题

1.1 三个把结论做废的经典陷阱

在动手写任何一行代码之前,得先明白观察性数据里那些“看起来没问题”的分析,通常是怎么坏掉的。我见过最多的三类问题,几乎覆盖了八成以上的返工。

第一类是适应证混杂,也叫 confounding by indication。医生给谁开药、不给谁开药,本身就是一次基于病情严重程度的判断。重症患者更容易上强效方案,而重症患者本身的结局就更差,于是你算出来的“用药组结局更差”,很可能只是病情差异的投影。这个问题用常规的多变量回归压不住,因为你根本不知道医生当时看到了哪些信息,而其中很大一部分并没有被记录进数据库。

第二类是不朽时间偏倚,英文叫 immortal time bias。这是最阴险的一种。假设你按“住院期间是否使用过某方案”来分组,那么用药组从入院到首次用药之间那段时间,患者在生物学上必须活着才能等到用药,这段“不死时间”被算进了用药组的随访里,凭空给用药组送了几天甚至几个月的生存优势。处理办法只有一个:随访起点必须定在“策略开始执行的那一刻”,而不是入院那一刻,也不是出院那一刻。

第三类是治疗转换与依从性漂移。真实世界里没人会老老实实吃同一种药五年。对照组的人半年后加了药,用药组的人三个月后停了药,你的“组”从第一天起就在互相渗透。传统做法是按初始治疗分组然后不管后续,这叫意向性分析,方向是对的,但你得明确写清楚“我估计的是开始用 A 相对开始用 B 的效果,不是一直用 A 的效果”。这三类问题不解决,后面所有权重、所有稳健标准误都是给一个错误的数字做精装修。

1.2 目标试验模拟的核心动作:先把 RCT 的方案表画出来

TTE 最有价值的地方,不在于它用了什么高级估计量,而在于它强制你先做一件很笨的事:假设你真的能做这个随机试验,你会怎么设计它。纳排标准是什么、两组分别接受什么策略、什么时候开始随访、随访多久、结局怎么定义、主要对比是什么、分析用什么模型——这七条写成一张表,这就是你的“目标试验方案”。

写完这张表,你才会发现很多平时被含混带过的东西其实根本没想清楚。比如“用药组”到底是“首次处方开具日”还是“首次实际取药日”?比如“对照”是“从未用过”还是“在某个时间点仍未用”?比如结局的观察窗口是固定的 365 天,还是从入组到删失?这些问题在方案表里必须给出唯一答案,而一旦给出答案,数据该怎么切、随访该怎么对齐、权重该怎么算,就全都自动确定了。

我个人的体会是,这张方案表的价值大约占整个项目的一半。很多人跳过它直接建模,结果模型跑完发现结果对不上常识,再回头查,才发现是随访起点定错了,前面所有工作推倒重来。所以我的习惯是先写方案表,写完给完全不懂统计的同事看一遍,如果他能指出“这两组人看起来不是同一批人”,那说明方案有问题,趁早改。

1.3 什么场景适合用,什么场景别硬套

TTE 不是什么都能救。它适合的场景有几个共同特征:有一份明确的、可在数据中识别的治疗策略(比如“在确诊后 30 天内启动 A 方案”对比“在确诊后 30 天内启动 B 方案”);有一个说得清的时间零点;有足够长的随访能观察到结局;并且混杂因素在时间零点之前或当时已经被测量。

反过来,几种情况别硬套。第一种是数据里根本没有可靠的用药记录,只有“是否诊断过某病”,那连策略都定义不出来。第二种是时间零点无法对齐,比如慢性病患者的“确诊日”在不同医院记录精度差异巨大,有的精确到天,有的只精确到年,这种情况下强行对齐会引入巨大的测量误差,而且误差方向不可知。第三种是治疗策略本身在随访中高度动态,几乎每个人都在不停换方案,那么即便用克隆删失加权,权重也会极端到有效样本量所剩无几,结论的可信度会大打折扣。

还有一个经常被忽略的前提:阳性对照要靠谱。如果你研究的是某个新方案对比“什么都不做”,那混杂几乎不可控,因为不用药的人群和用药人群在健康素养、就医频率、经济条件上系统性不同。这时候更稳妥的做法是找一个临床上“次优但仍在用”的对照,让两组人的就医行为模式更接近,混杂会小很多。

2. 七要素落地:把方案写死在数据之前

2.1 合格标准与治疗策略要写成可执行的判断语句

合格标准听起来简单,写起来最容易出岔子。我的经验是每一条都写成能用一行代码判断真假的布尔表达式,写不出来的说明这条标准本身含糊。比如“新确诊的 2 型糖尿病患者”,落地时至少要拆成:诊断编码在观察期内首次出现、此前无相关诊断记录、基线时未使用过任何降糖方案、年龄在某个区间、有至少一次基线体检记录。每一条都对应数据里的一个字段和一个判断,这样后面无论谁来复核,都能逐条对上。

治疗策略的写法更容易偷懒。我见过太多方案写“使用 A 药 vs 使用 B 药”,这种写法在数据里根本没有对应的操作。正确的写法是带时间窗口的:“在合格时点后的 30 天内启动 A 方案,且在启动前未使用 B 方案”对比“在合格时点后的 30 天内启动 B 方案,且在启动前未使用 A 方案”。窗口长度不是随便定的,它要反映临床实际决策的节奏——急性病可能只有 7 天,慢病管理可能给到 90 天。窗口定得太窄,符合条件的人太少;定得太宽,策略的临床意义就被稀释了。

这里有个容易被忽略的细节:策略要互相排斥且尽可能穷尽。如果你定义了两组,但数据里有三成人既没用 A 也没用 B,那这三成人去哪了?合理做法是明确写出“本次分析不纳入既不启动 A 也不启动 B 的个体”,并在讨论里说明这可能带来的选择偏倚。含糊带过是最危险的,因为读者会假设你把所有人都分进去了。

2.2 分配程序与随访起点:时间零点定错,全盘皆输

随机试验里的分配程序是抛硬币,观察性数据里没有硬币,只能假设“在给定已测混杂的前提下,治疗分配与潜在结局独立”。这句话写在方案里是免责声明,落到实操上就是:你得把所有能想到的、在时间零点之前测到的混杂因素都列出来,包括人口学、合并症、基线用药、实验室指标、既往就诊频次,甚至就诊机构的层级。

但真正决定成败的是随访起点,也就是时间零点。TTE 里有一条铁律:合格标准、治疗策略、随访起点这三者必须在同一个时刻对齐。也就是说,一个人在第 t 天满足所有纳排标准、并且在第 t 天开始执行某个策略,那么他的随访就从第 t 天开始。任何“先入组、再等几天看看他用不用药”的写法,都会把不死时间偷偷塞进随访里。

我见过几种典型的错误写法,值得单独拎出来说。一是用“首次诊断日”作为零点,但治疗策略是“诊断后 30 天内启动”,导致用药组有 30 天的等待期被计入随访。二是用“首次用药日”作为零点,那对照组就没有对应的零点了,只能人为指定一个日期,这个日期怎么选都是主观的。三是用“出院日”作为零点,但住院期间发生的结局被排除了,等于对两组做了不同的选择。正确的做法是把零点统一定义为“合格时点”,也就是首次满足全部纳排标准的那一天,然后要求策略必须在这个时点当天或之后的某个明确窗口内启动,未启动者按方案处理(比如归入对照或被删失)。

2.3 结局、因果对比与分析计划该怎么写

结局的定义要具体到能被独立复核的程度。“发生心血管事件”不是一个结局,“首次出现心梗或卒中的住院记录,以出院诊断编码为准,观察窗为时间零点后 1095 天内”才是。随访时间上限、事件判定依据、是否包含死亡作为竞争事件,这些都得写清楚。竞争风险在观察性数据里特别常见,老年人队列里死于其他原因的比例可能很高,直接算累积发病率会把风险高估,通常需要改用 Fine-Gray 或者做 cause-specific hazard,方案里要预先声明。

因果对比的写法是很多人卡住的地方。意向性对比问的是“开始用 A 相对开始用 B 的效果”,哪怕后面换药也算在初始组里,这个最接近随机试验的 intention-to-treat,也是 TTE 最常报的主结果。符合方案对比问的是“持续用 A 相对持续用 B 的效果”,需要处理换药和停药,方法上更复杂。两者回答的是不同的问题,不能混着报。我建议主分析报意向性对比,符合方案对比作为次要分析,并且明确说明后者的假设更强。

分析计划里要预先写清楚估计量(风险差、风险比、限制平均生存时间)、随访时长、亚组、敏感性分析清单。预注册这件事在观察性研究里同样重要,哪怕不公开注册,也要在团队内部把方案定稿并留存版本记录。原因很现实:如果你先看了结果再去挑亚组,那结论的可靠性就没了,而事后很难自证清白。

2.4 一张表把方案和数据对上

方案写完,我习惯做一张对照表,左边是目标试验的七要素,右边是“在本数据中如何操作化”。这张表既是自查工具,也是论文附录里最有说服力的材料。

目标试验要素本研究的操作化定义数据字段
合格标准观察期内首次诊断,年龄 40 至 80 岁,基线无相关用药,有基线体检记录诊断表、人口学表、处方表、检验表
治疗策略合格时点后 30 天内启动 A 方案且此前未用 B;对照为启动 B 且此前未用 A处方表、发药日期
分配程序假设在已测协变量条件下可交换,用逆概率权重校正基线协变量集合
随访起点首次满足全部纳排标准的日期各表日期字段取最大值
随访结束结局发生、死亡、退出数据库、或满 1095 天,取最早结局表、死亡表、参保表
结局首次心梗或卒中住院,出院诊断编码判定住院诊断表
因果对比5 年风险差与风险比,意向性对比为主计算得出
分析计划克隆-删失-加权 + 加权 Cox / 风险差;三项敏感性分析全流程脚本

这张表最大的用处是逼你把“我以为数据里有”变成“数据里确实有”。做完这张表,你会经常发现某一列填不出来,那恭喜你,在跑模型之前就发现了问题,比跑完再发现要好得多。

3. 从方案到估计量:克隆-删失-加权与序列试验

3.1 克隆与人工删失:把一个人变成两个人

目标试验里有随机分配,观察性数据里没有。克隆-删失-加权(clone-censor-weight,简称 CCW)的核心思路很巧妙:既然我不知道你“如果被分到 A 组”会怎样,那我就把你同时复制成两份,一份假设你被分到 A 组,一份假设你被分到 B 组,然后让数据自己说话。

具体操作是:对每个合格个体,复制出一条记录标记为策略 A,再复制一条标记为策略 B。然后在每条克隆记录上定义一个人工删失时间——如果这条克隆被标记为 A,但此人在随访中实际启动了 B,那这条克隆就在启动 B 的那一刻被人工删失;反过来同理。如果有人一直没启动任何策略,那两条克隆都在策略窗口关闭时被删失。最后,对每个人工删失时间拟合一个删失模型,算出逆概率权重,用权重把“被删失”造成的信息损失补回来。

这里的关键理解是:人工删失不是真的丢数据,而是让每条克隆只保留与它所属策略一致的那段随访。如果不加权,你会得到一个严重偏倚的估计,因为被删失的人不是随机缺失的——他们恰恰是那些偏离了初始策略的人,而偏离本身往往与预后相关。加权的作用就是把这些人“以另一个人的身份”还回来。

实操上有个细节特别容易错:删失模型的协变量必须包含时间更新的信息,不能只用基线变量。因为“什么时候换药”这件事本身受病情变化驱动,而病情变化又是结局的强预测因子。所以删失模型里通常要放进时变的实验室指标、时变的就诊记录,甚至上一次的用药情况。这部分的建模工作量往往比结局模型还大。

3.2 删失权重的构造与稳定化

权重的定义不复杂,写出来是一串连乘:某个个体在第 t 个时间点仍然未被人工删失的累积概率的倒数。用公式表达就是,第 i 个克隆在第 t 期的权重 = 1 / 从第 0 期到第 t 期每一期“未删失条件概率”的连乘。每一期的条件概率由一个以“是否删失”为因变量的模型估计,协变量包括策略标记、基线混杂、以及该期的时变协变量。

不稳定权重是这套方法最常见的翻车点。如果某一期某类人群的删失概率接近 1,权重就会爆到几十甚至上百,个别观测值会把整个估计量拖走。两个常规缓解手段:一是稳定化,把分子换成“只含策略标记、不含时变协变量”的删失概率,这样权重的均值会回到 1 附近;二是截尾,把权重在第 99 百分位或某个固定阈值处截断,同时报告截尾前后的结果差异。

我更看重的是有效样本量这个诊断指标,而不是权重的最大值。有效样本量大致等于权重平方和的倒数再乘上样本量,它能直观告诉你“加权之后相当于还剩多少人在起作用”。如果原始样本五万人,加权后有效样本量只剩八百,那无论结果多显著都得打问号。我一般要求有效样本量不低于原始样本的一成才敢往下写。

3.3 序列试验:换一个角度处理动态治疗

CCW 有个不太优雅的地方:它假设策略在时间零点是定死的,中途偏离就删失。但很多临床场景里策略本身就是“动态”的,比如“只要指标超过某个阈值就加药”。这种情况下更合适的是序列试验思路。

序列试验的做法是把随访期切成很多小段,在每一个小段开头,把当时仍然合格、且当时正在执行某个策略的人挑出来,组成一个“迷你试验”。每个人可以出现在多个迷你试验里,只要他在那个时点仍然符合条件。然后在每个迷你试验内部做加权分析,最后把所有迷你试验的结果按某种方式合并。

这个方法的优势是它天然允许策略随时间变化,也能处理“中途才符合条件”的人群。代价是计算量大,而且合并方式的选择会显著影响结论。我通常的做法是:如果策略是“一开始选定、之后不再变”,用 CCW;如果策略明确依赖时变指标,用序列试验。两者不是互斥的,有时我会同时跑一遍看结论是否一致,如果一致,可信度会高很多;如果不一致,那说明结果对方法假设敏感,得在讨论里老实交代。

3.4 手算一遍权重,心里才有底

很多人调包调得很顺,但一问权重怎么来的就说不清。我建议至少手工算一遍小例子。假设一个简化场景:随访 3 期,某克隆在各期的删失条件概率分别是 0.10、0.15、0.20。那么累积未删失概率是 0.9 × 0.85 × 0.8 = 0.612,第 3 期的未稳定权重就是 1 / 0.612 ≈ 1.634。如果这个人策略标记为 A,而全部样本里 A 策略的边际未删失概率是 0.75,那稳定化权重就是 0.612 的倒数乘以 0.75,约 1.225。

再往下走一步,假设结局模型是加权 Cox,那么这个人在第 3 期之后的时间内贡献的风险集权重就是 1.634(或 1.225)。如果他的权重是 8,那他在风险集里就顶八个人。这就直观解释了为什么极端权重可怕——一个人的事件状态会以八倍的力度影响部分似然。

算完这一步,你就明白为什么我要看有效样本量而不是看最大权重:最大权重告诉你最坏情况,有效样本量告诉你整体还剩多少信息。这两个指标一起看,判断才靠谱。

4. 代码骨架:把上面这套东西跑起来

4.1 数据准备与变量清单

先明确要准备哪些字段。以一个虚构的慢病管理队列为例,我一般需要:个体标识、合格时点、策略标记(0/1)、人工删失时间、结局时间、结局状态、一组基线协变量、以及若干时变协变量的长表。这些字段的准备阶段最容易出错的就是日期对齐——所有日期都要转成“距离合格时点的天数”,而不是原始日期,否则后面按时间展开人群-时间数据时会乱。

library(data.table) library(survival) # dt_base: 一人一行 # 字段: id, t0(合格时点), arm(策略 0/1), # censor_day(人工删失: 偏离策略/失访/研究结束 的最早天数), # event_day(结局天数), event(0/1), # x1, x2, x3 (基线协变量) dt <- as.data.table(dt_base) dt[, `:=`( last_day = pmin(censor_day, event_day, 1095, na.rm = TRUE), status = as.integer(event_day <= pmin(censor_day, 1095, na.rm = TRUE)) )] # 关键自查: 合格时点之后才允许出现策略启动 stopifnot(all(dt$arm_start_day >= dt$t0 | is.na(dt$arm_start_day)))

上面这段里最值钱的是最后的stopifnot。我习惯在每一步加断言,宁可脚本报错也不要静默出错。曾经有一次因为时区转换,一部分人的合格时点被算到了策略启动之后,脚本没报错,结果跑出来的风险比漂亮得反常,回头查了两天才发现问题。

4.2 克隆、删失、加权的完整实现

# 1. 克隆: 每人两条记录 d0 <- copy(dt)[, `:=`(clone = 1L, arm_clone = 0L)] d1 <- copy(dt)[, `:=`(clone = 2L, arm_clone = 1L)] dc <- rbind(d0, d1, use.names = TRUE) # 2. 计算每条克隆的人工删失时间 # - 若实际策略与克隆策略不一致, 在实际启动策略那天删失 # - 未启动任何策略者, 在策略窗口关闭日删失 dc[, censor_t := fifelse( arm_clone != arm, pmin(arm_start_day, last_day, na.rm = TRUE), last_day )] # 3. 展开为人群-时间长表 (按月) pt <- dc[, .(t = seq(0, ceiling(censor_t) - 1)), by = .(id, clone, arm_clone, x1, x2, x3)] pt[, `:=`( censored = as.integer(t == ceiling(censor_t) - 1), event = as.integer(t == ceiling(last_day) - 1 & status == 1) )] # 4. 删失模型: 分母含时变协变量, 分子只含策略标记 fit_den <- glm(censored ~ arm_clone + x1 + x2 + x3 + factor(t), data = pt, family = binomial()) fit_num <- glm(censored ~ arm_clone + factor(t), data = pt, family = binomial()) # 5. 逐期条件未删失概率 -> 累积 -> 权重 pt[, `:=`( p_den = 1 - predict(fit_den, newdata = .SD, type = "response"), p_num = 1 - predict(fit_num, newdata = .SD, type = "response") ), by = .(id, clone)] w <- pt[, .( cum_den = cumprod(p_den), cum_num = cumprod(p_num) ), by = .(id, clone)] pt <- merge(pt, w[, .(id, clone, t, cum_den, cum_num)], by = c("id", "clone", "t")) pt[, sw := cum_num / cum_den]

这里有两个地方值得单独说。第一,删失模型里factor(t)是让每一期有独立的基线风险,这在人群-时间数据里是必要的,否则等于强假设删失风险随时间按某个参数形式变化。第二,分子模型里我去掉了所有协变量,只留策略标记和时间,这是稳定化权重的标准做法,目的是让权重的均值接近 1,降低方差。

4.3 结局模型与置信区间

# 截尾处理 qc <- quantile(pt$sw, 0.99, na.rm = TRUE) pt[, sw_trunc := pmin(sw, qc)] # 有效样本量诊断 ess <- sum(pt$sw_trunc)^2 / sum(pt$sw_trunc^2) # 加权 Cox fit_out <- coxph(Surv(t, event) ~ arm_clone + strata(clone), data = pt, weights = sw_trunc, robust = TRUE) # 5 年风险差: 用加权生存曲线或标准化风险 summary(fit_out)

strata(clone)这一步不能省。因为克隆出来的两条记录来自同一个人,它们之间天然相关,不按克隆分层会让标准误被低估。更严格的做法是按个体聚类稳健标准误,我用robust = TRUE配合cluster(id)也行,具体用哪个看数据规模和计算资源。

风险差的计算我一般不直接用 Cox 的系数,而是标准化到人群:先拟合加权模型,再对全样本分别预测策略 0 和策略 1 下的生存概率,取平均后相减。这样得到的风险差有明确的总体平均解释,比直接对 log 风险比取指数要稳妥。

4.4 敏感性分析清单

至少准备三项。第一项是阴性与阳性对照:找一个理论上不应受该策略影响的结局(阴性对照结局),如果它做出来也“显著”,说明残余混杂很可能存在;再找一个已知有明确效果的结局(阳性对照结局),如果做不出来,说明数据或方法有问题。第二项是 E 值:量化“需要多强的未测混杂才能推翻结论”,这个数字比 p 值有信息量得多。第三项是方法敏感性:换用序列试验重跑一遍,或者改变策略窗口长度、截尾阈值,看结论是否稳健。

我个人的排序是:阴性对照结局 > E 值 > 方法敏感性。原因很实际,阴性对照能直接暴露系统性问题,而 E 值更多是给读者一个心理锚点。如果三项里有两项出问题,那这个结果我基本不会写进主结论。

5. 排查实录:那些把结果带偏的细节

5.1 时间零点错位的五种常见写法

这一条值得反复强调,因为我在至少五个项目里见过它。第一种:用首次诊断日做零点,但纳排标准里要求“有基线体检记录”,而体检可能发生在诊断后一个月,这就导致一部分人零点是诊断日,另一部分是体检日。第二种:用首次处方日做零点,对照组被迫用一个随机日期。第三种:用入院日做零点,但住院期间发生的结局被排除在外。第四种:用出院日做零点,等于把住院期间的死亡全部删掉,而在两组里这个比例并不相同。第五种:零点跟着数据更新的时间走,导致不同人随访时长系统性地不同。

排查方法很土但有效:对每个个体,打印出合格时点、策略启动日、首次事件日,人工看前一百行。你会惊讶地发现有很多人策略启动日早于合格时点,或者事件日早于零点。这些都必须清理掉,而且要在论文里写明清理了多少人、为什么。如果清理掉的比例超过 5%,那就不是数据质量问题,而是方案定义有问题,得回去改方案。

5.2 极端权重与有效样本量的实战处理

权重爆掉的时候,第一反应不应该是截尾,而是回头看删失模型。常见原因是某一期里某类人几乎全部被删失,比如“策略窗口最后一期才开始用药的人”,模型对它给出的删失概率接近 1,权重自然爆炸。这时候可以考虑把策略窗口缩短,或者把这类人明确排除在合格标准之外。

真要截尾,我建议至少报三个版本:不截尾、1% 截尾、按固定阈值截尾,看结论是否一致。如果只在某一版本下显著,那这个“显著”不要也罢。同时必须报告每个版本下的有效样本量,这是判断结果可靠性的硬指标。

还有一个细节:稳定化权重的分子分母要用同一套人群-时间数据拟合。有时候人会先用全样本拟合分母,再用限定的子样本拟合分子,这会导致权重系统性偏移。我一般两种都跑一遍做对比,如果没有实质差异,说明结果稳健。

5.3 用 E 值把未测混杂说清楚

E 值回答的问题是:需要多强的未测混杂(同时与治疗分配和结局相关),才能把观察到的风险比解释为零?计算方法不复杂,对于风险比大于 1 的情况,E 值约等于风险比加上它的平方根再乘上风险比减一。比如观察到风险比 1.5,E 值大约是 1.5 + sqrt(1.5) × 0.5 ≈ 2.11,意思是需要一个与治疗和结局的风险比都达到 2.11 的未测混杂才能完全解释掉这个结果。

这个数字的解读要谨慎。它不是“结论可靠”的证明,而是给读者一个量化的心理预期。如果 E 值只有 1.2,而你知道吸烟和这个结局的关联强度就有 2 倍,那基本可以判定结论不牢。反过来,如果 E 值是 3,而领域内已知最强的未测混杂也就 1.5 倍,那结论就相对稳。我通常会在讨论里把“已知最强混杂的强度”和 E 值放在一起对比,这段文字比一堆检验的 p 值都有说服力。

5.4 问题速查表

现象最可能的原因处理动作
风险比漂亮得反常(如 0.3)时间零点错位,不死时间被计入逐行核对零点与策略启动日
加权后置信区间极宽极端权重,有效样本量过低检查删失模型,缩短策略窗口,报告 ESS
阴性对照结局也显著残余混杂或选择偏倚扩充基线协变量,改用活性对照
两组基线差异巨大,加权后仍不平衡阳性概率模型设定错误改用协变量平衡权重,检查重叠性
换方法后结论翻转结果对假设高度敏感主分析用保守方法,讨论中交代
换药比例超过四成策略定义与临床实际脱节重定义策略窗口,或改用序列试验

这张表我是从多次返工里攒出来的,每次新项目开工前都会过一遍,能省掉不少试错时间。

6. 报告与答辩:让结论经得起推敲

6.1 报告清单:把“我做了什么”写到别人能复现

审稿人最烦的不是你做错了,而是看不出你做了什么。我现在的报告习惯是固定几个模块:目标试验方案表(放在附录,七要素逐条对应)、流程图(从原始人群到最终分析人群,每一步排除多少人、为什么,必须能逐级对上)、基线特征表(加权前后各一版,标准化差异要给出来)、主结果(风险差和风险比都要,不能只报一个)、敏感性分析汇总表(每个分析一行,点估计和区间都列)、有效样本量(这一项很多人不报,但我坚持放进去)。

流程图特别值得花时间。我见过太多论文的流程图中间某一步人数对不上,读者一算就知道有问题。做流程图的时候我建议直接从代码里导出计数,而不是手填数字,因为手填一定会错。另外,排除人数和排除原因要一一对应,不能出现“因数据质量问题排除 1203 人”这种没有信息量的表述。

6.2 评审常问的问题与应答思路

最高频的问题一定是“你怎么排除混杂”。标准答法是三段:第一段说明目标试验的可交换性假设;第二段列出用于校正的协变量集合及其临床依据(不是“我们调整了这些变量”,而是“这些变量是既往文献中已确认的、同时影响治疗选择与结局的因素”);第三段用阴性对照和 E 值给出量化边界。三段缺一不可,只讲第一段会被认为空谈,只讲第三段会被认为回避问题。

第二个高频问题是“换药的人怎么处理”。这时候要把意向性对比和符合方案对比说清楚,明确你报的是哪一个。如果报的是意向性对比,可以坦率地说“我们估计的是开始治疗的效应,换药后的效应不在本次估计范围内”,这样反而显得你清楚自己在做什么。

第三个是“样本量够不够”。不要只报总人数,要报事件数和有效样本量。加权分析里,事件数除以协变量个数这个经验法则仍然适用,但更该看的是加权后的事件数。我一般会在方法里直接写明加权后的有效样本量和事件数,省得审稿人来问。

最后一个,也是最难答的:“如果存在未测混杂怎么办”。诚实一点,把 E 值和已知最强混杂放在一起比较,说明结论在什么条件下成立、在什么条件下不成立。这种写法比硬说“我们的结论很稳健”可信得多,也更容易过审。

7. 几个我反复验证过的实操心得

7.1 先把“不可能做出效果”的结局跑一遍

新项目开工,我的第一件事不是跑主结局,而是随便挑几个理论上不该受影响的结局跑一遍。如果这些“安慰剂结局”也做出了显著效果,那说明整个数据管道有问题——可能是日期对齐错了,可能是人群选择有偏,也可能是权重算错了。这一步花不了两小时,但能省下两周的返工。

我曾经在一个项目里连着跑了三个安慰剂结局,全都显著,最后查到是合格时点用了一个会被后续事件反向影响的字段。这类问题在方案表上看不出来,只有跑数才能暴露。所以我现在的习惯是安慰剂结局和主结局同时跑,而不是等主结局出问题再回头补。

7.2 版本管理比想象中重要

TTE 的分析链条长,从方案到数据切分到权重到结局模型,任何一环改动都会让结果变化。我现在的做法是整个项目用一个脚本文件从头跑到尾,中间的每一步都输出一个带时间戳的中间数据集,论文里的每一个数字都能追溯到具体的输出文件。

这不是洁癖,是刚需。审稿人要求改一个协变量、改一个窗口长度的时候,你得能在半小时内重跑全套并更新所有表格。如果中间是手工操作的,那基本等于重做一遍。我见过太多团队在这一步崩掉,最后为了赶截止日期只能删掉一部分敏感性分析,反而削弱了文章的说服力。

7.3 别迷信“显著”,要看效应量的临床意义

加权分析做出来的风险比经常带着很宽的区间,1.05 到 1.6 这种结果太常见了。这时候纠结“是否显著”没什么意义,更有价值的是问:这个区间的下界对应的效应量,临床上算不算值得改变实践?如果下界是 1.02,那即便统计上显著,临床上也未必有意义;如果下界是 1.3,那即便区间很宽,也值得认真对待。

我在写讨论的时候会专门用一段来谈这个,把统计不确定性和临床决策分开讲。这段往往是审稿人评价最高的一段,因为它显示你不是在追 p 值,而是在回答一个真实的问题。

7.4 结论宁可窄一点,也别硬撑

最后说个心态上的事。TTE 做出来的结果,很多时候是“在某些假设下,某个人群的某个策略对比,效应大概是这个范围”。这个表述听起来不痛快,但它诚实。我见过不少分析为了追求“有故事”,硬把不稳健的结果包装成确定结论,最后要么被审稿人拆穿,要么发表后被后续研究推翻。

反过来,一个老老实实说“我们的结果对策略窗口长度敏感,在 30 天窗口下效应明显,在 90 天窗口下效应减弱”的分析,反而更容易被接受,因为它给读者提供了判断依据。数据能支撑多强的结论,就写多强,剩下的交给后续研究去补。这不是保守,这是把不确定性当成结果的一部分来报告,也是这套框架最核心的精神——我们模拟的是一个试验,而任何试验都有它回答不了的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询