Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
The above line creates the macro variable db and gives this variable the value of
test2.
(Note that when you start a statement with % this tells SAS that what follows is either a
built in macro facility statement (like %let) or function, or a macro you have created
(we will return to that later). The exceptions to this are %INCLUDE, %LIST, and
%RUN which are not part of the macro facility but instead are part of base SAS. Some
macro statements and functions can be run in open code like %let. Others must be run
from within macros.)
You could put this line near the top of a SAS program if you are using the same SAS
code but wish to run it on different data sets. Instead of doing search and replaces on
the data set name, you just change the value of the macro variable, and use the macro
variable in your code.
So that leads to the question how do you refer to a macro variable?
You refer to it by preceding the variable name with & (an ampersand), as in the
following statement:
proc print data=&db;
This statement will print the contents of the data set with a name equal to the value of
the macro variable. E.g., it would print a data set named test2 if we had assignd db
the value test2.
So let us actually work our way through such a very simple example. First create two
temporary SAS data sets. Start up SAS and create two different temporary data sets:
data test1;
input a b;
cards;
1 2
4 5
;
data test2;
input a b;
cards;
3 4
4 1
;
These exceedingly simple, even silly, example data sets each have two variables (a and
b) and two observations with values for variable. Create these data sets by submitting
your program.
Now enter the following simple code:
%let db=test2;
Now just change test2 to test1 in the %let statement and rerun the code. You now
should get a print out and plot for test1 instead of test2. Obviously in this example
using a macro variable did not save much work, but imagine a hundred lines of code
with many references to the data set. With the macro you only need to make one
change and you can run your code on a different version of the data set.
More on Macro Variables and Turning Blocks of SAS Code into Macro Variable
Values
Macro variables names are subject to usual naming conventions that apply for SAS data
set variables, but the names are not subject to the same length limits as SAS data set
variables (SAS dataset variable names must be eight characters or less). More
specifically, macro variable names must start with a letter or an underscore and can be
followed by letters or digits. You can assign any name to a macro variable as long as
the name is not a reserved word. The prefixes AF, DMS, SQL, and SYS are not
recommended because they are frequently used in SAS software for automatic macro
variables.
The values assigned to SAS macro variables are considered to just be a long sequence
of characters. I.e., text not numbers. The values can be quite long strings of characters
up to 65,534 characters.
A limitation is that if the value contains certain special characters (e.g., semicolons,
quotation marks, ampersands, and percent signs) or mnemonics (e.g., AND, OR, or
LT), you must use a macro quoting function to mask the special characters.
Otherwise, the special character or mnemonic might be misinterpreted by the macro
processor. A common example where you might want to do this is when you want the
value of a macro variable to be a block of SAS code containing several SAS statements
ending in semicolons. To handle this situation you can use the str function. Without
the str function SAS would interpret the first semicolon it came to as the end of %let
statement.
For example you could assign a value to the macro variable sascode as follows
%let sascode=%str(
proc plot data=&db;
title "plot for &db";
plot a*b;
run;
);
This code defines a macro called testm. Once we execute this code within the
session, SAS now knows about this macro. So if we want to run the macro we would
then need two lines, one to define/assign a value to db and the second to run testm:
%let db=test1;
%testm;
Note that the semicolon at the end of your macro invocation is not required.
A SAS Macro with an Argument
The above example is a bit cumbersome in that we need two lines of code each time we
execute testm for a different database, and because the value of db is changed
globally, not just for the function testm. A better way is to define the macro testm so it
takes an argument:
/* This macro takes one argument */
%macro testm(dbin);
proc print data=&dbin;
title "print out of data set &dbin";
run;
proc plot data=&dbin;
title "plot for data set &dbin";
plot a*b;
run;
%mend;
What this change does is create a macro variable dbin that is local, so it only exists
when the macro testm is running. We then call the macro with a line like:
%testm(test1);
SAS knows to assign the argument (value within parentheses) to the local macro
variable dbin, and this gets resolved to its actual value as the macro runs. We could
then just change test1 to test2 in the above line and get new results and not leave
any side effects like changing the value of a global macro variable. In general macro
variables created in open code are global in scope and those created within macros are
local in scope to that macro, so that once the macro is done they no longer exist. We
will talk more about scope of SAS macro variables later.
Let us get a bit fancier and include multiple arguments:
/* macro taking multiple arguments */
%macro testm(dbin, depvar, indvar);
proc print data=&dbin;
title "print out of &dbin";
/* use double quotes if you want macro variable resolved */
run;
proc plot data=&dbin;
title "plot of &depvar versus &indvar for dataset &dbin";
plot &depvar*&indvar;
run;
%mend;
%testm(test1,a,b);
In this example we just list multiple local macro variables separated by commas in the
header line of the macro definition. This style uses positional definition. When we
invoke the macro we need to provide the arguments in this same order so it knows
which value to assign to which local macro variable.
Note the use of some title statements to help us keep track on our output what dataset is
being printed or plotted. Note that we use double quotes. If you use single quotes the
macro variables dont get resolved.
Macros Defined using Keywords
Up to now we have been defining macros with positional arguments. Sometimes you
might tend to use default values for most arguments and just want to change one (or a
few out of many). In such cases its better to use keyword arguments and set defaults;
/* macro taking multiple arguments via keywords*/
%macro testm(dbin=test1, depvar=a, indvar=b);
proc print data=&dbin;
title "print out of &dbin";
/* use double quotes if you want macro variable resolved */
run;
proc plot data=&dbin;
title "plot of &depvar versus &indvar for dataset &dbin";
plot &depvar*&indvar;
run;
%mend;
* use defaults;
%testm;
* use test2 and default variables;
%testm(dbin=test2);
*use test2 and b as dep varible;
%testm(dbin=test2, depvar=b, indvar=a);
*does same as above;
%testm(depvar=b,indvar=a,dbin=test2);
*uses default data and b as dep variable;
%testm(depvar=b,indvar=a);
What happens here is that when the macro is invoked the parmbuff option tells SAS to
assign to the macro variable syspbuff a string equal to the text inside the parentheses.
%SCAN is a built in macro function where the first argument is a string, the second is
which item within the string to grab. There is a third argument to specify the
delimiter if your text is not separated into components by a legal delimiter for your
system (e.g., here by a comma).
%do, %while, and %end are built in macro statements that define a do loop that
executes until the while condition is false. In this case it continues to execute until the
value of currdb is null. The way this works is that dbcnt is set to 1, the first time
through the loop the value of currdb is set to the first argument (first block of text
before the first comma of syspbuff), then the proc print and proc plot statements get
executed using that value of currdb. Then dbcnt is incremented by 1 and the next value
from syspbuff is read. The loop continues until scan is told to read past the last element
syspbuff. This returns a null value and the loop is terminated.
Notice in the above code the use of the macro function %eval statement. This is
required because macro variables are just strings. If you replaced %eval(&dbcnt+1)
by just &dbcnt+1, The value of dbcnt at the end of the first time through the loop
would be the string 1+1 not the string 2. Note that %eval uses integer arithmetic.
If you need floating point arithmetic use %sysevalf .
More Things to Know about Building Macro Variables
It is often useful to combine text and the value of a macro variable, or the value of
several macro variables to obtain a single value for a new macro variable. Here are
some useful things to know.
1. Leading and trailing blanks are not included so
%let street=Maple ;
%let street= Maple;
%let street= Maple ;
%put
%put
%put
%put
In open code;
aglobal=&aglobal; /*print global macro variable to log */
indat=&indat;
/*print macro var local to subset to log */
labtext=&labtext; /*print macro var local to printit to log */
In the code above the following occurs. (1) A made-up dataset is created. 1 is meant to
designate data for males versus 0 for femailes. (2) A global macro is created. It is not
really used for anything except to demonstrate its global scope. (2) Then the macro
subset is defined. It grabs a specified data set and keeps only the observations with
the specified variable equal to 1. This macro then calls the macro printit, Note that a
SAS stores defines macros and their scope in symbol tables. There may be times when
you are developing macros that it would be useful to write all or part of the contents of
the global and local symbol tables to the SAS log. To do so, use the %PUT statement
with one of the following options:
_ALL_ to get all currently defined macro variables, regardless of scope. This includes
user-defined and automatic macro variables.
_AUTOMATIC_ to get all automatic macro variables. The scope is listed as
AUTOMATIC. All automatic macro variables are global except SYSPBUFF.
_GLOBAL_ to get all user-defined global macro variables. The scope is listed as
GLOBAL. Automatic macro variables are not listed.
_LOCAL_ describes user-defined local macro variables defined within the currently
executing macro. The scope is listed as the name of the macro in which the macro
variable is defined.
_USER_ describes all user-defined macro variables, regardless of scope. The scope is
either GLOBAL, for global macro variables, or the name of the macro in which the
macro variable is defined.
course it better exist when the macro is actually run!
Next the macro printit is defined; Finally the macro subset is run; and after
printit completes subset uses %put statements to write the values of macro
variables to the SAS log.
Note that %put statements are used to write the values of macro variables to the SAS
log from within each of the two macros and in open code. The point here is that the
global macro is defined at all times after it is created. The macro variables created
within subset are defined while subset is running and this includes while macros
called from within subset are running. However the macro variables created within
printit are not defined when printit is not running.
10
Note that the %put statement is one quick way to check that the values of macro
variables are what you think they are.
Sometimes it is useful to explicitly force a macro variable to be either global or local.
Use %local or %global to do this. Inside of macro you might want to create a variable
known outside the macro so it needs to be global. Inside a macro you might be creating
and using a variable you will only use inside the macro and you are not sure if there is
macro with same name at higher level. If you specify it is local you will use that
version within the macro and not change the value of higher level versions.
Some Tricks on Debugging Macro Code
Perhaps the most important thing to remember is not specific to writing SAS macros
but applies to any computer programming. Do things in small steps or modules and
then when the pieces work put things together. For example, I usually write the SAS
code for a single analysis to check it works before attempting to automate repeated
analyses using a macro. Then my first version of the macro I set up as just the header
and perhaps some preliminary processing of the arguments, to make sure it is getting
the information in the way it is supposed to. Then I might actually put the SAS
statements inside the macro without change and make sure it runs. Finally I would
replace appropriate text in the SAS statements by the macro variables created from the
macro arguments, and see if I could get this to produce the same results.
Second it is worth checking for common errors. The SAS macro reference (help)
suggests checking for the following common errors:
the names in the %MACRO and %MEND statements match, and there
11
12
Things get complicated with these kinds of errors because one problem often cascades
into additional ones because code is not being interpreted in the context you expected it
to be.
In addition to checking for common errors and good programming practice there are
some tools to help. You already saw the %put which allows you to write statements
including resolved macro variables to the SAS log. You can also set some SAS system
options to generate lots of output about what is happening and how things are being
resolved. Perhaps the most useful of these are MLOGIC, MPRINT, and
SYMBOLGEN.
MLOGIC traces the flow of execution of your macro, including resolution of
parameters, the scope of variables (global or local), conditions of macro expressions
being evaluated, number of iterations for loops, and the beginning and end of each
macro execution. MLOGIC is useful when a bug lies in the program logic. MLOGIC
can produce lots of output so you probably only want to turn it on when you really need
it.
MPRINT writes each SAS statement generated by a macro to the SAS log. This can be
very useful in finding cases where your macro is generating SAS code in a manner you
did not expect. Again this can produce very large log files and you probably only want
it on for debugging purposes.
SYMBOLGEN tells you what each macro variable resolves to each time a variable is
resolved by writing messages to the SAS log. Because it produces a statement for every
resolution it is sometimes easier to just use %put when you suspect you know which
statements and macro resolutions are the problem.
You turn these options on by using the SAS statement:
OPTION xxx;
where xxx is either MLOGIC, MPRINT, and SYMBOLGEN. You can turn these off
by submitting another OPTION statement with xxx being NOMLOGIC, NOMPRINT,
or NOSYMBOLGEN.
Even More on Resolving Macro Variables
Suppose you had created a series of macro variables with the same prefix, distinguished
by a series of numbers, say: myvar1, myvar2, myvar3,
Now (say in the middle of a loop) you want to access the correct one of them based on
the value of the macro variable i.
If you wrote &myvar&i you would probably get an error because SAS would attempt
to resolve myvar and then resolve i and concatenate them, rather than concatenate
13
the text myvar with the value of the variable i. The solution is to use a double &,
which SAS refers to as indirect referencing: So you would write: &&myvar&i. When
SAS encounters it actually makes two passes through to interpet this code. The first
time through it converts && to just &, and then continues reading in (and resolving
additional macro variables as needed), then attempts to resolve the result. So after the
first pass &&myvary&i might become &myvar3 (if i=3), and then the value of myvar3
would be found. The same basic logic applies if you use more than two &s. E.g., say
the macro variable varname had a value of myvar3. If you used &&&varname, the first
two && would be converted into a single &, &varname would be resolved to myvar3
and concatenated with the &, and then the result of the first pass, &myvar3 would be
resolved. This is useful when the value of one macro variable is the name of another
macro variable. This is one way to pass the names of sas macro variables, rather than
their values, to a SAS macro. This is useful when the values are long and especially if
they contain commas.
Below is an example of code using &&&:
/* In following teststring imbedded blanks are included */
%let teststring=%str(year depthcat year*depthcat,
year);
/* Value of stringref is just the name of the previous macro var */
%let stringref=teststring;
/* %put statements for debugging/demo purposes */
%put here is '&stringref:' &stringref;
%put here is '&&stringref:' &&stringref;
%put here is '&&&stringref:' &&&stringref;
%put here is '&teststring:' &teststring;
This next example shows a case where we create new macro variables with names built
from the values of two other macro variables. This can be useful when creating macro
variables in a general purpose macro in a situation where you do not know in advance
what their names should be.
/* This example creates a set of global macro variables
with names of testvar1, testvar2, ... testvar5 and sets values of
1, 2, ...5 to them */
%let prefix =testvar;
/* Need to make and run a macro because %do is not valid in open code
*/
%macro makevars;
%do i=1 %to 5;
%Global &prefix.&i;
%let &prefix.&i=&i;
%end;
14
%mend;
%makevars;
/* Just checking we got the intended results ! */
%put &testvar1 &testvar2 &testvar3 &testvar4 &testvar5;
LT
GE
AND
GT
--
OR
IN
NOT
, (comma) /
EQ
&
'
NE
<
"
>
LE
The kind of problems that can arise is illustrated by:
%let print=proc print; run;; /* ERROR */
This really repeats the issue we saw before when making a macro variable a block of
SAS statements. The problem is that the intent is to assign proc print; run; to print
but the first semicolon would be interpreted as the end of the %let statement. For this
example one of the simplest quoting functions, %str, which we have seen before, can
solve the problem.
%let print=%str(proc print; run;);
The most commonly used macro quoting functions are:
%STR and %NRSTR
%BQUOTE and %NRBQUOTE
%SUPERQ
The functions with N do the same thing as the functions without the N except they
mask the % and &. So if you want % and & to be part of the string you should use
the N version. But if you want the macro variables resolved you should not. The
functions with a B will treat single unmatched quotation marks and parentheses as
part of a string. %superq works a bit different than the other functions in that it takes a
15
macro variable as an argument, returns the value and does not attempt to resolve macro
references in the value.
Logical Expressions and Conditional Execution
Macros allow for if statements with the following syntax:
%IF expression %THEN statement;
The expression in the above can include things like
&first=&second or &first>&second . While the macro facility is essentially a text
based system it will temporary interpret the macro variables as integer numbers in such
expressions. If you need to have logical evaluation of decimal (floating point) numbers
(or missing values are involved) you need to use the %sysevalf function. E.g., like:
%if %sysevalf(&first>&second) %then %put &first is greater than
&second;
Sometimes you might want to use the symget function instead. In the above dataset
this would be:
Data cat&testval;
16
Set cat&testval;
cat=symget('testval');
run;
The symget function puts the value of testval into cat, and does not try to resolve the
value before doing this, which is useful in some situations.
Grabbing a data set variable value and assigning it to a macro variable is a bit more
tricky. In this case you cannot use a simple assignment statement like %let test=b
because this get resolves before the data step is even executing and so test is just set to
b not to a value of the data set variable b. Here you need to call the symput routine.
Symput is useful for either assigning the value of a data set variable to a macro
variable, or assigning a value to the macro variable conditional on the value of the data
step variable. The basic syntax is CALL SYMPUT(macro-variable, value); macrovariable is typically the name of a macro variable enclosed in single quotes. Value can
be name of a data set variable. If so the value of the value of the macro variable is set
equal to the value of the data set variable. If value is a string enclosed in single
quotes, that string becomes the macro variable value. If you do this conditionally
depending on a data set variable value then you can set the macro variable based on
those conditions. E.g., you could set the macro variable to the value of b when a=1;
If macro-variable is the name of a character variable in the SAS data set rather than a
string in quotes, then macro variables are created corresponding to each name. This is
really useful if the SAS data set is basically a list of macro variable names and the
values you want to assign to those macro variables.
For those who are heavy Proc SQL users, the select statement is the analog of the call
symput routine for data steps. An example application might be:
proc sql;
select min(aicc) into: min_aic from aics;
quit;
This code finds the minimum of the variable aiccc in the data set aics and assigns this
as the value for the macro variable min_aic.
17