swimmer plot:游泳图;泳道图,也叫作Napoleon plot.(拿破仑图?拿破仑做错了什么了?他不就拿了一个破轮子)
泳道图因为看起来像一条条泳道,就像下面的图形一样。因此得名。泳道图最常用的用途就是用来展示肿瘤研究中的评估结果,但是其实泳道图还能用来展示治疗/观察周期、事件发生、事件持续时间或受试者状态。下面这张图它展示的就不是肿瘤总体评估的数据,而是不同剂量组给药时长的数据。
但是一般来说,我们做项目也不会要你画给药的泳道图,大多数都是肿瘤总体评估的泳道图,所以,我还是以肿瘤评估数据举例怎么画泳道图。
介绍下泳道图各个部位代表的意思,以一张经典的泳道图为例(有些会有些许差异,具体还是按照shell画吧)。然后总体评估(Overall Response我在后面都用OR代替,少打点字)
1)泳道图的X轴代表试验进行的duration,至于刻度单位是weeks还是months,可以参考shell,但是我觉得如果试验只进行了很短的一段的时间,用monhths作为X轴的单位的话,泳道会很短,不美观,用WEEKS稍微合理一点,然后一般也都是从0开始,不像图中的还有负坐标轴。
2)泳道图的Y轴是具体的受试者编号,也就是subjid,一个subjid一条泳道。
3)接着就是图里面的这些标志(三角形、方形、圆形等),大家可以看右下角的标签,不同的形状代表受试者在对应的周期,肿瘤总体评估结果是什么?比如我们看第一条泳道,这个受试者在第6-7个月的时候,肿瘤总体评估结果是CR,用红色的三角形表示。
需要注意的是,这张图三角形的标签是CR start,但是我目前的做的项目只要标明这个点是CR/PR/PD/SD就行了,不用标明start/end;所以泳道图有些细节还是需要和统计师确认了,比如有的还要求在OR和OR之间用横线表示,像下面这样,但是发现下面这张图只求CR和PR,然后求CR和PR的持续时间,也就是图上三角形和圆形之间的横线。
今天只简单地展示各个周期的OR,不展示持续时间,看下以后再讲(其实我不会,哈哈),然后这次只用GTL,不用SGPLOT,因为SGPLOT我也不会,哈哈哈。
4)然后泳道的长度代表什么?泳道的终点代表什么?一般来说,泳道的起点都是首次给药日期,然后泳道的终点要跟统计师确认一下,因为终点可能为治疗结束日期(EOT),也可能为研究结束日期(EOS),而且如果统计师看EOS,但是医学却要看EOT,所以最好确认一下,先按统计师的来,如果统计和医学不一致,开会讨论一下呗。
如果受试者既没有EOT,也没有EOS或者Death,那么这个subjid就是continued,一般用箭头表示,像下面这样:
那么这时候泳道的长度就是RFPENDTC或者受试者已知的最大存活时间。
5)然后你们看上面几张图每条泳道的颜色都不一样,这是因为把不同组别的受试者都放到一张图里面,所以X轴下面还加了一个标签表明不同颜色代表不同组别(或治疗阶段),如果一个组别有很多受试者,我们就可以分组别出图,比如3组就3张图。然后泳道的颜色就一样。
泳道图的重点就上面几个点,下面我们开始画图了:
先创建数据:一般一项肿瘤试验,会在基线测量靶病灶、非靶病灶的信息,以便和给药后的肿瘤状态进行比较,进而得出总体评估疗效,一般可能是4周一次,6周一次,这个看方案。然后总体评估数据一般做到ADRS里面,一般是直接拿CRF上收集的总体评估数据画图,但是我也遇到过医学想用confimed后的结果拿去画图,这个最好确认下。
然后我从如何创建画图的数据集开始讲起,手把手教,假设你的ADRS已经做好了。如下(代码我看下直接上传到简书吧,简书好像可以markdown
上面的就像我们已经生成的ADaM数据集之ADRS,接着我们对数据集进行处理,生成画图需要的变量。生成的变量作用已经注释出来了,其中我们是根据是否EOT判断continued。
数据已经准备好了,接着我们就开始画图了
data adrs;
subjid="S001";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-15";RSDTC="2022-01-10";avisit="肿瘤评估周期1";avalc="SD";rfpendtc="2022-06-10";output;
subjid="S001";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-15";RSDTC="2022-02-15";avisit="肿瘤评估周期2";avalc="PR";rfpendtc="2022-06-10";output;
subjid="S001";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-15";RSDTC="2022-03-15";avisit="肿瘤评估周期3";avalc="SD";rfpendtc="2022-06-10";output;
subjid="S001";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-15";RSDTC="2022-04-18";avisit="肿瘤评估周期4";avalc="SD";rfpendtc="2022-06-10";output;
subjid="S001";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-15";RSDTC="2022-05-10";avisit="肿瘤评估周期6";avalc="CR";rfpendtc="2022-06-10";output;
subjid="S002";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-10";RSDTC="2022-01-10";avisit="肿瘤评估周期1";avalc="SD";rfpendtc="2022-05-10";output;
subjid="S002";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-10";RSDTC="2022-02-15";avisit="肿瘤评估周期2";avalc="SD";rfpendtc="2022-05-10";output;
subjid="S002";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-10";RSDTC="2022-03-15";avisit="肿瘤评估周期3";avalc="PD";rfpendtc="2022-05-10";output;
subjid="S002";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-10";RSDTC="2022-04-18";avisit="肿瘤评估周期4";avalc="PD";rfpendtc="2022-05-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-01-01";avisit="肿瘤评估周期1";avalc="CR";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-02-15";avisit="肿瘤评估周期2";avalc="PR";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-03-15";avisit="肿瘤评估周期3";avalc="PR";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-04-18";avisit="肿瘤评估周期4";avalc="PR";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-05-15";avisit="肿瘤评估周期5";avalc="SD";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-06-18";avisit="肿瘤评估周期6";avalc="SD";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-07-15";avisit="肿瘤评估周期7";avalc="PD";rfpendtc="2022-09-10";output;
subjid="S003";TRTA="A组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-01";RSDTC="2022-08-18";avisit="肿瘤评估周期8";avalc="PD";rfpendtc="2022-09-10";output;
subjid="S004";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-06-01";RSDTC="2022-07-15";avisit="肿瘤评估周期1";avalc="PD";dthdtc="2022-10-20";rfpendtc="2022-10-20";output;
subjid="S004";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-06-01";RSDTC="2022-08-18";avisit="肿瘤评估周期2";avalc="PD";dthdtc="2022-10-20";rfpendtc="2022-10-20";output;
subjid="S005";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-21";RSDTC="2022-01-19";avisit="肿瘤评估周期1";avalc="CR";EOTDTC="2022-05-01";rfpendtc="2022-05-21";output;
subjid="S005";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-21";RSDTC="2022-02-21";avisit="肿瘤评估周期2";avalc="PR";EOTDTC="2022-05-01";rfpendtc="2022-05-21";output;
subjid="S005";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-21";RSDTC="2022-03-22";avisit="肿瘤评估周期3";avalc="PR";EOTDTC="2022-05-01";rfpendtc="2022-05-21";output;
subjid="S005";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2021-12-21";RSDTC="2022-04-18";avisit="肿瘤评估周期4";avalc="SD";EOTDTC="2022-05-01";rfpendtc="2022-05-21";output;
subjid="S006";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-01-01";RSDTC="2022-01-29";avisit="肿瘤评估周期1";avalc="CR";EOTDTC="2022-06-01";dthdtc="2022-11-20";rfpendtc="2022-11-20";output;
subjid="S006";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-01-01";RSDTC="2022-02-21";avisit="肿瘤评估周期2";avalc="PR";EOTDTC="2022-06-01";dthdtc="2022-11-20";rfpendtc="2022-11-20";output;
subjid="S006";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-01-01";RSDTC="2022-03-25";avisit="肿瘤评估周期3";avalc="PR";EOTDTC="2022-06-01";dthdtc="2022-11-20";rfpendtc="2022-11-20";output;
subjid="S006";TRTA="B组";PARAMCD="OVRLRESP";TRTSDTC="2022-01-01";RSDTC="2022-04-28";avisit="肿瘤评估周期4";avalc="SD";EOTDTC="2022-06-01";dthdtc="2022-11-20";rfpendtc="2022-11-20";output;
RUN;
data adrs2;
set adrs;
resp=avalc;
id=subjid;
*这种做法是画多个散点图,将散点分好几种情况;
/* if avalc="CR" then CRDUR=round((input(RSDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1);*/
/* if avalc="PR" then PRDUR=round((input(RSDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1);*/
/* if avalc="SD" then SDDUR=round((input(RSDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1);*/
/* if avalc="PD" then PDDUR=round((input(RSDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1);*/
if RSDTC^="" then resp_dur=round((input(RSDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1); *每个OR据首次给药的间期;
if EOTDTC="" then ONGODUR=round((input(rfpendtc,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1); *continued标帜;
if DTHDTC^="" then DTHDUR=round((input(DTHDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1); *死亡标志;
if EOTDTC^="" then totdur=round((input(EOTDTC,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1); *泳道的长度;
else if EOTDTC="" and rfpendtc^="" then totdur=round((input(rfpendtc,YYMMDD10.)-input(TRTSDTC,YYMMDD10.))/7,0.1); *泳道的长度;
proc sort;by trta totdur;
run;
ods listing close;
ods rtf file = "&project\graph\swimplot.rtf" ;
proc template;
define statgraph swimmplot;
begingraph/;
discreteattrmap name='restype';
value 'SD' / markerattrs=(symbol=squarefilled color=blue size=10);
value 'PR' / markerattrs=(symbol=squarefilled color=yellow size=10);
value 'CR' / markerattrs=(symbol=squarefilled color=green size=10);
value 'PD' / markerattrs=(symbol=squarefilled color=red size=10);
enddiscreteattrmap;
discreteattrvar attrvar=resp_map var=resp attrmap='restype';
entrytitle "Swim Plot of Overall Response ";
layout overlay/ walldisplay=(fill)
xaxisopts=(label='Duration in Weeks' offsetmin=0 linearopts=(tickvaluesequence=(start=0 end=50 increment=5) viewmin=0 viewmax=60))
yaxisopts=(label='Subject' offsetmax=0.25);
barchart category=id response=totdur/ group=trta orient=horizontal name='bar' stat=mean barwidth=0.2 fillattrs= (transparency=0.2) INCLUDEMISSINGGROUP=FALSE;
scatterplot X=resp_dur Y=id/group=resp_map name="res";
scatterplot x=ONGODUR y=id / markerattrs=(symbol=trianglerightfilled color=orange size=8) name='ongo' legendlabel="Continued";
scatterplot x=DTHDUR y=id / markerattrs=(symbol=squarefilled color=black size=8) name='dth' legendlabel="Death";
discreteLegend "bar" "res" "ongo" "dth"/across=1 autoalign=(topright) location=inside valueattrs=(size=8pt ) border=false;
endlayout;
endgraph;
end;
run;
proc sgrender data=adrs2 template=swimmplot;
run;
ods rtf close;
ods listing;
靠,MARKDOWN在哪里设置,算了,我先复制成文本吧
下面我们简单介绍一些关键参数,后面有时间再慢慢介绍一些选项
①:discreteattrmap:定义一组可与用户定义的值集关联的图形属性。
也就是我们看上面,如果Avalc,它的值是CR/PR/SD/PD,那么它它们分别就由不同的形状和颜色,这是我们告诉SAS我们要定义成这样,如果图中用到这些值,那么你就给我展示成我设定的样式
②:discreteattrvar:在用户定义的离散属性映射和输入数据列之间创建命名关联。
2.1ATTRVAR:为属性映射和输入列之间的关联指定SAS名称。
2.2VAR:指定要在运行时与属性映射关联的输入数据列。我们这里用的就是数据集里面的RESP变量
2.3ATTRVAR:为属性映射和输入列之间的关联指定SAS名称。
这几个看起来有点抽象,但是你们看例子就明白了,如果我只用
discreteattrvar attrvar=resp_map var=resp attrmap='restype';
把discreteattrmap注释掉就这一段可以吗?
没问题,SAS也不会报错,然后你们看输出
这时候那么OR都变成SAS自己默认的样式,所以说discreteattrmap和discreteattrvar联用可以自己定义输出值的样式。
③:entrytitle :SAS明明说The maximum length for the title text is 512 characters,但是我的标题却被截断了,可能是因为我的图的宽度的问题,这个我后面看下是什么原因。
接下来就是定义X轴Y轴的标签等属性,这个简答就不说了。
其实泳道图就是条形图和散点图的结合,所以我们画图就用barchart和scatterplot两个选项就够了。至于barchart和scatterplot我们后面有时间单独讲讲。
Plus:总说有时间有时间,什么叫做有时间,渣男,一次次这样欺骗自己和别人,有意思吗?
③:barchart:category用的就是subjid,response就是泳道的长度,也即是我们上面求得totdur变量。同时注意我用了一个stat=mean,这个选项的作用是相当于让SAS展示成它原来的长度,因为用了response=选项后,stat的默认选项是sum,也就是把
response=变量的值都加起来。所以我们可以看下面的比较,比如S004,两个totdur相加是40多,但是实际上泳道的长度只有20多,
未加stat=mean
加stat=mean
所以这是需要注意的一个点,同时实际画图的时候需要对条形图的宽度样式那些进行一些调整,这里就不讲了。
接着就是散点图,我们需要分OR评估的散点和Continued的散点分别出图,因为Continued的散点是需要我们根据EOT自己判断的,X轴的值就是我们上面的各个OR DUR和totdur,Y轴的值还是subjid。
后面我还自己加上了判断是否死亡的标帜,做法跟continued一样的。
大致的泳道图就长这样了,然后可以的话,我们可以提前对数据集排个序,因为现在的图泳道不是很整齐,我们可以按照泳道长度从长到短,从上到下输出,像下面这样:
然后我们没有区分组别,就像文章一开始讲的,你可以根据有多少个组别分多张图画,但是如果我们想在一张图里面都展示出来呢?没问题,我加个班:
数据集里面我们加上TRTA变量,
然后在GTL里面加上group=trta就可以了,这时候用的是SAS默认的颜色,但是我们也可以用discreteattrmap自己定义,这个你们自己去尝试就行了。
输出如下:按照组别和TOTDUR排序
如果文中有哪些错误欢迎指正
网友评论