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

Dear all,

 

I followed the example from SAS to create a forest plot.

 

But there seems a "double-space" condition in my graphic.

 

Would you please help me find out the problem is?

 

Thank you very much!

 

Please find the attachment for my graphic, and

 

Here is my program:

 

/*--Add "Id" to identify methods headings from values                      --*/
data forest;
  input Id methods $3-20 Countpct $22-40 HR  lci  hci;
  indentWt=1;
  ObsId=_n_;
datalines;
1	Primary analysis	0.64 (0.43-0.95)	0.64	0.43	0.95
1	Grace period		.	                .	.       .
2	60 days	                0.70 (0.49-1.01)	0.7	0.49	1.01
2	90 days	                1.06 (0.86-1.32)	1.06	0.86	1.32
1	Age groups		    .	                .	.       .
2	<=65 years	        0.43 (0.19-0.93)	0.43	0.19	0.93
2	>65 years	        0.69 (0.44-1.08)	0.69	0.44	1.08
1	Gender		        .	                .	.       .
2	Female	                0.69 (0.43-1.10)	0.69	0.43	1.1
2	Male	                0.57 (0.28-1.15)	0.57	0.28	1.15
1	CVD history		.	                .	.       .
2	Without	                0.69 (0.43-1.08)	0.69	0.43	1.08
2	With	                0.50 (0.24-1.06)	0.5	0.24	1.06
;
run;

data forest2;
  set forest;
  /*if count ne . then CountPct=put(count, 4.0) || "(" || put(percent, 3.0) || ")";*/
  if mod(obsId-1, 6) in (1, 2, 3) then ref=obsId;
    /* Reduce indent for methods heading */
  if id=1 then indentWt=0;
run;

/**
 * Forest plot template using a one-column outermost lattice layout.
 * The header uses a nested lattice layout in the top side bar. 
 * An overlay layout makes up the main area belci it.
 * The left side table of values are axis tables placed inside
 * inner margin of the overlay layout.
 */
proc template;
  define statgraph forestAxisTable;
    dynamic  _bandColor _headerColor;
    begingraph;

      discreteattrmap name='text' / trimleading=true;
        value '1' / textAttrs=(size=7 weight=bold); 
        value '2' / textAttrs=(color=gray size=6 weight=normal); 
      enddiscreteattrmap;

      discreteattrvar attrvar=txtDAV var=id attrmap='text';

      layout lattice / columns=1;

         /*--Column headers--*/
        sidebar / align=top;
          layout lattice / columns=2 rowweights=uniform columnweights=(0.35 0.65)
                            backgroundcolor=_headerColor opaque=true;
              entry textAttrs=(size=8 weight=bold) hAlign=left "Methods"
                                          hAlign=right " HR (95% CI)";
              entry textAttrs=(size=8 weight=bold) "Hazard Ratio";
          endLayout;
        endsidebar;

        /* Single cell with inner margins for left and right tables */
        layout overlay / xAxisOpts=(display=(line ticks tickvalues) 
                            tickValueAttrs=(size=6 weight=bold)
                            labelAttrs=(size=6 weight=bold)
                            linearOpts=(tickValuePriority=true 
                                        tickValueList=(0.0 0.5 1.0 1.5 2.0))
                              offsetMin=0.1
                            )
                      yAxisOpts=(reverse=true display=none offsetMin=0) wallDisplay=none;

          /* Left-side table */
          innerMargin / align=left gutter=0.1in;
            axisTable y=obsId value=Methods / textGroup=txtDAV
                          indentWeight=indentwt display=(values);
            axisTable y=obsId value=CountPct /  display=(values);
          endInnerMargin;

          /* Odds Ratio plot */
          referenceLine y=ref / lineAttrs=(thickness=15 color=_bandColor);
          scatterPlot y=obsId x=HR / markerAttrs=(symbol=squareFilled)
                          xErrorLower=LCI xErrorUpper=HCI errorBarCapShape=none;
          referenceLine x=1;

        endLayout; /* overlay */

      endLayout; /* lattice */
    endgraph;
  end;
run;

ods _all_ close;
ods listing image_dpi=300;
ods graphics / reset width=6in height=4.5in imagename='SAS94M2_ForestPlot_GTL';

proc sgrender data=forest2 template=forestAxisTable;
  dynamic _bandColor='cxf0f0f0' _headerColor='cxd0d0d0';
run;

ods _all_ close;

forest plot.png
1 ACCEPTED SOLUTION

Accepted Solutions
Jay54
Meteorite | Level 14

The height of your plot is too tall for the number of observations yoy have.  I suggest you reduce your height o 2.6" in the BEGINGRAPH statement.  Also, remove the OFFSETMIN=0 in the YAxisOpts.  Program attached.

 

SAS94M2_ForestPlot_GTL.png

 

data forest;
input Id methods $3-20 Countpct $22-40 HR lci hci;
indentWt=1;
ObsId=_n_;
datalines;
1 Primary analysis 0.64 (0.43-0.95) 0.64 0.43 0.95
1 Grace period . . . .
2 60 days 0.70 (0.49-1.01) 0.7 0.49 1.01
2 90 days 1.06 (0.86-1.32) 1.06 0.86 1.32
1 Age groups . . . .
2 <=65 years 0.43 (0.19-0.93) 0.43 0.19 0.93
2 >65 years 0.69 (0.44-1.08) 0.69 0.44 1.08
1 Gender . . . .
2 Female 0.69 (0.43-1.10) 0.69 0.43 1.1
2 Male 0.57 (0.28-1.15) 0.57 0.28 1.15
1 CVD history . . . .
2 Without 0.69 (0.43-1.08) 0.69 0.43 1.08
2 With 0.50 (0.24-1.06) 0.5 0.24 1.06
;
run;

data forest2;
set forest;
/*if count ne . then CountPct=put(count, 4.0) || "(" || put(percent, 3.0) || ")";*/
if mod(obsId-1, 6) in (1, 2, 3) then ref=obsId;
/* Reduce indent for methods heading */
if id=1 then indentWt=0;
run;

/**
* Forest plot template using a one-column outermost lattice layout.
* The header uses a nested lattice layout in the top side bar.
* An overlay layout makes up the main area belci it.
* The left side table of values are axis tables placed inside
* inner margin of the overlay layout.
*/
proc template;
define statgraph forestAxisTable;
dynamic _bandColor _headerColor;
begingraph / designheight=2.6in;

discreteattrmap name='text' / trimleading=true;
value '1' / textAttrs=(size=7 weight=bold);
value '2' / textAttrs=(color=gray size=6 weight=normal);
enddiscreteattrmap;

discreteattrvar attrvar=txtDAV var=id attrmap='text';

layout lattice / columns=1;

/*--Column headers--*/
sidebar / align=top;
layout lattice / columns=2 rowweights=uniform columnweights=(0.35 0.65)
backgroundcolor=_headerColor opaque=true;
entry textAttrs=(size=8 weight=bold) hAlign=left "Methods"
hAlign=right " HR (95% CI)";
entry textAttrs=(size=8 weight=bold) "Hazard Ratio";
endLayout;
endsidebar;

/* Single cell with inner margins for left and right tables */
layout overlay / xAxisOpts=(display=(line ticks tickvalues)
tickValueAttrs=(size=6 weight=bold)
labelAttrs=(size=6 weight=bold)
linearOpts=(tickValuePriority=true
tickValueList=(0.0 0.5 1.0 1.5 2.0))
offsetMin=0.1
)
yAxisOpts=(reverse=true display=none) wallDisplay=none;

/* Left-side table */
innerMargin / align=left gutter=0.1in;
axisTable y=obsId value=Methods / textGroup=txtDAV
indentWeight=indentwt display=(values);
axisTable y=obsId value=CountPct / display=(values);
endInnerMargin;

/* Odds Ratio plot */
referenceLine y=ref / lineAttrs=(thickness=15 color=_bandColor);
scatterPlot y=obsId x=HR / markerAttrs=(symbol=squareFilled)
xErrorLower=LCI xErrorUpper=HCI errorBarCapShape=none;
referenceLine x=1;

endLayout; /* overlay */

endLayout; /* lattice */
endgraph;
end;
run;

ods _all_ close;
ods listing image_dpi=200;
ods graphics / reset imagename='SAS94M2_ForestPlot_GTL';

proc sgrender data=forest2 template=forestAxisTable;
dynamic _bandColor='cxf0f0f0' _headerColor='cxd0d0d0';
run;
ods _all_ close;

View solution in original post

4 REPLIES 4
Norman21
Lapis Lazuli | Level 10

Hi JayL

 

Can you try adjusting the width and height in the following statement to see if it makes a difference?

 

ods graphics / reset width=6in height=4.5in

 

 

Norman.
SAS 9.4 (TS1M6) X64_10PRO WIN 10.0.17763 Workstation

JayL
Calcite | Level 5

Hi Norman,

 

Thanks so much for your help.

 

I've tried the method you recommend, however, it only changes the overall size of the graph proporionally, with no changes in

 

the space between rows.

 

I am trying to find an option that can change the line space under axistable, but has not figured it out yet.

 

Jay

Jay54
Meteorite | Level 14

The height of your plot is too tall for the number of observations yoy have.  I suggest you reduce your height o 2.6" in the BEGINGRAPH statement.  Also, remove the OFFSETMIN=0 in the YAxisOpts.  Program attached.

 

SAS94M2_ForestPlot_GTL.png

 

data forest;
input Id methods $3-20 Countpct $22-40 HR lci hci;
indentWt=1;
ObsId=_n_;
datalines;
1 Primary analysis 0.64 (0.43-0.95) 0.64 0.43 0.95
1 Grace period . . . .
2 60 days 0.70 (0.49-1.01) 0.7 0.49 1.01
2 90 days 1.06 (0.86-1.32) 1.06 0.86 1.32
1 Age groups . . . .
2 <=65 years 0.43 (0.19-0.93) 0.43 0.19 0.93
2 >65 years 0.69 (0.44-1.08) 0.69 0.44 1.08
1 Gender . . . .
2 Female 0.69 (0.43-1.10) 0.69 0.43 1.1
2 Male 0.57 (0.28-1.15) 0.57 0.28 1.15
1 CVD history . . . .
2 Without 0.69 (0.43-1.08) 0.69 0.43 1.08
2 With 0.50 (0.24-1.06) 0.5 0.24 1.06
;
run;

data forest2;
set forest;
/*if count ne . then CountPct=put(count, 4.0) || "(" || put(percent, 3.0) || ")";*/
if mod(obsId-1, 6) in (1, 2, 3) then ref=obsId;
/* Reduce indent for methods heading */
if id=1 then indentWt=0;
run;

/**
* Forest plot template using a one-column outermost lattice layout.
* The header uses a nested lattice layout in the top side bar.
* An overlay layout makes up the main area belci it.
* The left side table of values are axis tables placed inside
* inner margin of the overlay layout.
*/
proc template;
define statgraph forestAxisTable;
dynamic _bandColor _headerColor;
begingraph / designheight=2.6in;

discreteattrmap name='text' / trimleading=true;
value '1' / textAttrs=(size=7 weight=bold);
value '2' / textAttrs=(color=gray size=6 weight=normal);
enddiscreteattrmap;

discreteattrvar attrvar=txtDAV var=id attrmap='text';

layout lattice / columns=1;

/*--Column headers--*/
sidebar / align=top;
layout lattice / columns=2 rowweights=uniform columnweights=(0.35 0.65)
backgroundcolor=_headerColor opaque=true;
entry textAttrs=(size=8 weight=bold) hAlign=left "Methods"
hAlign=right " HR (95% CI)";
entry textAttrs=(size=8 weight=bold) "Hazard Ratio";
endLayout;
endsidebar;

/* Single cell with inner margins for left and right tables */
layout overlay / xAxisOpts=(display=(line ticks tickvalues)
tickValueAttrs=(size=6 weight=bold)
labelAttrs=(size=6 weight=bold)
linearOpts=(tickValuePriority=true
tickValueList=(0.0 0.5 1.0 1.5 2.0))
offsetMin=0.1
)
yAxisOpts=(reverse=true display=none) wallDisplay=none;

/* Left-side table */
innerMargin / align=left gutter=0.1in;
axisTable y=obsId value=Methods / textGroup=txtDAV
indentWeight=indentwt display=(values);
axisTable y=obsId value=CountPct / display=(values);
endInnerMargin;

/* Odds Ratio plot */
referenceLine y=ref / lineAttrs=(thickness=15 color=_bandColor);
scatterPlot y=obsId x=HR / markerAttrs=(symbol=squareFilled)
xErrorLower=LCI xErrorUpper=HCI errorBarCapShape=none;
referenceLine x=1;

endLayout; /* overlay */

endLayout; /* lattice */
endgraph;
end;
run;

ods _all_ close;
ods listing image_dpi=200;
ods graphics / reset imagename='SAS94M2_ForestPlot_GTL';

proc sgrender data=forest2 template=forestAxisTable;
dynamic _bandColor='cxf0f0f0' _headerColor='cxd0d0d0';
run;
ods _all_ close;

JayL
Calcite | Level 5

Hi Sanjay,

 

Thanks so much!  This is really helpful, I've learned a lot.

 

best regards,

 

Jay

sas-innovate-wordmark-2025-midnight.png

Register Today!

Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.


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
  • 4 replies
  • 4684 views
  • 0 likes
  • 3 in conversation