I am writing a bigger script and basically need the following principles to work which I demonstrate by two examples (demonstrating different concepts):
Example 1. Depending on the positional argument, I want to set different data sets:
%macro MyFun2(random);
data MyData;
if &random. = AA then do;
set sashelp.cars(obs=2);
end;
else if &random. = BB then do;
set sashelp.cars(obs=3);
end;
run;
%mend MyFun2;
%MyFun2(AA);
%MyFun2(BB);
Result is:
Notice how the two first columns are created.
One message in the log is related to "Uninitialized variable" which I read about here http://statskom.com/sas-tips-tricks-9-note-variable-x-is-uninitialized/ . Nevertheless, I am not fully sure of how to handle it properly.
Example 2. Depending on the positional argument, I want to create a new column taking different values:
Notice the comment in the code below, it is obivously wrong but I don't know how to handle it properly.
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
if &random. = AA then NewCol = 'AA';
else if &random. = BB then NewCol = 'BB';
else NewCol = "Write something valid"; *This is obiviously wrong, but I would somehow like to do: if &random. is not equal to AA or BB, then assign the value "Write something valid" to NewColumn.;
run;
%mend MyFun3;
%MyFun3(AA);
%MyFun3(BB);
%MyFun3(Test);
The code does not run, I don't write the log since the code can be easily reproduced.
I hope it is clear of what I want to achieve. All advice of how to properly do what I want is appreciated.
Hi,
@1) Maxim No. 1 says: "Read the log". Log says:
1 %macro MyFun2(random); 2 data MyData; 3 if &random. = AA then do; 4 set sashelp.cars(obs=2); 5 end; 6 else if &random. = BB then do; 7 set sashelp.cars(obs=3); 8 end; 9 run; 10 %mend MyFun2; 11 12 %MyFun2(AA); NOTE: Variable AA is uninitialized. NOTE: Variable BB is uninitialized. NOTE: There were 2 observations read from the data set SASHELP.CARS. NOTE: The data set WORK.MYDATA has 2 observations and 17 variables. NOTE: DATA statement used (Total process time): real time 0.09 seconds cpu time 0.03 seconds 13 %MyFun2(BB); NOTE: Variable BB is uninitialized. NOTE: Variable AA is uninitialized. NOTE: There were 2 observations read from the data set SASHELP.CARS. NOTE: The data set WORK.MYDATA has 2 observations and 17 variables. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.01 seconds
Modify your code the following way:
%macro MyFun2(random);
data MyData;
%if &random. = AA %then %do;
set sashelp.cars(obs=2);
%end;
%else %if &random. = BB %then %do;
set sashelp.cars(obs=3);
%end;
run;
%mend MyFun2;
%MyFun2(AA);
%MyFun2(BB);
Log will be clean and if you add `mprint` option you will see the code of data step that was executed.
@2) The same, read the log.
Modify your code for example like that:
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
length NewCol $ 30;
if "&random." = "AA" then NewCol = 'AA';
else if "&random." = "BB" then NewCol = 'BB';
else
do;
if _N_ = 1 then put "WARNING: Write something valid";
NewCol = "Write something valid"; /*This is obiviously wrong, but I would somehow like to do: if &random. is not equal to AA or BB, then assign the value "Write something valid" to NewColumn.;*/
end;
run;
%mend MyFun3;
%MyFun3(AA);
%MyFun3(BB);
%MyFun3(Test);
All the best
Bart
Hi,
@1) Maxim No. 1 says: "Read the log". Log says:
1 %macro MyFun2(random); 2 data MyData; 3 if &random. = AA then do; 4 set sashelp.cars(obs=2); 5 end; 6 else if &random. = BB then do; 7 set sashelp.cars(obs=3); 8 end; 9 run; 10 %mend MyFun2; 11 12 %MyFun2(AA); NOTE: Variable AA is uninitialized. NOTE: Variable BB is uninitialized. NOTE: There were 2 observations read from the data set SASHELP.CARS. NOTE: The data set WORK.MYDATA has 2 observations and 17 variables. NOTE: DATA statement used (Total process time): real time 0.09 seconds cpu time 0.03 seconds 13 %MyFun2(BB); NOTE: Variable BB is uninitialized. NOTE: Variable AA is uninitialized. NOTE: There were 2 observations read from the data set SASHELP.CARS. NOTE: The data set WORK.MYDATA has 2 observations and 17 variables. NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.01 seconds
Modify your code the following way:
%macro MyFun2(random);
data MyData;
%if &random. = AA %then %do;
set sashelp.cars(obs=2);
%end;
%else %if &random. = BB %then %do;
set sashelp.cars(obs=3);
%end;
run;
%mend MyFun2;
%MyFun2(AA);
%MyFun2(BB);
Log will be clean and if you add `mprint` option you will see the code of data step that was executed.
@2) The same, read the log.
Modify your code for example like that:
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
length NewCol $ 30;
if "&random." = "AA" then NewCol = 'AA';
else if "&random." = "BB" then NewCol = 'BB';
else
do;
if _N_ = 1 then put "WARNING: Write something valid";
NewCol = "Write something valid"; /*This is obiviously wrong, but I would somehow like to do: if &random. is not equal to AA or BB, then assign the value "Write something valid" to NewColumn.;*/
end;
run;
%mend MyFun3;
%MyFun3(AA);
%MyFun3(BB);
%MyFun3(Test);
All the best
Bart
%IF and IF (without the %) do not do the same thing.
%IF tests the value of macro variables.
IF tests the value of data step variables.
When you write macro code, or code involving macro variables, you need to ask yourself if you are testing the value of a macro variable, or testing the value of a data step variable, to be sure you have the right one (%IF or IF).
Let's look at your Example 1. Are you trying to test the value of a macro variable, or a data step variable? What is your answer?
Thank you, it clarified things very much.
Now this is really weird to me:
In the code below, I use for instance %IF since I am testing a macro variable.
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
length NewCol $ 30;
%if &random. = "AA" %then NewCol = 'AA';
%else %if &random. = "BB" %then NewCol = 'BB';
%else %do;
*if _N_ = 1 then put "WARNING: Write something valid"; * Removed this from an solution suggestion since I don't fully understand it yet. ;
NewCol = "Write something valid";
%end;
run;
%mend MyFun3;
%MyFun3(AA);
This code yields the result:
As seen from the red box, it does not accept AA as the argument I was thinking of it to be.
The right thing to do (As indicated in the answers above) would be to write the code:
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
length NewCol $ 30;
if "&random." = "AA" then NewCol = 'AA';
else if "&random." = "BB" then NewCol = 'BB';
else do;
*if _N_ = 1 then put "WARNING: Write something valid";
NewCol = "Write something valid";
end;
run;
%mend MyFun3;
So now we for instance have
"&random."
which is seen as a string rather than a macro? I could understand this with "Back-engineering" (understanding it given the solution) but would have big trouble coming up with it myself?
Why is the first code wrong?
Thanks.
Your code should be like this:
%macro MyFun3(random);
data MyData;
set sashelp.cars(obs=2);
length NewCol $ 30;
%if &random. = AA %then
%do;
NewCol = 'AA';
%end;
%else %if &random. = BB %then
%do;
NewCol = 'BB';
%end;
%else
%do;
/*if _N_ = 1 then put "WARNING: Write something valid"; * Removed this from an solution suggestion since I don't fully understand it yet. ; */
NewCol = "Write something valid";
%end;
run;
%mend MyFun3;
%MyFun3(AA);
From macro language perspective _every_ value is a text so quotes are unnecessary.
To test if value of macro variable X is ABC you just write:
%if &X. = ABC %then...
Quotes are necessary on the 4GL language level, because you want to inform compilator that value `AB` is in fact a string text ("AB") an not a variable name (AB)
All the best
Bart
P.S. Always, always, always use block comments ( /* comment */) in macros.
options minoperator mprint mlogic symbolgen;
%macro MyFun3(random) / minoperator;
data MyData;
set sashelp.cars(obs=2);
%if &random. in (AA BB) %then %do;
NewCol ="&random";
%end;
%else %do;
NewCol="Write something valid";
%end;
run;
proc print data=mydata;
run;
%mend MyFun3;
%MyFun3(AA);
%MyFun3(BB);
%MyFun3(Test);
You can use minoperator. Also, you don't need to hardcode 'AA' and 'BB' if it's the same as the value of &random.
@yabwon wrote:
Hi,
@1) Maxim No. 1 says: "Read the log".
It's actually Maxim 2 😉
@Kurt_Bremser wrote:
@yabwon wrote:
Hi,
@1) Maxim No. 1 says: "Read the log".
It's actually Maxim 2 😉
🤔 So maybe it's time for
Maxim 0: Read the maxim's.
@mkeintz wrote:
@Kurt_Bremser wrote:
@yabwon wrote:
Hi,
@1) Maxim No. 1 says: "Read the log".
It's actually Maxim 2 😉
🤔 So maybe it's time for
Maxim 0: Read the maxim's.
Good point Mark (@mkeintz)! 👍
Sorry Kurt (@Kurt_Bremser) I was quoting off the top of my head. 🤓
All the best
Bart
You've received good advice on understanding the difference between %if and if (and %then vs then, %do vs do, %end vs end) in macro coding.
Below, I suggest another consideration in writing and using macros: avoid code duplication - a major benefit of good macro coding. In the case of Example 1, you have two instances of a DATA step, which vary only in the SET statement.
So instead, in your macro code, make a new macro variable DSN, as follows (which also eliminates the need for %do and %end).
%macro MyFun(random);
%local dsn ;
%if &random. = AA %then %let dsn=sashelp.cars(obs=2);
%else %if &random. = BB %then %let dsn=sashelp.cars(obs=3);
%else %goto eom ;
data MyData;
set &dsn ;
run;
%eom: %mend MyFun2;
options mprint;
%MyFun2(AA);
%MyFun2(BB);
This is especially useful if the embedded DATA step is long - needless duplication creates more chances for error, and makes code maintenance more difficult.
Take a step back and internalize these sentences:
"The macro language is a code generator that does its work before any code is actually executed"
"The macro language has one data type only: TEXT"
From this, you will get the answers to your questions.
From the first, you get that you can never access the values of data step/dataset variables in a macro statement.
From the second sentence, you get that no quotes are needed, they are actually part of the "data" (text).
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.