ODS Graphics Template Modification


Understanding Conditional Template Logic

You do not need to understand how the %Marginal macro works to use it. However, if you are interested, this section explains how this macro creates varying graph templates that depend on the number of variables and other options. This section can help you understand how you can use both GTL and macro conditional logic to construct flexible templates.

The %Marginal macro creates a graph template and then uses it one or more times to make graphs. The macro uses one block of code to create the template for all cases: single graphs, the default sizes ($2 \times 2$, $2 \times 3$, $3 \times 3$, $3 \times 4$, and $4 \times 4$), and other sizes. It displays the legend inside a cell for multicell panels and inside the graph for single graphs. It constructs the final graph of predicted values in a different way from the way that it constructs the graphs that correspond to the independent variables. It does all of this in only 49 lines by using macro %DO loops and by using conditional logic (macro %IF statements and GTL IF and ENDIF statements) to populate cells. Before going into detail about the general logic, here are some specific examples.

For a $2 \times 2$ panel, the generated template is as follows:

proc template;
   define statgraph __marginal;
      dynamic _ivar1 _ivar2 _ivar3 ncells pplot;
      begingraph / designwidth=defaultdesignheight;
         entrytitle "Marginal Models for LogSalary";
         legenditem type=line name='a' / lineattrs=GRAPHFIT label='Data';
         legenditem type=line name='b' / lineattrs=GRAPHFIT2 label='Model';
         layout lattice / columns=2 rows=2 rowdatarange=unionall rowgutter=10
            columngutter=10;
            if (1 LE NCELLS)
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=(label));
                  scatterplot y=LOGSALARY x=_IVAR1;
                  loessplot y=LOGSALARY x=_IVAR1 /;
                  loessplot y=P x=_IVAR1 / lineattrs=GRAPHFIT2;
               endlayout;
            endif;
            if (2 LE NCELLS)
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=(label));
                  scatterplot y=LOGSALARY x=_IVAR2;
                  loessplot y=LOGSALARY x=_IVAR2 /;
                  loessplot y=P x=_IVAR2 / lineattrs=GRAPHFIT2;
               endlayout;
            endif;
            if (3 LE NCELLS)
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=(label));
                  scatterplot y=LOGSALARY x=_IVAR3;
                  loessplot y=LOGSALARY x=_IVAR3 /;
                  loessplot y=P x=_IVAR3 / lineattrs=GRAPHFIT2;
               endlayout;
            endif;
            if (PPLOT)
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=(label)
                  label='Predicted Values');
                  scatterplot y=LOGSALARY x=P;
                  loessplot y=LOGSALARY x=P /;
                  seriesplot y=_Y x=_X / lineattrs=GRAPHFIT2;
               endlayout;
            endif;
            layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=none);
               discretelegend 'a' 'b' / location=inside across=1 border=false;
            endlayout;
         endlayout;
      endgraph;
   end;
run; quit;

The template can handle up to three independent variables (and no predicted values variable) or two independent variables along with one predicted values variable. (When there are three or more independent variables, the predicted values variable appears in a subsequent panel.) The following statements control which graphs are produced: IF (1 LE NCELLS), IF (2 LE NCELLS), IF (3 LE NCELLS), and IF (PPLOT).

This template can be used with the SGRENDER procedure and two independent variables as follows:

proc sgrender data=__tmpdat template=__marginal;
   dynamic _ivar1 = "nAtBat" _ivar2 = "nHits" ncells=2 pplot=1;
run;

Dynamic variables enable templates to be more flexible. The values of the dynamic variables are substituted into the template when PROC SGRENDER runs. The dynamic variables that begin with _ivar specify the names of the independent variables. Other dynamic variables specify that there are two independent variable cells (because ncells = 2, the third LAYOUT OVERLAY block is not used) and that the predicted values plot is displayed (because pplot = 1, which is a true Boolean constant).

This template can be used with PROC SGRENDER and four independent variables as follows:

proc sgrender data=__tmpdat template=__marginal;
   dynamic _ivar1 = "nAtBat" _ivar2 = "nHits" _ivar3 = "nhome" ncells=3 pplot=0;
run;

proc sgrender data=__tmpdat template=__marginal;
   dynamic _ivar1 = "nrbi" ncells=1 pplot=1;
run;

When the dynamic variable ncells = 3, three independent variable plots are produced. When the dynamic variable pplot = 1, a predicted values plot is produced that follows the specified number of independent variable plots. The first step produces three independent variable plots, and the second step creates one independent variable plot and the predicted values plot. A legend appears in the cell after the last graph in both panels.

When you specify PANEL=1 to create single graphs, the generated template is as follows:

proc template;
   define statgraph __marginal;
      dynamic _ivar1 ncells pplot;
      begingraph / designwidth=defaultdesignheight;
         entrytitle "Marginal Models for LogSalary";
         legenditem type=line name='a' / lineattrs=GRAPHFIT label='Data';
         legenditem type=line name='b' / lineattrs=GRAPHFIT2 label='Model';
         layout lattice / columns=1 rows=1 rowdatarange=unionall
                          rowgutter=10 columngutter=10;
            if (1 LE NCELLS)
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=(label));
                  scatterplot y=LOGSALARY x=_IVAR1;
                  loessplot y=LOGSALARY x=_IVAR1 /;
                  loessplot y=P x=_IVAR1 / lineattrs=GRAPHFIT2;
                  layout gridded / autoalign=(topright topleft bottomright
                                              bottomleft);
                     discretelegend 'a' 'b' / location=inside across=1;
                  endlayout;
               endlayout;
            endif;
            if (PPLOT)
               layout overlay / yaxisopts=(display=none)
                      xaxisopts=(display=(label) label='Predicted Values');
                  scatterplot y=LOGSALARY x=P;
                  loessplot y=LOGSALARY x=P /;
                  seriesplot y=_Y x=_X / lineattrs=GRAPHFIT2;
                  layout gridded / autoalign=(topright topleft bottomright
                     bottomleft);
                     discretelegend 'a' 'b' / location=inside across=1;
                  endlayout;
               endlayout;
            endif;
         endlayout;
      endgraph;
   end;
run; quit;

Now, the legend appears inside each graph rather than in a separate cell. When you have two independent variables, the template is used in three PROC SGRENDER steps:

proc sgrender data=__tmpdat template=__marginal;
   dynamic _ivar1 = "nAtBat" ncells=1 pplot=0;
run;

proc sgrender data=__tmpdat template=__marginal;
   dynamic _ivar1 = "nHits" ncells=1 pplot=0;
run;

proc sgrender data=__tmpdat template=__marginal;
   dynamic ncells=0 pplot=1;
run;

When the dynamic variable ncells = 1, an independent variable plot is produced. When the dynamic variable pplot = 1, a predicted values plot is produced. The first two steps produce independent variable plots, and the third step creates a predicted values plot.

The part of the %Marginal macro that generates the template is as follows:

proc template;
   define statgraph __marginal;
      dynamic %do i = 1 %to &rows*&cols - &paneled; _ivar&i %end; ncells pplot;
      begingraph / &gopts;
         entrytitle &ettl;
         legenditem type=line name='a' / lineattrs=GraphFit  label='Data';
         legenditem type=line name='b' / lineattrs=GraphFit2 label='Model';
         layout lattice / columns=&cols rows=&rows
                rowdatarange=unionall rowgutter=10 columngutter=10;
            %do i = 1 %to &rows * &cols - &paneled; /* ordinary cells */
               if(&i le ncells)
                  layout overlay / yaxisopts=(display=none)
                                   xaxisopts=(display=(label));
                     scatterplot  y=&dependent  x=_ivar&i;
                     &smooth.plot y=&dependent  x=_ivar&i / &smoothopts;
                     &smooth.plot y=&predicted  x=_ivar&i / &smoothopts
                                                            lineattrs=graphfit2;
                     %if not &paneled %then %do; /* not paneled?           */
                        layout gridded /         /* then put legend inside */
                           autoalign=(topright topleft bottomright bottomleft);
                           discretelegend 'a' 'b' / location=inside across=1;
                           endlayout;
                        %end;
                     endlayout;
                  endif;
               %end;
            if(pplot) /* predicted values plot is handled differently */
               layout overlay / yaxisopts=(display=none)
                      xaxisopts=(display=(label) label='Predicted Values');
                  scatterplot  y=&dependent  x=&predicted;
                  &smooth.plot y=&dependent  x=&predicted / &smoothopts;
                  seriesplot   y=_y x=_x / lineattrs=graphfit2;
                  %if not &paneled %then %do;
                     layout gridded /
                            autoalign=(topright topleft bottomright bottomleft);
                        discretelegend 'a' 'b' / location=inside across=1;
                        endlayout;
                     %end;
                  endlayout;
               endif;
            %if &paneled %then %do; /* legend in a cell by itself if paneled */
               layout overlay / yaxisopts=(display=none)
                                xaxisopts=(display=none);
                  discretelegend 'a' 'b' / location=inside across=1
                                           border=false;
               endlayout;
            %end;
         endlayout;
      endgraph;
   end;
run; quit;

The LAYOUT LATTICE statement in the GTL constructs a display of &rows and &cols (macro variables that are set in a preceding step). Inside the LAYOUT LATTICE block, there are LAYOUT OVERLAY blocks for the independent variables and for the predicted values, and optionally one for the legend. When the graph is a panel that contains multiple cells, the macro %DO loop generates &rows $\times $ &cols – 1 LAYOUT OVERLAY blocks for the independent variables. Additional code produces one LAYOUT OVERLAY block for the predicted values plot and a final LAYOUT OVERLAY block for the legend. This same template is used when there are multiple panels, and the LAYOUT OVERLAY block for the predicted values plot is suppressed in all but the last panel by the IF(PPLOT) statement.

When there is one cell per panel, two GTL IF statements control which type of graph is displayed. When there is one cell per panel, a macro %IF statement adds a legend inside the body of the plot, and a different macro %IF statement excludes the creation of a separate legend cell. When there are multiple cells per panel, the legend is added to the last cell and does not appear inside the graphs.

The macro language enables you to generate a long template by using a minimum number of statements. There is one LAYOUT OVERLAY block in the macro for independent variable plots. In contrast, the generated template can be much longer and has &rows $\times $ &cols – 1 LAYOUT OVERLAY blocks because of the macro %DO loop, each conditionally executed by a GTL IF statement. The template also contains two LAYOUT OVERLAY blocks that are outside the macro %DO loop. Although you would never know it by looking at the PROC TEMPLATE SOURCE statement template display, many of the multicell templates that SAS provides for use in its analytical procedures are created in a similar way by using macros and dynamic variables.