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

I am currently drawing a forest plot and I found a macro can do what I needed.

However, the template provided in this macro has one limitation (The paper was writtern by Janette Garner, title: An Enhanced Forest Plot Macro Using SAS).

The plot looks like as below. As I want to separate the header part and main contents of the plot, I do not know how to fix this. The default option for the header is to paint it with grey color, however I only need a bottom line for the header and make it looks a bit fomal for printing.

屏幕快照 2019-03-15 11.26.06.png

Does anyone who knows any solution for this purpose.

Thanks in advance!

proc import datafile="Y:\tab1.csv" dbms=csv out=tab1(where=(^missing(STATORD))) replace;
	getnames=yes;
run;


data forest;
		set tab1;
		by sortord statord;
		retain y y1 y2 y3 0;
		if first.sortord then do;
			y = y2 + 1;
			y1 = y - 1.5;
			y2 = y - 0.5;
		end;
		/* Adding 2.5 to each y1 and y2 will insert a small space (of size 0.5) between each subgroup chunk */
		y1 = y1 + 2.5;
		y2 = y2 + 2.5;
		y3 = mean(y1,y2);
run;

data plot;
	set forest;
	by y; /* Y-values associated with the header are only plotted once */
	if not first.y then y = .; /* We assign the variables "zero" and "one" as dummy variables to allow for
	plotting of text in some of the columns, in particular the labels. */
	zero = 0;
	one = 0.1;
	/* For alternating groups, add variables so that a reference band can be
	generated to aid in visualization */
	/* NOTE: To "disable" the reference bands, the user can set the color to
	white (cxffffff) in the PROC SGRENDER in Step 3 */
	if mod(sortord,2) = 0 then do;
		ref1=y1;
		ref2=y2;
		/* Third reference line to fill in any whitespace that may appear */
		if y ^ = . then ref3 = y;
		else if y = . then ref3 = ref1 - 0.5;
	end;
	*** Shift the "Overall" label down ***;
	if group = "Overall" then y = y3;
run;


%macro forest(	showcol2=Y,
				showcol3=Y,
				showcol4=Y,
				showcol5=Y,
				colwghts=%str(0.15 0.32 0.10 0.35 0.08),
				barval=%str(0 10 20 30 40 50 60 70 80 90 100),
				xlabbar=%bquote(Responders (%)),
				showpct=N,
				pctfmt=5.1,
				frsttype=DIFF/*RATIO*/,
				frstval=%str(-30 -20 -10 0 10 20 30)/*%str(0 0.5 1 2)*/,
				frstlabl=Y,
				xlabfrst=%bquote(Difference (%)),
				legtitle=%str(Treatment Group),
				ci_col=N,
				statlabl=%str(P-value)
				);
	
	
	data plot;
		set plot;
		%if &frsttype=DIFF %then %do;
			ci_txt = strip(put(est,5.1))||"% ("||strip(put(low,5.1))||"% to"||strip(put(high,5.1))||"%)";
		%end;
		%if &frsttype=RATIO %then %do;
			ci_txt = strip(put(est,5.1))||" ("||strip(put(low,5.1))||" to"||strip(put(high,5.1))||")";
		%end;
		n_txt1p = strip(n_txt1)||" ("||strip(put(percent1,&pctfmt))||"%)";
		n_txt2p = strip(n_txt2)||" ("||strip(put(percent2,&pctfmt))||"%)";
	run;

	proc sql noprint;
		select max(y2) into :ymax from plot;
	quit;

	%let colcnt = %eval(%sysfunc(count(&showcol2&showcol3&showcol4&showcol5,Y))+1);


	proc template;
		define statgraph ForestPlot;

		*** Assign dynamic variables, where the options are specified in the SGRENDER procedure ***;
		dynamic _headercolor _trt1labl _trt1color _trt2labl _trt2color _refcolor
			_refsize _barsize _ymin _ymax _title _footnote;
		begingraph;

		*** Add a title, if one is specified in the SGRENDER procedure ***;
		EntryTitle _title /;
		layout lattice / columns=&colcnt columnweights=(&colwghts);
			*** Specify column headers ***;
		sidebar / align=top;
			layout lattice / rows=1 columns=&colcnt columnweights=(&colwghts)
				backgroundcolor=_headercolor opaque=true;
			entry halign=center textattrs=(size=8 weight=bold) "";

			%if &showcol2=Y %then %do; 
				entry halign=center textattrs=(size=8 weight=bold) ""; 
			%end;

			%if &showcol3=Y %then %do; 
				entry halign=center textattrs=(size=8 weight=bold) 
				%if &showpct=N %then %do; 
					"n/N" %end; %else %do; "n/N (%)" 
				%end;
				; 
			%end;

			%if &showcol4=Y %then %do; 
				entry halign=center textattrs=(size=8 weight=bold) ""; 
			%end;

			%if &showcol5=Y %then %do; 
				entry halign=center textattrs=(size=8 weight=bold) "&statlabl" ; 
			%end;

		endlayout;
		endsidebar;

		*** Column 1: Labels of the comparisons ***;
		layout overlay / walldisplay=none xaxisopts=(display=none) yaxisopts=(reverse=true display=none 
			tickvalueattrs=(weight=bold) linearopts=(viewmin=_ymin viewmax=_ymax));
			referenceline y=ref1 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref2 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref3 / lineattrs=(thickness=_refsize color=_refcolor);

			*** Header, using "y" variable ***;
			highlowplot y=y low=zero high=zero / highlabel=group lineattrs=(thickness=0) labelattrs=(size=7 weight=bold);

			*** Subgroup, using "y3" variable ***;
			highlowplot y=y3 low=one high=one / highlabel=level lineattrs=(thickness=0) labelattrs=(size=7 weight=bold);
		endlayout;

		%if &showcol2=Y %then %do;
		*** Column 2: Barplots of the proportion of successes in each treatment group ***;
			layout overlay / xaxisopts=(label="&xlabbar" labelattrs=(size=7 weight=bold) 
				linearopts=(tickvaluepriority=true tickvaluelist=(&barval))
				/*display=(tickvalues)*/) /* This hides the x-axis label */
			yaxisopts=(reverse=true display=none linearopts=(viewmin=_ymin viewmax=_ymax)) walldisplay=none;

			referenceline y=ref1 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref2 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref3 / lineattrs=(thickness=_refsize color=_refcolor);

			*** The barplots are created using a HIGHLOW plot, where the thickness of the line is 
			controlled by user. Tweaking is required based on the number of rows, height of the 
			output and spacing between bars. ***;

			highlowplot y=y1 low=zero high=percent1 /lineattrs=(thickness=_barsize color=_trt1color)
			labelattrs=(size=7 weight=bold) name='trt1' legendlabel=_trt1labl;

			highlowplot y=y2 low=zero high=percent2 /lineattrs=(thickness=_barsize color=_trt2color)
			labelattrs=(size=7 weight=bold) name='trt2' legendlabel=_trt2labl;
			referenceline x=0;
			endlayout;
		%end;

		%if &showcol3=Y %then %do;
			*** Column 3: Column of values that support the barplots, n/N ***;
			layout overlay / walldisplay=none xaxisopts=(display=none) yaxisopts=(reverse=true display=none
				tickvalueattrs=(weight=bold) linearopts=(viewmin=_ymin viewmax=_ymax));

			referenceline y=ref1 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref2 / lineattrs=(thickness=_refsize color=_refcolor);
			referenceline y=ref3 / lineattrs=(thickness=_refsize color=_refcolor);

			highlowplot y=y1 low=zero high=zero / 
				%if &showpct=N %then %do;
					highlabel=n_txt1 
				%end; 
				%if &showpct=Y %then %do; 
					highlabel=n_txt1p 
				%end;
				lineattrs=(thickness=0) labelattrs=(size=7 color=_trt1color);

				highlowplot y=y2 low=zero high=zero / 
					%if &showpct=N %then %do;
						highlabel=n_txt2 
					%end; 
					%if &showpct=Y %then %do; 
						highlabel=n_txt2p 
					%end;

				lineattrs=(thickness=0) labelattrs=(size=7 color=_trt2color);
			endlayout;
		%end;

		%if &showcol4=Y %then %do;
			*** Column 4: Plot the CI for the difference (and include the point estimate) ***;

			layout overlay / xaxisopts=(label="&xlabfrst" labelattrs=(size=7 weight=bold)
				linearopts=(tickvaluepriority=true tickvaluelist=(&frstval)))
				yaxisopts=(reverse=true display=none linearopts=(viewmin=_ymin
				viewmax=_ymax)) walldisplay=none;

				referenceline y=ref1 / lineattrs=(thickness=_refsize color=_refcolor);
				referenceline y=ref2 / lineattrs=(thickness=_refsize color=_refcolor);
				referenceline y=ref3 / lineattrs=(thickness=_refsize color=_refcolor);

				%if &frsttype=DIFF %then %do;
					referenceline x=0 / lineattrs=(pattern=shortdash);
				%end;

				%if &frsttype=RATIO %then %do;
					referenceline x=1 / lineattrs=(pattern=shortdash);
				%end;

				highlowplot y=y3 low=low high=high / lineattrs=(thickness=1 color=black) 
				%if &frstlabl=Y %then %do; 
					lowlabel=low highlabel=high 
				%end; 
				;

				scatterplot y=y3 x=est / markerattrs=(symbol=circlefilled size=10 color=black) 
				datalabelposition=bottom 
				%if &frstlabl=Y %then %do;
					datalabel=est 
				%end; 
				;

				*** Insert labels that explain the plot, namely that the right
				side favors Treatment 1 and the left side favors Treatment 2. The labels for
				these are defined in the SGRENDER procedure. ***;
				layout gridded / rows=2 border=false autoalign=(topright);
					entry textattrs=(size=7) halign=right 'Favors';
					entry textattrs=(size=7 color=_trt1color) halign=right _trt1labl;
				endlayout;

				layout gridded / rows=2 border=false autoalign=(topleft);
					entry textattrs=(size=7) halign=left 'Favors';
					entry textattrs=(size=7 color=_trt2color) halign=left _trt2labl;
				endlayout;
			endlayout;
		%end;

		%if &showcol5=Y %then %do;
			*** Column 5: Statistics (Numerical Values) columns ***;
			layout overlay / walldisplay=none xaxisopts=(display=none)
				yaxisopts=(reverse=true display=none linearopts=(viewmin=_ymin viewmax=_ymax));
				referenceline y=ref1 / lineattrs=(thickness=_refsize color=_refcolor);
				referenceline y=ref2 / lineattrs=(thickness=_refsize color=_refcolor);
				referenceline y=ref3 / lineattrs=(thickness=_refsize color=_refcolor);
				highlowplot y=y3 low=zero high=zero / 
					%if &ci_col=Y %then %do;
						highlabel=ci_txt 
					%end; 
					%if &ci_col=N %then %do; 
						highlabel=stat_txt 
					%end;
				lineattrs=(thickness=0) labelattrs=(size=7);
			endlayout;
		%end;

		endlayout;

		%if &showcol2=Y %then %do; /* Only display legend if barplot shown */
			*** Add legend to bottom of the graph to explain colored bars ***;
			layout globallegend / type=row weights=preferred legendtitleposition=left;
			discretelegend 'trt1' 'trt2' / title="&legtitle" titleattrs=(size=8 weight=bold) 
			across=2 displayclipped=true;
			endlayout;

		%end;

		*** Add footnote text, if specified in the SGRENDER procedure ***;
		entryfootnote halign=left textattrs=(size=7) _footnote;
		endgraph;
	end;
	run;


	proc sgrender data=plot template=ForestPlot;
		dynamic _refcolor='cxf0f0f0' /* Set _refcolor='cxffffff' (white) to "hide" the reference bars */
		_refsize=15
		_headercolor='cxd0d0d0'
		_trt1labl='Switch group'
		_trt1color='cxb30437'
		_trt2labl='No-switch group'
		_trt2color='cx009fc3'
		_barsize=11.5
		_ymin=2
		_ymax=&ymax.
		_title=''
		_footnote='';
	run;

%mend forest;

%forest;








1 ACCEPTED SOLUTION

Accepted Solutions
JeffMeyers
Barite | Level 11

I would use the DRAWLINE statement (see it added to the sidebar below)

sidebar / align=top;
            layout lattice / rows=1 columns=&colcnt columnweights=(&colwghts)
                backgroundcolor=_headercolor opaque=true;


            drawline x1=0 x2=100 y1=0 y2=0  /drawspace=layoutpercent lineattrs=(thickness=1pt color=black pattern=1);



            entry halign=center textattrs=(size=8 weight=bold) "";

            %if &showcol2=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) ""; 
            %end;

            %if &showcol3=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) 
                %if &showpct=N %then %do; 
                    "n/N" %end; %else %do; "n/N (%)" 
                %end;
                ; 
            %end;

            %if &showcol4=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) ""; 
            %end;

            %if &showcol5=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) "&statlabl" ; 
            %end;

        endlayout;
        endsidebar;

SGRender.png

View solution in original post

2 REPLIES 2
JeffMeyers
Barite | Level 11

I would use the DRAWLINE statement (see it added to the sidebar below)

sidebar / align=top;
            layout lattice / rows=1 columns=&colcnt columnweights=(&colwghts)
                backgroundcolor=_headercolor opaque=true;


            drawline x1=0 x2=100 y1=0 y2=0  /drawspace=layoutpercent lineattrs=(thickness=1pt color=black pattern=1);



            entry halign=center textattrs=(size=8 weight=bold) "";

            %if &showcol2=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) ""; 
            %end;

            %if &showcol3=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) 
                %if &showpct=N %then %do; 
                    "n/N" %end; %else %do; "n/N (%)" 
                %end;
                ; 
            %end;

            %if &showcol4=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) ""; 
            %end;

            %if &showcol5=Y %then %do; 
                entry halign=center textattrs=(size=8 weight=bold) "&statlabl" ; 
            %end;

        endlayout;
        endsidebar;

SGRender.png

fanwayne
Fluorite | Level 6
This looks perfect! Thank you so much!

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
  • 2 replies
  • 1940 views
  • 1 like
  • 2 in conversation