欢迎来到SAS程序分享号
本文是上一篇的推文的续篇,本篇推文将主要介绍GTL绘制受试者维度的时药曲线(画拼图),并分享小编刚出炉的,还热腾腾的自动画图的宏程序。点击
授人以鱼不如授人以渔
授人以渔
既然决定授人以渔,那么小编打算从程序设计的原理开始分享。
1.数据集结构的处理(将数据集转置,并保留SUBJID、PKTPT、GROUP作为公共变量,每个受试者编号作为一个新的变量)
2.编写Template语句(利用column、rows来设置每页拼图数量,利用dynamic将template中的Y轴的变量开放出来,便于后面修改dynamic后的变量的值进行控制输出。稍后会列举一个2x1图开放式的代码作为例子,便于观察理解)
3.循环proc sgrender过程,每次修改dynamic后变量的值,即可实现。
程序的设计思路大概就是上面的那个样子的。下面看一个简单的例子。
自动输出宏程序
宏参数设置
小编写宏的时候,一般会事先考虑宏的功能,然后将一些参数进行开放出来。当然一些不太好自动处理的地方小编也是图省事,将其开放出来。下面来看看小编设置的宏参数。
/*************************************************************************************************************************
宏名称 : drw_pk
目的 : GTL语句画 colunms X rows图
参数说明: inds 输入数据集
subjid 受试者编号/随机号
pktpt 计划时间点的变量
pkorres 药物浓度
group 组别
xvalue X轴坐标轴数据
yvalue Y轴坐标轴数据
colunms 拼图列数
rows 拼图行数
width 每个子图的宽度(cm)
height 每个子图的高(cm)
outpath RTF输出的路径
__________________________________________________________________________________________________________________________
版本 日期 修改人 修改描述
--- ----------- ------------ ----------------------------------------------------------------------------------
1.0 2018.12.10 setup SAS程序分享号号号(微信号:xiaocgn)创建
****************************************************************************************************************************************/
定义线条颜色类型等
小编预先设定分组用到的标记符号、颜色、线条类型等,并根据数据中实际分组数进行自动获取分组标记,赋值给宏变量。便于后面在Template语句中调用。代码如下。
%macro drw_pk(inds=,subjid=,pktpt=%str(),pkorres=%str(),group=%str(),xvalue=%str(),yvalue=%str(),colunms=%str(),rows=%str(),width=%str(),
height=%str(),outpath=);
options nocenter nodate nonumber nobyline missing=' ';
/*解除变量名称限制*/
options validvarname=any;
/********************************************************************************************
*设置散点形状、线条类型、颜色;
*******************************************************************************************/
*获取分组;
proc sql noprint;
select distinct(&group.) into:gplist separated by "|" from &inds. ;
quit;
%let color=blue|red|green|Black|Purple|Orange;
%let lintype=solid|shortdash|mediumdash|longdash|mediumdashshortdash|dashdashdot;
%let marktype=circle|triangle|triangledown|triangleleft|triangleright|circlefilled;
%let _mgp=1;
%do %while(%qscan(&gplist,&_mgp,|)^=%str());
%let _gp&_mgp=%qscan(&gplist,&_mgp,|);
%let _color&_mgp=%qscan(&color,&_mgp,|);
%let _lintype&_mgp=%qscan(&lintype,&_mgp,|);
%let _marktype&_mgp=%qscan(&marktype,&_mgp,|);
%let _mgp=%eval(&_mgp+1);
%end;
%let _tmp=%eval(&_mgp-1);
上面代码控制的是下面圈起来的部分。实现根据组别,自动设置颜色、形状、线条类型等。
数据集转置
接下来,对数据集进行自动转置。最开始写原理的时候提到了,会将数据集进行转置。
/********************************************************************************************
*将数据集进行转置,一个受试者一列
*******************************************************************************************/
proc sort data=&inds. out=have2(keep=&subjid. &group. &pktpt. &pkorres.) ;by &pktpt. &group. &subjid. ;quit;
/*数据集转置将*/
proc transpose data=have2 out=have3 prefix=C_;
by &pktpt. &group.;
var &pkorres.;
id &subjid. ;
run;
/*获取循环次数:受试者个数、以及受试者名称并赋值给宏变量*/
proc sql noprint;
select count(distinct &subjid. ) into: _loop from &inds.;
select distinct &subjid. into: name1-:name%left(&_loop.) from &inds. order by &subjid.;
quit;
这部分会将数据集实现下图的转换。也就是一个受试者是一列。
一些计算及分组
写程序的时候,难免有一些考量,需要进行计算或者判断。比如,2*4图如果是50个受试者,一页放8张子图,所以一定有一页是放不满8个的,这个时候就需要根据是否有余数再设置一个Template(余数部分,也就是最后一页的受试者进行特殊处理)。
/********************************************************************************************
*根据受试者个数设置图像 (colunms*rows)
*******************************************************************************************/
*一页图片个数;
%let page_num_gif=%sysevalf(&colunms*&rows);
*计算(colunms*rows)页数;
%let _out_loop=%sysfunc(int(&_loop./&page_num_gif));
*计算余数(最后一页不够(colunms*rows));
%let _out_mod=%sysfunc(mod(&_loop.,&page_num_gif));
%let sum_width=%sysevalf(&width.*&colunms+&colunms-1);
%let sum_height=%sysevalf(&height.*&rows+&rows-1);
/********************************************************************************************
*根据 (colunms*rows) 将受试者划分组
*******************************************************************************************/
proc sort data=&inds.(keep=&subjid) out=_codds sortseq=linguistic(numeric_collation=on) nodupkey;by &subjid. ;quit;
data _codds;
set _codds;
_outgp=ceil(_n_/&page_num_gif.);
_outmod=mod(_n_,&page_num_gif.);
if _outmod=0 then _outmod=&page_num_gif.;
_title=strip('Ytitle')||strip(_outmod)||strip('="')||STRIP("&subjid.")||strip('=')||strip(&subjid.)||strip('"');
_dyn=strip('Yvar')||strip(_outmod)||strip('="C_')||strip(&subjid.)||strip('"');
run;
data _codds1 _codds2;
set _codds;
length finala finalb $4000.;
by _outgp notsorted;
retain finala finalb;
if first._outgp then do;
finala=_title;
finalb=_dyn;
end;
else do;
finala=catx(" ",finala,_title);
finalb=catx(" ",finalb,_dyn);
end;
%if &_out_loop. ne 0 %then %do;
if _N_<=%eval(&_loop.-&_out_mod.) and last._outgp then output _codds1;
else if last._outgp then output _codds2;
%end;
%if &_out_loop. eq 0 %then %do;
if last._outgp then output _codds2;
%end;
run;
上面这段处理会生成2个数据集,_codds1(非余数部分的人)与_codds2(余数部分的人)。然后会将他们的值赋值给宏变量,用于后面的循环。
Template过程
template过程的代码,其实不多。主要是几个循环,循环的次数会根据前面的设置进行确定的。
ods graphics / width=&sum_width. cm height=&sum_height. cm border=off;
%if &_out_loop. ne 0 %then %do;
data _null_;
set _codds1;
call symput('_lista'||compress(put(_n_,best.)),strip(finala));
call symput('_listb'||compress(put(_n_,best.)),strip(finalb));
run;
proc template;
define statgraph drwa;
begingraph/backgroundcolor=white border=false ;
dynamic %do i=1 %to &page_num_gif. ;%sysfunc(compress(YVAR&i.)) %end;%do i=1 %to &page_num_gif. ;%sysfunc(compress(Ytitle&i.)) %end;; ;
*循环实现 散点形状、线条类型、颜色; ;
discreteattrmap name="temp1" / ignorecase=true;
%do i=1 %to &_tmp.;
value "&&_gp&i." /markerattrs=GraphData1(color=&&_color&i. symbol=&&_marktype&i.) lineattrs=GraphData1(color=&&_color&i. pattern=&&_lintype&i.);
%end;
enddiscreteattrmap;
discreteattrvar attrvar=markers var=&group. attrmap="temp1";
layout lattice /columns=&colunms. rows=&rows. columngutter=1cm rowgutter=1cm border=false ;
%do i=1 %to &page_num_gif.;
layout OVERLAY/CYCLEATTRS=TRUE
Xaxisopts=(gridDisplay=off LABEL="时间(h)" LABELATTRS=( size=7pt)/*坐标轴标签属性修改*/ TICKVALUEATTRS=( size=7pt)/*坐标轴值属性修改*/ %if %length(&xvalue.) %then %do;linearopts=( TICKVALUELIST=(&xvalue. ) ) %end; )
Yaxisopts=(gridDisplay=off LABEL="血药浓度(ng/mL)" LABELATTRS=(size=7pt)/*坐标轴标签属性修改*/ TICKVALUEATTRS=( size=7pt)/*坐标轴值属性修改*/ %if %length(&yvalue.) %then %do; linearopts=( TICKVALUELIST=(&Yvalue. ) ) %end; );
seriesplot x=&PKTPT y=YVAR&i. / group=markers name="series&i." lineattrs=(thickness=2) display=(markers) ;
discretelegend "series&i." / valueattrs=(family="arial" size=7pt) title=Ytitle&i. titleattrs=GraphValueText(color=black size=8pt ) location=inside autoalign=(topright topleft) opaque=true border=false across=1;
endlayout;
%end;
endlayout;
Endgraph; end;
run;
%end;
%if &_out_mod. ne 0 %then %do;
data _null_;
set _codds2;
call symput('_mlista'||compress(put(_n_,best.)),strip(finala));
call symput('_mlistb'||compress(put(_n_,best.)),strip(finalb));
run;
proc template;
define statgraph drwb;
begingraph/backgroundcolor=white border=false ;
dynamic %do i=1 %to &_out_mod. ;%sysfunc(compress(YVAR&i.)) %end;%do i=1 %to &_out_mod. ;%sysfunc(compress(Ytitle&i.)) %end;;
*循环实现 散点形状、线条类型、颜色; ;
discreteattrmap name="temp1" / ignorecase=true;
%do i=1 %to &_tmp.;
value "&&_gp&i." /markerattrs=GraphData1(color=&&_color&i. symbol=&&_marktype&i.) lineattrs=GraphData1(color=&&_color&i. pattern=&&_lintype&i.);
%end;
enddiscreteattrmap;
discreteattrvar attrvar=markers var=&group. attrmap="temp1";
layout lattice /columns=&colunms. rows=&rows. columngutter=1cm rowgutter=1cm border=false ;
%do i=1 %to &_out_mod.;
layout OVERLAY/CYCLEATTRS=TRUE
Xaxisopts=(gridDisplay=off LABEL="时间(h)" LABELATTRS=( size=7pt)/*坐标轴标签属性修改*/ TICKVALUEATTRS=( size=7pt)/*坐标轴值属性修改*/ %if %length(&xvalue.) %then %do;linearopts=( TICKVALUELIST=(&xvalue. ) ) %end; )
Yaxisopts=(gridDisplay=off LABEL="血药浓度(ng/mL)" LABELATTRS=(size=7pt)/*坐标轴标签属性修改*/ TICKVALUEATTRS=( size=7pt)/*坐标轴值属性修改*/ %if %length(&yvalue.) %then %do; linearopts=( TICKVALUELIST=(&Yvalue. ) ) %end; );
seriesplot x=&PKTPT y=YVAR&i. / group=markers name="series&i." lineattrs=(thickness=2) display=(markers) ;
discretelegend "series&i." / valueattrs=(family="arial" size=7pt) title=Ytitle&i. titleattrs=GraphValueText(color=black size=8pt ) location=inside autoalign=(topright topleft) opaque=true border=false across=1;
endlayout;
%end;
endlayout;
Endgraph; end;
run;
%end;
ods rtf file="&outpath.";
%if &_out_loop. ne 0 %then %do;
%do i=1 %to &_out_loop.;
proc sgrender data=have3 template=drwa;
dynamic &&_lista&i.;
dynamic &&_listb&i.;
run;
%end;
%end;
%if &_out_mod. ne 0 %then %do;
proc sgrender data=have3 template=drwb;
dynamic &_mlista1;
dynamic &_mlistb1;
run;
%end;
ods rtf close;
proc delete data=have3 have2 _codds _codds1 _codds2;quit;
%mend;