🔄 数据重塑

← 知识体系

你遇到什么场景?

SDTM的EG/LB/V S数据是长格式(每个参数一行),Table需要宽格式(每个参数一列)。SUPPQUAL的连接动不动就要TRANSPOSE+MERGE好几轮。用对SQL可以一步到位。

决策树

场景推荐方案原因
单表长→宽(EG参数展开)MAX(CASE WHEN)一步完成,无需中间数据集
单表宽→长(分析集Listing)Array + OUTPUT灵活控制每行输出
SUPPQUAL连接主域LEFT JOIN SUPP × N次比TRANSPOSE+MERGE少50%步骤
两表拼接(如DA+SUPPDA)LEFT JOINTRANSPOSE也可,按需求选

代码模板

模板一:单表长→宽(EG参数展开)

ADEG中HR/PR/RR/QRS/QT/QTCF/INTP各占一行 → 变成一行七个变量。推荐MAX(CASE WHEN)一步到位。

SAS
/* 长→宽:一步完成,无需中间数据集 */
proc sql;
    create table final as
    select
        USUBJID,
        VISITNUM,
        EGTPTNUM,
        max(case when PARAMCD = "HR"   then AVALC else "" end) as HR,
        max(case when PARAMCD = "PR"   then AVALC else "" end) as PR,
        max(case when PARAMCD = "RR"   then AVALC else "" end) as RR,
        max(case when PARAMCD = "QRS"  then AVALC else "" end) as QRS,
        max(case when PARAMCD = "QT"   then AVALC else "" end) as QT,
        max(case when PARAMCD = "QTCF" then AVALC else "" end) as QTCF,
        max(case when PARAMCD = "INTP" then AVALC else "" end) as INTP,
        max(case when PARAMCD = "INTP" then EGDESC else "" end) as EGDESC1
    from ADAM.ADEG
    group by USUBJID, VISITNUM, EGTPTNUM;
quit;
要点:MAX在这里不是求最大值——因为每个(paramcd, usubjid, visitnum)组合只有一行,MAX只是把唯一值从CASE WHEN里"捞"出来。这是一种透视技巧,不是聚合计算。

模板二:SUPPQUAL连接——SQL替代TRANSPOSE+MERGE

DA域有SUPPDA存补充信息(如DAIPBSA、IPNUM)。传统做法先TRANSPOSE再MERGE;用SQL的LEFT JOIN直接拿到各QNAM的值。

SAS
/* SUPPDA → 主域DA:SQL替代TRANSPOSE+MERGE */
data SUPPDA;
    set SDTM.SUPPDA;
    DASEQ = input(IDVARVAL, best.);  /* IDVARVAL转数值用于join */
run;

proc sql;
    create table DA1 as
    select A.*,
        B.QVAL as DAIPBSA,
        C.QVAL as IPNUM,
        D.QVAL as DACOM,
        E.QVAL as IPCNCOM
    from SDTM.DA as A
    left join SUPPDA(where=(QNAM="DAIPBSA"))  B
        on A.USUBJID = B.USUBJID and A.DASEQ = B.DASEQ
    left join SUPPDA(where=(QNAM="IPNUM"))    C
        on A.USUBJID = C.USUBJID and A.DASEQ = C.DASEQ
    left join SUPPDA(where=(QNAM="DACOM"))    D
        on A.USUBJID = D.USUBJID and A.DASEQ = D.DASEQ
    left join SUPPDA(where=(QNAM="IPCNCOM")) E
        on A.USUBJID = E.USUBJID and A.DASEQ = E.DASEQ;
quit;

模板三:宽→长(Array + OUTPUT)

多个分析集flag变量(SAFFL/EFFFL/DLTSFL等)每个是一条记录 → 展开成多行Listing。

SAS
/* 宽→长:分析集flag展开为Listing行 */
data FINAL;
    length COL1-COL5 $200;
    set ADSL;

    COL1 = strip(substr(SPART,1,2)) || '/' || strip(compress(TRT01P));
    COL2 = SUBJID;

    array _FL{5} $ SAFFL EFFFL DLTSFL PKSFL PDSFL;
    array _REASONS{5} $ SAFREAS EFFREAS DLTSREAS PKSREAS PDSREAS;
    array _SETS{5} $200 _temporary_ (
        "安全分析集" "疗效分析集" "DLT可评价分析集"
        "PK分析集" "PD分析集"
    );

    do I = 1 to dim(_FL);
        COL3 = _SETS{I};
        if _FL{I} = "N" then do;
            COL4 = "是";
            COL5 = _REASONS{I};
            output;    /* 每条flag生成一行 */
        end;
    end;
    drop I;
run;

常见坑

⚠️ 两表拼接仍用MAX(CASE WHEN)?
MAX(CASE WHEN)仅适用单表长转宽。如果数据来自两个不同数据集(如DA + 另一来源),必须用LEFT JOIN
⚠️ SQL长转宽的效率
前期只保留必要变量(keep需要的列),减少SQL查询的冗余空间。参数越多LEFT JOIN越重,但比TRANSPOSE还是快。
⚠️ IDVARVAL转数值
SUPPQUAL的IDVARVAL是字符型,JOIN前需DASEQ = input(IDVARVAL, best.)转数值匹配主域的SEQ变量。