I can create an output with four plots on one page using ODS LAYOUT ABSOLUTE, as shown in code #1. But then I want to use a BY statement in PROC SGPLOT, such that the first I get four plots on one page, where all four plots are for the same level of the BY variable and the plots are always in the same position, see code #2.
Code #1, this works as expected, but I have not used a BY statement:
ods pdf file="absolute.pdf";
ods layout absolute ;
ods region x=.5in y=0.1in width=4in height=2.75in;
proc sgplot data=sashelp.cars;
histogram msrp;
run;
ods region x=5.1in y=.1in width=4in height=2.75in;
proc sgplot data=sashelp.cars;
histogram invoice;
run;
ods region x=.5in y=5.25in width=4in height=2.75in;
proc sgplot data=sashelp.cars;
histogram mpg_city;
run;
ods region x=5.1in y=5.25in width=4in height=2.75in;
proc sgplot data=sashelp.cars;
histogram horsepower;
run;
ods layout end;
ods pdf close;
Now, code #2, with a BY Statement, it doesn't work at all, how can I get something like this to work? I want three output pages, the first page would be four plots for origin='Asia', with MSRP top left, INVOICE top right, MPG_CITY bottom left and HORSEPOWER bottom right; second page would be for origin='Europe', exact same arrangement of plots; third page would be for origin='USA', exact same arrangement of plots.
proc sort data=sashelp.cars out=cars;
by origin;
run;
ods pdf file="absolute2.pdf";
ods layout absolute ;
ods region x=.5in y=0.1in width=4in height=2.75in;
proc sgplot data=cars;
by origin;
histogram msrp;
run;
ods region x=5.1in y=.1in width=4in height=2.75in;
proc sgplot data=cars;
by origin;
histogram invoice;
run;
ods region x=.5in y=5.25in width=4in height=2.75in;
proc sgplot data=cars;
by origin;
histogram mpg_city;
run;
ods region x=5.1in y=5.25in width=4in height=2.75in;
proc sgplot data=cars;
by origin;
histogram horsepower;
run;
ods layout end;
ods pdf close;
Adding: I'm sure I could just wrap this in a macro and loop through all possible values of the BY variable and do it that way, but over the years I have found that SAS is very good at anticipating needs and creating features to do this, so really I'd like to do this without a macro if possible.
A macro is not bad, but (as requested) here is an alternative that uses a SAS feature: replaying output in a different order by using PROC DOCUMENT. Do the following:
1. Read the article "Reorder the output from a BY-group analysis in SAS", which shows how to use ODS DOCUMENT to save your output to a document.
2. Use the second part of your program to write the graphs. (You can omit the ODS LAYOUT statements)
3. Use ODS LAYOUT and PROC DOCUMENT to replay the graphs in the order you want.
I haven't used ODS LAYOUT ABSOLUTE before and I am not skilled with PDF output, but the following (which uses ODS LAYOUT GRIDDED to HTML) should get you started and demonstrate the technique:
proc sort data=sashelp.cars out=cars;
by origin;
run;
/* write all output to a document */
ods document name=doc(write); /* wrap ODS DOCUMENT around the output */
proc sgplot data=cars;
by origin;
histogram msrp;
run;
proc sgplot data=cars;
by origin;
histogram invoice;
run;
proc sgplot data=cars;
by origin;
histogram mpg_city;
run;
proc sgplot data=cars;
by origin;
histogram horsepower;
run;
ods document close; /* wrap ODS DOCUMENT around the output */
/* list the outputs */
proc document name=doc(read);
list / levels=all bygroups; /* add column for each BY group var */
run;
/* Replay the graphs in a different order */
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup1#1\SGPlot#1; /* Asia : MSRP */
replay \SGPlot#2\ByGroup1#1\SGPlot#1; /* Asia : Invoice */
replay \SGPlot#3\ByGroup1#1\SGPlot#1; /* Asia : MPG_City */
replay \SGPlot#4\ByGroup1#1\SGPlot#1; /* Asia : Horesepower */
run;quit;
ods layout end;
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup2#1\SGPlot#1; /* Europe : MSRP */
replay \SGPlot#2\ByGroup2#1\SGPlot#1; /* Europe : Invoice */
replay \SGPlot#3\ByGroup2#1\SGPlot#1; /* Europe : MPG_City */
replay \SGPlot#4\ByGroup2#1\SGPlot#1; /* Europe : Horesepower */
run; quit;
ods layout end;
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup3#1\SGPlot#1; /* USA : MSRP */
replay \SGPlot#2\ByGroup3#1\SGPlot#1; /* USA : Invoice */
replay \SGPlot#3\ByGroup3#1\SGPlot#1; /* USA : MPG_City */
replay \SGPlot#4\ByGroup3#1\SGPlot#1; /* USA : Horesepower */
run; quit;
ods layout end;
If you don't want to type out all those statements, you can use a DATA _NULL_ step and CALL EXECUTE to automate this process, as shown in "Display output conditionally with PROC DOCUMENT."
A hat tip to @WarrenKuhfeld who taught me this technique.
A macro is not bad, but (as requested) here is an alternative that uses a SAS feature: replaying output in a different order by using PROC DOCUMENT. Do the following:
1. Read the article "Reorder the output from a BY-group analysis in SAS", which shows how to use ODS DOCUMENT to save your output to a document.
2. Use the second part of your program to write the graphs. (You can omit the ODS LAYOUT statements)
3. Use ODS LAYOUT and PROC DOCUMENT to replay the graphs in the order you want.
I haven't used ODS LAYOUT ABSOLUTE before and I am not skilled with PDF output, but the following (which uses ODS LAYOUT GRIDDED to HTML) should get you started and demonstrate the technique:
proc sort data=sashelp.cars out=cars;
by origin;
run;
/* write all output to a document */
ods document name=doc(write); /* wrap ODS DOCUMENT around the output */
proc sgplot data=cars;
by origin;
histogram msrp;
run;
proc sgplot data=cars;
by origin;
histogram invoice;
run;
proc sgplot data=cars;
by origin;
histogram mpg_city;
run;
proc sgplot data=cars;
by origin;
histogram horsepower;
run;
ods document close; /* wrap ODS DOCUMENT around the output */
/* list the outputs */
proc document name=doc(read);
list / levels=all bygroups; /* add column for each BY group var */
run;
/* Replay the graphs in a different order */
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup1#1\SGPlot#1; /* Asia : MSRP */
replay \SGPlot#2\ByGroup1#1\SGPlot#1; /* Asia : Invoice */
replay \SGPlot#3\ByGroup1#1\SGPlot#1; /* Asia : MPG_City */
replay \SGPlot#4\ByGroup1#1\SGPlot#1; /* Asia : Horesepower */
run;quit;
ods layout end;
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup2#1\SGPlot#1; /* Europe : MSRP */
replay \SGPlot#2\ByGroup2#1\SGPlot#1; /* Europe : Invoice */
replay \SGPlot#3\ByGroup2#1\SGPlot#1; /* Europe : MPG_City */
replay \SGPlot#4\ByGroup2#1\SGPlot#1; /* Europe : Horesepower */
run; quit;
ods layout end;
ods layout gridded rows=2 columns=2 advance=table
row_gutter=10px column_gutter=10px
x=.5in y=0.1in width=4in height=2.75in;
proc document name=doc(read);
replay \SGPlot#1\ByGroup3#1\SGPlot#1; /* USA : MSRP */
replay \SGPlot#2\ByGroup3#1\SGPlot#1; /* USA : Invoice */
replay \SGPlot#3\ByGroup3#1\SGPlot#1; /* USA : MPG_City */
replay \SGPlot#4\ByGroup3#1\SGPlot#1; /* USA : Horesepower */
run; quit;
ods layout end;
If you don't want to type out all those statements, you can use a DATA _NULL_ step and CALL EXECUTE to automate this process, as shown in "Display output conditionally with PROC DOCUMENT."
A hat tip to @WarrenKuhfeld who taught me this technique.
Thanks, @Rick_SAS , I will give it a try and report back ... but it does seem like a macro would be useful here too, especially if I have a lot more groups of the BY variable.
Can you explain this line? Why does SGPlot#1 appear twice? In particular, what does the second SGPlot#1 indicate? Where can I find this in the docs?
replay \SGPlot#1\ByGroup3#1\SGPlot#1;
\ProcName\ByGroup\ODSName
This is a version of the familiar PATH that you see when you use ODS TRACE. See SAS Help Center: Paths and Selection
Use
ODS TRACE ON;
prior to writing to the document to see "human readable" versions.
Once I wrote the output of the PROC DOCUMENT to a data set so I could look at it, it all became very clear to me. Either a macro or CALL EXECUTE based on the data set output from PROC DOCUMENT would get the job done.
The heuristic I always use is list the contents of the document, and then use *precisely* the names that the document says are there. I never try to understand or predict them, although before I retired, I could see the logic.
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.
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.
Ready to level-up your skills? Choose your own adventure.