BookmarkSubscribeRSS Feed
JeffMeyers
Barite | Level 11

Hello all,

   I'm working on updating a macro I have that creates forest plots to potentially use the newer graphics procedures (version 9.4M5), and am namely testing out the PREFERRED column weights option. One thing that the macro does is allow the user to specify a COLBY (column by) variable to repeat graphs and analysis across in multiple columns:

example1.png

 

In my current macro I calculate column weights and use that with a SIDEBAR to set up the headers that span columns, but I'm curious if there's a simpler way to do it.  I've been playing around with some code and have gotten this far (I'm not concerned with formatting at this point):

ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
proc template;

    define statgraph blah;
        begingraph / designwidth=12in designheight=4in;
        layout lattice / rows=1 columns=13 columnweights=preferred rowdatarange=union columndatarange=union;
            rowaxes;
                rowaxis / reverse=true display=none displaysecondary=none tickvalueattrs=(family="Albany AMT") type=linear  griddisplay=on
                    linearopts=( viewmin=0 viewmax=3 tickvaluepriority=false tickvaluesequence=(start=0 end=3 increment=1)) ;
            endrowaxes;
            columnaxes;
                /*Row Headers Column*/
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    
                /*Column section 1*/
                columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                     linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                     display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                   
                /*Column section 2*/ 
                columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                     linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                     display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0;
                    
                /*Column section 3*/
                columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                     linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                     display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
            endcolumnaxes;
            /*rowheaders*/
            axistable y=y value=subtitle / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=left indentweight=subind;
                    
            /*Column section 1*/
            layout overlay / walldisplay=none;
                highlowplot y=y low=lcl1_1 high=ucl1_1/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line" ;
                scatterplot x=estimate1_1 y=y / name="mark" ;
            endlayout;
            axistable y=y value=ev_t_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=km_est_range1_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=pval_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
                
            /*Column section 2*/
            layout overlay / walldisplay=none ;
                highlowplot y=y low=lcl1_2 high=ucl1_2/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line" ;
                scatterplot x=estimate1_2 y=y / name="mark";
            endlayout;
            axistable y=y value=ev_t_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=km_est_range1_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=pval_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
               
            /*Column section 3*/
            layout overlay / walldisplay=none ;
                highlowplot y=y low=lcl1_3 high=ucl1_3/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line";
                scatterplot x=estimate1_3 y=y / name="mark";
            endlayout;
            axistable y=y value=ev_t_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=km_est_range1_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
            axistable y=y value=pval_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                valuehalign=center indentweight=subind;
        endlayout;
        endgraph;
    end;
run;
data blah;
    set _plot;
run;
proc sgrender data=blah template=blah;
run;

This makes the following:

example2.png

 

I could normally use the SIDEBAR space to make spanning column headers, but with the preferred option I don't know the weights.  Is there another way to make column headers that span across multiple columns?  (I want the graph and 3 stat columns to share the same header).

 

Another way I tried this was to use nested LATTICE LAYOUTS, but that desyncs the row axes across the layouts.  Does anyone know if there's a way to keep the row axes synced across multiple nested layouts?

ods path WORK.TEMPLAT(UPDATE) SASHELP.TMPLMST (READ);
proc template;

    define statgraph blah;
        begingraph / designwidth=12in designheight=4in;
        layout lattice / rows=1 columns=4 columnweights=preferred rowdatarange=union columndatarange=union;
            rowaxes;
                rowaxis / reverse=true display=none displaysecondary=none tickvalueattrs=(family="Albany AMT") type=linear  griddisplay=on
                    linearopts=( viewmin=0 viewmax=3 tickvaluepriority=false tickvaluesequence=(start=0 end=3 increment=1)) ;
            endrowaxes;
            columnaxes;
                /*Row Headers Column*/
                columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                    griddisplay=off display=none offsetmin=0 offsetmax=0; 
            endcolumnaxes;   
            /*rowheaders*/
            axistable y=y value=subtitle / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=left indentweight=subind;
            layout lattice / columns=4 rows=1 columnweights=preferred rowdatarange=union columndatarange=union;
                columnaxes;
                    /*Column section 1*/
                    columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                         linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                         display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                endcolumnaxes;
                rowaxes;
                    rowaxis / reverse=true display=none displaysecondary=none tickvalueattrs=(family="Albany AMT") type=linear  griddisplay=on
                        linearopts=( viewmin=0 viewmax=3 tickvaluepriority=false tickvaluesequence=(start=0 end=3 increment=1)) ;
                endrowaxes;
                 /*Column section 1*/
                layout overlay / walldisplay=none;
                    highlowplot y=y low=lcl1_1 high=ucl1_1/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line" ;
                    scatterplot x=estimate1_1 y=y / name="mark" ;
                endlayout;
                axistable y=y value=ev_t_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=km_est_range1_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=pval_1 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
            endlayout;
            layout lattice / columns=4 rows=1 columnweights=preferred rowdatarange=union columndatarange=union;
                columnaxes;
                    /*Column section 2*/ 
                    columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                         linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                         display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0;
                endcolumnaxes;
                rowaxes;
                    rowaxis / reverse=true display=none displaysecondary=none tickvalueattrs=(family="Albany AMT") type=linear  griddisplay=on
                        linearopts=( viewmin=0 viewmax=3 tickvaluepriority=false tickvaluesequence=(start=0 end=3 increment=1)) ;
                endrowaxes;
                /*Column section 2*/
                layout overlay / walldisplay=none ;
                    highlowplot y=y low=lcl1_2 high=ucl1_2/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line" ;
                    scatterplot x=estimate1_2 y=y / name="mark";
                endlayout;
                axistable y=y value=ev_t_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=km_est_range1_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=pval_2 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
            endlayout;
            layout lattice / columns=4 rows=1 columnweights=preferred rowdatarange=union columndatarange=union;
                columnaxes;
                    /*Column section 3*/
                    columnaxis / type=linear tickvalueattrs=(family="Albany AMT" size=10pt weight=normal color=black) 
                         linearopts=(tickvaluesequence=(start=0 end=1 increment=0.2) viewmin=0 viewmax=1 tickvaluefitpolicy=none) griddisplay=off 
                         display=(tickvalues ticks line) offsetmin=0 offsetmax=0;
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                    columnaxis / type=linear linearopts=(viewmin=0 viewmax=1) labelsplitchar='`' LABELFITPOLICY=SPLITALWAYS
                        griddisplay=off display=none offsetmin=0 offsetmax=0; 
                endcolumnaxes;
                rowaxes;
                    rowaxis / reverse=true display=none displaysecondary=none tickvalueattrs=(family="Albany AMT") type=linear  griddisplay=on
                        linearopts=( viewmin=0 viewmax=3 tickvaluepriority=false tickvaluesequence=(start=0 end=3 increment=1)) ;
                endrowaxes;
                /*Column section 3*/
                layout overlay / walldisplay=none ;
                    highlowplot y=y low=lcl1_3 high=ucl1_3/ lowcap=lcl1_cap1 highcap=ucl1_cap1 name="line";
                    scatterplot x=estimate1_3 y=y / name="mark";
                endlayout;
                axistable y=y value=ev_t_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=km_est_range1_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
                axistable y=y value=pval_3 / valueattrs=(family="Albany AMT" weight=bold color=black size=10pt)
                    valuehalign=center indentweight=subind;
            endlayout;
        endlayout;
        endgraph;
    end;
run;


data blah;
    set _plot;
run;
proc sgrender data=blah template=blah;
run;

Which makes the following image.  I have the grid lines turned on to show the row axes being out of sync due to the first column not having any axes showing.  I have tried including the row headers axis table with one of the column sections, but then it takes space away from that section and leaves the other two sections bigger.

example3.png

 

I would appreciate any ideas for either of my situations above.

 

10 REPLIES 10
pau13rown
Lapis Lazuli | Level 10

you know a lot more about this than me, but if i just take your question to be "Is there another way to make column headers that span across multiple columns?", have you tried greplay? http://support.sas.com/kb/44/973.html. I use it all the time to create multiple figs under a single title, but not sure if that's what you want, see attached fig for an example

 

 

aft, km v aft.jpg

 

JeffMeyers
Barite | Level 11
I appreciate the suggestion but I would prefer to make one image rather than try to combine multiple separate files if possible.
pau13rown
Lapis Lazuli | Level 10

ps, do you plan on making your macro available to sas users? seems interesting.....

JeffMeyers
Barite | Level 11
I have an older version of it available (%forestplot) on the old sascommunity.org page but it's very out of date. I'm working on making a couple of updated articles of macros I have on there since the website is being decommissioned.
ballardw
Super User

It would be helpful to provide a data set to work with, or to indicate one of the SAS supplied data sets in the SASHELP library that would be appropriate for this plot.

JeffMeyers
Barite | Level 11
The dataset has been attached to my original post (example.sas7bdat).
Jay54
Meteorite | Level 14

Hey Jeff.  I can't think of anything simple to help out.  Since you are using PREFERRED, the column width is internally computed, and you have no way of knowing its width. There is no option to automatically create a spanning column across specific columns.  

 

Now, your plan of using separate lattices for each group has potential.  If you keep all the categories the same in each lattice, and ensure the ROWAXIS offsetmin and offsetmax are set (by you) to be the same for all three layouts, maybe you can get consistent synched layout.  Worth a try.  🙂

JeffMeyers
Barite | Level 11
Thanks Sanjay. Any ideas on ways to sync the ROWAXES when one layout lattice has a visible x axis and another one does not (see my second image in the original post)?
Jay54
Meteorite | Level 14

Why not keep the y axis (Row axis) as part of the first layout?  What is the benefit of using the AxisTable to show the y-axis?  Hide the Rowaxes for the other Lattice panels.

 

If necessary, have you tried including the first axis table in with the first lattice panel, instead of a separate axis table?  I expect that would keep the alignments between the left column and the first lattice values.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 10 replies
  • 1722 views
  • 1 like
  • 4 in conversation