BookmarkSubscribeRSS Feed
djrisks
Barite | Level 11

Hello, 

 

I have this graph below which I created in SAS 9.2 M2 using the Kaplan Meier Template - Stat.Lifetest.Graphics.ProductLimitSurvival. I would simply like to add another two curves to the plot (which are not related to Kaplan Meier). I would like to achieve the creation of the plot just by using the dataset that goes into proc lifetest and modifying the Stat.Lifetest.Graphics.ProductLimitSurvival, i.e. I want to avoid outputting a dataset from Proc Lifetest and then subsequently creating a dataset that has got all the statistics that I want to plot, and then using GTL and SGRENDER to make the plot. Is that possible please? One reason why I would like to avoid using GTL and SGRender is because I think that the confidence bands will be difficult to obtain and plot. Also it would be simpler to just use the dataset that goes into SGPLOT.

 

It would be interesting to know which dataset goes into using this template. I think Warren Kuhfeld desribes it as a Data object in his paper - https://support.sas.com/resources/papers/proceedings13/427-2013.pdf.

 

kaplan.png

 

Here is the code I used to create the above plot.

 

**** graphic options for KM plots - Changing LY colour to Red ***;
options mprint;
%macro ProductLimitStat(n=,fmt=F6.1);
   dynamic PctMedianConfid;
   %if &n=1 %then %do;
      dynamic NObs NEvent Median LowerMedian UpperMedian;
   %end;
   %else %do;
      %do i=1 %to &n;
        dynamic StrVal&i NObs&i NEvent&i Median&i LowerMedian&i UpperMedian&i;;
      %end;
   %end;

   %if &n=1 %then %let ncol=4;
   %else          %let ncol=5;

   layout overlay / pad=(top=5);
      layout gridded / columns=&ncol border=TRUE;
         %if &n>1 %then entry " ";;
         entry "No. of Patients";
         entry "Event";
         entry "Censored";
         entry "Median Survival (" PctMedianConfid " CI)";
         %do i=1 %to &n;
            %if &n=1 %then %do;
               entry NObs;
               entry NEvent;
               entry eval(NObs-NEvent);
               entry eval(put(Median,&fmt)) " ( "
                     eval(put(LowerMedian,&fmt)) " "
                     eval(put(UpperMedian,&fmt)) " )";
            %end;
            %else %do;
               entry halign=left StrVal&i;
               entry NObs&i;
               entry NEvent&i;
               entry eval(NObs&i-NEvent&i);
               entry eval(put(Median&i,&fmt)) " ( "
                     eval(put(LowerMedian&i,&fmt)) " "
                     eval(put(UpperMedian&i,&fmt)) " )";
            %end;
         %end;
      endlayout;
   endlayout;
%mend ProductLimitStat;

proc template;
source Stat.Lifetest.Graphics.ProductLimitSurvival;
define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
   dynamic NStrata xName plotAtRisk plotCensored plotCL plotHW plotEP labelCL labelHW labelEP
      maxTime method StratumID classAtRisk plotBand plotTest GroupName yMin Transparency
      SecondTitle TestName pValue;
    mvar legtit; * Using this instead of GROUPNAME to add the title;
   BeginGraph;
      if (NSTRATA=1)
         if (EXISTS(STRATUMID))
         entrytitle "";
      else
         entrytitle "";
      endif;
      if (PLOTATRISK)
         entrytitle "" / textattrs=GRAPHVALUETEXT;
      endif;
                  layout lattice / rows=1 columns=1;
      layout overlay / xaxisopts=(shortlabel=XNAME offsetmin=.05 linearopts=(viewmax=MAXTIME))
         yaxisopts=(label="Survival Probability" shortlabel="Survival" linearopts=(viewmin=0
         viewmax=1 tickvaluelist=(0 .2 .4 .6 .8 1.0)));
         if (PLOTHW=1 AND PLOTEP=0)
            bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / modelname="Survival" fillattrs
            =GRAPHCONFIDENCE name="HW" legendlabel=LABELHW;
         endif;
         if (PLOTHW=0 AND PLOTEP=1)
            bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / modelname="Survival" fillattrs
            =GRAPHCONFIDENCE name="EP" legendlabel=LABELEP;
         endif;
         if (PLOTHW=1 AND PLOTEP=1)
            bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / modelname="Survival" fillattrs
            =GRAPHDATA1 datatransparency=.55 name="HW" legendlabel=LABELHW;
         bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / modelname="Survival" fillattrs=
            GRAPHDATA2 datatransparency=.55 name="EP" legendlabel=LABELEP;
         endif;
         if (PLOTCL=1)
            if (PLOTHW=1 OR PLOTEP=1)
            bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / modelname="Survival" display
            =(outline) outlineattrs=GRAPHPREDICTIONLIMITS name="CL" legendlabel=LABELCL;
         else
            bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / modelname="Survival"
            fillattrs=GRAPHCONFIDENCE name="CL" legendlabel=LABELCL;
         endif;
         endif;
         stepplot y=SURVIVAL x=TIME / name="Survival" rolename=(_tip1=ATRISK _tip2=EVENT) tip=(y
            x Time _tip1 _tip2) legendlabel="Survival";
         if (PLOTCENSORED=1)
            scatterplot y=CENSORED x=TIME / markerattrs=(symbol=plus) name="Censored"
            legendlabel="Censored";
         endif;
         if (PLOTCL=1 OR PLOTHW=1 OR PLOTEP=1)
            discretelegend "Censored" "CL" "HW" "EP" / location=outside halign=center;
         else
            if (PLOTCENSORED=1)
            discretelegend "Censored" / location=inside autoalign=(topright bottomleft);
         endif;
         endif;
         if (PLOTATRISK=1)
            innermargin / align=bottom;
            blockplot x=TATRISK block=ATRISK / repeatedvalues=true display=(values) valuehalign=
               start valuefitpolicy=truncate labelposition=left labelattrs=GRAPHVALUETEXT
               valueattrs=GRAPHDATATEXT (size=7pt) includemissingclass=false;
         endinnermargin;
         endif;
      endlayout;
                  columnheaders;
               %ProductLimitStat(n=1);
            endcolumnheaders;
      endlayout;

      else
         entrytitle "";
      if (EXISTS(SECONDTITLE))
         entrytitle "" / textattrs=GRAPHVALUETEXT;
      endif;
                  layout lattice / rows=1 columns=1;
      layout overlay / xaxisopts=(shortlabel=XNAME offsetmin=.05 linearopts=(viewmax=MAXTIME))
         yaxisopts=(label="Survival Probability" shortlabel="Survival" linearopts=(viewmin=0
         viewmax=1 tickvaluelist=(0 .2 .4 .6 .8 1.0)));
         if (PLOTHW)
            bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / group=STRATUM index=STRATUMNUM
            modelname="Survival" datatransparency=Transparency;
         endif;
         if (PLOTEP)
            bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / group=STRATUM index=STRATUMNUM
            modelname="Survival" datatransparency=Transparency;
         endif;
         if (PLOTCL)
            if (PLOTBAND)
            bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / group=STRATUM index=
            STRATUMNUM modelname="Survival" display=(outline);
         else
            bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / group=STRATUM index=
            STRATUMNUM modelname="Survival" datatransparency=Transparency;
         endif;
         endif;
         stepplot y=SURVIVAL x=TIME / group=STRATUM index=STRATUMNUM name="Survival" rolename=(
            _tip1=ATRISK _tip2=EVENT) tip=(y x Time _tip1 _tip2);
         if (PLOTCENSORED)
            scatterplot y=CENSORED x=TIME / group=STRATUM index=STRATUMNUM markerattrs=(symbol=
            plus);
         endif;
         if (PLOTATRISK)
            innermargin / align=bottom;
            blockplot x=TATRISK block=ATRISK / class=CLASSATRISK repeatedvalues=true display=(
               label values) valuehalign=start valuefitpolicy=truncate labelposition=left
               labelattrs=GRAPHVALUETEXT valueattrs=GRAPHDATATEXT (size=7pt) includemissingclass
               =false;
         endinnermargin;
         endif;
         DiscreteLegend "Survival" / title=/*GROUPNAME*/ legtit location=outside;
         if (PLOTCENSORED)
            if (PLOTTEST)
            layout gridded / rows=2 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true
            BackgroundColor=GraphWalls:Color Opaque=true;
            entry "+ Censored";
            if (PVALUE < .0001)
               /* Removing log rank */
               /*entry TESTNAME " p " eval (PUT(PVALUE, PVALUE6.4))*/
               entry "";
            else
               /*entry TESTNAME " p=" eval (PUT(PVALUE, PVALUE6.4))*/
               entry "";
            endif;
         endlayout;
         else
            layout gridded / rows=1 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true
            BackgroundColor=GraphWalls:Color Opaque=true;
            entry "+ Censored";
         endlayout;
         endif;
         else
            if (PLOTTEST)
            layout gridded / rows=1 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true
            BackgroundColor=GraphWalls:Color Opaque=true;
            if (PVALUE < .0001)
               entry TESTNAME " p " eval (PUT(PVALUE, PVALUE6.4));
            else
               entry TESTNAME " p=" eval (PUT(PVALUE, PVALUE6.4));
            endif;
         endlayout;
         endif;
         endif;
      endlayout;
                  columnheaders;
                if (NStrata=2) %ProductLimitStat(n=2);endif;
                if (NStrata=3) %ProductLimitStat(n=3);endif;
                if (NStrata=4) %ProductLimitStat(n=4);endif;
                if (NStrata=5) %ProductLimitStat(n=5);endif;
                if (NStrata=6) %ProductLimitStat(n=6);endif;
      endcolumnheaders;
      endlayout;
      endif;
   EndGraph;
end;
run;
goptions reset = all;
ods graphics / reset = all;


*********************;
*Add this code before the lifetest.;


*********************;
*Add this code before the lifetest.;

data bmt;
  set sashelp.BMT;
  xval = 250;
  yval = 0.2;
run;


goptions reset = all;
ods graphics / reset = all imagename= "kaplan";

ods listing gpath = "C:\Users\Desktop\Helpful plots\kaplan_meier_with_new_data";

ods trace on;
proc lifetest data=bmt plots=survival(cb=hw atrisk=0 to 2500 by 500);
   time T * Status(0);
   strata Group / test=logrank adjust=sidak;
run;
ods trace off;

ods graphics off;

Many thanks in advance,

 

Kriss Harris

6 REPLIES 6
Reeza
Super User

You haven't actually explained what you want...

How are these other curves defined? Do they have a fixed definition or are they dynamic based on the LIFETEST procedure? 

 

If its fixed it's probably easier although it will appear on all KM plots. 

 

Getting the data for the chart should be trivial using and ODS OUTPUT statement. . 

djrisks
Barite | Level 11

Hi Reeza,

 

Thank you for your response. The Statistician wanted to plot their own curves which was obtained from a Bayesian analysis model. At first I thought that they wanted to keep the table of statistics below the graph, which automatically came from the Proc Lifetest - which is why I wanted the Proc Lifetest to automatically produce the figure and table. But they want the statistics from the Bayeisan model in the table instead, so I will just get the dataset in the right format and use GTL.

 

Thank you!

DarthPathos
Lapis Lazuli | Level 10

Hi Kriss,

 

Happy Friday 🙂

 

I'm trying to think of examples when I'd want additional, non-KM lines on a KM plot and I must admit I can't think of any.  I've poked around my SAS books and other Data Viz / Time Series books and I can't find any examples of what you're describing; based on the nature of the graph, I would say that the data displayed is limited to KM survival analysis.  If you can provide more detail about what other information you'd want to plot, I (or more than likely someone else) may be able to help.


Chris

Has my article or post helped? Please mark as Solution or Like the article!
djrisks
Barite | Level 11

Hi Chris,

 

Happy Monday 😉 Thank you for your response and for poking around. The Statistician would like to plot their own curves which are obtained from a Bayesian analysis model. At first I thought that they wanted to keep the table of statistics below the graph, which automatically came from the Proc Lifetest - which is why I wanted the Proc Lifetest to automatically produce the figure and table. But they want the statistics from the Bayeisan model in the table instead, so I will just get the dataset in the right format and use GTL.

 

Thank you for helping!

Rick_SAS
SAS Super FREQ

The data set that is used is the one that you get when you use

ODS OUTPUT on the ODS name of the graph (presumably ProductLimitSurvival).

For an example, see the article "How to get data values out of ODS graphics"

djrisks
Barite | Level 11

Thank you for your response Rick 🙂

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 6 replies
  • 1768 views
  • 3 likes
  • 4 in conversation