Macros with Parameters
SAS macro programs not only add conditional execution of SAS statements and looping to your global SAS code (not just DATA steps), but also the ability to set variable parameters when you call the macro.
In this example: We often want to merge group summary statistics back on to the original data set. I start with the example of merging group means, then generalize.
We also recommend a workflow for creating macros:
- Write a complete example of one thing your macro should accomplish.
- Convert elements of your macro into macro variables, and test.
Step 1: Write & debug an example
We’ll start with an example that calculates the groupwise mean income in 1967, by marital status.
options symbolgen mprint=off;
proc means data=y.nlswages noprint;
class R0002400;
types R0002400;
var R0043600;
output out=mean mean=R0043600mean;
run;
proc sort data=y.nlswages out=sorted;
by R0002400;
run;
data nlswages;
merge sorted mean;
by R0002400;
label R0043600mean="mean of 67 income"; *otherwise mean inherits its label;
format R0043600mean ; *otherwise it inherits a format, too;
run;
proc print data=nlswages(obs=5);
var R0002400 R0043600 R0043600mean;
run;
Obs R0002400 R0043600 R0043600mean
1 MARRIED SPOUSE PRESENT 5000-5999 1264.55
2 MARRIED SPOUSE PRESENT B 1264.55
3 MARRIED SPOUSE PRESENT 0 1264.55
4 MARRIED SPOUSE PRESENT 1-999 1264.55
5 MARRIED SPOUSE PRESENT 2000-2999 1264.55
Step 2: Convert to macro variables
Here we will generalize out example to cover other
- data set names
- variable names
- grouping variables
- statistics
%macro mergestat(dsn, var, group, stat);
proc means data=&dsn noprint;
class &group;
types &group;
var &var;
output out=&stat &stat=&var&stat;
run;
proc sort data=&dsn out=sorted;
by &group;
run;
data &dsn;
merge sorted &stat;
by &group;
label &var&stat="&stat of &var";
format &var&stat ;
run;
%mend mergestat;
Then, two examples of using the macro
proc copy in=y out=work;
select nlswages;
run;
*options mprint=on;
%mergestat(nlswages, R6204300, R0002400, mean);
proc print data=nlswages(obs=5);
var R0002400 R6204300 R6204300mean;
run;
Obs R0002400 R6204300 R6204300mean
1 MARRIED SPOUSE PRESENT B 12718.90
2 MARRIED SPOUSE PRESENT B 12718.90
3 MARRIED SPOUSE PRESENT A 12718.90
4 MARRIED SPOUSE PRESENT D 12718.90
5 MARRIED SPOUSE PRESENT A 12718.90
proc copy in=y out=work;
select nlswages;
run;
%mergestat(nlswages, R7312300, R0002400, median);
proc print data=nlswages(obs=5);
var R0002400 R7312300 R7312300median;
run;
Obs R0002400 R7312300 R7312300median
1 MARRIED SPOUSE PRESENT 03000-03999 8000
2 MARRIED SPOUSE PRESENT A 8000
3 MARRIED SPOUSE PRESENT A 8000
4 MARRIED SPOUSE PRESENT D 8000
5 MARRIED SPOUSE PRESENT A 8000
Nested Macros
In this example, we not only want to merge group summary stats, but we also want to score the original variable as greater than or less than that stat.;
From this (our first example, above):
data nlswages;
set nlswages;
R0043600cat=(R0043600>R0043600mean);
run;
proc freq;
tables R0043600cat;
run;
To this:
%macro aboveaverage(dsn, var, stat);
data &dsn;
set &dsn;
&var.cat=(&var>&var&stat);
run;
%mend aboveaverage;
It should be pretty obvious that this macro requires something like %mergestat
first, which leads to the next example:
%macro maa(dsn, var, group, stat);
%mergestat(&dsn, &var, &group, &stat);
%aboveaverage(&dsn, &var, &stat)
%mend maa;
An example in use
proc copy in=y out=work;
select nlswages;
run;
%maa(nlswages, R6204300, R0002400, mean);
proc freq;
tables R6204300cat / nocum;
run;
The FREQ Procedure
R6204300cat Frequency Percent
------------------------------------
0 4954 97.46
1 129 2.54
And another:
proc copy in=y out=work;
select nlswages;
run;
%maa(nlswages, R7312300, R0002400, median);
proc freq;
tables R7312300cat / nocum;
run;
The FREQ Procedure
R7312300cat Frequency Percent
------------------------------------
0 4963 97.64
1 120 2.36