MMRM 在临床试验中的 SAS 实现
PROC MIXED:
每一个关键词读走了数据里的哪一列、在数学上触发了哪一步运算、最终如何落到 TLF 表格。阅读导览
① 先看 §2 的联动动画
九步分解:点「下一步」,左边数据表的列会亮起,右边代码里对应的 token 同时亮起,中间用曲线连起来 —— 直观回答「这个参数到底在读哪一列」。
② 再看 §5 的参数沙盘
改 TYPE=、DDFM=、
METHOD=、是否加交互项,代码实时重写,右侧同步显示「协方差参数个数 / 设计矩阵列数 / 自由度怎么算」。
③ 多队列项目直接看 §3.10 / §6.5
LSMESTIMATE 的系数定位器(点格子生成代码)、
方括号里到底填 level 序号还是变量值、Pooled 汇总组的 divisor=4 与 −4,
以及 5 组 + Pooled 的完整生产代码。§9 是这次对话 32 问的逐条复盘。
MMRM 是什么、为什么临床上偏爱它
Mixed Model for Repeated MeasuresMMRM 是「对纵向重复测量数据做的、以访视为类别因子的、带受试者内相关结构的线性混合模型」。 它之所以成为慢病 / CNS / 免疫等长期随访试验主要疗效分析的默认方法,核心只有一句话: 它在 MAR 假设下用全部可观测数据做直接似然估计,不需要任何填补。
1.1 模型形式
设受试者 i 在访视 j(j = 1…k)的相对基线变化为 Yij:
| 符号 | SAS 里对应的写法 | 含义与临床解读 |
|---|---|---|
| τt(i) | TRTP(CLASS) | 治疗主效应;单独看几乎不用,重点在交互项 |
| γj | AVISIT(CLASS) | 访视主效应 —— 访视按类别因子进模型,不假设时间上的线性/任何形状 |
| (τγ)tj | TRTP*AVISIT | 治疗×访视交互;这是 MMRM 的灵魂,它允许每个访视有各自的组间差 |
| β·base | BASE(连续协变量) | 基线校正,提高精度;很多 SAP 还加 BASE*AVISIT,允许基线效应随时间衰减 |
| Σ | REPEATED … TYPE=UN | 受试者内 k×k 协方差矩阵;建模的是残差相关,不是随机效应 |
TRTP*AVISIT,模型就变成「所有访视共用同一个组间差」,你将拿不到分访视的 LS Mean 差值,
这与绝大多数 SAP 里「Week 24 的 LS mean difference」这一主要终点定义直接冲突。1.2 为什么不是 LOCF / 不是逐访视 ANCOVA
| 方法 | 缺失数据怎么处理 | 受试者内相关 | 有效假设 | 问题 |
|---|---|---|---|---|
| LOCF | 把上一次观测值搬到后面所有访视 | 被人为抹平 | 极强且不可检验 | 方差被低估 → I 类错误膨胀;对退化性疾病系统性偏乐观。监管已基本不接受作为主分析 |
| 逐访视 ANCOVA (Completers) | 直接删掉缺失者 | 完全忽略 | MCAR | 只用完成者 → 选择偏倚;每个访视样本量不同,结果不可比 |
| MMRM | 不填补,缺的行不进似然 | 显式建模 Σ | MAR | 需要预先指定协方差结构;UN 在小样本/多访视时可能不收敛 |
| Multiple Imputation + ANCOVA | 多重填补后合并 | 在填补模型里 | MAR(可扩展到 MNAR) | 步骤多、需 Rubin 合并;作为敏感性分析或 MNAR 情形的主力 |
1.3 MMRM 与 Estimand 的关系(ICH E9(R1))
MMRM 本身只是一个估计方法,它对应哪个 estimand,取决于你把哪些数据放进模型:
| Intercurrent Event 策略 | 数据处理 | MMRM 的角色 |
|---|---|---|
| Hypothetical (若未停药) | 停药后的数据不进模型(或本就没采集) | 标准 MMRM。这是历史上最常见的 “efficacy / de jure” estimand |
| Treatment Policy (不论是否停药) | 停药后仍继续随访并采集,全部进模型 | MMRM 仍可用,但缺失的是「本应采集却没采到」的部分;此时 MAR 更难成立,通常配合 retrieved dropout / 控制组填补 |
| Composite | 停药本身编码为不良结局(如记为无应答) | 一般改用二分类模型,MMRM 不直接适用 |
| While on treatment | 只用在治期间的观测 | MMRM 可用,但访视窗定义要重新设计 |
数据 ↔ 代码 联动演示
Which column does each keyword read?左边是一份 ADaM BDS 长格式数据(3 名受试者 × 4 个访视,含 2 个缺失),右边是 MMRM 的完整代码。 点「下一步」逐步推进:亮起的列就是这一步真正被读取的数据,曲线连到代码里对应的关键字, 下方卡片解释这一步在数学上做了什么运算。
| USUBJID | TRTP | AVISITN | AVISIT | BASE | AVAL | CHG |
|---|---|---|---|---|---|---|
| 101-001 | Drug A | 4 | Week 4 | 22.0 | 20.5 | -1.5 |
| 101-001 | Drug A | 8 | Week 8 | 22.0 | 19.8 | -2.2 |
| 101-001 | Drug A | 16 | Week 16 | 22.0 | 18.9 | -3.1 |
| 101-001 | Drug A | 24 | Week 24 | 22.0 | 18.1 | -3.9 |
| 101-002 | Placebo | 4 | Week 4 | 24.0 | 23.6 | -0.4 |
| 101-002 | Placebo | 8 | Week 8 | 24.0 | 23.9 | -0.1 |
| 101-002 | Placebo | 16 | Week 16 | 24.0 | 24.4 | 0.4 |
| 101-002 | Placebo | 24 | Week 24 | 24.0 | . | . |
| 101-003 | Drug A | 4 | Week 4 | 19.0 | 18.2 | -0.8 |
| 101-003 | Drug A | 8 | Week 8 | 19.0 | . | . |
| 101-003 | Drug A | 16 | Week 16 | 19.0 | 16.9 | -2.1 |
| 101-003 | Drug A | 24 | Week 24 | 19.0 | 16.5 | -2.5 |
ods output LSMeans=lsm Diffs=dif CovParms=cov SolutionF=fix Tests3=t3 ConvergenceStatus=cs; proc mixed data=ADQS method=REML; class USUBJID TRTP AVISIT; model CHG = BASE TRTP AVISIT TRTP*AVISIT / ddfm=KR solution cl; repeated AVISIT / subject=USUBJID type=UN r rcorr; lsmeans TRTP*AVISIT / diff cl alpha=0.05;run;
读入分析数据集
PROC MIXED 全参数解剖
Statement-by-statement referenceMMRM 只用到 PROC MIXED 的一小部分能力。下面按语句罗列,加粗的是临床项目里真正会用到的, 其余作为「知道它存在」的储备 —— 遇到审评问询或收敛问题时会派上用场。
3.1 语句总览
| 语句 | 可重复 | 作用 | MMRM 中的角色 |
|---|---|---|---|
| PROC MIXED | — | 全局设置:估计方法、收敛、输出 | 必需 METHOD=REML |
| CLASS | 否 | 声明分类变量 | 必需 受试者、治疗、访视 |
| MODEL | 否 | 固定效应(唯一必需语句) | 必需 |
| REPEATED | 否 | R 侧:残差协方差结构 | 必需 MMRM 的定义性语句 |
| RANDOM | 是 | G 侧:随机效应 | 一般不用 见 3.6 |
| LSMEANS | 是 | 最小二乘均值与两两差 | 必需 主要终点来源 |
| LSMESTIMATE | 是 | 对 LS Means 做自定义线性组合 | 多队列必需 Pooled 汇总组、加权对比 —— 见 §3.10 |
| ESTIMATE | 是 | 任意线性组合 L′β(作用在参数上) | 常用 跨访视平均、特定对比 |
| CONTRAST | 是 | 线性假设的 F 检验 | 多剂量组整体检验 |
| SLICE | 是 | 对 LS Means 做分片简单效应检验 | 等价于 LSMEANS … / SLICE=,可单独成句 |
| STORE | 否 | 把模型上下文存成 item store | 配 PROC PLM 事后再做对比,不必重跑模型 |
| PARMS | 否 | 协方差参数初值 / 固定值 | 救急 收敛失败时给初值 |
| ID / BY / WEIGHT | — | 输出附带变量 / 分组运行 / 加权 | BY 用于亚组批量分析 |
| PRIOR | 否 | 贝叶斯后验抽样 | 基本不用 |
LSMESTIMATE、SLICE、STORE 是 SAS/STAT 9.22 起加入 PROC MIXED 的「共享语句」,
在 MIXED 里完全可用(很多老资料仍说它们只属于 GLIMMIX,那是 9.2 之前的情况)。
真正只在 GLIMMIX 里有的是 NLOPTIONS 语句和 COVTEST 语句 ——
MIXED 的优化控制写在 PROC 语句选项上(MAXITER=、CONVH=、RIDGE=、SCORING=),
协方差参数检验用 PROC 语句的 COVTEST 选项。3.2 PROC MIXED 语句选项
| 选项 | 含义 | 临床里怎么填 |
|---|---|---|
| DATA= | 输入分析数据集 | 通常带 (where=(ITTFL='Y' and ANL01FL='Y')),把分析集筛选写在这里最不容易出错 |
| METHOD= | REML / ML / MIVQUE0 / TYPE1‑3 | REML(默认)。REML 对协方差参数无偏,小样本更稳;ML 仅在需要用似然比检验比较固定效应时才用 |
| CL | 协方差参数的置信限 | 需要报告方差成分时加 |
| COVTEST | 协方差参数的 Wald Z 检验 | 参考价值有限(边界问题),一般不加 |
| EMPIRICAL | 三明治(robust / sandwich)方差 | 协方差结构可能设错时的敏感性分析;小样本会低估方差,谨慎 |
| NOCLPRINT | 不打印 CLASS 水平清单 | USUBJID 有几百个水平时必加,否则 LST 爆炸;写 NOCLPRINT=20 可只压超过 20 水平的 |
| NOITPRINT | 不打印迭代历史 | 生产脚本常加;但调试收敛时要去掉 |
| NOINFO / NOPROFILE | 抑制信息表 / 不剖面化残差方差 | NOPROFILE 偶尔能救回收敛 |
| MAXITER= / MAXFUNC= | 最大迭代 / 函数求值次数 | 默认 50 / 150;UN 多访视时可提到 200 / 1000 |
| CONVH= / CONVF= / CONVG= | 收敛判据(Hessian 相对梯度 / 函数 / 梯度) | 默认 CONVH=1E‑8。放宽收敛判据来「制造收敛」在监管上是有风险的,需在 SAP 或注释中说明 |
| RIDGE= / SCORING= | 岭因子 / 前 n 次迭代用 Fisher scoring | SCORING=5 是最常见的救收敛手段之一 |
| NOBOUND | 允许方差估计为负 | 能让「边界上不收敛」变成收敛,但结果不再是合法协方差矩阵,主分析不建议 |
| IC | 输出一整套信息准则 | 比较协方差结构时方便 |
| ORDER= | CLASS 水平排序:FORMATTED / INTERNAL / DATA / FREQ | MMRM 里的高频坑,见 §8.1 |
| ASYCOV / ASYCORR | 协方差参数估计的渐近协方差 | 做 delta 方法推断时需要 |
| PLOTS= | ODS Graphics 诊断图 | PLOTS=(RESIDUALPANEL STUDENTPANEL) 做模型诊断 |
3.3 CLASS 语句
class USUBJID TRTPN(ref='0') AVISITN / order=internal;- USUBJID 必须在 CLASS 里(除非用
SUBJECT=且它已是分类变量)—— 它定义了协方差矩阵的分块。 - 访视必须是分类变量。把 AVISITN 当连续变量放进 MODEL,模型就变成线性斜率模型,不再是 MMRM。
(ref='...')只在配合MODEL … / SOLUTION看参数估计时影响解读;LS Means 的差值不受参考水平影响(只影响符号方向的呈现方式,DIFF 输出里两个方向都能查到)。- 把 USUBJID 放在 CLASS 会打印几百行水平表 —— 配
NOCLPRINT。
3.4 MODEL 语句
model CHG = BASE TRTPN AVISITN BASE*AVISITN TRTPN*AVISITN / ddfm=KR solution cl alpha=0.05 residual outp=pred;
| 选项 | 含义 | 说明 |
|---|---|---|
| DDFM= | 分母自由度算法 | 见下方对照表,MMRM 的关键选项 |
| SOLUTION | S | 打印固定效应参数估计 | QC 必看;主表一般不呈现 |
| CL / ALPHA= | 固定效应的置信限 | 默认 0.05 |
| CHISQ | 额外给出 Wald 卡方检验 | 大样本时与 F 检验接近 |
| HTYPE= / E1 E2 E3 | 假设检验类型(默认 Type 3)与系数打印 | MMRM 用 Type 3 |
| COVB / CORRB | 固定效应估计的协方差 / 相关阵 | 手工做 delta 方法或组合估计时需要 |
| OUTP= / OUTPM= | 输出预测值:含随机效应 / 仅边际 | MMRM 只有 R 侧,两者基本一致;OUTPM 更贴近「群体平均」 |
| RESIDUAL / VCIRY | 输出学生化、Cholesky 残差 | 正态性与方差诊断 |
| INFLUENCE | 影响诊断(Cook's D 等) | INFLUENCE(EFFECT=USUBJID ITER=5) 找异常受试者 |
| NOINT | 不含截距 | MMRM 不用 |
DDFM= 对照
| 取值 | 做法 | 是否调整 SE | 什么时候选 |
|---|---|---|---|
| KENWARDROGER (KR) | 对固定效应协方差做偏倚校正,再算近似自由度 | 是 | MMRM 的行业默认。小样本、UN 结构下 I 类错误控制最好。SAS 9.4 后可写 KR(LINEAR) 或 KR2 变体 |
| SATTERTHWAITE (SATTERTH) | Satterthwaite 近似自由度 | 否 | KR 太慢或不收敛时的替代;样本量大时与 KR 几乎一致 |
| BETWITHIN (BW) | 把自由度拆成受试者间/内 | 否 | REPEATED 模型的默认值。速度快,但小样本略激进 |
| RESIDUAL | 残差自由度 = n − rank(X) | 否 | 会严重高估自由度,MMRM 不用 |
| CONTAIN | 包含法(默认,用于 RANDOM 模型) | 否 | MMRM 不用 |
3.5 REPEATED 语句 —— MMRM 的定义性语句
repeated AVISITN / subject=USUBJID type=UN r rcorr;* 每臂各估一套协方差(参数翻倍);repeated AVISITN / subject=USUBJID type=UN group=TRTPN;* 访视间隔不等时的空间幂结构:不写重复效应,给连续时间变量;repeated / subject=USUBJID type=sp(pow)(AWEEK);
| 成分 | 含义与注意点 |
|---|---|
| 重复效应 (AVISITN) | 决定 Σ 的行列顺序。必须是 CLASS 变量。它的水平顺序 = 矩阵的第 1…k 行 —— 对 AR(1)/TOEP 这类依赖「相邻」的结构顺序错了结果就错。同一受试者内不能有重复水平(会报 “subject … has repeated levels”)。 |
| SUBJECT= | 定义分块:不同 SUBJECT 之间残差独立。可写成 subject=USUBJID,也可写 subject=SITE*SUBJID。数据建议按 SUBJECT 排序以提高效率。 |
| TYPE= | 协方差结构,见 §4。默认 VC(即 σ²I,等价于不考虑相关)。 |
| R / RCORR | 打印第一个受试者的 R 矩阵 / 相关阵。写 r=1,2,3 可指定第几个受试者。QC 时必看:能立刻发现维度或顺序不对。 |
| GROUP= | 按组分别估协方差。参数个数 × 组数,收敛难度陡增;用于两组变异明显不同的情形。 |
| LOCAL= / LOCALW | 在结构上再叠加异方差(如 local=exp(covariate)) |
| SSCP / LDATA= | 用平方和叉积形式 / 从数据集读入结构 |
3.6 RANDOM vs REPEATED:G 侧与 R 侧
标准 MMRM:只用 R 侧
不写 RANDOM,直接令 Vi = Ri = Σ(UN)。 因为 UN 已经是最一般的 k×k 对称正定矩阵,再加随机截距不会增加任何拟合能力,只会造成参数不可识别 / 收敛失败。
random intercept / subject=usubjid; 和 repeated / type=un; 是常见错误。什么时候才需要 RANDOM
- 中心效应:
random intercept / subject=SITEID;把中心作为随机效应(多中心试验的一种常见处理) - 随机斜率增长曲线模型(不是 MMRM):
random intercept AWEEK / subject=usubjid type=un; - 此时 REPEATED 通常降为
type=VC或不写
RANDOM 的常用选项:G GCORR V VCORR SOLUTION GROUP= TYPE= SUBJECT=
3.7 LSMEANS 语句
| 选项 | 含义 | MMRM 中的用法 |
|---|---|---|
| DIFF | PDIFF | 所有两两差;DIFF=CONTROL('0') 只对参照组 |
多剂量组时用 diff=control 省掉无关比较 |
| CL / ALPHA= | LS Mean 及其差值的置信限 | 主表要报 95% CI,必加 |
| SLICE= | 在指定因子的每个水平内做简单效应 F 检验 | slice=AVISITN 得到「每个访视内治疗效应」的 F 检验 |
| AT | 把连续协变量固定在指定值 | at BASE=20;默认是总体均值 |
| OM / BYLEVEL | 用观测边际权重代替等权 | 各访视/各组样本量差异大时的替代加权方式 |
| ADJUST= | 多重性调整:TUKEY / DUNNETT / BON / SIDAK / SIMULATE … | SAP 若规定层级检验,一般不在这里调整 |
| E | 打印 L 矩阵 | 核对 ESTIMATE 系数时的黄金工具 |
| COV / CORR | LS Means 之间的协方差 | 手工合并多个访视的估计时需要 |
lsmeans TRTP*AVISIT / diff; 会产出全部 C(8,2)=28 对两两比较,其中包含「A 组 Week 4 vs P 组 Week 24」这类无意义比较。
正确做法:从 Diffs 数据集里筛 where AVISIT = _AVISIT(同访视内比较),或用 ESTIMATE 精确指定。3.8 ESTIMATE / CONTRAST:系数怎么数
ESTIMATE 的系数顺序 = CLASS 水平的排序;交互项按「左因子慢变、右因子快变」展开。以
class TRTPN AVISITN、TRTPN∈{0,1}、AVISITN∈{4,8,16,24} 为例:
| 效应 | 系数位置(从左到右) |
|---|---|
| TRTPN | [0] [1] |
| AVISITN | [4] [8] [16] [24] |
| TRTPN*AVISITN | [0,4] [0,8] [0,16] [0,24] | [1,4] [1,8] [1,16] [1,24] |
/* Week 24 的组间差:Drug A(1) − Placebo(0) */estimate 'A - P @ Week 24' TRTPN -1 1 TRTPN*AVISITN 0 0 0 -1 0 0 0 1 / cl e; /* 跨 4 个访视的平均治疗效应(等权) */estimate 'A - P averaged over visits' TRTPN -4 4 TRTPN*AVISITN -1 -1 -1 -1 1 1 1 1 / divisor=4 cl;
BASE 与 BASE*AVISITN 在两个 LS Mean 里取值完全相同,
相减后自动抵消。所以即使模型里有基线交互项,ESTIMATE 里也不需要写它们。
永远用 / e 打印 L 矩阵,与 lsmeans … / diff 的结果核对一次。3.9 ODS OUTPUT 可提取的数据集
| ODS 表名 | 内容 | 下游用途 |
|---|---|---|
| LSMeans | 各 TRTP×AVISIT 的 LS Mean、SE、DF、CI | 主表的「LS Mean (SE)」列 |
| Diffs | 两两差值、SE、DF、tValue、Probt、CI | 主表的「Difference (95% CI), p」列;需筛同访视 |
| ConvergenceStatus | Status(0=收敛)、Reason、pdG、pdH | 自动降级逻辑的判据 |
| CovParms | 协方差参数估计 | QC、附录、结构比较 |
| SolutionF | 固定效应参数估计 | QC |
| Tests3 | Type 3 固定效应检验(含交互项 p) | 报告 treatment-by-visit interaction |
| FitStatistics / InfoCrit | −2 Res Log Lik、AIC、AICC、BIC | 协方差结构比较 |
| R / RCorr | R 矩阵与相关阵 | 附录、QC |
| Slices | SLICE= 的简单效应 F 检验 | 各访视内整体治疗效应 |
| Estimates / Contrasts | ESTIMATE / CONTRAST 结果 | 跨访视平均等自定义估计 |
| NObs / Dimensions / ClassLevels | 观测数、维度、水平 | QC:核对进模型的记录数与受试者数 |
| LSMEstimates | LSMESTIMATE 的估计、SE、DF、Probt、CI | Pooled 汇总组的结果出口 |
3.10 LSMESTIMATE 专题:Pooled 汇总组怎么算
这是多队列 / 多剂量试验(例如 1 个安慰剂 + 4 个队列)绕不开的语句。 LSMEANS 只能吐出模型里客观存在的水平;一旦 SAP 要求「Pooled Active」这种模型里根本不存在的组合, 就必须用 LSMESTIMATE 给 LS Means 做线性加权。
LSMEANS 能做的(直接用,别手写系数)
- 每个
TRTN×AVISITN单元格的 LS Mean - 各队列 vs 安慰剂的同访视差值(
DIFF后筛选) - 各访视内的简单效应 F 检验(
SLICE=)
只能用 LSMESTIMATE 的(2 项)
- Pooled Active 的 LS Mean(4 个队列等权平均)
- Pooled Active − Placebo 的差值(含正确的 SE、DF、p)
3.10.1 两种系数写法:位置向量 vs 名值对
| 写法 | 形式 | 要点 |
|---|---|---|
| 位置向量 (positional) | 'label' 0 0 -1 0 0 1 0 0 0 … | 必须写满 全部 g×k 个位置,顺序为「左因子慢变、右因子快变」。 新增一个访视 → 整套向量作废。数错一位不报错,只是算错。 |
| 名值对 (nonpositional) | 'label' [1, 2 3] [-1, 1 3] | 推荐 只写非零项,格式 [系数, 左因子level 右因子level]。
增删访视不影响已写好的语句。 |
也就是说,若
AVISITN 的取值是 28 和 84,那么 Day 84 要写 2(它是第 2 个水平),写 84 会直接报错:
ERROR: level 84 is not valid for CLASS variable AVISITN.
The level specifications for this variable must range from 1 to 2.ORDER= 排序 → 从 1 编号。
数值变量默认升序(28→1,84→2);但只要挂了 FORMAT,默认 ORDER=FORMATTED 会按格式化文本排序,
"Week 12" 会排到 "Week 4" 前面,序号直接颠倒。所以生产代码里请一律写
proc mixed … order=internal;,并用输出里的 Class Level Information 表复核。3.10.2 系数定位器(点格子生成代码)
下面按「1 个安慰剂 + 4 个队列 × 3 个访视」布置。 点格子循环切换系数 0 → +1 → −1 → −4,右侧实时生成两种写法与对应数学式。 先点预设按钮看标准写法,再自己改。
| Class | Levels | Values(从左到右 = Level 1,2,3…) |
|---|---|---|
| TRTN | 5 | 1 2 3 4 5 |
| AVISITN | 3 | 14 28 84 |
这条语句在算什么
3.10.3 为什么 Placebo 填 −4
目标是
(LSM2+LSM3+LSM4+LSM5)/4 − LSM1。
divisor=4 会把括号里所有系数一起除以 4,所以要让 Placebo 的有效系数等于 −1,
写进去的就必须是 −4(−4/4 = −1)。这样做的好处是括号里全是整数,既好读又避免浮点误差。
等价的小数写法是 [0.25,…] [−1, 1 3] 且不加 divisor。
3.10.4 LSMESTIMATE 的前置条件与 QC
| 条件 | 说明 / 违反时的现象 |
|---|---|
| 效应变量必须在 CLASS 里 | 否则 ERROR: Effect is not in CLASS |
| 效应必须在 MODEL 里 | 写 TRTN*AVISITN 就要求 MODEL 含该交互项;否则不可估 |
| level 序号必须在 1…k 范围内 | 超范围直接 ERROR(见上);范围内但填错则静默算错 |
| 交互项里 level 的先后顺序 必须与效应书写顺序一致 |
[1, 2 3] 中第一个 2 给 TRTN、第二个 3 给 AVISITN。写成 AVISITN*TRTN 含义就反了 |
| 对应单元格必须可估 | 某队列在某访视一个人都没有 → 输出 Non-est |
| / e | 必加 打印 L 矩阵系数表,逐格确认 −1 / +1 落在了正确的组与访视上 |
| / elsm | 额外打印参与计算的每个 LS Mean,方便手工验算差值 |
lsmeans TRTN*AVISITN;,记下 LS Means 表的行顺序;
② 用同样的 LSMESTIMATE 写一条已知能被 LSMEANS 算出的对比(如队列 1 vs Placebo);
③ 两边的 Estimate / StdErr / DF 必须逐位相同;④ 相同后,再信任 Pooled 那两条。3.11 Diffs 数据集的方向:带下划线的就是被减数
把下划线 _ 记成减号,带 _ 的那一组就是被减掉的组。由此推出两条实用规则:
| 筛选条件 | 实际算的是 | 结果 |
|---|---|---|
| where _TRTN=1 and AVISITN=_AVISITN | Active − Placebo | 正确 与 diff=control 完全一致 |
| where TRTN=1 and AVISITN=_AVISITN | Placebo − Active | 符号反了 与上面互为相反数 |
DIFF=CONTROL('1') 会报:
ERROR: Wrong number of control levels for TRTN*AVISITN.
- 要「各访视各自比当期安慰剂」(MMRM 最常见)→ 不要用 CONTROL,写
/ pdiff cl, 再筛_TRTN=1 and AVISITN=_AVISITN; - 要把某一个格子当全局锚点 →
/ pdiff=control('1' '84'), 注意这里填的是 变量的格式化值(84),与 LSMESTIMATE 方括号里填 level 序号正好相反。
control('1' '84') 时,Diffs 里所有 AVISITN≠84 的行都是
「某组 Day 28 vs 安慰剂 Day 84」这种跨访视错位比较,临床上无意义,必须在后处理里删掉。PDIFF 不会「只给 p 值」,也不会自动做多重性调整 —— 调整由 ADJUST= 控制。
项目内保持一种写法即可。协方差结构实验室
TYPE= · what the matrix actually looks like点选结构,右边的 4×4 矩阵会实时重画:格子颜色深浅 = 相关系数大小(同一色相由浅到深), 格子里是该结构对应的符号表达式。下方给出参数个数、SAS 写法和临床取舍。示例为 4 个访视(Week 4/8/16/24)。
4.1 结构对照总表
| TYPE= | 参数个数 (k=4) |
异方差 | 假设 | 临床取舍 |
|---|---|---|---|---|
| UN | 10 | ✔ | 不做任何假设 | 首选 监管默认。访视多 (k≥6) 或每臂 < 50 例时易不收敛 |
| UNR | 10 | ✔ | 同 UN,改用「方差+相关」参数化 | 与 UN 等价拟合,收敛行为有时更好 |
| TOEPH | 7 | ✔ | 相关只依赖访视间隔 | 常用降级 1 保留异方差,参数少 3 个 |
| ANTE(1) | 7 | ✔ | 一阶前依赖(相关沿时间累乘) | 纵向数据的自然结构,SAP 里偶见 |
| CSH | 5 | ✔ | 方差随访视变,相关恒定 | 常用降级 2 |
| ARH(1) | 5 | ✔ | 方差随访视变,相关按间隔幂衰减 | 访视等间隔时合理 |
| UN(1) | 4 | ✔ | 对角,方差各异,不相关 | 诊断用;不是真正的 MMRM |
| TOEP | 4 | ✘ | 同方差 + 带状相关 | 少用 |
| CS | 2 | ✘ | 方差恒定、任意两访视相关相同 | 降级末端 等价于随机截距模型;对长期随访通常过强 |
| AR(1) | 2 | ✘ | 相关按 ρ|i−j| 衰减 | 要求等间隔访视 |
| SP(POW)(t) | 2 | ✘ | ρ|ti−tj|,真实时间距离 | 被低估 访视间隔不等(4/8/16/24 周)时,它才是 AR(1) 的正确版本 |
| VC(默认) | 1 | ✘ | σ²I,完全独立 | 等价于逐访视 ANCOVA;忘了写 TYPE= 就会落到这里 |
repeated AVISITN / subject=USUBJID; —— 漏了 type=。SAS 不会报错,
它会安静地用 TYPE=VC(独立),你得到的其实是一个「加权 ANCOVA」,缺失机制的处理优势全部消失。4.2 结构该怎么定?
确证性试验:SAP 预先指定 + 降级序列
不要用 AIC 在解盲后挑结构 —— 那是数据驱动的选择,会被质疑。 标准做法是在 SAP 里写死:UN → TOEPH → CSH → CS,按顺序尝试直到收敛,并在报告中说明实际使用的结构。
探索性 / II 期:可以比 AICC
用 REML 比较协方差结构时,固定效应必须完全相同; 比较固定效应则必须换成 ML。小样本优先看 AICC 而非 AIC。
参数沙盘:填什么 = 算什么
Parameter sandbox改动下面的选项,代码会实时重写,右侧同步显示这一组参数在数学上意味着什么运算量: 设计矩阵有几列、协方差要估几个参数、自由度怎么来、收敛风险多大。
这组参数让 SAS 做什么运算
生产级代码模板
Production-ready macro with fallback三段:① ADaM 侧的数据准备与分析集筛选;② 带收敛降级序列的 MMRM 宏;③ 结果整理成 TLF 可用结构。 可直接复制到项目里改参数。
6.1 数据准备(ADQS / ADVS 等 BDS 结构)
/*-- 分析集:ITT,主要参数,仅基线后访视,且有分析标记 --------------*/data work.anl; set adam.adqs; where PARAMCD = 'ADASTOT' and ITTFL = 'Y' and ANL01FL = 'Y' /* 每访视窗内只保留 1 条记录 */ and AVISITN gt 0 /* 排除基线访视本身 */ and not missing(BASE); /* 无基线者无法进模型 */ keep USUBJID TRTPN TRTP AVISITN AVISIT BASE AVAL CHG SITEGR1 STRAT1;run; /*-- QC:核对进模型的记录数 / 受试者数 / 各访视 n ------------------*/proc sql; create table qc_n as select AVISITN, TRTP, count(distinct USUBJID) as n_subj, sum(case when missing(CHG) then 1 else 0 end) as n_miss from work.anl group by 1,2;quit; /*-- 排序:SUBJECT 连续存放可显著加快 MIXED 的分块计算 -------------*/proc sort data=work.anl; by USUBJID AVISITN; run;
6.2 核心宏:UN → TOEPH → CSH → CS 自动降级
/*--------------------------------------------------------------------* | MACRO : %mmrm | PURPOSE: MMRM 主分析,按 SAP 预设顺序尝试协方差结构直至收敛 | NOTE : 实际使用的结构写入全局宏变量 &MMRM_STRUCT,需在报告中说明 *--------------------------------------------------------------------*/%macro mmrm(data=work.anl, resp=CHG, base=BASE, trt=TRTPN, trtref=0, visit=AVISITN, subj=USUBJID, strat=, covseq=UN TOEPH CSH CS, ddfm=KR, alpha=0.05); %global MMRM_STRUCT MMRM_CONV; %local i nseq struct st done; %let nseq = %sysfunc(countw(&covseq)); %let done = 0; %do i = 1 %to &nseq; %if &done = 0 %then %do; %let struct = %scan(&covseq, &i); %put NOTE: [MMRM] attempt &i -- TYPE=&struct; /* ODS EXCLUDE ALL 抑制打印但保留 ODS OUTPUT 数据集 */ ods exclude all; ods output LSMeans = _lsm Diffs = _dif CovParms = _cov SolutionF = _fix Tests3 = _t3 FitStatistics = _fit RCorr = _rcorr ConvergenceStatus = _cs; proc mixed data=&data method=REML noclprint=20 noitprint maxiter=200 scoring=5; class &subj &trt(ref="&trtref") &visit &strat; model &resp = &base &trt &visit &strat &base*&visit &trt*&visit / ddfm=&ddfm solution cl alpha=α repeated &visit / subject=&subj type=&struct rcorr; lsmeans &trt*&visit / diff cl alpha=α run; ods exclude none; /* Status=0 表示收敛;同时要求 Hessian 正定 (pdH=1) */ %let st = 9; proc sql noprint; select max(Status) into :st from _cs; quit; %if &st = 0 %then %do; %let done = 1; %let MMRM_STRUCT = &struct; %let MMRM_CONV = CONVERGED; %put NOTE: [MMRM] converged with TYPE=&struct; %end; %else %put WARNING: [MMRM] TYPE=&struct failed (Status=&st) -- 降级; %end; %end; %if &done = 0 %then %do; %let MMRM_CONV = FAILED; %put %str(ER)ROR: [MMRM] 所有预设协方差结构均未收敛,请人工介入; %end;%mend mmrm; %mmrm(data=work.anl, strat=STRAT1);
6.3 结果整理成 TLF 结构
/*-- ① LS Mean(每组每访视) -------------------------------------*/data lsm_c; set _lsm; length col_lsm $24; col_lsm = cats(put(Estimate,8.2),' (',put(StdErr,8.2),')'); keep TRTPN AVISITN Estimate StdErr DF Lower Upper col_lsm;run; /*-- ② 只保留「同一访视内 vs 参照组」的比较 ----------------------* | Diffs 的 Estimate = LSM(TRTPN) - LSM(_TRTPN) | 故取 _TRTPN=0 即为「试验组 - 安慰剂」 *---------------------------------------------------------------*/data dif_c; set _dif; where AVISITN = _AVISITN /* 同访视 */ and _TRTPN = 0; /* 对照 = 安慰剂 */ length col_diff $34 col_p $10; col_diff = cats(put(Estimate,8.2),' (',put(Lower,8.2),', ', put(Upper,8.2),')'); col_p = put(Probt, pvalue6.4); keep TRTPN AVISITN Estimate StdErr DF Lower Upper Probt col_diff col_p;run; /*-- ③ 脚注自动带上实际使用的协方差结构(合规要求) --------------*/%let fn1 = %str(Covariance structure actually used: &MMRM_STRUCT (&MMRM_CONV).); /*-- ④ 跨访视平均效应(若 SAP 需要) -----------------------------*/proc mixed data=work.anl method=REML noclprint; class USUBJID TRTPN AVISITN; model CHG = BASE TRTPN AVISITN BASE*AVISITN TRTPN*AVISITN / ddfm=KR; repeated AVISITN / subject=USUBJID type=UN; estimate 'A - P averaged over visits' TRTPN -4 4 TRTPN*AVISITN -1 -1 -1 -1 1 1 1 1 / divisor=4 cl e;run;
6.4 敏感性分析:MNAR 方向
/*-- 基于对照组的填补 (Control-Based / J2R):把试验组退出者 的后续走势拉回安慰剂组,是最常用的保守 MNAR 敏感性分析 --*/proc mi data=wide seed=20260812 nimpute=100 out=mi_out; class TRTPN; var TRTPN BASE W4 W8 W16 W24; monotone reg(W4 W8 W16 W24 = TRTPN BASE); mnar model(W4 W8 W16 W24 / modelobs=(TRTPN='0')); /* 用安慰剂组建模 */run; /*-- 再逐个填补集跑 ANCOVA / MMRM,最后用 MIANALYZE 合并 --*/proc mianalyze data=est_all; modeleffects Estimate; stderr StdErr;run;
6.5 案例:5 组多队列 + Pooled 汇总组的完整实现
场景:TRTN = 1 (Placebo) + 2~5 (四个队列),AVISITN = 14 / 28 / 84,
SAP 要 11 项输出:5 个单组 LS Mean + 1 个 Pooled LS Mean + 4 个单组 vs Placebo + 1 个 Pooled vs Placebo。
推荐写法:LSMEANS 拿那 9 项,只用一条 LSMESTIMATE 补 Pooled 的 2 项 ——
比把 11 项全手写成 LSMESTIMATE 的出错概率低一个数量级。
/* Day 84 是 AVISITN 排序后的第 3 个 level(14→1, 28→2, 84→3) —— 方括号里填的是 level 序号,不是 84!先看 Class Level Information 确认 */%let vlev = 3; ods output LSMeans = raw_lsm Diffs = raw_dif LSMEstimates = raw_pool ConvergenceStatus = cs; proc mixed data=ads1(where=(avisitn gt 0 and not missing(chg) and not missing(base))) method=REML order=internal noclprint=20; class SUBJID TRTN AVISITN; model CHG = BASE TRTN AVISITN TRTN*AVISITN / ddfm=satterthwaite s cl; repeated AVISITN / subject=SUBJID type=UN rcorr; /* (1) 9 项:5 个单组 LSM + 全部两两差(后处理再筛同访视 vs Placebo) */ lsmeans TRTN*AVISITN / cl pdiff alpha=0.05; /* (2) 2 项:Pooled Active 的 LSM 与 Pooled vs Placebo 的差值 */ lsmestimate TRTN*AVISITN 'LSM for Pooled Active' [1, 2 &vlev] [1, 3 &vlev] [1, 4 &vlev] [1, 5 &vlev] divisor=4, 'Diff LSM for Pooled Active vs Placebo' [1, 2 &vlev] [1, 3 &vlev] [1, 4 &vlev] [1, 5 &vlev] [-4, 1 &vlev] divisor=4 / cl alpha=0.05 e; /* e = 打印 L 矩阵,QC 必看 */run;
/* --- 单组 LS Mean (ord 1-5) --------------------------------------- */data ds_lsm; set raw_lsm; length item $40 type $4; select(TRTN); when(1) do; ord=1; item='Placebo'; end; when(2) do; ord=2; item='Cohort 1'; end; when(3) do; ord=3; item='Cohort 2'; end; when(4) do; ord=4; item='Cohort 3'; end; when(5) do; ord=5; item='Cohort 4'; end; otherwise; end; type='LSM'; keep AVISITN ord item type Estimate StdErr DF Lower Upper;run; /* --- 单组 vs Placebo 差值 (ord 7-10) _TRTN 是被减数 → _TRTN=1 才是 Active - Placebo;筛 trtn=1 会得到相反数 */data ds_dif; set raw_dif; where _TRTN = 1 and AVISITN = _AVISITN; length item $40 type $4; select(TRTN); when(2) do; ord=7; item='Cohort 1 vs Placebo'; end; when(3) do; ord=8; item='Cohort 2 vs Placebo'; end; when(4) do; ord=9; item='Cohort 3 vs Placebo'; end; when(5) do; ord=10; item='Cohort 4 vs Placebo'; end; otherwise; end; type='DIFF'; keep AVISITN ord item type Estimate StdErr DF Probt Lower Upper;run; /* --- Pooled 两项 (ord 6, 11) 注意:LSMEstimates 数据集里没有 AVISITN 列,只有 Label,需自己贴回访视 */data ds_pool; set raw_pool; length item $40 type $4; AVISITN = 84; if index(Label, 'Diff') then do; ord=11; item='Pooled Active vs Placebo'; type='DIFF'; end; else do; ord=6; item='Pooled Active'; type='LSM'; end; keep AVISITN ord item type Estimate StdErr DF Probt Lower Upper;run; /* --- 合并:只 SET 不写 BY!三个输入没有共同的排序顺序, 写 by avisitn ord 会直接报 "BY variables are not properly sorted" --- */data all_res; set ds_lsm ds_pool ds_dif;run; proc sort data=all_res; by AVISITN ord; run; /* --- TFL 文本列 ---------------------------------------------------- */data tfl_res; set all_res; length c_est $24 c_ci $30 c_p $10; if not missing(Estimate) then c_est = cats(put(Estimate,8.2), ' (', put(StdErr,7.2), ')'); if not missing(Lower) then c_ci = cats('(', put(Lower,8.2), ', ', put(Upper,8.2), ')'); if type='DIFF' and not missing(Probt) then c_p = ifc(Probt lt 0.0001, '<0.0001', put(Probt, pvalue6.4));run;
COHORT 是由 TRT 直接派生的(每个队列对应唯一治疗组),两者完全别名:
模型里同时放 COHORT 和 TRT*AVISIT 会让部分参数不可估,
输出中出现 Non-est 或某些效应的 DF 被置 0。SAS 不会崩,但
LS Means 是否仍可估必须逐项核对。实务上有两条合规路径:① 严格按 SAP 保留
COHORT,并在 QC 文档中记录哪些估计变成 Non-est;
② 若确认完全别名,向统计师提 SAP clarification,把 COHORT 从模型中移除并留下书面依据 ——
不要自行在代码里静默注释掉。输出解读与 TLF 落地
Reading the LST, filling the shell下面是一次真实结构的 PROC MIXED 输出(数值为模拟)。灰色注释是看这段时该问自己的问题。
7.1 输出片段逐段读
Covariance Structure Unstructured Subject Effect USUBJID Estimation Method REML Residual Variance Method None Fixed Effects SE Method Kenward-Roger Degrees of Freedom Method Kenward-Roger Covariance Parameters 10 Columns in X 14 Subjects 240 Max Obs Per Subject 4 Number of Observations Read 960 Number of Observations Used 871 Number of Observations Not Used 89
Convergence criteria met.
Cov Parm Subject Estimate
UN(1,1) USUBJID 4.0132
UN(2,1) USUBJID 3.4820
UN(2,2) USUBJID 5.8104
UN(3,1) USUBJID 3.3391
UN(3,2) USUBJID 5.1552
UN(3,3) USUBJID 7.4988
UN(4,1) USUBJID 3.2160
UN(4,2) USUBJID 4.8419
UN(4,3) USUBJID 6.8951
UN(4,4) USUBJID 9.2077
Estimated G matrix is not positive definite 类警告。-2 Res Log Likelihood 4218.6 AIC (Smaller is Better) 4238.6 AICC (Smaller is Better) 4238.9 BIC (Smaller is Better) 4273.4 Effect Num DF Den DF F Value Pr > F BASE 1 236.0 118.42 <.0001 TRTPN 1 238.4 22.07 <.0001 AVISITN 3 235.7 14.85 <.0001 BASE*AVISITN 3 233.9 3.11 0.0267 TRTPN*AVISITN 3 234.6 9.64 <.0001
TRTPN*AVISITN 的 p<.0001 说明治疗效应随时间变化(差值在扩大),
这正是要分访视报告 LS Mean 差的理由。注意 Den DF 是非整数(234.6)—— 这是 KR / Satterthwaite 的特征,
看到整数自由度就要回头检查 DDFM 是不是掉回 BW / RESIDUAL 了。Effect TRTPN AVISITN Estimate StdErr DF Lower Upper TRTPN*AVISITN 0 24 0.9412 0.4585 231 0.0378 1.8446 TRTPN*AVISITN 1 24 -2.9188 0.4498 229 -3.8051 -2.0325
_TRTPN TRTPN AVISITN _AVISITN Estimate StdErr DF tValue Pr>|t| Lower Upper
0 1 4 4 -0.8604 0.4331 228.7 -1.99 0.0479 -1.7138 -0.0070
0 1 8 8 -1.7135 0.4980 231.2 -3.44 0.0007 -2.6948 -0.7322
0 1 16 16 -2.7908 0.5673 230.4 -4.92 <.0001 -3.9086 -1.6730
0 1 24 24 -3.8600 0.6420 229.8 -6.01 <.0001 -5.1250 -2.5950
Estimate = LSM(TRTPN) − LSM(_TRTPN)。这里 _TRTPN=0(安慰剂)为被减项,
所以负值 = 试验组下降更多 = 有效(ADAS-Cog 分数越低越好)。方向一定要在程序里写死并在脚注说明,
不要靠人工判断符号。7.2 图形:LS Mean 轮廓图
相对基线变化的 LS Mean 及 95% 置信区间(MMRM, ITT)
7.3 图形:治疗差值随访视变化
Drug A − Placebo 的 LS Mean 差值及 95% CI
7.4 主表 shell 映射
| Visit | LS Mean Change (SE) | Difference (95% CI) | p-value | |
|---|---|---|---|---|
| Placebo (N=120) | Drug A (N=120) | |||
| Week 4 | −0.42 (0.31) | −1.28 (0.30) | −0.86 (−1.71, −0.01) | 0.0479 |
| Week 8 | −0.15 (0.36) | −1.86 (0.35) | −1.71 (−2.69, −0.73) | 0.0007 |
| Week 16 | 0.38 (0.41) | −2.41 (0.40) | −2.79 (−3.91, −1.67) | <0.0001 |
| Week 24 主要终点 | 0.94 (0.46) | −2.92 (0.45) | −3.86 (−5.13, −2.60) | <0.0001 |
_lsm.Estimate / StdErr;「Difference (95% CI)」← dif_c.col_diff;
「p-value」← dif_c.col_p。N 一般用分析集人数而非各访视有数据的人数,若 shell 要求「n at visit」,
需另外从 work.anl 统计并单独成行。常见坑与交付前自检清单
Pitfalls & pre-delivery checklist8.1 高频坑清单
8.2 交付前自检清单
点击勾选(仅本地记录,刷新即清空)。
对话问答复盘
Q&A recap按你的原始提问顺序整理:33 个提问合并为 32 条(两个纯格式化请求合并为 Q18)。每条给出:当时答复的要点、 正确 / 需修正 的判定,以及落到项目里该怎么写。 标红的几条是当时答错或表述不严谨的,请以这里为准。
拓展与参考
Alternatives & references10.1 同一个模型的其他实现
| 工具 | 写法 | 与 PROC MIXED 的差异 |
|---|---|---|
| PROC MIXED | repeated AVISITN / subject=USUBJID type=UN; | 行业基准。KR 自由度、REML、R 侧建模 |
| PROC GLIMMIX | random AVISITN / residual subject=USUBJID type=UN; | 同一个模型的等价写法(residual 关键字 = R 侧)。多了 LSMESTIMATE、
STORE、NLOPTIONS、COVTEST 语句,优化控制更细;能顺带做非正态终点 |
| PROC GENMOD (GEE) | repeated subject=USUBJID / type=UN corrw; | 群体平均模型,用工作相关阵 + 三明治方差。只在 MCAR 下无偏(除非做 IPW 加权), 所以不能直接替代 MMRM 作为主分析 |
R · mmrm 包 | mmrm(CHG ~ TRT*AVISIT + BASE + us(AVISIT|USUBJID), data=) | 专为 MMRM 写的包,支持 KR 与 Satterthwaite,与 SAS 结果通常在数值精度内一致, 是目前 R 侧做监管提交的首选 |
R · nlme::gls | gls(CHG ~ ..., correlation=corSymm(), weights=varIdent()) | 能拟合 UN,但没有 KR 自由度,小样本 p 值会偏小 |
Number of Observations Used)—— 90% 的「SAS 和 R 结果不一致」最后都落在第 ④ 条。10.2 参考文献
- ICH E9(R1) (2019). Addendum on Estimands and Sensitivity Analysis in Clinical Trials. —— estimand 框架的规范来源。
- EMA (2010). Guideline on Missing Data in Confirmatory Clinical Trials (EMA/CPMP/EWP/1776/99 Rev.1).
- National Research Council (2010). The Prevention and Treatment of Missing Data in Clinical Trials.
- Mallinckrodt, C.H. Preventing and Treating Missing Data in Longitudinal Clinical Trials. Cambridge University Press. —— MMRM 与缺失数据的标准参考书。
- Mallinckrodt et al. (2008). Recommendations for the primary analysis of continuous endpoints in longitudinal clinical trials. Drug Information Journal, 42, 303–319. —— 「MMRM 作为主分析」的经典论证。
- Kenward, M.G. & Roger, J.H. (1997). Small sample inference for fixed effects from restricted maximum likelihood. Biometrics, 53, 983–997.
- Siddiqui, O., Hung, H.M.J., O'Neill, R. (2009). MMRM vs. LOCF: a comprehensive comparison. Journal of Biopharmaceutical Statistics, 19, 227–246.
- SAS Institute. SAS/STAT User's Guide: The MIXED Procedure —— TYPE= 结构清单与选项的权威定义。
- Sadler et al. (2023). The
mmrmR package. —— R 侧实现与 SAS 的一致性验证。