proc lifetest data=sascomp.adqs method=PL plots=(survival(atrisk cl) logsurv) alpha=0.05;
time RETIME*STATUS(0);
strata Group;
run;This is my PROC LIFETEST program, which can output a survival plot for me.
I've already know if I add survival(test), then I can add log rank's P-value on my plot, which is:
proc lifetest data=sascomp.adqs method=PL plots=(survival(test atrisk cl) logsurv) alpha=0.05;
time RETIME*STATUS(0);
strata Group;
run;But I don't know how to add median survival time and HR from Cox. Please help me, many thanks!
Hello @morenayan,
I see two possible approaches: One was suggested in the 2023 post Re: Kaplan Meier using proc lifetest and involves reproducing the Kaplan-Meier plot with PROC SGPLOT and using TEXT statements. The second approach might be simpler because it just modifies the existing Kaplan-Meier plot (template), which is relatively easy thanks to the comprehensive macros that SAS has provided for this purpose (see Controlling the Survival Plot by Modifying Graph Templates). Below I provide an example using the SAS-supplied dataset SASHELP.BMT, based on the example Adding a Small Inset Table with Event Information from the documentation.
/* Create sample data for demonstration */
data have;
set sashelp.bmt(where=(group ne 'ALL'));
run;
/* Compute statistics */
ods select none;
ods output homtests=ht quartiles=qt;
proc lifetest data=have;
time t*status(0);
strata group;
run;
ods output parameterestimates=est;
proc phreg data=have;
class group;
model t*status(0)=group / rl;
run;
ods select all;
/* Combine and format statistics */
data stats;
merge qt(where=(percent=50) drop=t:)
est(keep=ClassVal0 h: rename=(ClassVal0=Group));
by group;
length hr $20;
if hazardratio=. then hr='Ref';
else hr=put(HazardRatio,best4.)||' ('||put(HRLowerCL,best4.)||'-'||put(HRUpperCL,best4.)||')';
run;
proc format;
value med
.='NE'
other=[4.];
run;
/* Write statistics to macro variables */
proc sql noprint;
select probchisq into :pval from ht where test like 'L%';
select group, estimate format=med., lowerlimit format=med., upperlimit format=med., hr
into :grp1-, :med1-, :lcl1-, :ucl1-, :hr1- from stats;
quit;
/* Modify inset of Kaplan-Meier plot to contain statistics */
%ProvideSurvivalMacros /* from https://support.sas.com/documentation/onlinedoc/stat/ex_code/151/templft.html */
%let InsetOpts = ;
%let LegendOpts = title="+ Censored" location=inside autoalign=(Bottom);
%macro StmtsBottom;
%let t = / textattrs=(weight=bold);
layout gridded / columns=3 border=TRUE autoalign=(TopRight);
entry halign=left "Logrank p=&pval"; entry " "; entry " ";
entry " "; entry " "; entry " ";
entry halign=left "Group" &t; entry "Median (95% CI)" &t; entry "HR (95% CI)" &t;
entry halign=left "&grp1"; entry "&med1 (&lcl1-&ucl1)"; entry "&hr1";
entry halign=left "&grp2"; entry "&med2 (&lcl2-&ucl2)"; entry "&hr2";
endlayout;
%mend;
%CompileSurvivalTemplates
/* Create the modified Kaplan-Meier plot */
ods graphics on;
proc lifetest data=have plots=(survival(test atrisk cl) logsurv);
time t*status(0);
strata group;
run;
Result:
Hello @morenayan,
I see two possible approaches: One was suggested in the 2023 post Re: Kaplan Meier using proc lifetest and involves reproducing the Kaplan-Meier plot with PROC SGPLOT and using TEXT statements. The second approach might be simpler because it just modifies the existing Kaplan-Meier plot (template), which is relatively easy thanks to the comprehensive macros that SAS has provided for this purpose (see Controlling the Survival Plot by Modifying Graph Templates). Below I provide an example using the SAS-supplied dataset SASHELP.BMT, based on the example Adding a Small Inset Table with Event Information from the documentation.
/* Create sample data for demonstration */
data have;
set sashelp.bmt(where=(group ne 'ALL'));
run;
/* Compute statistics */
ods select none;
ods output homtests=ht quartiles=qt;
proc lifetest data=have;
time t*status(0);
strata group;
run;
ods output parameterestimates=est;
proc phreg data=have;
class group;
model t*status(0)=group / rl;
run;
ods select all;
/* Combine and format statistics */
data stats;
merge qt(where=(percent=50) drop=t:)
est(keep=ClassVal0 h: rename=(ClassVal0=Group));
by group;
length hr $20;
if hazardratio=. then hr='Ref';
else hr=put(HazardRatio,best4.)||' ('||put(HRLowerCL,best4.)||'-'||put(HRUpperCL,best4.)||')';
run;
proc format;
value med
.='NE'
other=[4.];
run;
/* Write statistics to macro variables */
proc sql noprint;
select probchisq into :pval from ht where test like 'L%';
select group, estimate format=med., lowerlimit format=med., upperlimit format=med., hr
into :grp1-, :med1-, :lcl1-, :ucl1-, :hr1- from stats;
quit;
/* Modify inset of Kaplan-Meier plot to contain statistics */
%ProvideSurvivalMacros /* from https://support.sas.com/documentation/onlinedoc/stat/ex_code/151/templft.html */
%let InsetOpts = ;
%let LegendOpts = title="+ Censored" location=inside autoalign=(Bottom);
%macro StmtsBottom;
%let t = / textattrs=(weight=bold);
layout gridded / columns=3 border=TRUE autoalign=(TopRight);
entry halign=left "Logrank p=&pval"; entry " "; entry " ";
entry " "; entry " "; entry " ";
entry halign=left "Group" &t; entry "Median (95% CI)" &t; entry "HR (95% CI)" &t;
entry halign=left "&grp1"; entry "&med1 (&lcl1-&ucl1)"; entry "&hr1";
entry halign=left "&grp2"; entry "&med2 (&lcl2-&ucl2)"; entry "&hr2";
endlayout;
%mend;
%CompileSurvivalTemplates
/* Create the modified Kaplan-Meier plot */
ods graphics on;
proc lifetest data=have plots=(survival(test atrisk cl) logsurv);
time t*status(0);
strata group;
run;
Result:
HI, thanks!!!
i've already solved my problem perfectly by using the second solutions you've provived.
Hi @FreelanceReinh thank you for the solution on this post. I have a question on this . Is there any way I can move the legend inside for groups, like in the image below? I was able to get this graph generated from %newsurvmacro from @JeffMeyers (https://communities.sas.com/t5/SAS-Communities-Library/Kaplan-Meier-Survival-Plotting-Macro-NEWSURV/...). However, I am trying to lift the code from the above macro and incorporate in your code. Is it possible?
Here you go:
proc format;
value grpLabel 1='ALL' 2='AML low risk' 3='AML high risk';
run;
data BMT;
input DIAGNOSIS Ftime Status Gender@@;
label Ftime="Days";
format Diagnosis grpLabel.;
datalines;
1 2081 0 1 1 1602 0 1
1 1496 0 1 1 1462 0 0
1 1433 0 1 1 1377 0 1
1 1330 0 1 1 996 0 1
1 226 0 0 1 1199 0 1
1 1111 0 1 1 530 0 1
1 1182 0 0 1 1167 0 0
1 418 2 1 1 383 1 1
1 276 2 0 1 104 1 1
1 609 1 1 1 172 2 0
1 487 2 1 1 662 1 1
1 194 2 0 1 230 1 0
1 526 2 1 1 122 2 1
1 129 1 0 1 74 1 1
1 122 1 0 1 86 2 1
1 466 2 1 1 192 1 1
1 109 1 1 1 55 1 0
1 1 2 1 1 107 2 1
1 110 1 0 1 332 2 1
2 2569 0 1 2 2506 0 1
2 2409 0 1 2 2218 0 1
2 1857 0 0 2 1829 0 1
2 1562 0 1 2 1470 0 1
2 1363 0 1 2 1030 0 0
2 860 0 0 2 1258 0 0
2 2246 0 0 2 1870 0 0
2 1799 0 1 2 1709 0 0
2 1674 0 1 2 1568 0 1
2 1527 0 0 2 1324 0 1
2 957 0 1 2 932 0 0
2 847 0 1 2 848 0 1
2 1850 0 0 2 1843 0 0
2 1535 0 0 2 1447 0 0
2 1384 0 0 2 414 2 1
2 2204 2 0 2 1063 2 1
2 481 2 1 2 105 2 1
2 641 2 1 2 390 2 1
2 288 2 1 2 421 1 1
2 79 2 0 2 748 1 1
2 486 1 0 2 48 2 0
2 272 1 0 2 1074 2 1
2 381 1 0 2 10 2 1
2 53 2 0 2 80 2 0
2 35 2 0 2 248 1 1
2 704 2 0 2 211 1 1
2 219 1 1 2 606 1 1
3 2640 0 1 3 2430 0 1
3 2252 0 1 3 2140 0 1
3 2133 0 0 3 1238 0 1
3 1631 0 1 3 2024 0 0
3 1345 0 1 3 1136 0 1
3 845 0 0 3 422 1 0
3 162 2 1 3 84 1 0
3 100 1 1 3 2 2 1
3 47 1 1 3 242 1 1
3 456 1 1 3 268 1 0
3 318 2 0 3 32 1 1
3 467 1 0 3 47 1 1
3 390 1 1 3 183 2 0
3 105 2 1 3 115 1 0
3 164 2 0 3 93 1 0
3 120 1 0 3 80 2 1
3 677 2 1 3 64 1 0
3 168 2 0 3 74 2 0
3 16 2 0 3 157 1 0
3 625 1 0 3 48 1 0
3 273 1 1 3 63 2 1
3 76 1 1 3 113 1 0
3 363 2 1
;
run;
ods select none;
ods output SurvivalPlot=SurvivalPlot;
proc lifetest data=bmt plots=(survival);
time ftime*Status(0);
strata diagnosis;
run;
ods select all;
data legend;
input x y treat & $20. event $ median & $40. hr & $20.;
label treat='DIAGNOSIS' event='Events/Total' median='Median(95%CI)' hr='HR(95%CI)';
cards;
0 4 ALL 26/38 32.7 (15.3-45.2) Reference
1 4 ALL 26/38 32.7 (15.3-45.2) Reference
0 3 AML high risk 45/54 44.8 (33.8-51.5) 0.56 (0.34-0.93)
1 3 AML high risk 45/54 44.8 (33.8-51.5) 0.56 (0.34-0.93)
0 2 AML low risk 24/45 37.3 (11.9-70.1) 0.56 (0.31-1.01)
1 2 AML low risk 24/45 37.3 (11.9-70.1) 0.56 (0.31-1.01)
0 1 Logrank P-value: 0.0541 . + Censor
1 1 Logrank P-value: 0.0541 . + Censor
;
ods listing gpath="%sysfunc(pathname(work))" image_dpi=300 style=htmlblue;
ods graphics /ATTRPRIORITY=none height=80px width=450px noborder imagename='legend' outputfmt=png;
ods html exclude sgplot;
/*generate a Legend graph*/
title;
proc sgplot data=legend noborder pad=0 noautolegend;
styleattrs datalinepatterns=(solid longdash dot dot) DATACONTRASTCOLORS=(black red green white);
series x=x y=y/group=treat lineattrs=(thickness=4);
yaxistable treat event median hr/pad=10 labelattrs=(size=16 weight=bold) valuejustify=center valueattrs=(size=14);
xaxis display=none;
yaxis display=none;
run;
%SGANNO;
data sganno;
%SGIMAGE(image="%sysfunc(pathname(work))\legend.png",
drawspace="wallpercent", x1=98, y1=98,width=80,
anchor="topright",
border="false"
);
run;
ods html dpi=300;
ods graphics / reset=all ATTRPRIORITY=none;
proc sgplot data=SurvivalPlot sganno=sganno noautolegend;
styleattrs datalinepatterns=(solid longdash dot) DATACONTRASTCOLORS=(black red green);
step x=Time y=Survival / group=Stratum lineattrs=(thickness=2);
scatter x=Time y=Censored / group=Stratum markerattrs=(symbol=plus);
run;
thank you @JeffMeyers .
Thank you @Ksharp for the code. It worked like a gem.
Nearly 200 sessions are now available on demand with the SAS Innovate Digital Pass.
Explore Now →Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.