本系列是纯属个人根据工作经验写的CDISC和SAS相关文章,正确与否都是个人观点,如有错误坚决不让喷,把错误帮我改正过来,我会谢谢你的。如果按照我写的程序跑不出来,就找找自己原因,找不到自己原因你再过来找我的原因。
上一节简单输出了年龄+身高的描述性统计量,这次输出年龄+身高+性别,像这样:
做成这样:
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.输出结果
完活收工!下一节做PK浓度的描述性统计表。