5.加上性别变量-优化DM表格

本系列是纯属个人根据工作经验写的CDISC和SAS相关文章,正确与否都是个人观点,如有错误坚决不让喷,把错误帮我改正过来,我会谢谢你的。如果按照我写的程序跑不出来,就找找自己原因,找不到自己原因你再过来找我的原因。

上一节简单输出了年龄+身高的描述性统计量,这次输出年龄+身高+性别,像这样:


年龄+身高+性别.png

做成这样:


TFL_data.png

1.开始写TFL程序

/*********************************************************************
*           **********  Corporate name   **********
*  Program Name :T14_1_1_01 .sas
*  Program Target: TABLE 14.1.1.01 Demographic and Baseline Characteristics
*
*  Reference: sap,spec
*  Input: adam.adsl
*  Output: T14_1_1_01
* 
************************Version**************************************
*  Version: V1.0
*  Authour:    cdisc 2099-01-01
*  Validation: cdisc 2023-01-01
*  Other: 
**********************************************************************/
%let progname=T14_1_1_01 ;

**删除日志输出;
DM 'log;clear;output;clear';
proc datasets lib=work nolist kill memtype=data;run;quit;

**创建一个主数据集_minds,是最全的数据集,保证受试者例数不少,结合分析数据集_inds,生成_inds;
data _minds;set adam.adsl;  where index(SAFFL,"Y"); run;
data _inds ;set adam.adsl;  where index(SAFFL,"Y"); run;

proc sort data=_minds ;by USUBJID;run;
proc sort data=_inds  ;by USUBJID;run;

data __inds ;
    merge _inds(in=A) _minds(in=B keep=USUBJID);
    by USUBJID;
    IF A and B;
    run;    
proc sql noprint;create table _inds as select distinct * from __inds;

**得到主数据集各组的人数;
%local _grp1 _grp2 _grp3 _grp9999 _gmp1 _gmp2 _gmp3 _gmp9999;
proc sql noprint;
    select count(distinct USUBJID) into : _grp1 TRIMMED from _minds where TRT01P="Xanomeline Low Dose";
    select count(distinct USUBJID) into : _grp2 TRIMMED from _minds where TRT01P="Xanomeline High Dose";
    select count(distinct USUBJID) into : _grp3 TRIMMED from _minds where TRT01P="Placebo";
    select count(distinct USUBJID) into : _grp9999 TRIMMED from _minds;
    quit;

**创建AD_Z数据集,是后续分析数据的核心数据集;
data ad_z;set _inds;run;

**创建4个主要的空数据集,用于后面,_null用作增加空行;
data _null;run;
data T14_1_1_01; run;
data _Tinds;  set AD_Z;run;
data tout1;  if _n_=0; run;

**取到age的变量type和fmt,属性赋值给宏变量vtype和vfmt。;
data _null_;
    set _Tinds;
    call symput('vtype',vtype(AGE));
    call symput('vfmt',vformat(AGE));
    run;

**求出分析数据集的各组人数,要与主数据集的人数分开;
proc sql noprint;
    select count(distinct USUBJID) into: _gmp9999 TRIMMED from _Tinds where AGE is not null;
    select count(distinct USUBJID) into : _gmp1 TRIMMED from _Tinds where TRT01P="Xanomeline Low Dose" and AGE is not null;
    select count(distinct USUBJID) into : _gmp2 TRIMMED from _Tinds where TRT01P="Xanomeline High Dose" and AGE is not null;
    select count(distinct USUBJID) into : _gmp3 TRIMMED from _Tinds where TRT01P="Placebo" and AGE is not null;
    quit;

/*创建一个框架输出表__tab1,一共两行*/
data __tab1;
   length COL1 COL2 COL3 COL4 COL5 $600.;
   COL1=tranwrd(unicodec(trim("(*ESC*)R/RTF'\b Age(Years)"),"ncr"),"&#","\u");
   output;
   COL1=repeat("    ",0)||trim("N(NMiss)");
   COL2=catt(put(&_gmp1.,2.)   ,' (',put(&_grp1.-&_gmp1.,2.),')');
   COL3=catt(put(&_gmp2.,2.)   ,' (',put(&_grp2.-&_gmp2.,2.),')');
   COL4=catt(put(&_gmp3.,2.)   ,' (',put(&_grp3.-&_gmp3.,2.),')');
   COL5=catt(put(&_gmp9999.,3.),' (',put(&_grp9999.- &_gmp9999.,2.),')');
   output;
   keep COL:;
   run;

/*核心算法,计算描述性统计量*/
proc means data=_Tinds q1 q3 noprint;
   class TRT01P;
   var AGE;
   output out=mean1 n=n nmiss=nmiss mean=mean std=std lclm=lclm uclm=uclm min=min max=max median=median Q1=Q1 Q3=Q3;
   run;

/*处理加转换数据*/
data mean1;
    set mean1;
    retain colnm ;
    if mean = . then MD=cats('NE±NE');
        else if std ne . then MD=cats(put(mean,8.2)," ±",put(std,8.3));
        else MD = cats(put(mean,8.2)," ±NE");
    if median ne . then MQ=cats(put(median,8.2),'(',put(Q1,8.2),',',put(Q3,8.2),')');
        else MQ=cats('NE(NE,NE)');
    if min ne . then MM=cats(put(min,8.1),"~",put(max,8.1));
        else MM=cats('NE~NE');
    if TRT01P="Xanomeline Low Dose" then colnm = 2;
    if TRT01P="Xanomeline High Dose" then colnm = 3;
    if TRT01P="Placebo" then colnm = 4;
    if TRT01P = '' then colnm =5;
    keep colnm md mq mm;
    run;
proc transpose data=mean1 out=meant1 prefix=COL;
   var md mq mm;
   id colnm;
   run;

/*将框架表与经处理后的表合并*/
data __tab1;
    set __tab1 meant1;
    retain COL1;
    if upcase(_name_)='MD' Then COL1=repeat("    ",0)||trim("Mean±SD");
    if upcase(_name_)='MQ' Then COL1=repeat("    ",0)||trim("Median(Q1, Q3)");
    if upcase(_name_)='MM' Then COL1=repeat("    ",0)||trim("Min~Max");
    if missing(COL2) and upcase(_name_)='MD' then COL2 = "NE";
    if missing(COL3) and upcase(_name_)='MD' then COL3 = "NE";
    drop _name_;
    run;

/*输出表*/
data tout1;
    set  __tab1 ;
    if index(COL1, '#')=0;
    run;

/*身高开始************************************************************************************/

    **求出分析数据集的各组人数,要与主数据集的人数分开;
proc sql noprint;
    select count(distinct USUBJID) into: _gmp9999 TRIMMED from _Tinds where HEIGHTBL is not null;
    select count(distinct USUBJID) into : _gmp1 TRIMMED from _Tinds where TRT01P="Xanomeline Low Dose" and HEIGHTBL is not null;
    select count(distinct USUBJID) into : _gmp2 TRIMMED from _Tinds where TRT01P="Xanomeline High Dose" and HEIGHTBL is not null;
    select count(distinct USUBJID) into : _gmp3 TRIMMED from _Tinds where TRT01P="Placebo" and HEIGHTBL is not null;
    quit;

    /*创建一个框架输出表__tab2,一共两行*/
data __tab2;
   length COL1 COL2 COL3 COL4 COL5 $600.;
   COL1=tranwrd(unicodec(trim("(*ESC*)R/RTF'\b Height(cm)"),"ncr"),"&#","\u");
   output;
   COL1=repeat("    ",0)||trim("N(NMiss)");
   COL2=catt(put(&_gmp1.,2.)   ,' (',put(&_grp1.-&_gmp1.,2.),')');
   COL3=catt(put(&_gmp2.,2.)   ,' (',put(&_grp2.-&_gmp2.,2.),')');
   COL4=catt(put(&_gmp3.,2.)   ,' (',put(&_grp3.-&_gmp3.,2.),')');
   COL5=catt(put(&_gmp9999.,3.),' (',put(&_grp9999.- &_gmp9999.,2.),')');
   output;
   keep COL:;
   run;

    /*核心算法,计算描述性统计量*/
proc means data=_Tinds q1 q3 noprint;
   class TRT01P;
   var HEIGHTBL;
   output out=mean2 n=n nmiss=nmiss mean=mean std=std lclm=lclm uclm=uclm min=min max=max median=median Q1=Q1 Q3=Q3;
   run;

    /*处理加转换数据*/
data mean2;
    set mean2;
    retain colnm ;
    if mean = . then MD=cats('NE±NE');
        else if std ne . then MD=cats(put(mean,8.2)," ±",put(std,8.3));
        else MD = cats(put(mean,8.2)," ±NE");
    if median ne . then MQ=cats(put(median,8.2),'(',put(Q1,8.2),',',put(Q3,8.2),')');
        else MQ=cats('NE(NE,NE)');
    if min ne . then MM=cats(put(min,8.1),"~",put(max,8.1));
        else MM=cats('NE~NE');
    if TRT01P="Xanomeline Low Dose" then colnm = 2;
    if TRT01P="Xanomeline High Dose" then colnm = 3;
    if TRT01P="Placebo" then colnm = 4;
    if TRT01P = '' then colnm =5;
    keep colnm md mq mm;
    run;
proc transpose data=mean2 out=meant2 prefix=COL;
   var md mq mm;
   id colnm;
   run;

    /*将框架表与经处理后的表合并*/
data __tab2;
    set __tab2 meant2;
    retain COL1;
    if upcase(_name_)='MD' Then COL1=repeat("    ",0)||trim("Mean±SD");
    if upcase(_name_)='MQ' Then COL1=repeat("    ",0)||trim("Median(Q1, Q3)");
    if upcase(_name_)='MM' Then COL1=repeat("    ",0)||trim("Min~Max");
    if missing(COL2) and upcase(_name_)='MD' then COL2 = "NE";
    if missing(COL3) and upcase(_name_)='MD' then COL3 = "NE";
    drop _name_;
    run;

    /*输出表*/
data tout2;
    set  tout1 _null __tab2;
    if index(COL1, '#')=0;
    run;
/*身高结束************************************************************************************/


/*增加性别*/
proc sql noprint;
    select count(distinct USUBJID) into: _gmp9999 TRIMMED from _Tinds where SEX is not null;
    select count(distinct USUBJID) into : _gmp1 TRIMMED from _Tinds where TRT01P="Xanomeline Low Dose" and SEX is not null;
    select count(distinct USUBJID) into : _gmp2 TRIMMED from _Tinds where TRT01P="Xanomeline High Dose" and SEX is not null;
    select count(distinct USUBJID) into : _gmp3 TRIMMED from _Tinds where TRT01P="Placebo" and SEX is not null;
    quit;

data __tab3;
    length COL1 COL2 COL3 COL4 COL5 $600.;
    COL1=tranwrd(unicodec(trim("(*ESC*)R/RTF'\b Sex,n(%)"),"ncr"),"&#","\u");
    output;
    COL1=repeat("    ",0)||trim("N(NMiss)");
    COL2=catt(put(&_gmp1.,2.),' (',put(&_grp1.-&_gmp1.,2.),')');
    COL3=catt(put(&_gmp2.,2.),' (',put(&_grp2.-&_gmp2.,2.),')');
    COL4=catt(put(&_gmp3.,2.),' (',put(&_grp3.-&_gmp3.,2.),')');
    COL5=catt(put(&_gmp9999.,3.),' (',put(&_grp9999.- &_gmp9999.,2.),')');
    output;
    keep COL:;
    run;

proc sql noprint;
    select count(distinct USUBJID) INTO: _gap1_9999 TRIMMED from _Tinds where SEX = "F";
    select count(distinct USUBJID) INTO: _gap1_1 TRIMMED from _Tinds where TRT01P="Xanomeline Low Dose" and SEX = "F";
    select count(distinct USUBJID) INTO: _gap1_2 TRIMMED from _Tinds where TRT01P="Xanomeline High Dose" and SEX = "F";
    select count(distinct USUBJID) INTO: _gap1_3 TRIMMED from _Tinds where TRT01P="Placebo" and SEX = "F";
    select count(distinct USUBJID) INTO: _gap2_9999 TRIMMED from _Tinds where SEX = "M";
    select count(distinct USUBJID) INTO: _gap2_1 TRIMMED from _Tinds where TRT01P="Xanomeline Low Dose" and SEX = "M";
    select count(distinct USUBJID) INTO: _gap2_2 TRIMMED from _Tinds where TRT01P="Xanomeline High Dose" and SEX = "M";
    select count(distinct USUBJID) INTO: _gap2_3 TRIMMED from _Tinds where TRT01P="Placebo" and SEX = "M";
    quit;

data __tab3T;
    length COL1 COL2 COL3 COL4 COL5 $200.;
    COL1=repeat("    ",0)||trim("F");
    COL2=cats(ifc(&_grp1^=0 ,catt(put(&_gap1_1,3.),' (',put(&_gap1_1/&_grp1*100,5.1),')'),"NE"));
    COL3=cats(ifc(&_grp2^=0 ,catt(put(&_gap1_2,3.),' (',put(&_gap1_2/&_grp2*100,5.1),')'),"NE"));
    COL4=cats(ifc(&_grp3^=0 ,catt(put(&_gap1_3,3.),' (',put(&_gap1_3/&_grp3*100,5.1),')'),"NE"));
    COL5=catt(put(&_gap1_9999,3.),' (',put(&_gap1_9999/&_grp9999*100,5.1),')');
    output;
    COL1=repeat("    ",0)||trim("M");
    COL2=cats(ifc(&_grp1^=0 ,catt(put(&_gap2_1,3.),' (',put(&_gap2_1/&_grp1*100,5.1),')'),"NE"));
    COL3=cats(ifc(&_grp2^=0 ,catt(put(&_gap2_2,3.),' (',put(&_gap2_2/&_grp2*100,5.1),')'),"NE"));
    COL4=cats(ifc(&_grp3^=0 ,catt(put(&_gap2_3,3.),' (',put(&_gap2_3/&_grp3*100,5.1),')'),"NE"));
    COL5=catt(put(&_gap2_9999,3.),' (',put(&_gap2_9999/&_grp9999*100,5.1),')');
    output;
    keep COL:;
    run;

data __tab3;
  set __tab3 __tab3T;
  run;

data tout3;
    set tout2 _null __tab3 ;
    if index(COL1, '#')=0;
    run;

/*增加性别结束*/
            
    
/*输出T14_1_1_01*/
data T14_1_1_01;
    set tout3;
    run;

/*做个标题表*/
data prop_tmp;
keep tabname title name label cellw ;
length tabname title name label $200.;
    tabname ="T14_1_1_01";  title="";   name = "COL1";  label = ""; cellw=15;   output;
    tabname ="T14_1_1_01";  title="";   name = "COL2";  label=cats("Xanomeline Low Dose"||"|(N=&_grp1.)");  cellw=10;output;
    tabname ="T14_1_1_01";  title="";   name = "COL3";  label=cats("Xanomeline High Dose"||"|(N=&_grp2.)"); cellw=10;output;
    tabname ="T14_1_1_01";  title="";   name = "COL4";  label=cats("Placebo"||"|(N=&_grp3.)");  cellw=10;output;
    tabname ="T14_1_1_01";  title="";   name = "COL5";  label=cats("Overall"||"|(N=&_grp9999.)");   cellw=10;output;
run;

/*用公司内部宏输出到rtf中*/
%ods_rtf_One(Title=%str(TABLE 14.1.1.01 Demographic and Baseline Characteristics|Summary Statistics: Safety Population
                             )
                ,inds=T14_1_1_01
                ,fname=T14_1_1_01
                ,xpath=C:\Users\90526\Desktop\temp\Prog_001_CDISCPILOT01\TFL\output
                ,foot=%str(Note:The data in this table are presented in listing x.x.|I casually wrote a footnote, and no one cares what it was written. I copied a few more copies, so there was more text and it looked very impressive.)
                ,prop=Prop_tmp,oritype=landscape,pg_row=30 ,spacing=2,fs=8pt);

2.输出结果

最终结果.png

完活收工!下一节做PK浓度的描述性统计表。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容