The macro facility -- manipulates text

Create and resolve macro variables anywhere within a SAS program

Automatic macro variables

User Defined macro variables

Write and call macro programs that generate SAS code

Simple macros

Macro Programming

Automatic Macro Variables

In [ ]:
%put _automatic_;

The %put statement puts text to the log

In [ ]:
%put this is some text to be put to the log; 
The text can include automatic macro variables. The name is preceded by & (known as a macro "trigger"). It finds the value of the text and inserts it in place of the reference. In the following example the macro variables sysday and sysdate9 were created when SAS was initialized. They are stored in the "global symbol table" as text.
In [ ]:
%put This text was output:  &systime &sysday &sysdate9 ;
%put The last dataset created was:  &syslast;

Automatic macro variables are useful for documentation

In [ ]:
/*without macro variables*/
proc freq data=orion.Customer;
   table Country / nocum;
   footnote1 "Created 11:47 Saturday, 11Feb2017";
   footnote2 "By user dlm1 on system Linux";
run;

footnote;

When using macro variables inside quotes you must use double quotes!

In [ ]:
/*with macro variables*/
proc freq data=orion.Customer;
   table Country / nocum;
   footnote1 'Created &systime &sysday, &sysdate9';
   footnote2 'By user &sysuserid on system &sysscpl';
run;

footnote;
In [ ]:
/*with macro variables*/
proc freq data=orion.Customer;
   table Country / nocum;
   footnote1 "Created &systime &sysday, &sysdate9";
   footnote2 "By user &sysuserid on system &sysscpl";
run;

footnote;

User defined macro variable are created with the %let statement

In [ ]:
%let mymacvar=Some text (often code goes here);
%put Mymacvar:  &mymacvar;

Any quotes in the text after %let= is included in the definition (not wanted very often)

In [ ]:
%let txt="Some text in quotes";
%put The macro variable txt is:  &txt;

Some examples of use of macro variables. First, the program without macro variables, then the program with macro variables

In [ ]:
/* without macro variables*/
proc freq data=orion.order_fact;
   where year(order_date)=2007;
   table order_type;
   title "Order Types for 2007";
run;
proc means data=orion.order_fact;
   where year(order_date)=2007;
   class order_type;
   var Total_Retail_Price;
   title "Price Statistics for 2007";
run;
title;
In [ ]:
/* with  macro variables*/
%let year=2006;
proc freq data=orion.order_fact;
   where year(order_date)=&year;
   table order_type;
   title "Order Types for &year";
run;
proc means data=orion.order_fact;
   where year(order_date)=&year;
   class order_type;
   var Total_Retail_Price;
   title "Price Statistics for &year";
run;
title;

Generate 1,000 samples of size 10 from a standard normal distribution

In [ ]:
data normals;
    array x{10} x1-x10;
    call streaminit(654321);
    do rep=1 to 1000;
        do n=1 to 10;
        x{n}=rand("normal");
        end;
        mn=mean(of x{*});
        output;
    end;
run;  
ods select histogram;
proc univariate data=normals;
var mn;
histogram mn/normal;
run;

Rewrite with user defined macro variables

In [ ]:
%let obs=25;
%let sampsize=1000;
%let seed=54321;
data normals;
    array x{*} x1-x&obs;
    call streaminit(&seed);
    do rep=1 to &sampsize;
        do n=1 to &obs;
        x{n}=rand("normal");
        end;
        mn=mean(of x{*});
        output;
    end;
run;  
ods select histogram;
proc univariate data=normals;
var mn;
histogram mn/normal;
run;

Simple Macros

An aside -- controlling output

Consider the following program that generates a lot of output

In [ ]:
%let numobs=30;
%let lambda=5;
%let numrep=100;
data expons;
call streaminit(1254731); /* set random number seed */
do rep=1 to &numrep;
do i = 1 to &numobs;
   x = &lambda*rand("Exponential"); 
   output;
end;
end;
run;
proc means data=expons ;
by rep;
var x;
output out=mnexp;
run;
data mnexp;
  set mnexp;
where _stat_ ="MEAN";
  rename x=mean;
run;

Turn output off

In [ ]:
%let numobs=30;
%let lambda=5;
%let numrep=100;
options nonotes;
ods graphics off;
ods exclude all;
ods noresults;
data expons;
call streaminit(1254731); /* set random number seed */
do rep=1 to &numrep;
do i = 1 to &numobs;
   x = &lambda*rand("Exponential"); 
   output;
end;
end;
run;
proc means data=expons ;
by rep;
var x;
output out=mnexp;
run;
data mnexp;
  set mnexp;
where _stat_ ="MEAN";
  rename x=mean;
run;

Turn output back on and produce results

In [ ]:
%odson;

proc univariate data=mnexp;
  var mean;
histogram mean /normal;
run;

Imbed this code for shutting down and turning on output into simple macros.

In [ ]:
%macro ODSOff(); /* Call prior to BY-group processing */
/* from the do loop, adapted*/
options nonotes;
ods graphics off;
ods exclude all;
ods noresults;
%mend;
%macro ODSOn(); /* Call after BY-group processing */
/* from the do loop, adapted*/
options notes;
ods graphics on;
ods exclude none;
ods results;
%mend;

Simple Macro with parameters

Consider the following program that imports a csv file -- note that to rerun the program for a different file there are only two occurrences of the word "staff" that need to be changed. I could do this with a macro variable, but if I'm going to do it a lot a macro would be handier.

In [ ]:
filename filenm url "http://socrates.stat.fsu.edu/csv/orion/staff.csv";
proc import out=staff
    datafile=filenm
    dbms=csv replace;
    getnames=yes;
    datarow=2;
run;    

Writing a simple macro (with parameters)

In [ ]:
%macro impdat(datanm);
filename filenm url "http://socrates.stat.fsu.edu/csv/orion/&datanm..csv";
proc import out=&datanm
    datafile=filenm
    dbms=csv replace;
    getnames=yes;
    datarow=2;
run; 
%mend;

Call the macro

In [ ]:
%impdat(customer)

Developing macro applicatons often involves several steps.

  1. Write and debug the SAS program without macro coding.
  2. Generalize the program by replacing hardcoded values with macro variable references.
  3. Create a macro definition with macro parameters.
  4. Add macro-level programming for conditional and iterative processing.
  5. Add data-driven customization.