最近第一次做了SV数据集,记录一下自己的感受和想法。SV数据集包含了每个受试者每个访视的时间信息。个人理解可以想象成一个受试者都有一把尺子,尺子的两头分别是试验开始和结束的时间。而尺子上有许许多多个刻度,这些刻度就是每一个访视的开始和结束时间,也就是svstdtc和svendtc。对于受试者来说,每个访视之间不应该出现重叠,也就是一个访视的开始和结束时间内,不应该出现其他的刻度。
我们看一下SDTMIG上关于SV的变量有这些,变量并不多,其中最重要的就是SVSTDTC和SVENDTC了。
对于SVSTDTC和SVENDTC来说,可以使用之前提到的RFPENDTC的做法,使用retain语句和vcolumn数据集来生成获取所有时间信息的程序。但是应当至少注意一下几个数据,首先是既往用药,既往非药物治疗以及病史等过去史的问询,以及受试者的生日信息。
其次就是计划外访视的处理。对于计划外访视来说,首先要确定计划外访视以及计划外访视编号的命名规则。我遇到的几个项目一般都是寻找计划外访视发生前的最后一次访视,并以此确定访视名以及信息。
data test;
set sashelp.vcolumn;
where upcase(libname)="RAW" and upcase(type)="CHAR" and upcase(memname) ne "OH" and upcase(memname) ne "AE" and upcase(memname) ne "AH" and upcase(memname) ne "MH" and upcase(memname) ne "CM" and upcase(memname) ne "DS" and upcase(name) ne "BRTHDAT" and find(upcase(name),"DAT") ne 0 and find(upcase(label),"日期") ne 0;
keep libname memname name;
proc sort;
by memname name;
run;
data dat;
length memname2 memname3 dat $200;
retain memname2 ;
set test;
by memname name;
if first.memname then memname2=strip(name);
else memname2=strip(memname2)||" "||strip(name);
dat=strip(name);
memname3="raw."||strip(memname)||"(keep=subject instancename datapagename "||strip(memname2)||" rename=("||strip(dat)||"=DTC))";
run;
data dat1;
set dat end=last;
length memname4 $4000.;
retain memname4;
if _N_=1 then memname4=memname3;
else MEMNAME4 = strip(MEMNAME4)||" "||strip(MEMNAME3);
if last;
run;
proc sql noprint;
select memname4 into :dat from dat1;quit;
data dat_en;
set &dat.;
where dtc ne "";
time=input(dtc,??yymmdd10.);
svendtc=put(time,yymmdd10.);
if time ne .;
keep subject svendtc instancename datapagename time;
proc sort;
by subject instancename descending time;
proc sort nodupkey;
by subject instancename;
run;
使用RFPENDTC的做法先做出访视结束时间,再依次类推做出结束时间。有几点需要注意的,首先是做的时候应该采用完整时间,避免出现缺失值。否则开始时间会变成缺失值。其次,相比RFPENDTC,我们做的时候应当保留原始数据中的访视名称以及出现的数据集名称。方便溯源以及之后做计划外访视的编号。
data svdat;
merge dat_en dat_st;
by subject instancename;
run;
data svinp;
set svdat;
where find(instancename,"计划外") = 0;
length visit $200;
if instancename ne "提前退出" then visit=instancename;
else visit="EOT";
visitnum=input(visit,vis.);
proc sort;
by subject;
run;
data uns;
set svdat;
where find(instancename,"计划外") ^= 0;
svdtc=svstdtc;
proc sort;
by subject;
run;
proc sort data=uns out=sub(keep=subject) nodupkey;
by subject;
run;
data svp;
merge sub(in=a) svinp(in=b);
by subject;
if a and b;
rename subject=subject_;
run;
proc sql;
create table temp as select a.svdtc,a.subject,b.* from uns a cross join svp b;
quit;
data temp;
set temp;
if subject=subject_;
run;
data temp1;
set temp;
day=input(svdtc,yymmdd10.)-input(svendtc,yymmdd10.);
if day>=0;
proc sort;
by subject svdtc day;
proc sort nodupkey;
by subject svdtc;
run;
proc sort data=temp1 out=temp2;
by subject visitnum day;
run;
data temp3;
retain visn 0;
set temp2;
by subject visitnum day;
length unsvisit $200;
visn=visn+1;
if first.visitnum then visn=1;
unsnum=input((strip(put(visitnum,best.))||"."||strip(put(visn,best.))),best.);
unsvisit="计划外访视"||" "||strip(put(unsnum,6.1));
keep subject svdtc unsvisit unsnum;
run;
data temp4;
set temp3;
length svstdtc svendtc $200;
svstdtc=svdtc;
svendtc=svdtc;
rename unsvisit=visit unsnum=visitnum;
run;
data unssp;
set &dat.;
if find(instancename,"计划外") ne 0;
proc sort;
by subject dtc datapagename instancename;
proc sort nodupkey;
by subject dtc datapagename;
run;
data unsp;
length svupdes $200;
retain svupdes "";
set unssp;
by subject dtc datapagename;
if first.dtc then svupdes=strip(datapagename);
else svupdes=strip(svupdes)||","||strip(datapagename);
if last.dtc;
dtc=put(input(dtc,yymmdd10.),yymmdd10.);
run;
proc sql noprint;
create table temp4_1 as select a.*,b.svupdes from temp4 a left join unsp b on a.subject=b.subject and a.svdtc=b.dtc;
quit;
接着合并访视开始与结束时间,并进行拆分。拆分为计划内访视和计划外访视。使用cross join将每个计划外访视与计划内访视进行合并,并比较。此处使用cross join时,应注意,需要在合并后筛选受试者编号相同的观测。保留计划外访视发生时间小于计划内访视的观测,并进行排序,得到差值最小的观测。这就是我们需要的计划外访视的名称前缀。最后与SDTM.DM进行合并,获取RFSTDTC信息,计算STDY和ENDY,就可以了。