BookmarkSubscribeRSS Feed
ChrisNZ
Tourmaline | Level 20
Hi all,

By default, each procedure output in a PDF table of content has a parent (which by default is the procedure name). So:

ods pdf ;
proc gchart data=sashelp.class ;
vbar age;run;
proc gplot data=sashelp.class ;
plot weight*height;run;
quit;
ods pdf close;

yields TOC:
+ The Gchart procedure
- VBAR chart of Age
+ The Gplot procedure
- plot of Weight * Height

I want the following TOC:
+ Class Data
- Age Distribution
- Weight vs Height

I looked at proc document to massage the output via a convoluted process (see
http://www.dataceutics.com/papers/CC10.pdf ).

The setlabel statement allows me to change the labels (is there a better way like content= ?), but can't get a single menu parent for both outputs as the document's Dir entries seem to contain both the parent and the child.

Is this possible? In 9.1.3?

Thanks.
18 REPLIES 18
Andre
Obsidian | Level 7
x deleted


Message was edited by: Andre
Andre
Obsidian | Level 7
Chris easy
look at this code

[pre]
title ;
goptions device=pdfc papersize=A4 orientation=portrait;
proc gchart data=sashelp.class ;
vbar age/name="Age";run;
proc gplot data=sashelp.class ;
plot weight*height/name="WH";run;
quit;


proc greplay tc=work.templt nofs;
tdef un des="Age Distribution"
1/llx=0 lly=0 ulx=0 uly=100 urx=100 ury=100 lrx=100 lry=0 color=blue;
template un;
tdef deux des="Weight v Height"
1/def;
template deux;
quit;


ods listing close;
ods pdf file="d:\tabsas\chris.pdf";
ods proclabel="Classdata";ods noptitle;
proc greplay igout=work.gseg tc=work.templt nofs;
tc work.templt;
template un;
treplay 1:age;
run;
template deux;
treplay 1:wh;
run;
quit;
ods pdf close;ods listing;
[/pre]
ChrisNZ
Tourmaline | Level 20
Thanks a lot for this André.

Any chance of doing the same for text outputs and adding a third menu entry "List of pupils" created by running

proc print data=sashelp.class;run;

Or even better adding a third menu entry "List of pupils aged 12 and 13" created by running

proc print data=sashelp.class; where age=12;run;
proc print data=sashelp.class; where age=13;run;

Thanks again, Chris
Andre
Obsidian | Level 7
x
Andre
Obsidian | Level 7
Chris
I recognize it was limited to graphics.
Then if ods document in 9.2.2 is not permitting this easy : reorganizing of items
i would say ( like for a complication inside tagset.excelxp )
it is better and easier to apply this in Acrobat or ExpertPDf ...
as it is only copy pasting

Andre
Cynthia_sas
SAS Super FREQ
Hi:
ODS PROCLABEL should work to label the highest level node in any procedure -- that high level node will be used in any TOC.

You can also use CONTENTS= with PROC PRINT, PROC REPORT or PROC TABULATE in order to label or suppress the second level node created by these procedures. See the program below (only shows PROCLABEL and CONTENTS= behavior with PROC PRINT).

cynthia
[pre]
ods noptitle;

ods pdf file='toc_proclabel.pdf';

** use ProcLabel and Contents=;
ods proclabel = 'Top Proc Print';
proc print data=sashelp.class contents='second level TOC';
where age ge 14;
run;

** blank proclabel and will get "The Print Procedure" as top node;
ods proclabel = '';
proc print data=sashelp.class contents='second level TOC';
where age = 12;
run;

** blank contents= suppresses second-level node for proc print;
ods proclabel = 'Using ProcLabel';
proc print data=sashelp.class contents='';
where age = 12;
run;

ods pdf close;


[/pre]
Andre
Obsidian | Level 7
Cynthia,
It is not so easy

The main ask of Chris (from NZ !!!)
was multiple children under one head item

in your solution, the proc print is appearing again at the same level than classdata
see firtst post description

avoiding the proc print did not work with ods noptitle.
here some basic code to retest eventually:


[pre]
title ;
goptions device=pdfc papersize=A4 orientation=portrait;
proc gchart data=sashelp.class ;
vbar age/name="Age";run;
proc gplot data=sashelp.class ;
plot weight*height/name="WH";run;
quit;

proc greplay tc=work.templt nofs;
tdef un des="Age Distribution"
1/llx=0 lly=0 ulx=0 uly=100 urx=100 ury=100 lrx=100 lry=0 color=blue;
template un;
tdef deux des="Weight v Height"
1/def;
template deux;
quit;


ods listing close;
ods pdf file="d:\tabsas\chris.pdf";
ods noptitle;
ods proclabel="Classdata";
proc greplay igout=work.gseg tc=work.templt nofs;
tc work.templt;
template un;
treplay 1:age;
run;
template deux;
treplay 1:wh;
run;
quit;
ods noptitle;
ods proclabel = '';
proc print data=sashelp.class contents='Age 14 report';
where age ge 14;
run;
ods noptitle;
** blank proclabel and will get "The Print Procedure" as top node;
ods proclabel = '';
proc print data=sashelp.class contents='Age 12 report';
where age = 12;
run;
ods pdf close;ods listing;
[/pre]
Cynthia_sas
SAS Super FREQ
Hi:
In this ODS DOCUMENT example, I have one high level node with 6 child nodes underneath it. The child nodes were created with PROC TABULATE and PROC UNIVARIATE. With ODS DOCUMENT, I can create the node/folder structure and then copy output objects into the new structure.

I know there are some limitations to ODS DOCUMENT, however, quite a bit of customization is possible with ODS DOCUMENT -- including rearranging and replaying graphical output objects as well as tabular output objects:
http://support.sas.com/documentation/cdl/en/odsug/61723/HTML/default/a002291458.htm
http://support.sas.com/forums/thread.jspa?threadID=807
http://support.sas.com/documentation/cdl/en/graphref/61884/HTML/default/a003287577.htm
http://support.sas.com/resources/papers/sgf09/318-2009.pdf

cynthia
[pre]
*** 1) Make ODS Document;
title;
footnote;
options nodate nonumber center;
ods listing close;

proc sort data=sashelp.prdsale out=prdsale;
by Country;
run;

ods document name=work.prddoc(write);
proc tabulate data=prdsale;
by Country;
var predict;
class prodtype;
table prodtype all,
predict*(min mean max);
run;

ods select ExtremeObs;
proc univariate data=prdsale;
by Country;
var actual;
run;
ods document close;

** 2) rearrange ODS document by creating a new ODS document that will;
** have 1 high level node and many children underneath the top node;

ods listing;
proc document name=work.neworder(write);
make ONETOP;
run;

dir ^^;
dir ONETOP;
dir;
copy \work.prddoc\Tabulate#1\ByGroup1#1\Report#1\Table#1 to ^;
copy \work.prddoc\Univariate#1\ByGroup1#1\ACTUAL#1\ExtremeObs#1
to ^ ;
copy \work.prddoc\Tabulate#1\ByGroup2#1\Report#1\Table#1 to ^ ;
copy \work.prddoc\Univariate#1\ByGroup2#1\ACTUAL#1\ExtremeObs#1
to ^ ;
copy \work.prddoc\Tabulate#1\ByGroup3#1\Report#1\Table#1 to ^ ;
copy \work.prddoc\Univariate#1\ByGroup3#1\ACTUAL#1\ExtremeObs#1
to ^ ;
run;
quit;

** 3) after rearranging, look to see what new object names are;
proc document name=work.neworder;
list / levels=all;
run;
quit;

** 4) now use new object names to set labels;
ods listing;
proc document name=work.neworder(update);
dir ^^;
dir ONETOP;
setlabel \ONETOP#1 'Top Level Node';
setlabel \ONETOP#1\Table#1 'Canada Tabulate';
setlabel \ONETOP#1\ExtremeObs#1 'Canada Extreme Obs';
setlabel \ONETOP#1\Table#2 'Germany Tabulate';
setlabel \ONETOP#1\ExtremeObs#2 'Germany Extreme Obs';
setlabel \ONETOP#1\Table#3 'USA Tabulate';
setlabel \ONETOP#1\ExtremeObs#2 'USA Extreme Obs';

run;
quit;

** 5) Now replay the new document to ODS PDF;
ods listing close;
ods pdf file='c:\temp\replay_neworder.pdf';
proc document name=work.neworder;
replay;
run;
quit;

ods _all_ close;
[/pre]
ChrisNZ
Tourmaline | Level 20
Wow, thanks for this Cynthia (and André). This looks really good!

Now I played (quite) a bit (I still have to soak in Cynthia's paper's goodness) with this and found the following stumbling blocks:

- How to automate the retrieval of the object names, i.e can proc document name; list / levels=all; be sent to macro variables or tables so the process can be run in batch?

- I replaced proc univariate with proc gchart and my labels are not respected.

- How to get one country's information together with no page break, i.e table and chart on the same page?

[pre]

*** 1) Make ODS Document;
title;
footnote;
options nodate nonumber center;
ods listing close;

proc sort data=sashelp.prdsale out=prdsale;
by Country;
run;

ods document name=work.prddoc(write);
proc tabulate data=prdsale;
by Country;
var predict;
class prodtype;
table prodtype all,
predict*(min mean max);
run;

ods select Gchart;
goption dev=actximg xpixels=2000 ypixels=1200;
proc gchart data=prdsale;
by Country;
vbar prodtype/sumvar=actual des="desc";
run;
quit;
ods document close;

**Look to see what object names are;
ods listing;
proc document name=work.prddoc;
list/levels=all;
run;
quit;

**2)rearrange ODS document by creating a new ODS document that will;
** have 1 high level node and many children underneath the top node;
proc document name=work.neworder(write);
make ONETOP;
run;
dir ^^;
dir ONETOP;
dir;
copy \work.prddoc\Tabulate#1\ByGroup1#1\Report#1\Table#1 to ^;
copy \work.prddoc\Gchart#1\ByGroup1#1\Gchart#1 to ^;
copy \work.prddoc\Tabulate#1\ByGroup2#1\Report#1\Table#1 to ^;
copy \work.prddoc\Gchart#1\ByGroup2#1\Gchart#1 to ^;
copy \work.prddoc\Tabulate#1\ByGroup3#1\Report#1\Table#1 to ^;
copy \work.prddoc\Gchart#1\ByGroup3#1\Gchart#1 to ^;
run;
quit;


**3)after rearranging,look to see what new object names are;
** 3) after rearranging, look to see what new object names are;
proc document name=work.neworder;
list / levels=all;
run;
quit;

** 4) now use new object names to set labels;
ods listing;
proc document name=work.neworder(update);
dir ^^;
dir ONETOP;
setlabel \ONETOP#1 'Top Level Node';

obpage \ONETOP#1\Table#1 /delete;
setlabel \ONETOP#1\Table#1 'Canada Tabulate';
setlabel \ONETOP#1\Gchart#1 'Canada Chart';
setlabel \ONETOP#1\Table#2 'Germany Tabulate';
setlabel \ONETOP#1\Gchart#2 'Germany Chart';
setlabel \ONETOP#1\Table#2 'USA Tabulate';
setlabel \ONETOP#1\Gchart#2 'USA Chart';
run;
quit;

** 5) Now replay the new document to ODS PDF;
ods listing close;
ods pdf file='c:\temp\replay_neworder.pdf';
proc document name=work.neworder;
replay;
run;
quit;

ods _all_ close;
David_SAS
SAS Employee
How to automate the retrieval of the object names, i.e can

[pre]proc document name; list / levels=all;
[/pre]
be sent to macro variables or tables so the process can be run in batch?

Chris, try this:

[pre]
proc document name=temp(write);
import data=sashelp.class to ^;
run;
ods output properties=properties;
list/levels=all;
run;
quit;
proc print data=properties;
run;
[/pre]

-- David Kelley, SAS
Cynthia_sas
SAS Super FREQ
Hi, Chris:

Here's the location of the tip sheet for ODS DOCUMENT:
http://support.sas.com/rnd/base/ods/scratch/document-tips.pdf

Also, in addition to David's suggestion, for the automation of object names, your other questions were:

- How to get one country's together information with no page break, i.e table and chart on the same page?
The OBPAGE .../DELETE doesn't belong to the table object. Page breaks come -before- procedures -- so the page break "belongs" to the graph object. Therefore, the OBPAGE statement should reference the GRAPH object to delete the page break -- not the TABLE object. You have:

obpage \ONETOP#1\Table#1 /delete;
and it should be:
[pre]
obpage \ONETOP#1\GCHART#1 /delete;
obpage \ONETOP#1\GCHART#2 /delete;
obpage \ONETOP#1\GCHART#3 /delete;
[/pre]

(Keep in mind that you might need to adjust the original creation size of the image with SAS/GRAPH options if you find that it doesn't fit here.)


- I replaced proc univariate with proc gchart and my labels are not respected.
Yes, I see that. It looks like the DESC= option in GCHART is being used for the PDF TOC. But, in the Document Window, I see that the SETLABEL statement -DID- change the description of the graph object. So, somehow the SETLABEL is working to change what you can see in the document window, but apparently, for the graph object, the description from the document store doesn't seem to be used for the ODS PDF TOC. This behavior may be related to this Tech Support note:
http://support.sas.com/kb/11/888.html

But, the note doesn't mention anything about ODS DOCUMENT. This last question might be a question for Tech Support. I ran your code (with the OBPAGE change) and only see "desc" in the PDF TOC for each chart - -not the SETLABEL text. I'm not sure how to get around this. Since you're creating the graphs with BY group processing, it may be possible to somehow use BY group labels, but I haven't played much with that new feature of ODS DOCUMENT and 9.2.

cynthia
ChrisNZ
Tourmaline | Level 20
Ahah, almost all working now! 🙂

I wouldn't call this easy or intuitive or obvious, but at least it is there. It looks like mastering proc template and proc document to make best use of ODS would be a full time job. Thanks all for all the information and tips so far.

Here is the final code of the mockup fyi.

The charts seem not only to ignore the ToC labels, but also the page breaks in 9.1.3 (plus they are not centered, but I guess they were never told to be). Is it possible to change report justification with proc document?

Is it the same in 9.2 Cynthia?


[pre]

*** 1) Make ODS Document;
title;
footnote;
options nodate nonumber center;
ods listing close;

proc sort data=SASHELP.PRDSALE out=PRDSALE;
by COUNTRY;
run;
proc sql noprint;
select unique COUNTRY into :countries separated by '|'
from PRDSALE
order by COUNTRY;
quit;

ods document name=WORK.PRDDOC(write);
proc tabulate data=PRDSALE;
by COUNTRY;
var PREDICT;
class PRODTYPE;
table PRODTYPE all,
PREDICT*(min mean max);
run;

/* ods select Gchart;
goption dev=actximg xpixels=1000 ypixels=500;
proc gchart data=PRDSALE;
by COUNTRY;
vbar PRODTYPE/sumvar=ACTUAL des='';
run;
quit;*/

ods select ExtremeObs;
proc univariate data=prdsale;
by Country;
var actual;
run;
ods document close;

** fetch ODS document object names and arrange report order;
ods listing;
proc document name=WORK.PRDDOC;
ods output properties=PROPERTIES;
list/levels=all;
run;
ods output close;
quit;
proc sql;
create table REPORTS as
select *
,scan(PATH, 2,'\') as BYGROUP
,monotonic() as REPORT_ORDER
,min(calculated REPORT_ORDER) = calculated REPORT_ORDER as PAGE_BREAK
from PROPERTIES
where TYPE ne 'Dir'
group by BYGROUP
order by BYGROUP, REPORT_ORDER;
quit;


** create a new ODS document with 1 high level node and many children;
proc document name=WORK.NEWORDER(write);
make ONETOP;
run;
dir ^^;
dir ONETOP;
dir;
%macro loop_entries;
%local dsid rc ;
%let dsid=%sysfunc(open(REPORTS));
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc=0);
copy \work.prddoc%sysfunc(getvarc(&dsid,1)) to ^;
%let rc=%sysfunc(fetch(&dsid));
%end;
%let rc=%sysfunc(close(&dsid));
%mend;
%loop_entries
run;
quit;


** fetch new ODS document object names and add page break information;
proc document name=WORK.NEWORDER;
ods output properties=PROPERTIES2;
list/levels=all;
run;
ods output close;
quit;
data PROPERTIES2;
if _N_=1 then do;
set PROPERTIES2;
output;
end;
merge PROPERTIES2(firstobs=2)
REPORTS(keep=PAGE_BREAK);
output;
run;

** use new object names to set labels and page breaks;
ods listing;
proc document name=work.neworder(update); option mprint;
dir ^^;
dir ONETOP;
setlabel \ONETOP#1 'Top Level Node';

%macro loop_entries;
%local dsid rc country path report pb;
%let dsid=%sysfunc(open(PROPERTIES2(where=(TYPE ne 'Dir'))));
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc=0);
%let path=%sysfunc(getvarc(&dsid,1));
%let country=%substr(&path,%length(&path),1);
%let report=%substr(&path,11,%length(&path)-12);
setlabel &path "&report for %scan(&countries,&country,|)";
%let pb=%sysfunc(getvarn(&dsid,3));
%if &pb=0 %then
obpage &path /delete;;
%let rc=%sysfunc(fetch(&dsid));
%end;
%let rc=%sysfunc(close(&dsid));
%mend;
%loop_entries
run;
quit;

** 5) Now replay the new document to ODS PDF;
ods listing close;
ods pdf file='c:\temp\replay_neworder.pdf';
ods escapechar='^';
footnote "Page ^{thispage} of ^{lastpage}";
proc document name=work.neworder;
replay;
run;
quit;

ods _all_ close;



Message was edited by: Chris@NewZealand

Add question: Is it possible to change report justification with proc document?
Cynthia_sas
SAS Super FREQ
Hi:
In SAS 9.2, in my output, the graphs were centered under the table on each page. But, if I set
OPTIONS NOCENTER;
before replaying my document store, the tabular output and by lines are left justified, but it appears that the justification of the graph object doesn't change. So I do not think you can change the justification of the graph object once it is in the document store.

As I said, in SAS 9.2, the DOCUMENT WINDOW shows the result of the SETLABEL statement -- so, from that standpoint, SETLABEL is working. However, the PDF TOC does not use SETLABEL for graph objects. But, since you know that the DESC= value is used for the PDF TOC, you could run each BY group separately (within a macro program) and set the DESC= value for each BY group separately in GCHART to be the label that you want when the PDF TOC is built. This does put more of a burden on you to alter the SAS/GRAPH program and that will possibly alter the names of the graph objects that are created. But since you know that DESC= is used in the PDF TOC, you can use this fact to touch the PDF TOC. (According to one of the DOCUMENT developers, DESC= is part of the graph's data and is inaccessible to PROC DOCUMENT).

I'm not sure what your OBPAGE is doing, Mine deleted the page break -BEFORE- each SAS/GRAPH output. I no longer have SAS 9.1.3 to test with, so you would have to check with Tech Support about OBPAGE and behavior in 9.1.3.

cynthia
ChrisNZ
Tourmaline | Level 20
Hi Cynthia,,

When I run the following in 9.1.3, I get a PDF with:
- 1st page with centered table, then left-justified chart
- 2nd page with centered table
when I would expect all 3 outputs centered on one page.

Is the behaviour back to normal in 9.2?

In opposition to the PDF engine, the HTML engine does center the graph, though it doesn't respect the graph ToC label either.

Also, centering cannot be modified as part of the formatting in proc document, it must be chosen at report-producing time, right?

Thanks, Chris

[pre]



*** 1) Make ODS Document;
title;
footnote;
options nodate nonumber center;
ods listing close;

ods document name=WORK.PRDDOC(write);
proc tabulate data=SASHELP.PRDSALE ;
var PREDICT;
class PRODTYPE;
table PRODTYPE all, PREDICT*(min mean max);
run;

ods select Gchart;
goption dev=actximg xpixels=700 ypixels=500;
proc gchart data=SASHELP.PRDSALE ;
vbar PRODTYPE/sumvar=ACTUAL des='Chart';
run;
quit;

ods select ExtremeObs;
proc univariate data=SASHELP.PRDSALE ;
var actual;
run;
ods document close;

** 2) Reorganise ODS Document;
proc document name=WORK.PRDDOC(update);
make \ONETOP;
dir \ONETOP;
move \Tabulate#1\Report#1\Table#1 to ^;
move \Gchart#1\Gchart#1 to ^;
move \Univariate#1\ACTUAL#1\ExtremeObs#1 to ^;
delete \Tabulate#1 ;
delete \Gchart#1 ;
delete \Univariate#1 ;
obpage \ONETOP#1\Table#1 /delete;
obpage \ONETOP#1\Gchart#1 /delete;
obpage \ONETOP#1\ExtremeObs#1 /delete;
setlabel \ONETOP#1 'Sales';
setlabel \ONETOP#1\ExtremeObs#1 'Extremes';
setlabel \ONETOP#1\Gchart#1 'Histogram';
run;
quit;

** 3) Replay ODS document;
ods html body='f:\temp\replay_neworder.htm'
page='f:\temp\bod.htm' frame='f:\temp\fr.htm'
contents='f:\temp\co.htm';
ods pdf file='f:\temp\replay_neworder.pdf';
ods escapechar='^';
footnote "Page ^{thispage} of ^{lastpage}";
proc document name=work.prddoc;
replay;
run;
quit;

ods _all_ close;

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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