Differences between revisions 6 and 7
Revision 6 as of 2020-02-16 01:05:20
Size: 14718
Comment:
Revision 7 as of 2020-02-16 02:03:32
Size: 15762
Comment:
Deletions are marked like this. Additions are marked like this.
Line 127: Line 127:
Note: this is contrived to demonstrate conversion. If the macro variable is not required for other processing, then this is an entirely superior method.
Line 144: Line 142:

`resolve` works in a similar manner, but takes a macro label with the trigger prefix (`&` or `%`), and can be used to get the value of a macro variable or function. For more details, see [[#Macro-Functions-In-The-Data-Step|below]].



=== Macro Variables in the SQL Process ===

A macro variable can be set by `proc sql`. This has many of the same benefits as with the data step.

{{{
proc sql noprint;
  select sum(sales) format=dollar10.2
    into :total_sales
    from LIBREF.TABLE;
quit;
}}}

`proc sql` also sets three background macro variable.

 * `SQLOBS` is the number of rows produced with a SELECT statement
 * `SQLRC` is the return code from the ultimate statement
 * `SQLOOPS` is the number of iterations from the ultimate (i.e., highest-level) loop
Line 257: Line 277:


=== Macro Functions in the Data Step ===

A macro function can be called within a data step using `resolve()`. `resolve` actually can be used to get the value of any type of macro, including a macro variable.

`resolve()` can be called in three ways:

{{{
method1 = resolve("&mymacro");

method2call = '&mymacro';
method2 = resolve(method2call);

method3 = resolve(cats('&','my','macro'));
}}}

SAS Macros

SAS supports a macro feature. For more options for extending and programming in SAS (or related software), see this crosswalk of programming features.


Macro Labels

A macro label is 1 to 32 characters, beginning with a letter or underscore and optionally followed by letters, numbers, and underscores. A macro value is accessed by prepending the label with a macro trigger-depending on the type of macro, this is either an ampersand (&) or a percent sign (%).

Note that, similar to Unix shells, the macro processor does not alter single-quoted strings.


Macro Expansion

A macro expands to literal text, which is then interpreted either at compilation or execution. This literal text does not need to follow any syntax rules until it is inserted and interpreted.

Macros often need to contain syntax keywords or operators. In order to defer the interpretation of these grammars, masking functions are employed.

  • %str and %nrstr mask text until compilation

  • %quote and %nrquote mask text until execution

  • %bquote and %nrbquote mask text until execution AND resolve mismatched quote marks

    • That is, the macro processor believes O'Hara is a syntax error because the string Hara is never finished. Beyond these functions, the resolution would be to escape quote marks with percent signs (%').

  • %superq prevents interpretation of text entirely

Special characters include (but are not limited to):

  • semicolons (;)

  • commas (,)

  • operators, including...
    • arithmetic (+, -, etc.)

    • expression (<, |, etc.)

    • mnemonic expression (LE, OR, etc.)

  • macro triggers (&, %)

The nr versions of the above functions mask all special characters, while the non-nr versions do not mask macro triggers.

Resolution Order

The macro processor scans from right to left in each token. It processes every macro label, macro function, macro statement (i.e. %if), or escaped character (i.e. %') it encounters, except for those masked by a macro function. However, it will re-scan until all ampersands have been cleared. Therefore, double ampersands can be used to 'defer' processing.

%let section1=Math;
%let section2=Science;
%let section3=Literature;

%let n = 2;
proc means data=LIBREF.TABLE;
  where section=&&section&n;
run;

The rabbit hole does not end here.

%let wherevar=section;
proc means data=LIBREF.TABLE;
  where &wherevar=&&&wherevar&n;
run;


Macro Variables

Macro variables can be used in any location other than data input. A macro variable is accessed by prepending the label with an ampersand (&).

To delimit a macro label from immediately-appended text, use a period. That is, &prefix1 looks-up prefix1, while &prefix.1 looks-up prefix. (To escape the period, use two.)

Built-in Variables

There are several built-in macro variables including %sysdate (system date as DATE7), or %systime (system time as TIME5).

Defining Variables

A macro variable can be defined as:

%let LABEL = VALUE;

Macros are referenced using &LABEL. A macro definition can reference another macro. To use a SAS function in defining a macro variable, the %sysfunc macro function must be used.

%let mon       = 1;
%let curr_yr   = %sysfunc(year("&sysdate"d));
%let mon_title = %sysfunc(mdy(&mon,1,&curr_yr), monname9.);
%let mon_title = %sysfunc(left(%qsysfunc(mdy(&mon,1,&curr_yr), monname9.)));

For considerations on using macro functions including %sysfunc, see Macro Functions.

Macro Variables in the Data Step

A macro variable can be set by symputx calls, and retrieved by symget calls. This enables programmatic creation and use of macros, i.e. for aggregation of data.

data _NULL_;
  set LIBREF.TABLE end=lastobs;
  retain sum_sales 0;
  sum_sales = sum_sales + sales;
  if lastobs then call symputx('total_sales', put(sum_sales, dollar10.2));
run;

data temp;
  set LIBREF.TABLE;
  total_sales = input(symget('total_sales'), dollar10.2);
run;

Setting Macro Variables

symput and symputx both take two string arguments-the macro variable name and its value. In order to set a numeric value into the macro, it must be converted using put.

symput and symputx are very similar. The latter is silent and trims leading spaces, whereas the former will log information and preserve leading spaces. Generally, symputx is the right option.

Getting Macro Values

symget and symgetn take a single string argument-the macro variable name. The variable must be defined.

symget is for character data, while symgetn implicitly converts to numeric.

resolve works in a similar manner, but takes a macro label with the trigger prefix (& or %), and can be used to get the value of a macro variable or function. For more details, see below.

Macro Variables in the SQL Process

A macro variable can be set by proc sql. This has many of the same benefits as with the data step.

proc sql noprint;
  select sum(sales) format=dollar10.2
    into :total_sales
    from LIBREF.TABLE;
quit;

proc sql also sets three background macro variable.

  • SQLOBS is the number of rows produced with a SELECT statement

  • SQLRC is the return code from the ultimate statement

  • SQLOOPS is the number of iterations from the ultimate (i.e., highest-level) loop

Scope of Variables

A macro variable defined by open code is stored in the global table. (And therefore: a macro variable defined in a separate program is local.) By the nature of SAS, most custom macros will be global.

If necessary, it is possible to force local resolution of a label.

%local A;
%let A=B;


Macro Functions

Macro functions are identified by a leading percent sign (%). They take one or more positional arguments within parentheses. They otherwise are used like macro variables.

Quoted Functions

Many macro functions have two versions: one that is interpreted immediately, and one that is masked until execution.

%let a=foo;
%let b=%nrstr(&a bar);

%let substring1 = %substr(&b,1,2);  /* => 'foo', **even though a substring of length 2 was requested** */
%let substring2 = %qsubstr(&b,1,2); /* => '&a' */

Built-in Functions

There are also a number of computational macro functions.

Given:

%let a=Foo;
%let b=%nrstr(&a bar);
%let c=%nrstr(&a);

The following functions can be used as:

Name

Action

Example Input

Example Result

%sysfunc(function call)

Evaluates a function call

%qsysfunc(function call)

Same, but masks value

%substr(value,position,length)

Copies a substring from a value by literal position

%substr(&c,1,2)

'Foo'

%qsubstr(value,position,length)

Same, but masks value

%qsubstr(&c,1,2)

'&a'

%scan(value,index,delimiter)

Copies a substring from a value by parsed position

%scan(&c,1,' ')

'Foo'

%qscan(value,index,delimiter)

Same, but masks value

%qscan(&c,1,' ')

'&a'

%upcase(value)

Translates lowercase into uppercase

%upcase(&c)

'Foo'

%qupcase(value)

Same, but masks value

%qupcase(&c)

'&A'

%lowcase(value) position

Translates uppercase into lowercase

%lowcase(&c)

'Foo'

%qlowcase(value)

Same, but masks value

%qlowcase(&c)

'&a'

%cmpres(value)

Strips leading/trailing and multiple whitespace

%qcmpres(value)

Same, but masks value

%left(value)

Strips leading whitespace

%qleft(value)

Same, but masks value

%eval(expression)

Evaluates expression using integer arithmetic

%eval(10/3)

'3'

%sysevalf(expression)

Evaluates expression using floating point arithmetic

%sysevalf(10/3)

'3.333333'

SAS Functions in Macros Functions

To use a SAS function inside a macro, the %sysfunc macro function must be used. %sysfunc cannot take nested SAS functions, but can take nested macro functions.

%let mon_title = %sysfunc(left(%qsysfunc(mdy(&mon,1,&curr_yr), monname9.)));

Remember the availability of the quoted %qsysfunc. In this context, the most contentious special character is the comma, which can be confused for an argument delimiter.

This results in an error:

%let date=%sysfunc(left(%sysfunc(today(),worddate.)));
/*                      ^
                        June 7, 2002
          ^
          left(June 7, 2002)
                     ^
                     Error: `left` takes only one argument
*/

This will not:

%let date=%sysfunc(left(%qsysfunc(today(),worddate.)));

Defining Macro Functions

A macro function is just a macro that resolves to a value, rather than running as a subroutine. Note that, in order to be as flexible as a macro function, certain restrictions must be born. Chiefly, a data step or process cannot be invoked.

%macro exist(dsname);
  %sysfunc(exist(&dsname))
%mend exist;

Macro Functions in the Data Step

A macro function can be called within a data step using resolve(). resolve actually can be used to get the value of any type of macro, including a macro variable.

resolve() can be called in three ways:

method1 = resolve("&mymacro");

method2call = '&mymacro';
method2 = resolve(method2call);

method3 = resolve(cats('&','my','macro'));


Macro Variables as Macro Programs

Disclosure: this is a terrible idea.

%let mymacro=%str(proc means data=LIBREF.TABLE; var foo bar; run;);

&mymacro


Macro Programs

Macro programs can be used in any location other than data input. A macro program is executed by prepended the label with an ampersand (%).

Macro programs can be called alone, or can be called with arguments within parentheses.

Do not add a semicolon (;) after a macro program call to terminate the line-the macro expands out to syntax that is correctly terminated.

Comments

Generally, use the /* comment */ style. The alternate options have tricky limitations.

%macro mymacro;
  /* This is a comment that will be treated literally by the macro processor */
  %* This is a comment that the macro processor will interpret, so don t use any special characters like quotes in me;
  *  This is a comment that the macro processor will tokenize, so don't use any macro triggers in me;
%mend mymacro;

Arguments and Options

A macro can take both positional arguments and keyword options. Keyword options can also take default values, or be left null.

%macro mymacro(start, stop, opts=, stats=N SUM MIN MAX);
  proc means data=LIBREF.TABLE &stats &opts;  /* default stats are "N SUM MIN MAX", default opts are "" */
    where "&start"d <= date <= "&stop"d;
  run;
%mend mymacro;

%mymacro(01JAN2020, 01JAN2021)

Looping

Looping a SAS macro is accomplished through one of three structures. The most common structure is iterating over ranges. This is done %do ... %to ... %by ....

%macro mymacro;
  %* Note: '&year.TABLE' becomes '2000TABLE'-the period is indicating the end of the macro label;
  %do year=2000 %to 2020 %by 1;
    /* do something with "&year" */
  %end;
%mend mymacro;

%mymacro

Note that %eval is automatically called on the values supplied to loops.

Other structures are %do %until ... and %do %while ....

Variable Number of Arguments

It is also possible to use a variable number of arguments. This is most feasibly used with a %do %until ... loop.

Enable the parmbuff option and access the arguments from the &syspbuff array.

%macro mymacro / parmbuff;
  %let index=1;                    /* initialize the index */
  %do %until (%scan(&syspbuff,&index) = '');
    /* do something with "%scan(&syspbuff,&index)" */

    %let index=%eval(&index + 1);  /* increment the index */
  %end;
%mend mymacro;

%mymacro(1, 2, 3)

Conditional Processing

To process a macro statement conditionally, use the %if statement.

if EXPR then ASSIGNMENT;
else if EXPR then ASSIGNMENT;
else ASSIGNMENT;

Similar to conditional processing in data steps, an %if statement can be followed by %else (or %else %if) statements. And a %do block can be used to conditionally process multiple macro statements.

Similar to looping statements, %eval is automatically called on the expression.

%macro mymacro(dsname);
  %let dsid = %sysfunc(open(&dsname));  /* open file, and do something different if the id=0 (indicates an error) */

  %if (&dsid > 0) %then %do
    /* do something like "%let nobs = %sysfunc(attrn(&dsid, nobs));" */

    %let rc = %sysfunc(close(&dsid));   /* close file, though we can't do anything if the return code is an error */
  %end;
  %else %if %superq(&dsname = "") %then %put You forgot to set the dataset name!;
  %else %put Could not open the dataset &dsname..;
%mend;

%mymacro(myds)

Branching

Lastly, SAS macros can use %goto to branch out. This is useful if an error could be reached at multiple points of a program.

%macro mymacro(dsname);
  %if (&dsname = "") %then %goto error;

  /* do something */
  %goto success;

  %error:
    %put You forgot to set the dataset name!;
    quit;
  %success:
%mend mymacro;

%mymacro("")

Macro Programs in the Data Step

A macro program can be called from within a data step using call execute(). execute takes a single string argument-the name of the macro program to run.

While the macro is immediately interpreted, any contained processes or data steps are added to the stack and will execute after the parent data step ends.


Debugging Macros

Set the mcompilenote option to see compilation errors in macro programs logged.

options mcompilenote=all;
/* do something */
options mcompilenote=none;

Enable the symbolgen option to see macro variable substitutions logged as they are processed. Enable the mprint option to see macro program commands as they are submitted.

options symbolgen mprint;
/* do something */
options nosymbolgen nomprint;

The %put macro command can log arbitrary text.

%if (&dsid=0) %then %put Could not open file &dsname;


CategoryRicottone

SAS/Macros (last edited 2022-06-08 20:13:15 by DominicRicottone)