愉快的周末又要结束了,繁忙的周一也即将开始!小编今天给大家Share的一个Macro是关于临床数据清理中的一个实用性的Macro,SAS Edit Check Output Query. -- Setup
背景介绍
临床试验数据清洗
小编曾从事临床试验数据清洗编程的工作,
虽然跳槽换了title,但是暂时本质还是一样...后面应该会变。。。还是回到主题上来。
什么是数据清洗?
在临床试验中,对试验数据进行前期的清理与逻辑核查,一般在数据清洗完成后在进行数据库的Database Lock ,Lock后的DataSet在交由统计师以及相关的编程人员进行统计分析,出相应的TFL等。
清理的目的?
为了后期统计分析的效果与质量,不管是在CRO还是在药厂,一般都是会有专门的数据部门存在,负责数据的清理。今天这个Macro就是用来帮助数据清理的。
在说程序前,还是要说一个东西,DVP(Data Validation Plan,数据验证计划)是由数据部门的DM编写,里面的是对临床试验收集的数据点进行验证的规则及其描述,SAS Programmer/Database Designer会更具DVP来实现一条一条的Edit Check,Edit Check 又分Online 和offline俩种,由于今年EDC系统的更新换代,很多核查都以及Online了,对于Offline的核查多是paper的项目与一些Online实现不了的核查,通过Edit Check核查出来的东西叫做Query,回有DM反馈给研究者,再由研究者解答疑问或者反馈会正确的数据。
说了半天还没说这个Macro是干嘛的,这个Macro就是用来写Edit Check的,更加便捷的输出Query。
Macro可以干嘛
跨表单执行逻辑核查,自动抓取变量的值,统一输出样式,批量执行Edit Check等等。。(见下图Macro简单的使用描述)
Excel模板
执行Log
执行结果
看Code
小编也知道,Code排版肯定会很烂,不要着急,文章结尾会给大家附上百度网盘链接,可以直接下载Code与Template,程序也可以直接使用。。我好像太帅了~
%macroChkQueryOutput(CheckData=,ChkLib=,SubjidKey=,VisitKey=,PageKey=);
Optionsmsglevel=N nomprint nonotes;
%put ;
%put ;
%put 开始执行!;
%put ;
%put ;
/*获取待核查逻辑库下的变量并进行预处理找出包含Format的变量*/
Proc contentsdata=&ChkLib.._all_ Out=FMTData(keep=MEMNAME name FORMAT) noprint;run;
proc sortdata=FMTData out=FMTData nodupkey; by_all_;run;
data FMTData;/**/
set FMTData;
name=upcase(name);
ifmissing(FORMAT) or FORMAT='$' then delete;
FORMAT=cats(FORMAT,'.');
if FORMAT="YYMMDD." Then FORMAT="YYMMDD10.";
DName=cats(MEMNAME,'.',NAME);
Output=cats("put(",name,",",FORMAT,")");
DOutput=cats("{put(",Dname,",",FORMAT,")}");
run;
%let dsid=%sysfunc(open(FMTData));/*获取FMTData数据集的观测为替换变量做准备*/
%let FMT1=%sysfunc(attrn(&dsid,nobs));/*nobs为观测数*/
%let rc= %sysfunc(close(&dsid));
data _null_;
set FMTData;
call symput('FNM'||compress(put(_n_,best.)),strip(Dname));
call symput('FOM'||compress(put(_n_,best.)),strip(DOutput));
run;
/*预处理EC不规范*/
data &CheckData. &CheckData._e;
set &CheckData.;
Deugw=count(QUERY,'{');
Deugm=count(QUERY,'}');/*判断{}是否是成对出现以及QUERY中是否包含英文双引号*/
ifDeugw-Deugm^=0 then output &CheckData._e;
/* if count(QUERY,'"')>0 or Deugw-Deugm^=0 orcount(Description,'"')>0 thenoutput &CheckData._e;*/
else output&CheckData.;
drop Deugw Deugm;
run;
data _null_;
set&CheckData._e;
call symput('deug'||compress(put(_n_,best.)),strip(DVPCODE));
run;
%let dsid=%sysfunc(open(&CheckData._e));
%let nobs7=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
proc deletedata=&CheckData._e;quit;
%do k=1%to &nobs7.;
%put EC中QUERY填写不规范的DVP:&&deug&k.(DVP中包含“{}”不是成对出现),请核实;
%put ;
%put ;
%end;
dataCheckData_1;
set &CheckData.;
Description=tranwrd(Description,'"', '""');
QUERY=tranwrd(QUERY,'"', '""');
Query=Cats('Cats("',QUERY,'")');
%do i=1%to &FMT1.;
QUERY=tranwrd(QUERY,"{&&FNM&i.}","&&FOM&i.");
%end;
QUERY=tranwrd(QUERY,"{", '",');
QUERY=tranwrd(QUERY,"}", ',"');
run;
proc sqlUNDO_POLICY=NONE;
create tableCheckData_1 as
selectdistinct *
from CheckData_1
;
quit;
data _null_;
setCheckData_1;
call symput('ChkMainDATA'||compress(put(_n_,best.)),strip(DOMAIN));/*创建宏变量:获取主数据集*/
call symput('ChkDvpCode'||compress(put(_n_,best.)),strip(DVPCODE));/*创建宏变量:获取DVP编号*/
call symput('ChkSEQ'||compress(put(_n_,best.)),strip(SEQ));/*创建宏变量:获取SEQ(针对同条DVP拆分多种情况)*/
call symput('ChkDesc'||compress(put(_n_,best.)),strip(Description));/*创建宏变量:获取DM给到的DVP描述*/
call symput('ChkCondt'||compress(put(_n_,best.)),strip(Condition));/*创建宏变量:获取核查条件*/
call symput('ChkQUERY'||compress(put(_n_,best.)),strip(QUERY));/*创建宏变量:获取输出QUERY描述部分*/
call symput('ChkOtherSet'||compress(put(_n_,best.)),strip(OtherSet));/*创建宏变量:获取垮表单数据集*/
run;
/*获取EC条数以便进行循环*/
%let dsid=%sysfunc(open(CheckData_1));
%let nobs1=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
proc deletedata=work.CheckData_1 FMTDATA;quit;
%do i=1%to &nobs1.;/*建立循环:从第一条EC开始循环*/
%let CDataSet1=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,1,' ')));
%let CDataSet2=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,2,' ')));
%let CDataSet3=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,3,' ')));
%let CDataSet4=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,4,' ')));
%let CDataSet5=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,5,' ')));
%let CDataSet6=%sysfunc(compress(%qscan(&&ChkOtherSet&i.,6,' ')));
proc copyin=&ChkLib. out=Work; select&&ChkMainDATA&i. &&ChkOtherSet&i. ;run;
proc sqlUNDO_POLICY=NONE;/*where语句核查情况*/
create tableChk_Shun as
select &&ChkMainDATA&i...&SubjidKey.,&&ChkMainDATA&i...&VisitKey. ,&&ChkMainDATA&i...&PageKey. ,
"&&ChkDvpCode&i." as DVP_CODE length=200,"&&ChkDesc&i." as DECL length=2000,&&ChkQUERY&i.as Query length=2000
from&&ChkMainDATA&i.
%if%length(&CDataSet1.)^=0%then%do;
full join&CDataSet1. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet1..&SubjidKey.
%end;
%if%length(&CDataSet2.)^=0%then%do;
full join&CDataSet2. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet2..&SubjidKey.
%end;
%if%length(&CDataSet3.)^=0%then%do;
full join&CDataSet3. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet3..&SubjidKey.
%end;
%if%length(&CDataSet4.)^=0%then%do;
full join&CDataSet4. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet4..&SubjidKey.
%end;
%if%length(&CDataSet5.)^=0%then%do;
full join&CDataSet5. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet5..&SubjidKey.
%end;
%if%length(&CDataSet6.)^=0%then%do;
full join&CDataSet6. on &&ChkMainDATA&i...&SubjidKey.=&CDataSet6..&SubjidKey.
%end;
where &&ChkCondt&i.
;
quit;
dataCHK_DAI_&i.;
set Chk_Shun;
run;
proc deletedata=work.Chk_Shun &&ChkMainDATA&i. &&ChkOtherSet&i.;quit;/*删除过程中衍生数据集*/
%let dsid=%sysfunc(open(CHK_DAI_&i.));
%let nobs4=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%put DVPCODE=&&ChkDvpCode&i...&&ChkSEQ&i. 执行完毕! Run出Query条数:【&nobs4.】;
%end;
dataChk_Output;
set CHK_dai_1;
run;
proc deletedata=CHK_dai_1;quit;
%if &nobs1.>1%then%do;
%do i=2%to &nobs1.;
proc appendbase=Chk_Output data=CHK_dai_&i.;run;
proc delete data=work.CHK_dai_&i.;quit;
%end;
%end;
%let dsid=%sysfunc(open(Chk_Output));
%let nobs3=%sysfunc(attrn(&dsid,nobs));
%let rc= %sysfunc(close(&dsid));
%put ;
%put 总计 :执行EC核查条数:【&nobs1.】 RUN出QUERY条数:【&nobs3.】;
%put ;
;
%mend;