The SAS Output Delivery System and reporting techniques

Defining "local" ods options

Reply
Frequent Contributor
Posts: 101

Defining "local" ods options

[ Edited ]

It is possible to use PROC LUA to define SAS options "locally"; the options defined will not be inherited by the rest of the program. That is, you can set SAS options within a PROC LUA, run a SAS program that operates under those options, then run further SAS code outside of PROC LUA without having to reset the options. 

 

For example:

 

PROC LUA;
	submit;
		sas.submit[[
			options obs=1;
			PROC PRINT data=SASHELP.HEART;
			run;
		]]
	endsubmit;
run;

PROC PRINT data=SASHELP.HEART;
run;

The first PROC PRINT is run using the SAS option obs=1; i.e. it only prints the first observation of the dataset. You can see that the second PROC PRINT call, which is outside of the PROC LUA submit block, will print the entire dataset without having to specifically reset the obs option. 

 

Now, this example is a bit contrived, but the same logic can be usefully extended to more complicated scenarios, when you may want to set a number of SAS options to a particular setting for the purposes of a specific chunk of code, without having to worry about accidentally leaving that option activated for later chunks of code for which it is no longer needed. 

 

My question is: is there any way in SAS to similarly define ODS options "locally", e.g. for a specific call to PROC SGPLOT, without those options being inherited later in the program? Note that PROC LUA will not work for this:

 

PROC LUA;
submit;
sas.submit[[
ods html close;
PROC PRINT data=SASHELP.HEART;
run;
]]
endsubmit;
run; PROC PRINT data=SASHELP.HEART; run;

The ODS HTML CLOSE call is still 'active' for the rest of the program. It is still a global option, unlike the standard SAS options when called within PROC LUA.

 

Is there any other workaround? I have never seen a solution for ODS options other than to manally reset the options. ODS GRAPHICS has a RESET=ALL option that will automatically clear all ODS GRAPHICS calls, but to my knowledge there is no similar functionality for ALL ODS options. That is, ODS GRAPHICS RESET=ALL has no impact on ODS HTML, ODS LISTING, or other ODS destination options. And it can be tedious in a program designed to generate a variety of different output types to have to keep resetting options and carefully monitoring which options are active and for what reasons.

SAS Super FREQ
Posts: 9,424

Re: Defining "local" ods options

[ Edited ]
Posted in reply to RyanSimmons

Hi:
There is a huge difference between SAS system options like OBS=1 and ODS destination options like ODS HTML CLOSE;. They have different purposes and do different things and they do NOT behave the same. As an example, you don't need PROC LUA to do what you did with your PROC PRINT code. There are other approaches that work equally well and without the complicating code involving PROC LUA.

I don't expect ODS GRAPHICS to have any impact on ODS destination statements like ODS HTML or ODS RTF or ODS PDF because that is not what ODS GRAPHICS was designed to do.

If you have a use case for PROC LUA it might be better to ask your question of Tech Support. The use case you post, however, can be replicated using just regular ODS statements, as shown below.

Cynthia

 

** Turning Options on and off without PROC LUA;

options obs=1;
PROC PRINT data=SASHELP.HEART;
title 'OBS=1 step';
run;

options obs=max;
PROC PRINT data=SASHELP.HEART;
title 'obs=max step';
run;

 

** Turning destinations on and off without PROC LUA;


title;
ods html close;
ods listing;
PROC PRINT data=SASHELP.HEART;
run;

ods html path='c:\temp' file='maxprint.html';
ods pdf file='c:\temp\maxprint.pdf';
PROC PRINT data=SASHELP.HEART;
run;
ods _all_ close;

As an alternative to ODS _ALL_ CLOSE; you can also use an explicit ODS HTML CLOSE; and ODS PDF CLOSE; instead.

Frequent Contributor
Posts: 101

Re: Defining "local" ods options

Posted in reply to Cynthia_sas

I understand that there are other ways other than PROC LUA to get the effect with regular SAS system options. I only posted the PROC PRINT as a simple replicable example, because my real example is more complicated with a more elaborate bit of code embedded in PROC LUA, and with a variety of different options that I am switching on and off at multiple points through the program.

 

The reason I brought up PROC LUA is specifically because the way in which PROC LUA defines these options locally, without having to manually reset them for the second PROC PRINT call as in your example. That's the point; in a long, complicated SAS program it can be cumbersome to have to keep switching global options on and off, especially if it is an organic program that is consistently being modified and updated (potentially by different coders). Defining such options locally is less cumbersome and reduces the chance of error. PROC LUA allows you to do this (and it has some other nice functionality as well), but only for SAS options. There are other ways you can achieve the same de facto behavior; for example, using %include on a list of separate SAS programs that run under different option configurations and then resetting those options at the end of each program. However, there are advantages to the PROC LUA approach. 

 

My question is specifically whether there is a way to get similar behavior (not necessarily in PROC LUA, but through other means) with ODS options, without having to clumsily reset the options as you do in your example. For example, one could do the %include example I mentioned above, but this has a disadvantage in that for suitably complicated projects you end up splitting a single SAS program into dozens of separate programs, which has some downsides. I was curious whether anyone knew of a clever way to get SAS to define ODS options in a local manner.

 

I am grateful for the ODS _ALL_ CLOSE, however, I wasn't aware of that functionality. That can simplify the problem a bit. If you close all ODS destinations, and reopen them, are the settings for each ODS destination reset to their defaults? I still see this as a bit clumsier than some way of defining ODS options in a completely local manner, but it's an improvement over calling each ODS destination separately. 

Super User
Posts: 13,913

Re: Defining "local" ods options

Posted in reply to RyanSimmons

The report procedures such as Print, Report and Tabulate allow setting style overrides for specific purposes. Perhaps that is what you want.

For example

PROC PRINT data=SASHELP.HEART;
var status /style=[background=red];
var deathcause /style=[color=blue];
var _numeric_ / style=[background=green color=white]; run;

changes the background of the status variable to red, the text of the deathcause to blue and the background for all numeric variables to be green and the text color white. Text properties like font and weight (bold), cell properties such as width, background, cell boundary color and thickness and other properties may be set this way.

 

Frequent Contributor
Posts: 101

Re: Defining "local" ods options

I appreciate the response. I know that those procedures offer a lot of style functionality. In fact, in the REAL program that inspired this question (which is a lot more complex than the simple example in my OP), I have multiple PROC REPORT calls that each have a variety of different and customized style options that I configure the way you show. The problem is that I am also outputting the tables from these calls to different ODS destinations (e.g. ODS RTF and ODS PDF), and the same program also relies on multiple calls to ODS GRAPHICS, ODS LISTING, ODS HTML, etc. Most of these ODS output options cannot be defined within procedures, due to the need to output them from the SAS environment. 

SAS Super FREQ
Posts: 9,424

Re: Defining "local" ods options

Posted in reply to RyanSimmons

Hi:

  For simple output, you can generally just use multiple ODS destinations statements around one PROC REPORT step.

ods rtf file='c:\temp\report1.rtf';
ods pdf file='c:\temp\report1.pdf';
ods html file='c:\temp\report1.html';
  proc report data=sashelp.class
     style(header)={background=purple color=white};
  run;
ods rtf close;
ods pdf close;
ods html close;

 

  However, if you want to some kind of HTML specific control with ODS HTML and then the same report, but not using HTML controls, then you might need separate invocations for every destination -- or one invocation for ODS HTML (such as below, where I use specific HTML tags to make changes for some reason) and then a separate invocation for ODS RTF and ODS PDF. In the example below, I want to use the HTMLID= style attribute to put an ID= option (an HTML attribute frequently used when also using JavaScript) into the HTML file, but I don't want to use HTMLID with the code for RTF and HTML. Then I use separate invocations. Notice that in the HTML report I also use explicit HTML tags to alter the title text:


ods html file='c:\temp\report2.html';
ods escapechar='^';
  proc report data=sashelp.class(obs=3)
     style(header)={background=purple color=white};
	 title c=red '^{style[protectspecialchars=off]<div><h1>Report2 -- HTML</h1></div>}';
	 column name age height weight;
	 define name / 'Name'
            style(header)={htmlid='wombat'};
	 define age / 'Age'
            style(header)={htmlid='koala'};
	 define height / 'Height';
	 define weight / 'Weight';
  run;
ods html close;

ods rtf file='c:\temp\report2.rtf';
ods pdf file='c:\temp\report2.pdf';
title c=black h=16pt 'Report 2 -- RTF and PDF';
  proc report data=sashelp.class(obs=3)
     style(header)={background=gray33 color=white};
	 column name age height weight;
	 define name / 'Name';
	 define age / 'Age';
	 define height / 'Height';
	 define weight / 'Weight';
   run;

ods rtf close;
ods pdf close;

 If you do a View Source on the HTML File, you'll see where the ID= information was inserted into the HTML output file:

report2_html_output.png

 

  Another way to do the second report would be to use a SAS Macro program to write the changeable bits of code conditionally based on the destination. But that's a different solution.

 

Cynthia

Ask a Question
Discussion stats
  • 5 replies
  • 103 views
  • 1 like
  • 3 in conversation