今天要写的是关于SAS在临床试验中自动输出频数表的程序。在临床试验中,我们会对不良事件与合并用药进行医学编码,编码后,我们会对编码进行分级频数汇总。汇总表长的什么样子呢,来见下图!
先来看1张频数表:
看到上面的那个表,还是需要在看看数据集里面的内容。表格是将数据集里变量的值进行频数统计,并且按照从分级大到小的规律进行缩进显示,也就上面表格的结果(表1)
一万年太短,一个例子太少,在来看一个图!
嗯,看完结果,还是在来看看编码的数据集!
其实看到这里,就不难发现,又是rtf输出相关的,其实也不尽然。在一年前小编写这个自动输出的程序废了九牛二虎之力而且写的也不是很好,前段时间再一次重新写了下这个程序,虽然可能写的也不是太好,但是比先前写的优化了很多很多。自我感觉还是良好的,所以拿出来与大家交流,看看各位大神哪里有什么更加好的程序....
首先来说说这个表格的格式输出要求,可能各公司对这个表格的格式要求可能不一样,这里小编就以小编待过的几个公司的格式要求来说吧,其实就是前面的图中张的那样,按照一定规律缩进,规律是啥呢,层级从大到小进行缩进。其实仔细看一下,很容易意会的!
接下来就来看看我是怎么写这个程序的,不过看之前,先来看看这个宏的宏参数,以及运行结果:
代码语言:javascript复制%freq_coding(usubjid=usubjid,inds=test,varlist=%str( SOC_NAME PT_Name LLT_Name ),outds=test2,type=1);
其实这里的第一个参数(usubjid)与最后一个参数(type)暂时没有作用,留着这里的原因是在写这个程序的时候,就在想是否做一键输出(例次、人次),关于不良事件、合并用药编码汇总,有时候有人想要看人次,这里的例次和人次有区别的。所以就留了一个参数,以后在完善。type的作用是输出自动做好的数据集类型,主要是控制缩进的方式,暂时就写了一个类型,就是适用于输出rtf的数据集。
在来说几个有作用的参数:inds:输入编码数据集,varlist:按照分级大小顺序先后输入并以进行隔开,outds:汇总后输出的数据集(可以直接进行report过程的数据集)
来看一下运行后的效果:此数据集为自动添加了缩进、加粗后可以直接用于Report过程输出的数据集。
在来看一下程序是如何实现的:
代码语言:javascript复制%macro freq_coding(usubjid=usubjid,inds=,varlist=%str(),outds=,type=1);
*---------------------------------------------------------*
| 提取输入的变量并赋值给宏变量 |
*---------------------------------------------------------*;
%let i=1;
%do %while(%scan(%sysfunc(compbl(&varlist)),&i,' ')^=%str());
%let var&i=%scan(&varlist,&i,' ');
%put NOTE:&&var&i;
%let i=%eval(&i 1);
%end;
%let j=%eval(&i-1);
*---------------------------------------------------------*
| 筛选出需要的变量,衍生变量retain出现次数 |
*---------------------------------------------------------*;
proc sort data=&inds. out=pgm_temp1 sortseq=linguistic(numeric_collation=on);by &varlist.;quit;
data pgm_temp1;
retain &varlist. ;
set pgm_temp1;
line=_N_;
by &varlist.;
%do i=1 %to &j.;
if first.&&var&i. then fg&i.=1;
else fg&i. 1;
%end;
keep &varlist. line fg: ;
run;
%let dsid=%sysfunc(open(pgm_temp1));
%let all_nobs=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%put NOTE:&all_nobs;
*---------------------------------------------------------*
| 此处编码分级上限为6个变量 |
*---------------------------------------------------------*;
proc sql noprint;
%do i=1 %to &j.;
%if &i.=1 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &&var&i. ;%end;
%if &i.=2 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &var1.,&&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &var1.,&&var&i.;%end;
%if &i.=3 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &var1.,&var2.,&&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &var1.,&var2.,&&var&i.;%end;
%if &i.=4 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &var1.,&var2.,&var3.,&&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &var1.,&var2.,&var3.,&&var&i.;%end;
%if &i.=5 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &var1.,&var2.,&var3.,&var4.,&&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &var1.,&var2.,&var3.,&var4.,&&var&i.;%end;
%if &i.=6 %then %do;create table sm_ds_&i.(where=(fg&i.=N)) as select &var1.,&var2.,&var3.,&var4.,&var5.,&&var&i.,fg&i.,max(fg&i.) as N from pgm_temp1 group by &var1.,&var2.,&var3.,&var4.,&var5.,&&var&i.;%end;
%end;
quit;
data lst_ds;
set sm_ds_1-sm_ds_&j.;
run;
proc delete data=sm_ds_1-sm_ds_&j.;quit;
proc sort data=lst_ds out=lst_ds sortseq=linguistic(numeric_collation=on);by &varlist.;quit;
*---------------------------------------------------------*
| 数据置空处理 |
*---------------------------------------------------------*;
data lst_ds1;
set lst_ds;
by &varlist.;
%if &j.>=2 %then %do;
if not first.&var1. then call missing(&var1.);
if ^missing(&var1.) then lv=1;
%end;
%if &j.>=3 %then %do;
if not first.&var2. then call missing(&var1.,&var2.);
if ^missing(&var2.) then lv=2;
%end;
%if &j.>=4 %then %do;
if not first.&var3. then call missing(&var1.,&var2.,&var3.);
if ^missing(&var3.) then lv=3;
%end;
%if &j.>=5 %then %do;
if not first.&var4. then call missing(&var1.,&var2.,&var3.,&var4.);
if ^missing(&var4.) then lv=4;
%end;
%if &j.>=6 %then %do;
if not first.&var5. then call missing(&var1.,&var2.,&var3.,&var4.,&var5.);
if ^missing(&var5.) then lv=5;
%end;
if missing(lV) and ^missing(&&var&j.) then lv=&j.;
keep &varlist. N lv;
run;
*---------------------------------------------------------*
| 针对缺失数据进行处理 |
*---------------------------------------------------------*;
data lst_ds1;
set lst_ds1;
retain temp_var1;
if ^missing(lv) then temp_var1=lv;
else do; lv=temp_var1 1;temp_var1=lv;end;
run;
*---------------------------------------------------------*
| 输出类型判断以及缩进处理 |
*---------------------------------------------------------*;
%if &type.=1 %then %do;
data &outds.;
set lst_ds1;
length final $400.;
%if &j.>=1 %then %do;
if lv=1 then final="^R/RTF'b'"||ifc(^missing(strip(&var1.)),strip(&var1.),'<此处为空>');%end;
%if &j.>=2 %then %do;
if lv=2 then final="^R/RTF'u32?u32?u32?u32?'"||ifc(^missing(strip(&var2.)),strip(&var2.),'<此处为空>');%end;
%if &j.>=3 %then %do;
if lv=3 then final="^R/RTF'u32?u32?u32?u32?u32?u32?u32?u32?'"||ifc(^missing(strip(&var3.)),strip(&var3.),'<此处为空>');%end;
%if &j.>=4 %then %do;
if lv=4 then final="^R/RTF'u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?'"||ifc(^missing(strip(&var4.)),strip(&var4.),'<此处为空>');%end;
%if &j.>=5 %then %do;
if lv=5 then final="^R/RTF'u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?'"||ifc(^missing(strip(&var5.)),strip(&var5.),'<此处为空>');%end;
%if &j.>=6 %then %do;
if lv=6 then final="^R/RTF'u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?u32?'"||ifc(^missing(strip(&var6.)),strip(&var6.),'<此处为空>');%end;
keep N final;
run;
proc sql;
insert into &outds.(final,N)
values ("^R/RTF'b'总计",&all_nobs. )
;
quit;
%end;
proc delete data=lst_ds lst_ds1 pgm_temp1;quit;
%mend;
上面的程序运行完就会自动生成一个适合RTF输出的数据集,其实也是可以将RTF输出过程放到这里面,这里小编没都柔到里面去,接下来在来看看rtf输出的过程。下面代码用到的宏,都可以在我历史文章中找到。或者上一篇文章中有一个宏包可以下载。点击此处(SAS-给公众号做一个秩和检验)
代码语言:javascript复制%macro xls2sas(path,excelname,sheet,outds);
proc import out=&outds.
datafile= "&path.&excelname."
dbms=excel replace;
range="&sheet.$";
getnames=yes;
run;
%mend;
%xls2sas(path=E:macro,excelname=titles.xlsx,sheet=sheet1,outds=titles);
/*import template*/
%rtf_ods_temp;
%rtf_ods_title(pgmname=freq_coding.sas,tablename=不良事件,inds=titles,tableid=不良事件,ftyn=N)
/*Set output file name and path*/
%rtf_ods_st(path=D:,report=不良事件,style=style_tb);
/*Set title*/
/*
对要输出的数据集进行分页设置
及设置插入空白行的位置
*/
data final;
set test2;
_page=int(_N_/30);
order=_N_;
run;
proc report data=final nowd headskip headline split='|' missing nocenter
; column _page order final N ;
define _page / order order=internal noprint;
define order / order order=internal noprint;
define final / display "编码" style(header)=[just=left] style(column)=[just=left cellwidth=88% asis=on] flow;
define N / display "例次" style(header)=[just=left] style(column)=[cellwidth=10% just=center asis=on] flow;
break after _page/page;
%rtf_ods_on;
run;
ods rtf close;
ods listing;
然后大体基本写完了。
今天就这么多了,后续内容,敬请期待~