Chapter 25: Introducing the SAS Macro Language

25.1  Introduction

25.2  Macro Variables: What Are They?

25.3  Some Built-In Macro Variables

25.4  Assigning Values to Macro Variables with a %LET Statement

25.5  Demonstrating a Simple Macro

25.6  Describing Positional and Keyword Macro Parameters

25.7  A Word about Tokens

25.8  Another Example of Using a Macro Variable as a Prefix

25.9  Using a Macro Variable to Transfer a Value between DATA Steps

25.10  Problems

 

25.1  Introduction

Although the SAS macro language is usually thought of as an advanced topic, there are aspects of this language that are useful and easy to use, even to the beginning or intermediate SAS programmer. This chapter gives you the tools to use macro variables and write simple macros. Macros are particularly useful if you want to make your SAS programs more flexible and allow them to be used in different situations without having to rewrite entire programs. Macro variables also provide a useful method for passing information from one DATA step to another. You can even select which procedures within a large program to run, depending on the day of the week or the values in your data.

If you want to learn more about the SAS macro language, I highly recommend two books. The first book, by Michele Burlew, might be more appropriate to beginning programmers (Michele M. Burlew, SAS Macro Programming Made Easy, Third Edition (Cary, NC: SAS Institute Inc., 2014). The second book, by Art Carpenter, covers more advanced topics. (Art Carpenter, Carpenter’s Complete Guide to the SAS Macro Programming Language, Third Edition (Cary, NC: SAS Institute Inc., 2016).

25.2  Macro Variables: What Are They?

You may have seen programs with funny-looking variable names such as &VAR or &SYSDATE in them. Perhaps you have seen statements such as %LET or other statements containing percent signs. When you submit a SAS program for processing, before SAS starts to compile and then execute your program, it first checks for the existence of ampersands (&) or percent signs (%) in the program and calls in the macro processor if it sees one. In a sense, the macro processor works like the find-and-replace feature of Microsoft Word—it looks for all the macro variables (names starting with ampersands) and replaces them with text values. There are several ways to assign values to macro variables and you will see several described in this chapter.

25.3  Some Built-In Macro Variables

There are several built-in macro variables available to you as soon as you begin a SAS session. Two useful ones are &SYSDATE9 and &SYSTIME. As you may guess, the former stores the date you started your SAS session (in DATE9. format) and the latter stores the time the session started. As an example, you could use these automatic macro variables to include a date and time in a TITLE statement, like this:

Program 25.1: Using an Automatic Macro Variable to Include a Date and Time in a Title

  title "The Date is &Sysdate9 - the Time is &Systime";

  proc print data=Learn.Test_Scores noobs;

  run;

If you started your SAS session at 10:02 on December 10, 2017, the macro processor would first resolve the macros variables (substitute text for the macro variable) before running the program. The listing would look like this:

Figure 25.1: Output from Program 25.1

Figure 25.1: Output from Program 25.1

This is a good time to tell you that if you place a macro variable inside quotation marks, SAS resolves only macro variables that are inside double quotation marks. If you used single quotation marks in Program 25.1, the title would read:

Figure 25.2: Output If You Use Single Quotation Marks in the Title

Figure 25.2: Output If You Use Single Quotation Marks in the Title

25.4  Assigning Values to Macro Variables with a %LET Statement

You can assign a value to a macro variable with a %LET statement. You usually place %LET statements in open code, that is, not inside a DATA or PROC step. Here is a simple example:

Program 25.2: Assigning a Value to a Macro Variable with a %LET Statement

  %let Var_List = RBC WBC Chol;

  title "Using a Macro Variable List";

  proc means data=Learn.Blood n mean min max maxdec=1;

     var &Var_List;

  run;

Every place in your program where you use the macro variable &Var_List, SAS substitutes the text RBC WBC Chol. This is a simple but useful trick when you need to repeat lists of variables.

Here is another example of using a %LET statement. Suppose you want to generate a data set containing random integers from 1 to 100. You want the program to be flexible so that you can choose how many random numbers to generate each time you run the program. Here is the program:

Program 25.3: Another Example of Using a %LET Statement

  %let n = 3;

  data Generate;

     do Subj = 1 to &n;

        x = ceil(100*rand('uniform');

        output;

     end;

  run;

  title "Data Set with &n Random Numbers";

  proc print data=Generate noobs;

  run;

Notice that the %LET statement comes before the DATA step. When this program runs, each occurrence of &n is replaced with a 3. While it would be easy to use the SAS Program Editor to change a 3 to some other value each time you run this program, imagine that this value was used many times in a longer program. By simply editing the single %LET statement, you can change the value of n everywhere in the program.

25.5  Demonstrating a Simple Macro

So far, you have seen macro variables used in a program. Now it’s time to see an actual SAS macro. Macros can be entire SAS programs or just pieces of SAS code. You start a macro with a %MACRO statement and end it with a %MEND (macro end) statement. Here is an example.

 

You want to make Program 25.3 more general. Not only do you want to change the number of random numbers you generate, you want to change the starting and ending values of these numbers as well. The macro that follows does just that:

Program 25.4: Writing a Simple Macro

  %macro Gen(n,Start,End);

     data Generate;

        do Subj = 1 to &n;

         x = int(rand('uniform') * (&End - &Start + 1) + &start);

           output;

        end;

     run;

     proc print data=generate noobs;

        title "Randomly Generated Data Set with &n Obs";

        title2 "Values are Integers from &Start to &End";

     run;

  %mend Gen;

The macro name is GEN and there are three positional arguments: n, Start, and End. To generate four random integers from 1 to 100, you would call the macro like this:

%Gen(4,1,100)

The macro calling statement consists of a percent sign followed by the macro name. In this calling statement, you specify the values that you want to substitute for n, Start, and End in parentheses following the macro name. If you submit this macro with the option MPRINT turned on (options mprint;), you see the actual SAS code that was generated. Here is a listing of the SAS log (with the MPRINT option in effect):

Figure 25.3: Listing of the SAS Log with Option MPRINT Turned On

Figure 25.3: Listing of the SAS Log with Option MPRINT Turned On

Notice that each macro variable was replaced by the value specified in the calling sequence.

 

Here is the output:

Figure 25.4: Output from Program 25.4

Figure 25.4: Output from Program 25.4

When you call the macro, there is no semicolon at the end of the statement. If you look at the SAS code that was generated (in the previous SAS log), you can see that each SAS statement that was generated ends in a semicolon. Remember that SAS macros may generate pieces of SAS code, sometimes even parts of other SAS statements. Therefore, following the macro call with a semicolon could result in an error.

25.6  Describing Positional and Keyword Macro Parameters

The macro shown in Program 25.4, used what are called positional parameters. That is, each of the macro variables in parentheses following the macro name are separated by commas. When you call this macro, you also list the values that you want to substitute for each macro variable, in order, separated by commas.

There is an alternate (and superior way, at least the belief of this author) to specify the macro parameters. It is called keyword parameters. You follow each macro variable in the list of parameters with an equal sign. When you call the macro, you also list the name of the parameter, followed by an equal sign, followed by the value that you want to substitute for the macro variable.

What follows is a repeat of Program 25.4, rewritten to use keyword parameters. A recommended way to document these parameters is also included in the program.

Program 25.5: Program 25.4 Rewritten to Use Keyword Parameters

  %macro Gen(n=,      /* number of random numbers */

             Start=,  /* Starting value           */

             End=     /* Ending value             */);

     /********************************************

     Example: To generate 4 random numbers from

     1 to 100 use:

     %Gen(n=4, Start=1, End=100)

     *********************************************/

     data Generate;

        do Subj = 1 to &n;

         x = int(rand('uniform') * (&End - &Start + 1) + &start);

           output;

        end;

     run;

 

     proc print data=Generate noobs;

        title "Randomly Generated Data Set with &n Obs";

        title2 "Values are Integers from &Start to &End";

     run;

  %mend Gen;

You would call the macro this way:

%Gen(n=4, Start=1, End=100)

This method looks more tedious than simply listing the parameters with commas. However, here are some of the advantages of keyword parameters:

        It provides better documentation. Someone reading your calling routine can see what values are associated with each macro variable.

        You can list your parameters in any order. When you call positional parameters, they have to be listed in the same order that they were listed in the macro definition.

        You can supply default values when you write your macro. If you call the macro and leave out a parameter that you defined with a default value that value will be used—if you include a value for that parameter, it will override the default value.

As an example, suppose you want to write a macro that prints the first n observations of a data set. This macro also prints out a title stating how many observations were printed. Finally, you want to set a default value of 10 observations if you call the macro without specifying the value of n.

Such a macro is shown below in Program 25.6:

Program 25.6: Macro Demonstrating Keyword Parameters and Default Values

%macro Print(Dsn=, /* Name of the data set to print */

             n=10  /* The number of obs to print, default

                      is 10 */);

title "Listing of Data Set &Dsn";

title2 "First &n Observations";

proc print data=&Dsn(Obs=&n) noobs;

run;

%mend Print;

If you call the macro like this:

%Print(Dsn=Learn.Blood, n=3)

the output looks like this:

Figure 25.5: Output From Calling the Macro

Figure 25.5: Output From Calling the Macro

Notice that setting n = 3 overrode the default of 10. If you call the macro like this (without specifying a value for n):

%Print(Dsn=Learn.Blood)

You will obtain a listing of the first 10 observations in the Learn.Blood data set. Considering all the advantages of keyword parameters, this author will use this method in the remaining macros in this chapter.

Note: This is a change from the first edition where all the macro examples used positional parameters. Even “old dogs” can learn new tricks!

25.7  A Word about Tokens

The SAS macro processor must figure out where your macro variables start and end. In the previous macro, each time a macro variable was used, it was followed by a character that was not valid in a variable name. Suppose you submitted the following SAS code:

Program 25.7: Demonstrating a Problem with Resolving a Macro Variable

  %let Prefix = abc;

  data &Prefix123;

     x = 3;

  run;

You are hoping to generate a data set called abc123. However, take a look at the SAS log:

Figure 25.6: SAS Log After Running Program 25.7

Figure 25.6: SAS Log After Running Program 25.7

This message tells you that the macro processor cannot resolve (find) a macro variable with the name PREFIX123. SAS assumes that the macro variable name is PREFIX123 since this is a valid macro variable name. To tell SAS that the macro variable name is &PREFIX, you use a period to indicate where the macro variable name ends, like this:

Program 25.8: Program 25.7 Corrected

  %let Prefix = abc;

  data &prefix.123;

     x = 3;

  run;

The period following the word Prefix tells the macro processor that &PREFIX is the macro variable name, not &PREFIX123. Notice that the SAS log no longer shows an error:

Figure 25.7: SAS Log after Running Program 25.8

Figure 25.7: SAS Log after Running Program 25.8

Notice that the value ABC was substituted for the macro variable &Prefix to generate the data set name work.ABC123.

25.8  Another Example of Using a Macro Variable as a Prefix

Suppose you want to use a macro variable to specify a libref (the first part of a two-part data set name). You might be tempted to write a program like this:

Program 25.9: Using a Macro Variable as a Prefix (Incorrect Version)

  %let libref = Learn;

  proc print data=&libref.Survey;

     title "Listing of Survey";

  run;

Here’s what happens when you try to run this program:

Figure 25.8: SAS Log after Running Program 25.9

Figure 25.8: SAS Log after Running Program 25.9

What happened? The period between the &libref and the name Survey was eaten up by the macro processor. It told the macro processor that the name of the macro variable was &libref. With the period removed, SAS was looking to create a data set called Work.LearnSurvey. The solution (as strange as it looks) is to use two periods: one to tell the macro processor where the macro variable name ends and the other to separate the libref from the data set name. The corrected program is as follows:

Program 25.10: Using a Macro Variable as a Prefix (Corrected Version)

  %let libref = learn;

  proc print data=&libref..survey;

     title "Listing of SURVEY";

  run;

Here is the SAS log after running this program:

Figure 25.9: SAS Log After Running Program 25.10

Figure 25.9: SAS Log After Running Program 25.10

You can see that SAS resolved the macro variable and the result was a data set called Learn.Survey.

25.9  Using a Macro Variable to Transfer a Value between DATA Steps

Macro variables defined outside of a macro are, by default, global. That is, they maintain their value during a SAS session. This makes them a very useful tool for transferring values between DATA steps.

As an example, suppose you ran PROC MEANS to compute the means of the variables RBC and WBC from the Blood data set and wanted to compare each individual value of RBC and WBC against the mean values. One way to do this is to include a conditional SET statement in your DATA step, like this:

if _n_ = 1 then set Means;

Here, Means is the one-observation data set produced by PROC MEANS. Another way, which we describe next, uses macro variables to hold the mean values. Here is the program:

Program 25.11: Using Macro Variables to Transfer Values from One DATA Step to Another

  proc means data=learn.Blood noprint;

     var RBC WBC;

     output out=Means mean= / autoname;

  run;

  data _null_;

     set Means;

     call symputx('AveRBC',RBC_Mean);

     call symputx('AveWBC',WBC_Mean);

  run;

  data New;

     set learn.Blood(obs=5 keep=Subject RBC WBC);

     Per_RBC = RBC / &AveRBC;

     Per_WBC = WBC / &AveWBC;

     format Per_RBC Per_WBC percent8.;

  run;

PROC MEANS creates a data set (called Means in this example) consisting of one observation and two variables (RBC_Mean and WBC_Mean), which represent the mean values of RBC and WBC, respectively.

Note: As a reminder, the AUTONAME option in the OUTPUT statement of PROC MEANS names the variables in the output data set automatically by using the variable names and adding an underscore, followed by the name of the statistic. That is why the two means are called RBC_Mean and WBC_Mean.

The DATA _NULL_ step uses a CALL routine called SYMPUTX. This CALL routine assigns the value of a DATA step variable to a macro variable. The first argument in this CALL routine is the name of a macro variable, and the second argument is the name of a DATA step variable. You cannot use a %LET statement here because you don’t know the value of RBC_Mean or WBC_Mean ahead of time.

When this DATA step executes, each of the two macro variables (RBC_Ave and WBC_Ave) will be the two mean values. The values of these two macro variables are not available in the same DATA step, so a final DATA step is needed.

In the final DATA step, the two variables Percent_RBC and Percent_WBC are computed by dividing the value in the current observation by the mean value. You may wonder why this statement doesn’t multiply the resulting value by 100 to create a percentage. The SAS format PERCENT not only adds a percent sign, it multiplies by 100 as well.

Here is a listing of data set Compare_to_Mean:

Figure 25.10: Partial Listing of Data Set Compare_to_Mean

Figure 25.10: Partial Listing of Data Set Compare_to_Mean

Hopefully, this very brief introduction to SAS macros has shown you that the SAS macro language is not as scary as you might have thought and has given you the courage to try using them in your own programs.

25.10  Problems

Solutions to odd-numbered problems are located at the back of this book. Solutions to all problems are available to professors. If you are a professor, visit the book’s companion  website at support.sas.com/cody for information about how to obtain the solutions to all problems.

1.       Use PROC PRINT to list the first five observations from data set Stocks. Use the system macro variables &SYSDAY (returns the day of the week), &SYSDATE, and &SYSTIME to customize the title (see the following example, which was run on a Friday):

image shown here

2.       Rewrite the following program using %LET to assign the starting and ending values of the DO loop to macro variables. Be sure these values also are printed in the PROC PRINT output.

  data Sqrt_Table;

     do n = 1 to 5;

        Sqrt_n = sqrt(n);

        output;

     end;

  run;

  title "Square Root Table from 1 to 5";

  proc print data=Sqrt_Table noobs;

  run;

3.       Create a macro (call it PRINT_N) to produce a listing of the first n observations from a selected data set. Use the following program as a guide, replacing the data set name and the number of observations to print as calling arguments to the macro.

  title "Listing of the first 5 Observations from "

        "Data set Stocks";

  proc print data=Learn.Stocks(obs=5) noobs;

  run;

Use the macro to list the first four observations from SAS data set Bicycles. Your listing should look like this:

image shown here

4.       Turn the following program into a macro (call it Stats), making it more general. Use as calling arguments the input data set name (Dsn), the CLASS variables (Class), and variables listed in the VAR statement (Vars):

  title "Statistics from Data Set Learn.Bicycles";

  proc means data=Learn.Bicycles n mean min max maxdec=1;

     class Country;

     var Units TotalSales;

  run;

Test your macro by calling it like this:

   %Stats(Dsn=Learn.Bicycles, Class=Country, Vars=Units TotalSales)

5.       List three variables (TimeMile, RestPulse, and MaxPulse) from the data set Fitness. In addition to these three variables, compute three new variables (call them P_TimeMile, P_RestPulse, and P_MaxPulse) that represent these three values as a percentage of the mean for all subjects in the data set. Do this by first computing the three means using PROC MEANS. Next, in a DATA _NULL_ step, use CALL SYMPUTX to create three macro variables representing the means of these three values. Finally, in a DATA step, use these three macro variables in assignment statements to compute the percentage values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset