Hello,
I am trying to modify my KM plots to include dashed lines rather than solid (SAS 9.4) by modifying the template. I see this question was asked and solved here: https://communities.sas.com/t5/SAS-GRAPH-and-ODS-Graphics/Survival-Plot-with-dashed-line/td-p/88446
but the solution
"
Add LINEATTRS=(pattern=dashed) to this line:
stepplot y=SURVIVAL x=TIME / name="Survival" rolename=(_tip1=
ATRISK _tip2=EVENT) tip=(y x Time _tip1 _tip2) legendlabel=
"Survival" lineattrs=(pattern=dashed);
"
is not working for me (I get errors in my proc template).
Any assistance would be greatly appreciated!
Error (snippet) from the log:
7225 stepplot y=SURVIVAL x=TIME / name="Survival" rolename=(_tip1=ATRISK _tip2=EVENT) tiplabel=(_tip1="Number at
7225! Risk"
7226 _tip2="Observed Events") tip=(x y _tip1 _tip2) legendlabel="Survival" lineattrs=(pattern=dashed);
------
772
7227 if (PLOTCENSORED=1)
-
79
200
ERROR 772-580: Syntax error: expecting a constant or a dynamic.
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
Full code (without the "lineattrs=(pattern=dashed)") below:
ods graphics on;
ods path(prepend) work.templat(update);
%let TitleText0 = METHOD " Survival Estimate";
%let TitleText1 = &titletext0 " for " STRATUMID;
%let TitleText2 = &titletext0 "s";
%let yOptions = label="Probability of Seeking Care for Back Pain"
shortlabel="Probability of Seeking Care for Back Pain"
labelattrs=(size=9pt weight=bold)
tickvalueattrs=(size=8pt)
linearopts=(viewmin=0 viewmax=1
tickvaluelist=(0 .2 .4 .6 .8 1.0));
%let xOptions = shortlabel="Number of Years to Back Pain"
offsetmin=.05
labelattrs=(size=10pt weight=bold)
tickvalueattrs=(size=8pt)
linearopts=(viewmax=MAXTIME tickvaluelist=XTICKVALS
tickvaluefitpolicy=XTICKVALFITPOL);
proc template;
define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
dynamic NStrata xName plotAtRisk plotCensored plotCL plotHW plotEP labelCL
labelHW labelEP maxTime xtickVals xtickValFitPol method StratumID
classAtRisk plotBand plotTest GroupName yMin Transparency SecondTitle
TestName pValue;
BeginGraph;
if (NSTRATA=1)
if (EXISTS(STRATUMID))
entrytitle &titletext1;
else
entrytitle &titletext0;
endif;
if (PLOTATRISK)
entrytitle "with Number of Subjects at Risk" / textattrs=
GRAPHVALUETEXT;
endif;
layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
if (PLOTHW=1 AND PLOTEP=0)
bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
GRAPHCONFIDENCE name="HW" legendlabel=LABELHW;
endif;
if (PLOTHW=0 AND PLOTEP=1)
bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
GRAPHCONFIDENCE name="EP" legendlabel=LABELEP;
endif;
if (PLOTHW=1 AND PLOTEP=1)
bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
GRAPHDATA1 datatransparency=.55 name="HW" legendlabel=LABELHW;
bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false 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 / displayTail=false modelname="Survival" display=(
outline) outlineattrs=GRAPHPREDICTIONLIMITS name="CL" legendlabel=LABELCL;
else
bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
GRAPHCONFIDENCE name="CL" legendlabel=LABELCL;
endif;
endif;
stepplot y=SURVIVAL x=TIME / name="Survival" rolename=(_tip1=ATRISK _tip2=EVENT) tiplabel=(_tip1="Number at Risk"
_tip2="Observed Events") tip=(x y _tip1 _tip2) legendlabel="Survival" lineattrs=(pattern=shortdash longdash);
if (PLOTCENSORED=1)
scatterplot y=CENSORED x=TIME / markerattrs=(symbol=plus) tiplabel=(y="Probability of an Episode of Seeking Care for Back Pain") 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;
axistable x=TATRISK value=ATRISK / display=(label) valueattrs=(size=7pt);
endinnermargin;
endif;
endlayout;
else
entrytitle &titletext2;
if (EXISTS(SECONDTITLE))
entrytitle SECONDTITLE / textattrs=GRAPHVALUETEXT;
endif;
layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
if (PLOTHW=1)
bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM modelname
="Survival" datatransparency=Transparency;
endif;
if (PLOTEP=1)
bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM modelname
="Survival" datatransparency=Transparency;
endif;
if (PLOTCL=1)
if (PLOTHW=1 OR PLOTEP=1)
bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM
modelname="Survival" display=(outline) outlineattrs=(pattern=ShortDash);
else
bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false 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)
tiplabel=(_tip1="Number at Risk" _tip2="Observed Events") tip=(x y _tip1 _tip2);
if (PLOTCENSORED=1)
scatterplot y=CENSORED x=TIME / group=STRATUM index=STRATUMNUM tiplabel=(y="Probability of an Episode of Seeking Care for Back Pain") markerattrs=(
symbol=plus);
endif;
if (PLOTATRISK=1)
innermargin / align=bottom;
axistable x=TATRISK value=ATRISK / display=(label) valueattrs=(size=7pt) class=CLASSATRISK colorgroup=
CLASSATRISK;
endinnermargin;
endif;
DiscreteLegend "Survival" / title=GROUPNAME location=outside;
if (PLOTCENSORED=1)
if (PLOTTEST=1)
layout gridded / rows=2 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true BackgroundColor=
GraphWalls:Color Opaque=true;
entry "+ Censored";
if (PVALUE < .0001)
entry TESTNAME " p " eval (PUT(PVALUE, PVALUE6.4));
else
entry TESTNAME " p=" eval (PUT(PVALUE, PVALUE6.4));
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=1)
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;
endif;
EndGraph;
end;
run;
With error messages it is best to post the CODE and the Error messages from the log, one copy and paste, into a code box opened with the forum {i} menu icon.
Error messages often have indicators along with the code that indicates likely problem spots. And the reason to include the code that was run (not another version) is because the actual cause of the error could be a missing semicolon, paranthese, comma, quote on a line above where the error was reported. Note that in your snippet there are several dashes or underscores, they appear where the expected element should be but the main message windows reformat text and we cannont see where they should appear in relation to the code. The codebox will retain the formatting to show where the missing element was expected.
And the entire data step or proc code submitted, not a snippet.
Thanks, and sorry about that; here is it:
491
492 ods graphics on;
493 ods path(prepend) work.templat(update);
494
495 %let TitleText0 = METHOD " Survival Estimate";
496 %let TitleText1 = &titletext0 " for " STRATUMID;
497 %let TitleText2 = &titletext0 "s";
498 %let yOptions = label="Probability of Seeking Care for Back Pain"
499 shortlabel="Probability of Seeking Care for Back Pain"
500 labelattrs=(size=9pt weight=bold)
501 tickvalueattrs=(size=8pt)
502 linearopts=(viewmin=0 viewmax=1
503 tickvaluelist=(0 .2 .4 .6 .8 1.0));
504 %let xOptions = shortlabel="Number of Years to Back Pain"
505 offsetmin=.05
506 labelattrs=(size=10pt weight=bold)
507 tickvalueattrs=(size=8pt)
508 linearopts=(viewmax=MAXTIME tickvaluelist=XTICKVALS
509 tickvaluefitpolicy=XTICKVALFITPOL);
510
511
512 proc template;
513 define statgraph Stat.Lifetest.Graphics.ProductLimitSurvival;
514 dynamic NStrata xName plotAtRisk plotCensored plotCL plotHW plotEP labelCL
515 labelHW labelEP maxTime xtickVals xtickValFitPol method StratumID
516 classAtRisk plotBand plotTest GroupName yMin Transparency SecondTitle
517 TestName pValue;
518 BeginGraph;
519 if (NSTRATA=1)
520 if (EXISTS(STRATUMID))
521 entrytitle &titletext1;
522 else
523 entrytitle &titletext0;
524 endif;
525 if (PLOTATRISK)
526 entrytitle "with Number of Subjects at Risk" / textattrs=
527 GRAPHVALUETEXT;
528 endif;
529 layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
530 if (PLOTHW=1 AND PLOTEP=0)
531 bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
532 GRAPHCONFIDENCE name="HW" legendlabel=LABELHW;
533 endif;
534 if (PLOTHW=0 AND PLOTEP=1)
535 bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
536 GRAPHCONFIDENCE name="EP" legendlabel=LABELEP;
537 endif;
538 if (PLOTHW=1 AND PLOTEP=1)
539 bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
540 GRAPHDATA1 datatransparency=.55 name="HW" legendlabel=LABELHW;
541 bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
542 GRAPHDATA2 datatransparency=.55 name="EP" legendlabel=LABELEP;
543 endif;
544 if (PLOTCL=1)
545 if (PLOTHW=1 OR PLOTEP=1)
546 bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false modelname="Survival" display=(
547 outline) outlineattrs=GRAPHPREDICTIONLIMITS name="CL" legendlabel=LABELCL;
548 else
549 bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false modelname="Survival" fillattrs=
550 GRAPHCONFIDENCE name="CL" legendlabel=LABELCL;
551 endif;
552 endif;
553 stepplot y=SURVIVAL x=TIME / name="Survival" rolename=(_tip1=ATRISK _tip2=EVENT) tiplabel=(_tip1="Number at
553! Risk"
554 _tip2="Observed Events") tip=(x y _tip1 _tip2) legendlabel="Survival" lineattrs=(pattern=dashed);
------
772
555 if (PLOTCENSORED=1)
-
79
200
ERROR 772-580: Syntax error: expecting a constant or a dynamic.
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
556 scatterplot y=CENSORED x=TIME / markerattrs=(symbol=plus) tiplabel=(y="Probability of an Episode of Seeking
556! Care for Back Pain") name="Censored"
557 legendlabel="Censored";
558 endif;
559 if (PLOTCL=1 OR PLOTHW=1 OR PLOTEP=1)
-
79
76
ERROR 79-322: Expecting a ).
ERROR 76-322: Syntax error, statement will be ignored.
560 discretelegend "Censored" "CL" "HW" "EP" / location=outside halign=center;
561 else
562 if (PLOTCENSORED=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
563 discretelegend "Censored" / location=inside autoalign=(topright bottomleft);
564 endif;
565 endif;
566 if (PLOTATRISK=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
567 innermargin / align=bottom;
568 axistable x=TATRISK value=ATRISK / display=(label) valueattrs=(size=7pt);
569 endinnermargin;
570 endif;
571 endlayout;
572 else
573 entrytitle &titletext2;
574 if (EXISTS(SECONDTITLE))
-
79
76
ERROR 79-322: Expecting a ).
ERROR 76-322: Syntax error, statement will be ignored.
575 entrytitle SECONDTITLE / textattrs=GRAPHVALUETEXT;
576 endif;
577 layout overlay / xaxisopts=(&xoptions) yaxisopts=(&yoptions);
578 if (PLOTHW=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
579 bandplot LimitUpper=HW_UCL LimitLower=HW_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM
579! modelname
580 ="Survival" datatransparency=Transparency;
581 endif;
582 if (PLOTEP=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
583 bandplot LimitUpper=EP_UCL LimitLower=EP_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM
583! modelname
584 ="Survival" datatransparency=Transparency;
585 endif;
586 if (PLOTCL=1)
-
79
200
587 if (PLOTHW=1 OR PLOTEP=1)
-
79
76
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
ERROR 76-322: Syntax error, statement will be ignored.
588 bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM
589 modelname="Survival" display=(outline) outlineattrs=(pattern=ShortDash);
590 else
591 bandplot LimitUpper=SDF_UCL LimitLower=SDF_LCL x=TIME / displayTail=false group=STRATUM index=STRATUMNUM
592 modelname="Survival" datatransparency=Transparency;
593 endif;
594 endif;
595 stepplot y=SURVIVAL x=TIME / group=STRATUM index=STRATUMNUM name="Survival" rolename=(_tip1=ATRISK _tip2=EVENT)
596 tiplabel=(_tip1="Number at Risk" _tip2="Observed Events") tip=(x y _tip1 _tip2);
597 if (PLOTCENSORED=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
598 scatterplot y=CENSORED x=TIME / group=STRATUM index=STRATUMNUM tiplabel=(y="Probability of an Episode of
598! Seeking Care for Back Pain") markerattrs=(
599 symbol=plus);
600 endif;
601 if (PLOTATRISK=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
602 innermargin / align=bottom;
603 axistable x=TATRISK value=ATRISK / display=(label) valueattrs=(size=7pt) class=CLASSATRISK colorgroup=
604 CLASSATRISK;
605 endinnermargin;
606 endif;
607 DiscreteLegend "Survival" / title=GROUPNAME location=outside;
608 if (PLOTCENSORED=1)
-
79
200
609 if (PLOTTEST=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
610 layout gridded / rows=2 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true BackgroundColor=
611 GraphWalls:Color Opaque=true;
612 entry "+ Censored";
613 if (PVALUE < .0001)
-
79
200
614 entry TESTNAME " p " eval (PUT(PVALUE, PVALUE6.4));
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
615 else
616 entry TESTNAME " p=" eval (PUT(PVALUE, PVALUE6.4));
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
617 endif;
618 endlayout;
619 else
620 layout gridded / rows=1 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true BackgroundColor=
621 GraphWalls:Color Opaque=true;
622 entry "+ Censored";
623 endlayout;
624 endif;
625 else
626 if (PLOTTEST=1)
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
627 layout gridded / rows=1 autoalign=(TOPRIGHT BOTTOMLEFT TOP BOTTOM) border=true BackgroundColor=
628 GraphWalls:Color Opaque=true;
629 if (PVALUE < .0001)
-
79
200
630 entry TESTNAME " p " eval (PUT(PVALUE, PVALUE6.4));
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
631 else
632 entry TESTNAME " p=" eval (PUT(PVALUE, PVALUE6.4));
-
79
200
ERROR 79-322: Expecting a ).
ERROR 200-322: The symbol is not recognized and will be ignored.
633 endif;
634 endlayout;
635 endif;
636 endif;
637 endlayout;
638 endif;
639 EndGraph;
640 end;
WARNING: Object will not be saved.
641 run;
NOTE: PROCEDURE TEMPLATE used (Total process time):
real time 0.08 seconds
cpu time 0.09 seconds
WARNING: Errors were produced.
NOTE: The SAS System stopped processing this step because of errors.
Dashed is not a valid name for a dashed line style. So since it is not recognized the syntax checker thinks that Dashed should be a dynamic variable or possibly enclosed in quotes as literal text value.
Choices for line type names are Solid, ShortDash, MediumDash, LongDash,MediumDashShortDash, DashDashDot, DashDotDot, Dash, LongDashShortDash, Dot, ThinDot, ShortDashDot, MediumDashDotDot or numbers from 1 (solid) to 46 which are many combinations of dashes, dots, lengths and numbers of dashes, numbers and sizes of dots.
So that is the error at line 554.
The errors occuring at = such as
566 if (PLOTATRISK=1)
-
79
200
might be the result of copy and paste from a source using a different symbol for the = or sometimes an invisible character from some sources. Or maybe correcting the pattern may clear some of this up.
Thanks! That solved the error message at least although I was only able to change all of the lines using this method (I couldn't figure out how to change one to dashed, one to solid, etc.).
I found another way to modify the proc template, which is easy and worked!
data _null_;
%let url = //support.sas.com/documentation/onlinedoc/stat/ex_code/131;
infile "http:&url/templft.html" device=url;
file 'macros.tmp';
retain pre 0;
input;
if index(_infile_, '</pre>') then pre = 0;
if pre then put _infile_;
if index(_infile_, '<pre>') then pre = 1;
run;
%inc 'macros.tmp' / nosource;
%ProvideSurvivalMacros
%let GraphOpts = attrpriority=none
DataLinePatterns=(Solid ShortDash LongDash);
%let StepOpts = lineattrs=(thickness=1.5);
%let TitleText0 = METHOD " Survival Estimate";
%let TitleText1 = &titletext0 " for " STRATUMID;
%let TitleText2 = &titletext0 "s";
%let yOptions = label="Probability of Seeking Care for Back Pain"
shortlabel="Probability of Seeking Care for Back Pain"
labelattrs=(size=9pt weight=bold)
tickvalueattrs=(size=8pt)
linearopts=(viewmin=0 viewmax=1
tickvaluelist=(0 .2 .4 .6 .8 1.0));
%let xOptions = shortlabel="Number of Years to Back Pain"
offsetmin=.05
labelattrs=(size=10pt weight=bold)
tickvalueattrs=(size=8pt)
linearopts=(viewmax=MAXTIME tickvaluelist=XTICKVALS
tickvaluefitpolicy=XTICKVALFITPOL);
%CompileSurvivalTemplates
Have you tried the %modstyle macro instead. It may be easier to work with.
Thanks, I did look into this but I think I figured out a different way (I spent so much time on modifying the proc template and I wasn't sure if the %modstyle macro was compatible)
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
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.