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)
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.