BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
pink_poodle
Barite | Level 11

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:

pink_poodle_0-1668008240501.png

Can SAS do that? I know R can, but I am worse at it. Any suggestions are appreciated.

Thank you!

1 ACCEPTED SOLUTION

Accepted Solutions
Ksharp
Super User

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;

Ksharp_0-1668170610877.png

 

View solution in original post

14 REPLIES 14
ballardw
Super User

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.

pink_poodle
Barite | Level 11

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. 

 

 

ballardw
Super User

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
Barite | Level 11
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
ballardw
Super User

@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;

 

pink_poodle
Barite | Level 11

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. 

ballardw
Super User

@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.

Ksharp
Super User
/*
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;

Ksharp_0-1668080697515.png

 

pink_poodle
Barite | Level 11
@Ksharp, this looks great, thank you! I did same numbers because what if CIs overlap. I guess we would need to shift one group a little below the other.
Ksharp
Super User
/*
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;

Ksharp_0-1668167473445.png

 

pink_poodle
Barite | Level 11
@Ksharp, beautiful! I am sorry to “torture” you, but could you please add one more group in navy blue called “Non-isolated TBI” ? That will make it perfect, and the boss will be very happy :).
Ksharp
Super User
How do you want to display or layout ? Post a picture .
Ksharp
Super User

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;

Ksharp_0-1668170610877.png

 

pink_poodle
Barite | Level 11
Yes, this is perfect, thank you so much!

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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
  • 14 replies
  • 2819 views
  • 13 likes
  • 3 in conversation