Lesson 33: SAS Macro Programming for Beginners
Lesson 33: SAS Macro Programming for BeginnersOverview
This lesson introduces the most commonly used features of the SAS macro language. When you write a program that will be run over and over again, you might want seriously to consider using "macros" in your code, because:
- macros allow you to make a change in one location of your program so that SAS can cascade the change throughout your program
- macros allow you to write a section of code once and use it over and over again, in the same program or even different programs
- macros allow you to make programs data-driven, letting SAS decide what to do based on actual data values.
To whet our appetite for SAS macros, we'll read this paper:
SAS Macro Programming for Beginners
written by Susan J. Slaughter and Lora D. Delwiche and presented as a tutorial at the 2004 SAS Users Group International (SUGI) Meeting in Montreal, Canada.
Objectives
- understand what a SAS macro is
- distinguish between local and global macro variables
- create a macro variable using a %LET assignment statement
- use a macro variable in a SAS program
- write and invoke a basic SAS macro
- write and invoke a SAS macro that uses parameters
- write a macro with conditional macro %IF-%THEN-%ELSE statements
- use and understand automatic macro variables such as &SYSDATE and &SYSDAY
- use CALL SYMPUT to write data-driven programs
Textbook Reference
SAS Macro Programming for Beginners paper written by Susan J. Slaughter and Lora D. Delwiche.
33.1 - Lesson Notes
33.1 - Lesson NotesThe Macro Processor
Page 1. You can refer to page 339 of our textbook for an example of a program that SAS would need to pre-process before executing the code. In that particular case, before executing the code, SAS would have to run the program through the macro processor in order to replace all the occurrences of "&NUMBER" with the value "5".
Substituting text with %LET
Page 3. In case you want to try the program out on your own, here it is:
OPTIONS PS = 58 LS = 72 NODATE NONUMBER;
DATA models;
infile DATALINES truncover;
input Model $ 1-12 Class $ Price Frame$ 28-38;
DATALINES;
Black Bora Track 796 Aluminum
Delta Breeze Road 399 CroMoly
Jet Stream Track 1130 CroMoly
Mistral Road 1995 Carbon Comp
Nor'easter Mountain 899 Aluminum
Santa Ana Mountain 459 Aluminum
Scirocco Mountain 2256 Titanium
Trade Wind Road 759 Aluminum
;
RUN;
%LET bikeclass = Mountain;
*Use a macro variable to subset;
PROC PRINT DATA = models NOOBS;
where Class = "&bikeclass";
format Price dollar6.;
title "Current Models of &bikeclass Bicycles";
RUN;
Model | Class | Price | Frame |
---|---|---|---|
Nor'easter | Mountain | $899 | Aluminum |
Santa Ana | Mountain | $459 | Aluminum |
Scirocco | Mountain | $2,256 | Titanium |
You might also want to change the value of Mountain in the %LET statement to, say, Road, and re-run the program to see the effect.
Creating Modular Code with Macros
Page 4. Again, in case you want to try the program out on your own, here it is:
%MACRO printit;
PROC PRINT data = models NOOBS;
title 'Current Models';
var Model Class Frame Price;
format Price dollar6.;
RUN;
%MEND printit;
%printit
PROC SORT data = models;
by Price;
RUN;
%printit
Adding Parameters to Macros
Page 6. Here's the code for the example:
%MACRO sortandprint(sortseq=, sortvar=);
PROC SORT data = models;
by &sortseq &sortvar;
RUN;
PROC PRINT data = models noobs;
title 'Current Models';
title2 "Sorted by &sortseq &sortvar";
var Model Class Frame Price;
format price dollar6.;
RUN;
%MEND sortandprint;
%sortandprint(sortseq=Descending, sortvar=Price)
%sortandprint(sortseq=, sortvar=Class)
Page 7. Here's the code for the example with the MPRINT option invoked:
OPTIONS MPRINT;
%MACRO sortandprint(sortseq=, sortvar=);
PROC SORT data = models;
by &sortseq &sortvar;
RUN;
PROC PRINT data = models noobs;
title 'Current Models';
title2 "Sorted by &sortseq &sortvar";
var Model Class Frame Price;
format price dollar6.;
RUN;
%MEND sortandprint;
%sortandprint(sortseq=Descending, sortvar=Price)
%sortandprint(sortseq=, sortvar=Class)
Conditional Logic
Page 9. Here's the program:
DATA orders;
input CustomerID $ 1-3 @5 OrderDate date7. Model $ 13-24 Quantity;
DATALINES;
287 15OCT03 Delta Breeze 15
287 15OCT03 Santa Ana 15
274 16OCT03 Jet Stream 1
174 17OCT03 Santa Ana 20
174 17OCT03 Nor'Easter 5
174 17OCT03 Scirocco 1
347 18OCT03 Mistral 1
287 21OCT03 Delta Breeze 30
287 21OCT03 Santa Ana 25
;
RUN;
%MACRO reports;
%IF &SYSDAY = Monday %THEN %DO;
PROC PRINT data = orders NOOBS;
format OrderDate date7.;
title "&SYSDAY Report: Current Orders";
RUN;
%END;
%ELSE %IF &SYSDAY = Friday %THEN %DO;
PROC TABULATE data = orders;
class CustomerID;
var Quantity;
table CustomerID ALL, Quantity;
title "&SYSDAY Report: Summary of Orders";
RUN;
%END;
%MEND reports;
%REPORTS
By the way, you might notice that I embedded the RUN; statements in the macro, rather than placing them outside of the macro as the authors of the paper did. Either approach works... it's just a matter of preference.
Data-driven Programs
Page 12. Here's the program:
*Sort by Quantity;
PROC SORT data = orders;
by descending Quantity;
RUN;
*Use CALL SYMPUT to find the biggest order;
DATA _NULL_;
set orders;
if _N_ = 1 then CALL SYMPUT("biggest", CustomerID);
else STOP;
RUN;
*Print all obs for customer with biggest order;
PROC PRINT data = orders NOOBS;
where CustomerID = "&biggest";
format OrderDate date7.;
title "Customer &biggest Had the Single Largest Order";
RUN
CustomerID | OrderDate | Model | Quantity |
---|---|---|---|
28 | 21OCT00 | Santa Ana | 25 |
28 | 15OCT00 | Santa Ana | 15 |
28 | 15OCT00 | Delta Breez | . |
28 | 21OCT00 | Delta Breez | . |
33.2 - Summary
33.2 - SummaryIn this lesson, we learned how to use the SAS macro facility to write more flexible code.
The homework for this lesson will give you more practice with macros and macro variables so that you become even more familiar with how they work and can use them in your own SAS programming.