BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
SwissC
Obsidian | Level 7

I am creating KM plots using a modified lifetest template (https://support.sas.com/resources/papers/proceedings13/427-2013.pdf)

 

My axistable statement is straightforward

 

   %macro AtRiskLatticeEnd(useclassopts);
         endcell;
         cell;           
            layout overlay / walldisplay=none xaxisopts=(display=none);
               axistable x=TATRISK value=ATRISK / &atriskopts
                                                  headerlabel="No. of patients still at risk &_xtlbl" 
                         %if &useclassopts ne %then &classopts;;
            endlayout;
         endcell;
      endlayout;
   %mend AtRiskLatticeEnd;

As is the lifetest call

proc lifetest data=plotdata PLOTS=SURVIVAL( ATRISK (atrisktickonly outside)= 0 TO 24 BY 3) alphaqt=0.05 METHOD=KM CONFTYPE=LOGLOG;
   time os*censor(1);
   strata treatment / order=internal test=none;
run;quit;

But for some reason the values seem not to be correctly aligned to the tickmarks on the axis.  Any idea what may be causing this? Is it a format issue?

 

Annotation 2019-08-13 184721.png

 

1 ACCEPTED SOLUTION

Accepted Solutions
Reeza
Super User
Why are the fonts different sizes? I suspect that's why they don't align.

View solution in original post

5 REPLIES 5
Reeza
Super User
Why are the fonts different sizes? I suspect that's why they don't align.
SwissC
Obsidian | Level 7

I tried changing the font size and it is still the same issue. I have attached the full template code below.

%macro mkmtemplate;

   %global atriskopts bandopts censored censorstr classopts
           graphopts groups insetopts legendopts ntitles stepopts tiplabel
           tips titletext0 titletext1 titletext2 xoptions yoptions srvtble _colr;

   %let yOptions   = label=&_ylabel shortlabel="Survival" LABELFITPOLICY=splitalways LABELSPLITCHAR="#" labelsplitjustify=bottom  
                     linearopts=(viewmin=0 viewmax=1
                                 tickvaluelist=(0 0.2 0.4 0.6 0.8 1) tickvalueFormat= percent10. );

   %IF %length(&_MAXTIME) %THEN %DO;
     %let xOptions   = shortlabel=XNAME offsetmin=.05 linearopts=(viewmax=&_maxtime tickvaluelist=XTICKVALS tickvaluefitpolicy=XTICKVALFITPOL);
   %END;
   %ELSE %DO;
     %let xOptions   = shortlabel=XNAME offsetmin=.05 linearopts=(viewmax=&__maxmon tickvaluelist=XTICKVALS tickvaluefitpolicy=XTICKVALFITPOL);
   %END;
   %IF %upcase(&_colour)=YES or %upcase(&_colour)=Y %THEN %DO;
     %LET _colr=rtf;
   %END;
   %ELSE %DO;
     %LET _colr=journal;
   %END;

   %let Tips       = rolename=(_tip1= ATRISK _tip2=EVENT)
                     tiplabel=(_tip1="Number at Risk" _tip2="Observed Events")
                     tip=(x y _tip1 _tip2);
   %let TipLabel   = tiplabel=(y="Survival Probability");
   %let StepOpts   = ;

   %let Groups     = group=STRATUM index=STRATUMNUM;

   %let BandOpts   = &groups modelname="Survival";

   %let InsetOpts  = autoalign=(TOPRIGHT BOTTOMLEFT BOTTOMRIGHT TOP BOTTOM)
                     border=false BackgroundColor=GraphWalls:Color Opaque=true;

   %let LegendOpts = title=&_legtitl location=inside &InsetOpts;

   %let AtRiskOpts = valueattrs=(size=9pt);
   %let ClassOpts  = class=CLASSATRISK colorgroup=CLASSATRISK display=(label);

   %let Censored   = markerattrs=(symbol=plus);
   %let CensorStr  = "Censored";

   %let GraphOpts  = ;

   %LET srvtble    = YES;

   %macro StmtsBeginGraph; %mend;
   %macro StmtsTop;        %mend;
   %macro StmtsBottom;     %mend;

   %macro CompileSurvivalTemplates;
      %local outside;
      proc template;
            define statgraph
               Stat.Lifetest.Graphics.ProductLimitSurvival2;
               dynamic NStrata xName plotAtRisk
                  %if %nrbquote(&censored) ne %then plotCensored;
                  plotCL plotHW plotEP labelCL labelHW labelEP maxTime xtickVals
                  xtickValFitPol rowWeights method StratumID classAtRisk 
                  plotTest GroupName Transparency SecondTitle TestName pValue
                  _byline_ _bytitle_ _byfootnote_ _byval_ _byvar_ _hrtext   _byval1_  _byval2_  _byval3_;
               BeginGraph %if %nrbquote(&graphopts) ne %then / border=false pad=0 &graphopts;;


               if (NSTRATA=1)           
                  %StmtsBeginGraph
                  %AtRiskLatticeStart
                  layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
                     %StmtsTop
                     %SingleStratum
                     %StmtsBottom
                  endlayout;
                  %AtRiskLatticeEnd
               else
               
                  legenditem  type=marker name="CenLgd" / label=&CensorStr lineattrs=(color=black) markerattrs=(symbol=plus color=black);
                  %StmtsBeginGraph
                  %AtRiskLatticeStart
                  layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
                     %StmtsTop
                     %MultipleStrata
                     %StmtsBottom
                  endlayout;
                  %AtRiskLatticeEnd(class)
               endif;

               
               %IF %length(&_by) %THEN %DO;
               entrytitle halign=left "&bylbl.: " _byval_ / textattrs=GRAPHVALUETEXT; 
               %END;

               EndGraph;
            end;
      define style graph;
         parent=styles.&_colr;
         %** important thing to notice is the protectspecialchars, which allows us to push the RTF codes through **;
         style table from table / protectspecialchars=off;
                                                
               replace body from document /
                  bottommargin = 0.00cm
                  topmargin    = 2.50cm
                  rightmargin  = 0.00cm
                  leftmargin   = 1.10cm;

               replace fonts /
                        'CellFont'             = ("arial",9pt)
                        'TitleFont2'           = ("arial",9pt)
                        'TitleFont'            = ("arial",9pt)
                        'StrongFont'           = ("arial",9pt)
                        'EmphasisFont'         = ("arial",9pt)
                        'FixedEmphasisFont'    = ("arial",9pt)
                        'FixedStrongFont'      = ("arial",9pt)
                        'FixedHeadingFont'     = ("arial",9pt)
                        'BatchFixedFont'       = ("arial",9pt)
                        'FixedFont'            = ("arial",9pt)
                        'headingEmphasisFont'  = ("arial",9pt)
                        'headingFont'          = ("arial",9pt)
                        'docFont'              = ("arial",9pt);
                  %IF %SYSMACEXIST(&_opts2) %THEN %DO;
                    %&_opts2;
                  %END;

                  style GraphFonts from GraphFonts /
                        'GraphDataFont' = ("arial",9pt)
                        'GraphUnicodeFont' = ("arial",9pt)
                        'GraphValueFont' = ("arial",9pt)
                        'GraphLabelFont' = ("arial",9pt)
                        'GraphFootnoteFont' = ("arial",9pt)
                        'GraphTitleFont' = ("arial",9pt)
                        'GraphAnnoFont' = ("arial",9pt); 
            style GraphData3 from GraphData3 / LineStyle = 3;
                  end;

            quit;
      run;
   %mend CompileSurvivalTemplates;


   %macro SingleStratum;
      stepplot y=eval(SURVIVAL) x=TIME / name="Survival" &tips legendlabel="&_strat"
               &stepopts;

      if (PLOTCENSORED=1)
         scatterplot y=eval(CENSORED) x=TIME / &censored &tiplabel
            name="Censored" legendlabel="Censored";
      endif;

      if (PLOTCENSORED=1)
         MERGEDLEGEND "Survival" "Censored" / across=1 &legendopts ADDITIONALNAMES=("CenLgd");
      endif;

      %if %nrbquote(&srvtble) ne %then %do;
         %SurvivalTable;
      %end;
   %mend SingleStratum;

   %macro MultipleStrata;
      stepplot y=eval(SURVIVAL)  x=TIME / &groups name="Survival" &tips &stepopts;

      if (PLOTCENSORED=1)
         scatterplot y=eval(CENSORED) x=TIME / &groups &tiplabel &censored name="Censored" legendlabel="Censored";
      endif;

      %if %nrbquote(&legendopts) ne %then %do;
         MERGEDLEGEND "Survival" "Censored" / across=1 &legendopts ADDITIONALNAMES=("CenLgd");
      %end;

      %if %nrbquote(&srvtble) ne %then %do;
         %SurvivalTable;
      %end;
   %mend MultipleStrata;

   %macro SurvTabHeader(multiple);
      entry "";
      entry "";
      entry "";
      entry &r "Median";
      entry "";
      entry "";

      entry "";
      entry "";
      entry "";
      entry &r "Survival";
      entry "";
      entry "";

      entry "";
      entry &r "Subjects";
      entry &r "Event";
      entry &r "&_kmtime";
      entry &r PctMedianConfid;
      entry halign=left  "CI";
   %mend SurvTabHeader;

   %macro SurvivalTable;
      %local fmt r i t;
      %let fmt = &_fmt;
      %let r = halign = right;
            if(NSTRATA=1)
               layout gridded / columns=6 &InsetOpts;
                     
                  dynamic StrVal _strat PctMedianConfid NObs NEvent Median LowerMedian UpperMedian ;
                  %SurvTabHeader(1)
                  entry &r "&_strat" &t;
                  if(NObs ne .)         entry &r NObs&i &t;                         else entry &r "N.E" &t; endif;
                  if(NEvent ne .)       entry &r eval(NEvent&i) &t;                 else entry &r "N.E" &t; endif;
                  if(Median ne .)       entry &r eval(put(Median&i,&fmt)) &t;       else entry &r "N.E" &t; endif;
                  if(LowerMedian ne .)  entry &r eval(put(LowerMedian&i,&fmt)) &t;  else entry &r "N.E" &t; endif; 
                  if(UpperMedian ne .)  entry &r eval(put(UpperMedian&i,&fmt)) &t;  else entry &r "N.E" &t; endif;
               endlayout;
            else
               layout gridded / columns=1  &InsetOpts;
               layout gridded / columns=6 &InsetOpts;
                  dynamic PctMedianConfid;
                  %SurvTabHeader(1)
                  %do i = 1 %to 5;
                     %let t = / textattrs=GraphData&i;
                     dynamic StrVal&i NObs&i NEvent&i Median&i LowerMedian&i UpperMedian&i _hrtext _hrby _pvtext _pvby;
                     if (&i <= nstrata)
                        entry &r StrVal&i &t;
                        if(NObs&i ne .)         entry &r NObs&i &t;                         else entry &r "N.E" &t; endif;
                        if(NEvent&i ne .)       entry &r eval(NEvent&i) &t;                 else entry &r "N.E" &t; endif;
                        if(Median&i ne .)       entry &r eval(put(Median&i,&fmt)) &t;       else entry &r "N.E" &t; endif;
                        if(LowerMedian&i ne .)  entry &r eval(put(LowerMedian&i,&fmt)) &t;  else entry &r "N.E" &t; endif; 
                        if(UpperMedian&i ne .)  entry &r eval(put(UpperMedian&i,&fmt)) &t;  else entry &r "N.E" &t; endif;
                     endif;
                  %end;                             
               endlayout;          
                  %IF %length(&_hr) %THEN %DO;
                     entry halign = left &_hrby;
                  %END;
                  %IF %length(&_pval) %THEN %DO;
                     entry halign = left &_pvby ;
                  %END;
               endlayout;
            endif;
   %mend SurvivalTable;

   %macro AtRiskLatticeStart;
         layout lattice / rows=2 rowweights=ROWWEIGHTS
                          columndatarange=union rowgutter=10;
         cell;
   %mend AtRiskLatticeStart;

   %macro AtRiskLatticeEnd(useclassopts);
         endcell;
         cell;           
            layout overlay / walldisplay=none xaxisopts=(display=none);
               axistable x=TATRISK value=ATRISK / &atriskopts 
                                                  headerlabel="No. of patients still at risk &_xtlbl" 
                         %if &useclassopts ne %then &classopts;;
            endlayout;
         endcell;
      endlayout;
   %mend AtRiskLatticeEnd;

%CompileSurvivalTemplates;

%mend mkmtemplate;
Reeza
Super User
That output isn't from the code with the fonts the same. Can you show that image then?
SwissC
Obsidian | Level 7

My apologies Reeza, you were correct.  I deleted all the outputs I had currently produced and reran in a clean session and it is now correct.  Something had not updated somewhere.  Thanks for your help.

JeffMeyers
Barite | Level 11
I wanted to weigh in to say that font sizes should make no difference in this scenario. The issue that causes the axes to delink within a lattice layout is when the offsets do not match. This is usually caused by either not using COLUMNDATARANGE=UNION in the layout lattice statement along with a COLUMNAXES block with COLUMNAXIS statements along, or setting up OFFSETS in the xaxisopts differently within the different lattice rows. If you don't set up OFFSETS in each row to be the same when you're not using COLUMNDATARANGE=UNION then they will be potentially different and this delinking happens. Just add and OFFSETMAX and OFFSETMIN to each XAXISOPTS and make them all the same and the issue will resolve.

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 5 replies
  • 2332 views
  • 6 likes
  • 3 in conversation