Previous Page | Next Page

Statistical Graphics Using ODS

Modifying the Layout and Adding a New Inset Table

Example 49.2 of Chapter 49, The LIFETEST Procedure, uses the following statements to make the plot shown in Output 21.6.3:

   proc lifetest data=BMT plots=survival(atrisk=0 to 2500 by 500);
      ods select SurvivalPlot;
      time T * Status(0);
      strata Group / test=logrank adjust=sidak;
      run;

Output 21.6.3 Default Survival Plot with Number of Subjects At Risk
Default Survival Plot with Number of Subjects At Risk

Output 21.6.3 displays the estimated disease-free survival functions for the three leukemia groups with the number of subjects at risk at 0, 500, 1000, 1500, 2000, and 2500 days. The rest of this example shows you how to modify the template to produce the plot displayed in Output 21.6.4. This new plot differs from the old plot in several ways. It has a new inset table in the top right corner with the number of observations and the number of events in the each stratum. The legend has been moved inside the plot and combined with the old inset table that showed the marker for censored observations. The information about the subjects at risk has been moved into a table below the plot. Also, the title change from the first part of the example has been retained.

Output 21.6.4 Kaplan-Meier Plot with a Different Layout
Kaplan-Meier Plot with a Different Layout

These changes are easy, if they are broken down and performed one step at a time. You can use the template with the new title from the beginning of this example as a starting point for these modifications. Before proceeding, you should notice the outermost layouts of this template, which are shown next:

   proc template;
      define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
         . . .
         BeginGraph;
            if (NSTRATA=1)
               . . .
            layout overlay / . . .;
               . . .
            endlayout;
            else
               . . .
            layout overlay / . . .;
               . . .
            endlayout;
            endif;
         EndGraph;
      end;
   run;

This template consists of two major parts: a layout that is used when there is only one stratum and a layout that is used with more than one stratum. Every section of this example has more than one stratum, so it is the changes to the second layout (or more precisely the ELSE portion of the template) that are affecting the results.

PROC LIFETEST makes available a series of dynamic variables that it does not display by default. See the section Additional Dynamic Variables for Survival Plots Using ODS Graphics in Chapter 49, The LIFETEST Procedure, for information about these dynamic variables. You can use these dynamic variables to add the new inset table to the plot. The following step creates the table in a gridded layout that is added to the second layout:

   proc template;
      define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
         . . .
         BeginGraph;
            if (NSTRATA=1)
               . . .
            layout overlay / . . .;
               . . .
            endlayout;
            else
               . . .
            layout overlay / . . .;
               . . .
               dynamic NObs1 NObs2 NObs3 NEvent1 NEvent2 NEvent3;
               layout gridded / columns=3 border=TRUE autoalign=(TopRight);
                  entry "";      entry "Event";   entry "Total";
                  entry "1";     entry NEvent1;   entry NObs1;
                  entry "2";     entry NEvent2;   entry NObs2;
                  entry "3";     entry NEvent3;   entry Nobs3;
               endlayout;
            endlayout;
            endif;
         EndGraph;
      end;
   run;

The at-risk information in Output 21.6.3 is produced by a BLOCKPLOT statement in the second layout. The modified template, with the first layout removed (since it is not needed in this example) and the BLOCKPLOT statement displayed is as follows:

   proc template;
      define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
         . . .
         BeginGraph;
            layout overlay / . . .;
               . . .
               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;
               . . .
               dynamic NObs1 NObs2 NObs3 NEvent1 NEvent2 NEvent3;
               layout gridded / columns=3 border=TRUE autoalign=(TopRight);
                  entry "";      entry "Event";   entry "Total";
                  entry "1";     entry NEvent1;   entry NObs1;
                  entry "2";     entry NEvent2;   entry NObs2;
                  entry "3";     entry NEvent3;   entry Nobs3;
               endlayout;
            endlayout;
         EndGraph;
      end;
   run;

In the next step, the at-risk information is moved out of the plot and into a table below the plot. The template is given a new overall layout—a LAYOUT LATTICE that has two panels stacked vertically, one for the plot and one for the at-risk information. Using ROWWEIGHTS=(.85 .15), the plot on top occupies 85% of the display and the at-risk information in the second panel occupies 15%. The option COLUMNDATARANGE=UNIONALL is used to create a common axis across the two panels. In these next steps, you also move the legend inside (similar to the previous part of this example) and rearrange the three inset boxes. The new template structure is as follows:

   proc template;
      define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
         . . .
         BeginGraph;
            . . .
            layout lattice /  rows=2 columns=1 columndatarange=unionall
                              rowweights=(.85 .15);
   
               layout overlay / . . .;
                  . . .
                  DiscreteLegend "Survival" / location=inside
                                 autoalign=(BottomRight);
                  . . .
                  layout gridded / rows=1 autoalign=(BottomLeft)
                        border=true BackgroundColor=GraphWalls:Color
                        Opaque =true;
                     entry "+ Censored";
                  endlayout;
                  . . .
                  dynamic NObs1 NObs2 NObs3 NEvent1 NEvent2 NEvent3;
                  layout gridded / columns=3 border=TRUE autoalign=(TopRight);
                     entry "";      entry "Event";   entry "Total";
                     entry "1";     entry NEvent1;   entry NObs1;
                     entry "2";     entry NEvent2;   entry NObs2;
                     entry "3";     entry NEvent3;   entry Nobs3;
                  endlayout;
               endlayout;
   
               layout overlay / xaxisopts=(display=none);
                  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;
               endlayout;
            endlayout;
         EndGraph;
      end;
   run;

You can further simplify the plot by removing the title from the legend (which is currently the variable name Group) and instead adding "+ Censored" (the contents of the inset table) to the legend in place of the title, as in the following statement:

   DiscreteLegend "Survival" / title="+ Censored"
      titleattrs=GraphValueText location=inside autoalign=(Bottom);

The option TITLEATTRS=GRAPHVALUETEXT is specified so that the "+ Censored" appears in the same font as the other entries in the legend and appears to be just another part of the legend. All of the statements for making the old inset table can now be removed from the template. The full template also plots bands, which are not used in this example, so they can be removed as well. The resulting template is as follows:

   proc template;
      define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
   
         dynamic NStrata xName plotAtRisk plotCensored plotCL plotHW plotEP
            labelCL labelHW labelEP maxTime StratumID classAtRisk plotBand
            plotTest GroupName yMin Transparency SecondTitle TestName pValue;
   
         BeginGraph;
   
            entrytitle "Kaplan-Meier Plot";
            if (EXISTS(SECONDTITLE))
               entrytitle SECONDTITLE / textattrs=GRAPHVALUETEXT;
            endif;
   
            layout lattice / rows=2 columns=1 columndatarange=unionall
                             rowweights=(.85 .15);
   
               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)));
   
                  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;
   
                  DiscreteLegend "Survival" / title="+ Censored"
                     titleattrs=GraphValueText location=inside
                     autoalign=(BOTTOM);
   
                  dynamic NObs1 NObs2 NObs3 NEvent1 NEvent2 NEvent3;
                  layout gridded / columns=3 border=TRUE autoalign=(TopRight);
                     entry "";      entry "Event";   entry "Total";
                     entry "1";     entry NEvent1;   entry NObs1;
                     entry "2";     entry NEvent2;   entry NObs2;
                     entry "3";     entry NEvent3;   entry Nobs3;
                  endlayout;
               endlayout;
   
               layout overlay / xaxisopts=(display=none);
                  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;
               endlayout;
            endlayout;
         EndGraph;
      end;
   run;

The following step uses the new template to create the desired plot:

   proc lifetest data=BMT plots=survival(atrisk=0 to 2500 by 500);
      ods select SurvivalPlot;
      time T * Status(0);
      strata Group / test=logrank adjust=sidak;
      run;

The plot is displayed in Output 21.6.4 at the beginning of this section.

This example removed a great deal of functionality from the default template, so that the final, modified template would be relatively simple and understandable. This is not necessary. The template could have been modified without deleting the first LAYOUT OVERLAY and other statements. The strategy for template modification illustrated in this example can be applied to any complicated template: identify the overall structure, isolate the relevant pieces, and then make changes in stages. Since the modified template will no longer work for all analyses, it is important that you delete it when you are done, as in the following example:

   proc template;
      delete Stat.Lifetest.Graphics.ProductLimitSurvival;
   run;
Previous Page | Next Page | Top of Page