Below is a simplified example of what I am trying to accomplish. I have a data set that has a character variable called MOD with values "N", "0", "1", ... "9". I am using a macro, stored in a separate file to condense the length of the program, to define a new variable called PRODUCT. The PRODUCT value in the third record in the output data set should be "SCOOTER" based on ID = "0001" and MOD = "6". However, I have something coded incorrectly because it is returning "BIKE". I suspect that it is in the IN operator since I haven't used this functionality in a macro before but I suppose it could be anything. Any help is appreciated. Thanks in advance.
%MACRO PRODUCT(ID, MOD) / MINOPERATOR;
%local M1;
%if &MOD = N %then %let M1 = N; %else %if %EVAL(&MOD in 6 8 9) %then %let M1 = Y; %else %let M1 = N;
if &ID = "0001" and "&M1" = "Y" then PRODUCT = "SCOOTER";
else if &ID = "0001" then PRODUCT = "BIKE";
else if &ID = "0002" then PRODUCT = "SKATEBOARD";
%MEND;
data TEST1;
infile datalines;
input ID $ MOD $;
length PRODUCT $ 10;
%PRODUCT(ID, MOD)
datalines;
0001 N
0001 0
0001 6
0002 8
;
run;
Since you have gotten this far, I would suggest you would learn more by solving this one yourself.
Add this statement at the beginning:
options MLOGIC;
That will show you what macro language is trying to do at each step of the way.
I can see this is getting too complex, and that MLOGIC might not be enough to paint the right picture. Here's some more detail.
Macro programming statements such as %IF %THEN are never part of a DATA step. Your program generates this code instead:
%if &MOD = N %then %let M1 = N; /* As Tom points out, &MOD has the value of the letters MOD */
%else %if %EVAL(&MOD in 6 8 9) %then %let M1 = Y;
%else %let M1 = N; /* Always assigned as N, and never changed during the DATA step */
data TEST1;
infile datalines;
input ID $ MOD $;
length PRODUCT $ 10;
if &ID = "0001" and "&M1" = "Y" then PRODUCT = "SCOOTER";
else if &ID = "0001" then PRODUCT = "BIKE";
else if &ID = "0002" then PRODUCT = "SKATEBOARD";
datalines;
0001 N
0001 0
0001 6
0002 8
;
run;
The %IF %THEN statements execute just once, before the DATA step even begins to run. The DATA step does contain the DATA step programming statements such as IF THEN that generates PRODUCT. But the macro %IF %THEN statements run just once, before the DATA step begins to execute. Use DATA step programming statements to change the values of DATA step variables as the DATA step executes.
When beginning with the macro language, it's helpful to start by having working SAS code with no macros. Then you can try to write a macro that will generate the same SAS code.
Can you post an example of working SAS code for what you want. I imagine it's something like below?
data TEST1;
infile datalines;
input ID $ MOD $;
length PRODUCT $ 10;
if MOD = "N" then M1 = "N";
else if MOD in ("6" "8" "9") then M1 = "Y";
else M1 = "N";
if ID = "0001" and M1 = "Y" then PRODUCT = "SCOOTER";
else if ID = "0001" then PRODUCT = "BIKE";
else if ID = "0002" then PRODUCT = "SKATEBOARD";
datalines;
0001 N
0001 0
0001 6
0002 8
;
run;
Let's be the macro processor and see what you asked it to do.
You called the macro with ID=ID and MOD=MOD.
So the first %IF will become:
%if MOD = N %then ...
Which is never true. The comparison fails on the first letter since the letter M does not equal the letter N.
So we need to evaluate the %ELSE branch instead. That has another %IF that will look like:
%if %EVAL(MOD in 6 8 9) %then ...
And again this can never be true no matter what value you set for the MINDELIMITER option since the string MOD does not appear after the IN operator. PS There is no need for %EVAL here since %IF always used %EVAL() to evaluate the test expression. You might leave the () to make it easier for the humans to read.
I suspect your logic actually depends on the value of the dataset variable MOD so you need to code IF statements and not %IF statements.
I assume that what you are trying to do is actually what this code does (no macro stuff):
data TEST1;
infile datalines;
input ID $ MOD $;
length PRODUCT $ 10;
if MOD = 'N' then M1 = 'N';
else if MOD in (6,8,9) then M1 = 'Y';
else M1 = 'N';
if ID = "0001" and M1 = "Y" then PRODUCT = "SCOOTER";
else if ID = "0001" then PRODUCT = "BIKE";
else if ID = "0002" then PRODUCT = "SKATEBOARD";
datalines;
0001 N
0001 0
0001 6
0002 8
;
run;
I assume that you turned the stuff into a macro because you want to be able to call the code with different input variables. The problem you have comes from the fact that the macro code is executed BEFORE the actual datastep (the macro is simply a code generator which creates code for the data step, which is then compiled).
To get an idea of what happens, you can use the MPRINT option:
78 data TEST1; 79 infile datalines; 80 input ID $ MOD $; 81 length PRODUCT $ 10; 82 83 %PRODUCT(ID,MOD); MPRINT(PRODUCT): if ID = "0001" and "N" = "Y" then PRODUCT = "SCOOTER"; MPRINT(PRODUCT): else if ID = "0001" then PRODUCT = "BIKE"; MPRINT(PRODUCT): else if ID = "0002" then PRODUCT = "SKATEBOARD"; 84 datalines;
What happens is that the macro variable MOD in the macro is interpreted during macro execution(with the value "MOD" in the call shown, not the value of the datastep variable), not during datastep execution. The local macro variable M1 is thus always set to N.
What you may try is something like this
%MACRO PRODUCT(ID, MOD) ;
if &ID = "0001" and &mod in(6,8,9) then PRODUCT = "SCOOTER";
else if &ID = "0001" then PRODUCT = "BIKE";
else if &ID = "0002" then PRODUCT = "SKATEBOARD";
%MEND;
options mprint;
data TEST1;
infile datalines;
input ID $ MOD $;
length PRODUCT $ 10;
%PRODUCT(ID,MOD);
datalines;
0001 N
0001 0
0001 6
0002 8
;
run;
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
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.