In having come this far in Stat 483, you should proudly know that you've completed 23 lessons and are about to complete your 24th lesson! Every time we've executed a program in the preceding 23 lessons, we've immediately looked at our output window in SAS to find the listing output that SAS creates by default. In this lesson, we'll learn how to use the SAS System's Output Delivery System (ODS) to create other forms of output, such as HTML output that can be viewed by your web browser, PDF files that are formatted for high-resolution printers, and RTF files that can be easily imported into Microsoft Word. Along the way, we'll also learn how to modify the appearance of the output that is obtained by default from any procedure. Finally, we'll learn how to use the Output Delivery System to create SAS data sets instead of the default output from various procedures.
Upon completing this lesson, you should be able to do the following:
You might be getting the impression that by learning about the Output Delivery System (ODS) in this lesson that it will be the first time we use it. In reality, SAS has been using it behind the scenes all along to create the listing output that the procedures we've used generates by default. All we want to do in this lesson is learn how ODS works and how to change the default settings so that we can get the output that we want rather than the output that SAS wants.
So how does ODS work? Whenever you submit a program that creates output, ODS does the following:
1) ODS creates your output in the form of output objects. Each output object is comprised of two components. The data component contains the results — think numbers — of a procedure or a DATA step. The table definition tells SAS how to render the results — think structure. For example, suppose we executed the FREQ procedure so that it created the following output:
SAS actually creates this piece of output from its two parts, the table definition:
and the data component:
2) Once SAS creates all of the output objects from an executed program, it then just needs to figure out where to send the objects. It's actually pretty easy... SAS sends the output to whatever ODS destination(s) you tell SAS to send it. And when doing so, SAS sends the output in the format specified by the destination. This is where ODS is really powerful and therefore really neat! Besides the listing output generated by SAS procedures by default, and among others, you can send your output to...
|this destination ...||to produce...|
|HTML||output that is formatted in HyperText Markup Language (HTML), and therefore viewable by web browsers|
|Output||SAS data sets|
|Printer Family||output that is formatted for a high-resolution printer, such as Post Script (PS), Portable Document Format (PDF), and Printer Control Language (PCL) files|
|RTF||rich text format output for use with Microsoft Word|
In the next section, we'll learn how to tell SAS where to send the output it generates by "opening" and "closing" these various ODS destinations.
If you are perfectly content with your output being sent to the output window in the default listing format, then you don't have tell SAS anything at all. That's because the Listing destination is open by default. On the other hand, if you want to tell SAS to send your output to another ODS destination, HTML say, then you have to open the destination before the SAS code that generates your output.
To open a destination, you simply submit the following ODS statement:
where open-destination is a keyword (as well as any required options for the destination) that tells SAS where you want to send your output. In this lesson, we'll focus only on the most commonly used keyword destinations: Listing, HTML, RTF, and PDF.
After the SAS code that generates your output, you have to tell SAS to close the destination so that you can access your output. To close a destination, you simply submit the following ODS statement:
ODS close-destination CLOSE;
where close-destination is the same keyword as the open-destination.
In theory, you can submit ODS statements in any order, depending on whether you need to open or close an ODS destination. In practice, however, most ODS destinations are closed by default, so that you open them at the beginning of your program and close them at the end. The exception is the Listing destination, which is open by default. Let's take a look at an example.
Example 24.1. You might recall that the SAS data set called penngolf  contains information, such as the total yardage and par, of eleven golf courses in Pennsylvania. The following program opens the HTML destination so that a subset of the penngolf data set can be printed in HTML format as well as the default Listing format:
This code illustrates the standard ODS practice mentioned earlier... open your destinations at the top of your program, and close them at the bottom. Here, the first ODS statement tells SAS to open the HTML destination and to save the HTML output generated by the PRINT procedure that follows to the specified file name. The second ODS statement tells SAS to close the HTML destination so that we can access the created HTML file.
Download and save the penngolf  (right-click!) data set to a convenient location on your computer. Then, launch the SAS code, and edit the LIBNAME statement so that it reflects the location in which you saved the data set. Also, edit the first ODS statement's FILE= option so that it reflects the location and name of the file where you want the resulting HTML output to be sent. (Make sure that you give your filename the standard .html extension.) Finally, run the SAS program. In doing so, you should note that SAS generates two pieces of output. The default listing output is displayed, as always, in the output window:
and the specifically requested HTML output:
is displayed in the Results Viewer. If the HTML output doesn't pop up on its own, you can see it by clicking on the Results Viewer button:
along the bottom of your SAS window.
Even though we didn't specifically request the listing output in the above program, we got it anyway because, as mentioned earlier, the listing output is open by default. We have to specifically tell SAS to close the Listing destination, if we don't want SAS to generate listing output:
ODS LISTING CLOSE;
In general, because open destinations use system resources, it's a good idea to close the Listing destination at the beginning of your program if you don't want to produce listing output. Upon submitting the above ODS statement, the Listing destination remains closed until we end our current SAS session or until we re-open the destination. It's good programming practice to re-set ODS to the default Listing destination at the end of your programs:
Let's take a look at an example.
Example 24.2. The following program is identical to the previous program, except here the Listing destination is closed at the beginning of the program, and re-opened again at the end:
The first ODS statement tells SAS to close the Listing destination. The second ODS statement tells SAS to open the HTML destination and to save the HTML output generated by the PRINT procedure to the specified file name. The third ODS statement tells SAS to close the HTML destination so that we can access the created HTML file. And, the last ODS statement tells SAS to re-open the Listing destination.
Launch the SAS program, and edit the second ODS statement's FILE= option so that it reflects the location and name of the file where you want the resulting HTML output to be sent. (Again, make sure that you give your filename the standard .html extension.) Before you execute the program, you might want to clear your SAS output window of all previously generated listing output by activating the output window by clicking on it, selecting the Edit menu, and selecting Clear All. Now, run the SAS program to convince yourself that this time SAS generates just HTML output. That is, note, in particular, that the output window contains no listing output.
As we saw above in Example 24.1, one of the primary features of ODS is that you can simultaneously produce output in multiple formats by having more than one ODS destination open at a time. When you have more than one open ODS destination you can use the keyword shortcut _ALL_ to close all of the destinations concurrently. That is, the following statement:
ODS _ALL_ CLOSE;
closes all currently open destinations at once.
As we saw in the last section, in order to create HTML output, you simply open the HTML destination using the HTML keyword in the ODS statement. Simple enough! In this section, we'll extend what we learned there by:
Example 24.3. The following program uses the penngolf  data set to simultaneously create HTML output from the PRINT and REPORT procedures:
Before launching and running the program, let's take a quick look at the code to make sure we know what it's doing:
Now, go ahead and launch the SAS program. Again, you'll have to edit the first ODS HTML statement to reflect where you would like your HTML file stored. Then, run the SAS program, and review the output as it appears in the SAS Results Viewer. You should first see the output from the PRINT procedure:
and then the output from the REPORT procedure:
You should also note that SAS saves the generated HTML output in the file specified in the first ODS HTML statement. To see the file, go to the folder in which you told SAS to store the HTML file. Here's what my folder looks like after running the program:
When we run the code in Example 24.2, SAS creates the golf.html file. And, when we run the above code from Example 24.3, SAS creates the golf2.html file. For some reason, I have trouble opening either file with Mozilla Firefox, but am able to do so with Internet Explorer. To do so, right-click on the golf2.html file (or whatever you've called the file), and select Open With... Internet Explorer. You should see the same output that SAS displays in the SAS Results Viewer. It is this physical golf2.html file though that you could easily post to a public web site or e-mail to someone else.
Example 24.4. When you have a program that creates many pages of output, you might find it useful for SAS to create a table of contents for the output. The following program is identical to the previous program, except the first ODS HTML statement has been modified to tell SAS to create a table of contents for the output that SAS generates:
Since the code is almost identical to the previous program, the only code that needs explanation this time around is that first ODS HTML statement. As before, the BODY= option tells SAS where we want to store the HTML output generated from the subsequent PRINT and REPORT procedures. Not surprisingly, the CONTENTS= option tells SAS where we want to store the table of contents. The FRAME= option gives SAS a place to store the HTML page containing the integrated table of contents and body file. Note that the FRAME= option and the CONTENTS= option go together. That is, if you include the FRAME= option in your ODS HTML statement, you must also include the CONTENTS= option.
Okay, go ahead and launch the SAS program, and edit the first ODS HTML statement to reflect where you would like your HTML files stored. Then, run the SAS program, and review the output as it appears in the SAS Results Viewer. Oooops! The output looks no different than the output from the previous example. The only thing you should see in the SAS Results Viewer is the body file golf3.html. To see the results along with the table of contents and table of pages, you have to open the frame file golf3frame.html (or whatever you called the frame file). To do so, go to the folder in which you told SAS to store the HTML files. Here's what my folder looks like now after running the program:
If you right-click on the golf3.html file (or whatever you called the body file), and select Open With... Internet Explorer, you should again see the results of the PRINT and REPORT procedures. If you right-click on the golf3toc.html file (or whatever you called the table of contents file), and select Open With... Internet Explorer, you should see the table of contents with links to each procedure output in the body file. It's the frame file that you should find the most useful in this situation. If you right-click on the golf3frame.html file (or whatever you called the frame file), and select Open With... Internet Explorer, you should see the table of contents and the body file integrated into one page. If you click on the links in the table of contents, you can see how SAS moves you to the relevant output.
If you're not familiar with at least the concept of Hypertext Markup Language (HTML), then you would find this topic quite challenging. In short, HTML is the behind-the-scenes language that tells your web browser what to display. If you go to any web page, and view the page source, you'll see the HTML code that displays the web page that you are viewing. (Using Mozilla's Firefox browser, you can view the page source by selecting View and then Page Source. Using an Internet Explorer browser, you can view the page source by selecting Page and then View Source). This topic concerns the pathnames that SAS creates when it creates HTML output files for you. If the pathnames aren't well specified, then you would have trouble sharing your SAS-created HTML output files with others.
It is not fair to expect students who are not already familiar with HTML to fully understand this topic. On the other hand, students who are planning on taking the SAS 9 Base Programmer Certification Exam might find a question or two concerning this topic on the exam. For those students, I am providing for their reference a copy of five pages  from the SAS Certification Prep Guide that concerns using options to specify links and paths. For the purpose of this course, I can not, and therefore will not, expect students to master this topic. Of course, if you are planning on taking the exam, and you have questions concerning these options, please don't hesitate to ask them.
Thus far, we have used ODS statements to tell SAS to create HTML output. As mentioned earlier, we can also use ODS statements to tell SAS to create other kinds of output. In this section, we'll take a look at two examples in which we tell SAS to make different kinds of output. In the first example, we make RTF output that can be easily copied into Microsoft Word. In the second example, we make PDF output that can be then sent to a high-resolution printer.
Example 24.5. The following program tells SAS to print a subset of the penngolf  data set and when doing so to send the output to an RTF destination:
As you can see, to tell SAS to send output to the RTF destination, we simply use the RTF keyword in an ODS statement. By default, titles and footnotes are put into Word headers and footers. The BODYTITLE option in the ODS RTF statement tells SAS to instead put titles and footnotes in the main part of the RTF document. Note that the second-to-last ODS statement tells SAS to close the RTF destination, while the last ODS statement tells SAS again to re-open the Listing destination.
Launch the SAS program. Then, edit the ODS RTF statement to reflect where you would like your RTF file stored. Then, run the SAS program. When you do so, a pop-up window that looks something like this should appear:
If you select Open, you'll see the contents of the golf5.rtf file (or whatever you called it) in the SAS Results Viewer. You can then easily copy the contents of the Results Viewer into a Word document. Alternatively, you can go to the folder in which you told SAS to store your RTF file:
and double-click on the file to open it in Word.
Example 12.6. The following program does exactly the same thing as the previous program, except the output here is sent to a PDF file:
Pretty straightforward... as you can see, to tell SAS to send output to the PDF destination, we simply use the PDF keyword in an ODS statement. Note again that the second-to-last ODS statement tells SAS to close the PDF destination, while the last ODS statement tells SAS again to re-open the Listing destination.
Launch the SAS program. Then, edit the ODS PDF statement to reflect where you would like your PDF file stored. Then, run the SAS program. Again, there are a couple of things you can do to view the resulting PDF file. First, you can review the PDF file as it appears in the SAS Results Viewer. Alternatively, you can go to the folder in which you told SAS to store your PDF file:
and double-click on the file to open it.
As discussed earlier, when ODS receives data from a procedure, it combines the data component with a table definition to create an output object. For many procedures, ODS creates just one output object, while for others it produces several. Procedures involving a BY statement, for example, typically produce an output object for each BY group. When a procedure does create more than one output object, you might not want SAS to include all of them in your output. You might instead want to tell SAS to select just one or two of the output objects. In this section, we learn how to use the ODS TRACE and ODS SELECT statements to choose the specific output objects that you want SAS to display in your output.
The ODS TRACE ON statement tells SAS to print information in the log about the output objects created by all of the code in your program between the ODS TRACE ON statement and a closing ODS TRACE OFF statement.
Example 24.7. The following program uses ODS TRACE statements to capture information about the output objects created by the MEANS procedure on a data set called golfbypar, which is just a sorted version of the penngolf  data set:
The SORT procedure, of course, just sorts the permanent data set stat481.penngolf by par and stores the sorted result in a temporary data set called golfbypar. Then, the ODS TRACE ON statement tells SAS to start capturing information about any output objects that are created. The MEANS procedure tells SAS to summarize the golfbypar data set for each level of par, that is, when par equals 70, 71 and 72. Finally, the ODS TRACE OFF statement tells SAS to stop capturing information about any output objects that are created.
Launch and run the SAS program. You can go ahead and review the output from the MEANS procedure, but what we're really interested in here is the information SAS displays about the output objects in the log window:
As the log suggests, the MEANS procedure creates one output object for each BY group (par = 70, par = 71, and par = 72). The three output objects share the same name, label, and template, but different paths. The path for the par = 70 output object, for example, is called Means.ByGroup1.Summary, while the path for the par = 71 output objects is called Means.ByGroup2.Summary. Once we know the names of the output objects, we can use an ODS SELECT statement to tell SAS the specific output objects that we want displayed. To select specific output objects, simply place an ODS SELECT statement within the relevant procedure. By default, the ODS SELECT statement lasts only for the procedure in which it is contained.
Example 24.8. The following program uses an ODS SELECT statement and what we learned from tracing our MEANS procedure to print just the portion of the output that pertains to the par 70 golf courses:
Launch and run the SAS program, and review the output to convince yourself that SAS displays only the portion of the MEANS procedure that pertains to the par 70 golf courses.
I have good news and bad news for you about changing the appearance of your output. The good news is that if you had enough time to learn all of the ways in which you could change the appearance of your SAS output, you could create just about anything you wanted. The bad news is that we don't have enough time in this course to explore all of the possibilities. In fact, we'll barely nibble the surface. In this section, we will only investigate how to use the ODS HTML statement's STYLE= option to change the appearance of the default HTML output by using one of the many predefined style templates built into SAS.
Example 24.9. The following program uses the ODS HTML statement's STYLE= option to tell SAS to use the meadow style when displaying the HTML output created by printing a subset of the stat481.penngolf data set:
As you can see, telling SAS what style to use is as simple as adding the STYLE= option to the ODS HTML statement. Launch and run the SAS program, and review the output to see the appearance of the HTML output when created using the meadow style template.
Example 24.10. Of course you are asking yourself "how would I know that meadow is one of the available predefined styles?" Fortunately, the answer is simple enough. The following TEMPLATE procedure produces a list of the predefined style templates that are available on your system:
Launch and run the SAS code, and review the output to see the list of predefined styles that are shipped with SAS. You might want to try some of the styles out yourself. While I do find some of the styles rather nice, I personally find some of them rather hideous (and therefore useless to me).
You may recall in the first part of Stat 483 that we used an OUTPUT statement in the MEANS procedure to create a data set containing summary statistics, such as means and standard deviations. We'll see in this section that we could have alternatively used ODS to first save the summary statistics and then send it to the OUTPUT destination. In fact, we can use ODS to save just about any part of any procedure's output!
Example 24.11. The following program uses an ODS OUTPUT statement to create a temporary SAS data set called summout from the Summary output object created by the MEANS procedure, and then prints the resulting summout data set:
Now, something that might not be obvious from this code is that the name of the output object, Summary, was determined from first tracing the MEANS procedure. If you refer back to the information SAS displayed in the log for Example 24.7, you can see that, since we want to capture all of the output from the MEANS procedure, the desired output object is called Summary. The ODS OUTPUT statement tells SAS that we want to save the data contained in the Summary output object in a data set called summout. Of course, the PRINT procedure then tells SAS to print the summout data set. Launch and run the SAS program, and review the output to convince yourself that the summout data set does indeed contain the data summarized by the MEANS procedure.
You do need to be careful where you put ODS statements in your program. For example, if rather than putting the ODS OUTPUT statement just before the MEAN procedure's RUN statement, we had instead put it after the MEAN procedure's RUN statement and before the PRINT procedure's PROC PRINT statement, we would not have captured the Summary data set. Instead, we would get the following Warning message:
You might want to move the ODS statement as described, and re-run the SAS program just to see this for yourself.
In this lesson, we've learned how to use ODS statements to alter the format of the output that is obtained by default from any procedure.
The homework for this lesson will give you more practice with these techniques so that you become even more familiar with how they work and can use them in your own SAS programming.