Hi friends,
This is hand-drawn, and please also see sample code of forest plot:
proc sgplot data=whole noautolegend;
scatter y=reg x=oddsratioest / xerrorlower=LowerCL xerrorupper=UpperCL
markerattrs=(symbol=diamondfilled) datalabel=oddsratioest
datalabelattrs=(weight=bold);
scatter y=reg x=lowercl / datalabel = lowercl markerattrs=(size=0 color=crimson);
scatter y=reg x=uppercl / datalabel = uppercl markerattrs=(size=0 color=crimson);
refline 1 / axis=x;
* xaxis grid type=log label="Odds Ratio (log scale)"; /* specify log scale */
* yaxis grid display=(nolabel) discreteorder=data reverse;
run;
I want to take it one step further by color-coding groups like I drew here:
Can SAS do that? I know R can, but I am worse at it. Any suggestions are appreciated.
Thank you!
You want this ?
data whole;
input type $ outcome $ OddsRatioEst LowerCL UpperCL pValue;
cards;
tbi mort6 1.104 1.08 1.118 1.4641
tbi mort24 1.12 1.10 1.18 0.4554
tbi mort 1.031 1.001 1.18 0.0284
no_tbi mort6 0.98 0.888 1.00 0.7641
no_tbi mort24 0.964 0.894 1.013 0.4554
no_tbi mort 0.97 0.901 0.98 0.0284
isolated mort6 1.01 0.988 1.16 0.7641
isolated mort24 1.1 0.94 1.13 0.4554
isolated mort 0.98 0.971 1.08 0.0284
;
title c=green "No TBI " c=navy "Non-isolated TBI" c=orange " Isolated TBI " ;
proc sgplot data=whole noautolegend noborder;
styleattrs datacolors=( orange green navy) datacontrastcolors=( orange green navy) ;
refline 1 / axis=x lineattrs=(color=red thickness=3) label='1.000' labelpos=min labelattrs=(weight=bold color=red size=12);
highlow y=outcome low=LowerCL high=UpperCL /lineattrs=(thickness=3) group=type groupdisplay=cluster;
scatter y=outcome x=oddsratioest / group=type
markerattrs=(symbol=squarefilled size=12) datalabel=oddsratioest groupdisplay=cluster
datalabelattrs=(weight=bold size=12);
yaxis colorbands=odd type=discrete display=(nolabel noline noticks) discreteorder=data reverse;
xaxis label='Odds Ratio' ;
run;
What variable controls / indicates membership in a "group"?
Typically if you provide a GROUP= option then your ODS style will typically provide different colors, markers, lines for each level of a group.
For example, variable type. I output one-line datasets with ORs and CIs from proc logistic and add outcome = "mort" (one of y-axis labels), type = "TBI" (one of the groups to color-code). Then I set the one-line datasets and try to color-code the resulting CI forest plot by type.
I am not sure I understand your data but provide an actual variable named TYPE or similar to each record in your plot data set and use the Group option.
If you are plotting different data sets then combine them and provide the appropriate Type value for each plot.
Or show what your data set that you are attempting to plot look like.
Instructions here: https://communities.sas.com/t5/SAS-Communities-Library/How-to-create-a-data-step-version-of-your-dat... will show how to turn an existing SAS data set into data step code that can be pasted into a forum code box using the <> icon or attached as text to show exactly what you have and that we can test code against.
@pink_poodle wrote:
I am lazier than Mark from the dataset link 🙂 :
type outcome OddsRatioEst LowerCL UpperCL pValue
tbi mort6 0.998 0.988 1.008 0.7641
tbi mort24 1.004 0.994 1.013 0.4554
tbi mort 1.009 1.001 1.018 0.0284
no_tbi mort6 0.998 0.988 1.008 0.7641
no_tbi mort24 1.004 0.994 1.013 0.4554
no_tbi mort 1.009 1.001 1.018 0.0284
This data as shown will not plot at all with the Sgplot code shown:
proc sgplot data=whole noautolegend;
scatter y=reg x=oddsratioest / xerrorlower=LowerCL xerrorupper=UpperCL
markerattrs=(symbol=diamondfilled) datalabel=oddsratioest
datalabelattrs=(weight=bold);
scatter y=reg x=lowercl / datalabel = lowercl markerattrs=(size=0 color=crimson);
scatter y=reg x=uppercl / datalabel = uppercl markerattrs=(size=0 color=crimson);
refline 1 / axis=x;
* xaxis grid type=log label="Odds Ratio (log scale)"; /* specify log scale */
* yaxis grid display=(nolabel) discreteorder=data reverse;
run;
The Y variable REG does not appear in your "example". If you had a Reg variable you could drop the Markerattrs as adding Group=Type should use a different marker for each level of type (and create a legend linking the text to the marker appearance).
Use LINEATTRS and LABEL plus LABELATTRS to address the appearanc of the Refline.
Here is a small example of a Scatter plot with a Refline that you should be able to run using the SASHELP.CLASS data set.
(Since your "example data" did not include all the variables you say you want to plot)
proc sgplot data=sashelp.class; scatter x=height y=weight/group=sex; refline 60 / axis=x lineattrs=(color=red thickness=.1in) label='Some label text' labelattrs=(color=red) ; run;
Reg is called outcome in this dataset, sorry about the confusion. I think the group option does not work here, because group splits up a scatter statement which is one “tree” in a forest plot. I want the “trees” to be intact.
@pink_poodle wrote:
Reg is called outcome in this dataset, sorry about the confusion. I think the group option does not work here, because group splits up a scatter statement which is one “tree” in a forest plot. I want the “trees” to be intact.
Okay, if a group isn't going to work then you may need separate data , as in a different Y variable for each "plot". Then separate plot statements for the different y. Set the characteristics for each plot separately.
/*
Your data is not right .
Why these two type have the same value of odds ratio?
*/
data whole;
input type $ outcome $ OddsRatioEst LowerCL UpperCL pValue;
cards;
tbi mort6 1.104 1.08 1.118 1.4641
tbi mort24 1.12 1.10 1.18 0.4554
tbi mort 1.031 1.001 1.18 0.0284
no_tbi mort6 0.98 0.888 1.00 0.7641
no_tbi mort24 0.964 0.894 1.013 0.4554
no_tbi mort 0.97 0.901 0.98 0.0284
;
title c=green "No TBI " c=orange " Isolated TBI " ;
proc sgplot data=whole noautolegend noborder;
styleattrs datacolors=( orange green) datacontrastcolors=( orange green) ;
refline 1 / axis=x lineattrs=(color=red thickness=3) label='1.000' labelpos=min labelattrs=(weight=bold color=red size=12);
highlow y=outcome low=LowerCL high=UpperCL /lineattrs=(thickness=3) group=type;
scatter y=outcome x=oddsratioest / group=type
markerattrs=(symbol=squarefilled size=12) datalabel=oddsratioest
datalabelattrs=(weight=bold size=12);
yaxis grid type=discrete display=(nolabel noline noticks) discreteorder=data reverse;
xaxis label='Odds Ratio' ;
run;
/*
Then you need option:
groupdisplay=cluster
*/
data whole;
input type $ outcome $ OddsRatioEst LowerCL UpperCL pValue;
cards;
tbi mort6 1.104 1.08 1.118 1.4641
tbi mort24 1.12 1.10 1.18 0.4554
tbi mort 1.031 1.001 1.18 0.0284
no_tbi mort6 0.98 0.888 1.00 0.7641
no_tbi mort24 0.964 0.894 1.013 0.4554
no_tbi mort 0.97 0.901 0.98 0.0284
;
title c=green "No TBI " c=orange " Isolated TBI " ;
proc sgplot data=whole noautolegend noborder;
styleattrs datacolors=( orange green) datacontrastcolors=( orange green) ;
refline 1 / axis=x lineattrs=(color=red thickness=3) label='1.000' labelpos=min labelattrs=(weight=bold color=red size=12);
highlow y=outcome low=LowerCL high=UpperCL /lineattrs=(thickness=3) group=type groupdisplay=cluster;
scatter y=outcome x=oddsratioest / group=type
markerattrs=(symbol=squarefilled size=12) datalabel=oddsratioest groupdisplay=cluster
datalabelattrs=(weight=bold size=12);
yaxis grid type=discrete display=(nolabel noline noticks) discreteorder=data reverse;
xaxis label='Odds Ratio' ;
run;
You want this ?
data whole;
input type $ outcome $ OddsRatioEst LowerCL UpperCL pValue;
cards;
tbi mort6 1.104 1.08 1.118 1.4641
tbi mort24 1.12 1.10 1.18 0.4554
tbi mort 1.031 1.001 1.18 0.0284
no_tbi mort6 0.98 0.888 1.00 0.7641
no_tbi mort24 0.964 0.894 1.013 0.4554
no_tbi mort 0.97 0.901 0.98 0.0284
isolated mort6 1.01 0.988 1.16 0.7641
isolated mort24 1.1 0.94 1.13 0.4554
isolated mort 0.98 0.971 1.08 0.0284
;
title c=green "No TBI " c=navy "Non-isolated TBI" c=orange " Isolated TBI " ;
proc sgplot data=whole noautolegend noborder;
styleattrs datacolors=( orange green navy) datacontrastcolors=( orange green navy) ;
refline 1 / axis=x lineattrs=(color=red thickness=3) label='1.000' labelpos=min labelattrs=(weight=bold color=red size=12);
highlow y=outcome low=LowerCL high=UpperCL /lineattrs=(thickness=3) group=type groupdisplay=cluster;
scatter y=outcome x=oddsratioest / group=type
markerattrs=(symbol=squarefilled size=12) datalabel=oddsratioest groupdisplay=cluster
datalabelattrs=(weight=bold size=12);
yaxis colorbands=odd type=discrete display=(nolabel noline noticks) discreteorder=data reverse;
xaxis label='Odds Ratio' ;
run;
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.