🧩 ADaM 编程模式

← 知识体系

这里有什么?

从ADAE、ADCM、ADPC中提炼的反复出现的编程模式。不是列出所有变量怎么算——而是"这种数据结构怎么处理"的通用解法。

模式一:连续AE分组

同一个受试者、同一个PT的多次AE——如果前一事件的结束日紧接后一事件的开始日(相邻),视为同一段连续事件。需要给它们打上相同的groupid。

解法:RETAIN groupid + pre_aendt

SAS
/* 连续AE分组:相邻事件 → 同一groupid */
proc sort data=ADAE4D; by USUBJID AEDECOD ASTDT; run;

data ADAE5A;
    set ADAE4D;
    by USUBJID AEDECOD ASTDT;
    retain GROUPID PRE_AENDT;

    if first.AEDECOD then do;
        GROUPID  = 1;
        PRE_AENDT = AENDT;
    end;
    else do;
        if PRE_AENDT = ASTDT then GROUPID = GROUPID;    /* 相邻:同组 */
        else GROUPID + 1;                          /* 不连续:新组 */
        PRE_AENDT = AENDT;
    end;
run;
原理:PRE_AENDT存上一个事件的结束日。当前事件的开始日==上一个结束日→相邻→同组。否则新组。GROUPID+1是SAS的SUM语句(自动retain)。

然后基于分组取统计

SAS
/* 取每组首条记录,计算首次发生时间 */
proc sort data=ADAE5A out=A; by USUBJID AEDECOD GROUPID ASTDT; run;
data A1;
    set A;
    by USUBJID AEDECOD GROUPID ASTDT;
    if first.GROUPID;
    ADUR1 = ASTDT - TRTSDT + (ASTDT >= TRTSDT);  /* 首次发生天数 */
    keep USUBJID AEDECOD GROUPID ADUR1;
run;

模式二:ADCM周期展开

一条合并用药记录可能同时属于第一周期和第二周期。需要展开为多行——每行代表一个周期的归属。

SAS
/* ADCM:一条记录同时属于多周期时output多行 */
data ADCM3;
    set ADCM2;
    length APERIODC $40;

    /* 同时满足周期1和周期2 → output两次 */
    if ASTDT <= TR02SDT and (AENDT = . or AENDT >= TR01SDT) then do;
        APERIOD  = 1;
        APERIODC = '第一周期';
        output;
    end;
    if (ASTDT <= TR02EDT or ASTDT = .) and
       (AENDT >= TR02SDT or AENDT = .) then do;
        APERIOD  = 2;
        APERIODC = '第二周期';
        output;
    end;
    else do;
        APERIOD  = .;
        APERIODC = '';
        output;
    end;
run;

模式三:ADPC BQL处理

PK浓度数据中的BQL(Below Quantification Limit)需要特殊处理。规则来自SAP,逻辑链较长:

情况处理
Tmax之前BQL设为 0
Tmax之后单个BQL设为 缺失
Tmax之后连续BQL后再出现可测浓度后面的可测浓度也设为 缺失
单个BQL落在两可测浓度之间设为 缺失
首次给药前缺失可设为 0
关键是状态追踪:一旦Tmax后出现连续BQL(≥2个),after_consec_bql标志永久置1。之后所有可测浓度都处理为缺失——因为此时药物已基本清除。

模式四:seq & last.variable陷阱

按分组赋seq

SAS
/* 按visitnum内对seq赋值 */
data WANT;
    set HAVE;
    by USUBJID VISITNUM;
    retain SEQ;
    if first.VISITNUM then SEQ = 0;
    SEQ + 1;
run;
⚠️ last.variable不要在if条件内使用。
如果你在if TRTEMFL='Y' then do;块内用if last.USUBJID——二者选不到同一位置时flag会悄然置空。last./first.变量只应在最外层无条件判断。
⚠️ output是提交观测。
执行output之后对该观测的变量修改不会回溯到已输出的记录。如果你output之后再改var,已输出的那条记录不受影响。
⚠️ 计算前确保所有变量非缺失。
if TRTSDT<=ASTDT<=min(TRTSDT+90, SATSTDT)——如果SATSTDT缺失,整个表达式可能出问题。先检查所有变量非缺失再计算。