在上一篇文章SAS如何生成箱型图(Box-Plot) 2--SAS代码介绍中介绍了,SAS中生成Boxplot的4种方法,示例代码生成的图形比较简单。在临床试验分析中,我们通常使用GTL来生成“复杂精美”的图形,下面分享运用GTL生成Boxplot的具体案例代码。
Boxplot template这张图形是,某项目中各访视某指标的基线变化值的箱型图。图形中,X轴为试验访视,Y轴为基线变化值;在每一访视中,Boxplot按试验用药进行分组;在Y=0处,有一条灰色的参考线;在每一访视分组中,需要计算各组的描述统计量。
如何实现上图的效果?我是用sashelp.class数据集进行展示,考虑到需要访视信息,我将数据集重复Set 4次,添加访视信息。
data class;
set sashelp.class( in = a )
sashelp.class( in = b)
sashelp.class( in = c)
sashelp.class( in = d);
if a then avisitn = 1;
else if b then avisitn = 2;
else if c then avisitn = 3;
else if d then avisitn = 4;
run;
用GTL模板出图,首先要制作模板,先把模板框架写好,不断向框架中添加图形中需要的元素。
proc template;
define statgraph boxplot_;
begingraph;
···
···
endgraph;
end;
run;
先在模板中定义坐标轴的属性,考虑到坐标轴需要覆盖数据的所有范围,Y轴需要确定范围,最小值设为50,最大值设为90,间隔为5;X轴为研究访视,数目是确定的,但需要指定每一个访视的名称。tickdisplaylist=()
选项需要和tickvaluelist=()
选项一起使用,并且这两个选项只能用于线性轴 (linear axes only).
layout overlay/
yaxisopts=(
label = "Height" labelattrs = (size = 7) tickvalueattrs=(size=7pt)
linearopts=(ViewMin=50 ViewMax=90 tickValueSequence=(start = 50 end = 90 increment = 5))
)
xaxisopts=(
label = "Study Visit" labelattrs = (size = 7) tickvalueattrs=(size=7pt) type=linear
linearopts=(tickvaluelist=(1 2 3 4) tickdisplaylist=("M6" "M12" "M18" "M24"))
);
···
···
endlayout;
坐标轴属性设置后,进行Boxplot图形的设置。groupdisplay=
选项有两个可选值Stack、Cluster。Stack选项使各组堆叠显示;Cluster选项使各组分散显示。
layout overlay/
···
···
boxplot y = height x = avisitn /
display=(caps mean median outliers)
group = sex groupdisplay=cluster name = "Boxplot";
···
···
endlayout;
将参考线设为Y=65:
layout overlay/
···
···
referenceline y = 65 / lineattrs=(color=grey);
···
···
endlayout;
设置图标属性,这里的图标名称与Boxplot语句中的name一致:
layout overlay/
···
···
discretelegend "Boxplot" / location=inside halign=left valign=top across=1 border=false;
···
···
endlayout;
以上属性设置后,生成的图形为如下:
Boxplot1图形的上半部分已经完成,现在需要完成图形下半部分中各组别的统计量。按照avisitn*sex分组来看 ,这个图形有8个子组。考虑到Boxplot已经使用avisitn作为X轴,这样的话X轴上只要4个“展示位”,我们需要将同一访视中的不同男女性别的统计量合并起来。
proc means data=class ;
class avisitn sex;
var height;
output n=n mean=mean std=sd median=median q1=q1 q3=q3 min=min max=max out=stat(where =(_type_=3));
run;
data stat_m;
set stat(rename = (n=m_n mean=m_mean sd=m_sd median=m_median q1=m_q1 q3=m_q3 min=m_min max=m_max) drop = _:);
where sex = "M";
run;
data stat_f;
set stat(rename = (n=f_n mean=f_mean sd=f_sd median=f_median q1=f_q1 q3=f_q3 min=f_min max=f_max) drop = _:);
where sex = "F";
run;
data stat_final;
merge stat_m(drop=sex) stat_f(drop=sex);
by avisitn ;
length n mean sd median q1q3 minmax $40;
n = put(m_n, 5.)||" "||put(f_n, 5.);
mean = put(m_mean, 8.3)||" "|| put(f_mean, 8.3);
sd = put(m_sd, 8.3)||" "||put(f_sd, 8.3);
median = put(m_median, 8.3)||" "|| put(f_median, 8.3);
q1q3 = put(m_q1, 8.3)||", "||put(m_q3, 8.3)||" "||put(f_q1, 8.3)||", "||put(f_q3, 8.3);
minmax = put(m_min, 8.3)||", "||put(m_max, 8.3)||" "||put(f_min, 8.3)||", "||put(f_max, 8.3);
drop f_: m_: ;
proc sort data = stat_final;
by avisitn;
run;
data class_stat;
merge class(in = a) stat_final;
by avisitn;
if a;
run;
统计量是直接放置在Boxplot图形下方区域,这个区域需要innermargin / align=bottom; -- endinnermargin;
语句进行创建;具体的统计量数值,需要blockplot
语句进行放置。示例代码中,开头是一个宏变量Size的赋值,这是因为编程中每一个block内的数字大小需要根据出图的结果进行不断调整,使用宏变量后,不需要一个一个调整Blockplot语句中的Size。
%let size = 5pt;
innermargin / align=bottom;
blockplot x=avisitn block = n / label="n" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
blockplot x=avisitn block = mean / label="Mean" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
blockplot x=avisitn block = sd/ label="SD" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
blockplot x=avisitn block = media/ label="Median" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
blockplot x=avisitn block = q1q3/ label="Q1, Q3" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
blockplot x=avisitn block = minmax/ label="Min, Max" display=(label values) repeatedvalues=true
valuehalign = start valuefitpolicy= none labelposition=left labelattrs=graphdata1(size=6pt)
valueattrs=graphdata1(size=&size.)
endinnermargin;
模板设置基本完成,我们来看一下调用模板后运行的结果:
proc sgrender data=class_stat template=boxplot_;
run;
Boxplot2
图形的内容已经都展现出来,不过有以下几个问题:
- 底部区域中的Q1Q3、Minmax内容重叠;
- 图标(M/F)应该在Y轴坐标范围之上;
- X轴两侧空白区域太大,需要扩大(扩大后可以解决底部区域重叠的问题);
- Y轴坐标上限值太大,可以缩小;
- 示例图片中,Y轴有密集的小坐标,现在图形中没有。
前3个问题,其实可以归为一类,都是坐标轴范围问题,可以用轴选项OFFSETMIN=/OFFSETMAX=
进行设置。这两个选项的作用是,在坐标轴的最小末端或最大末端保留一块区域,区域内不显示数据内容 (Reserves an area at the minimum/maximum end of the axis. No tick marks are displayed in the reserved area.)。目前图形中的问题是,Y轴上端保留的区域过小,X轴左右两端保留的区域过大。
Y轴坐标数值过大,可以直接设置显示最大值为80。为保证参考线Y=65左侧有数字65显示,将Y轴坐标数字间隔设为5。
关于坐标轴的小坐标,可以使用minorticks=true/false
选项来控制。minorticks=true
默认添加一个小坐标,如果想要小坐标更密集一点,可以使用minortickcount=
选项,自定义小坐标的数目。
修改后的结果如下:
layout overlay/
yaxisopts=(
label = "Height" labelattrs = (size = 7) tickvalueattrs=(size=7pt)
linearopts=(minorticks=true minortickcount=5 ViewMin=50 ViewMax=80 tickValueSequence=(start = 50 end = 80 increment = 5))
offsetmin=0.08 offsetmax=0.15
)
xaxisopts=(
label = "Study Visit" labelattrs = (size = 7) tickvalueattrs=(size=7pt) type=linear
linearopts=(tickvaluelist=(1 2 3 4) tickdisplaylist=("M6" "M12" "M18" "M24"))
offsetmin=0.15 offsetmax=0.15
);
···
···
endlayout;
Boxplot3
X轴坐标值范围扩大后,Boxplot箱体部分显得有些宽,这个可以在Bolplot语句中通过boxwidth=
进行调节,这里就不做代码分享了。
关于图标M/F, 在一些项目中会要求显示各组总人数,例如:Male (N = XXX)。这里分享两个思路:
- 第一个,直接将变量Sex的"M"值修改为“Male (N = XXX)”;
- 第二个,新建一个图标,
discretelegend
语句中直接展示新建的图标。
legenditem type=line name="M" /label = "Male (N = XXX)" lineattrs = (pattern = solid color=blue);
legenditem type=line name="F" /label = "Female (N = XXX)" lineattrs = (pattern = solid color=red);
layout overlay/
···
···
discretelegend "M" "F" / location=inside halign=left valign=top across=1 border=false;
···
···
endlayout;
这里有一个小的注意点,通常N的值我们是保存在宏变量中的,直接调用的话前后可能会有空格,需要处理一下去掉空格。
legenditem type=line name="M" /label = "Male (N = %sysfunc(strip(&N_M.)))" lineattrs = (pattern = solid color=blue);
legenditem type=line name="F" /label = "Female (N = %sysfunc(strip(&N_F.)))" lineattrs = (pattern = solid color=red);
Boxplot4
至此,Boxplot系列分享结束。如有疑问,欢迎留言反馈。
GTL语法问题可以参考:SAS® 9.4 Graph Template Language Reference, Fifth Edition
)
相关文章:
SAS如何生成箱型图(Box-Plot) 1--箱型图简介
SAS如何生成箱型图(Box-Plot) 2--SAS代码介绍
网友评论