I have run into this problem in 2 separate iterations, but it appears to be the same issue. In a macro, I use an 'if' conditional that should evaluate to false but SAS evaluates the code under the 'if' anyway.
Scenario 1:
%macro weird_if(table, sort1, sort2); proc sort data = work.data_1; by &sort1 &sort2; run; data work.data_&table; set work.data_1; by &sort1 &sort2; if "&sort2" ~= "" then do; if first.&sort2 then total_2 = 0; end; if first.&sort1 then total = 0; total + 1; total_2 + 1; if last.&sort1 then output; run; %mend weird_if;
When I run this with no value in for sort2, it throws an error (expecting punctuation at if.firstthen total_2 = 0; - it goes ahead and evaluates all lines even though if "&sort2" ~= "" should evaluate to 0/false).
Scenario 2 is similar in that the 'if' statement evaluates when it shouldn't. I have a list of variables (var01 ... var12) and run it in a do loop to step through each variable. However, the 0 in the tens spot makes it more difficult, so I use code like this:
%macro zeroes_ones();
%do i = 1 %to 12;
if i < 10 then do; var0&i = xyz; end; else do; var&i = abc; end;
%end
%mend zeroes_ones;
Running that code results in var1 ... 9 and var010 ... var012 being created. That is, it evaluates the statements below the 'if' even when it evaluates to false.
Can anyone help me understand this behavior? I've been able to write (longer) programs to accomplish the same tasks, but would like to get a better idea of what is going on. I apologize if I've made mistakes in my code - this is off the top of my head.
Thanks.
Regarding the first question, you are thinking that macro logic and DATA step logic are interchangeable. There are not. They are always different. A very small recommended change would be to change this logic:
if "&sort2" ~= "" then do; if first.&sort2 then total_2 = 0; end;
One possible replacement:
%if %length(&sort2) %then %do;
if first.&sort2 then total_2=0;
%end;
That way, macro language determines whether or not "first&sort2" appears within your DATA step.
Regarding the second question, again it appears that you use macro language and DATA step language interchangeably. They are always different, and "i" is always different than "&i". You would find it easier to use two loops:
%do i=1 %to 9;
%end;
%do i=10 %to 12;
%end;
Keep in mind that data steps are compiled, so you get
if "" ~= "" then do;
if first. then total_2 = 0;
end;
if &sort2 is empty.
You will want to use macro code
%if "&sort2" ~= "" %then %do;
if first.&sort2 then total_2 = 0;
%end;
so that the data step if is only compiled if &sort2 contains a variable name.
Using macro language triggers the macro processor, that scans through the whole program. It checks the syntax, substituting the macro values you supply. The if " " ~= " " then do ; statement is syntactically correct. However, if first. then total_2 = 0; is not, generating an error.
In the second case, maybe you get to the compilation stage. This stage creates a table header with vars 01-012. The macro processor let a program get that far, since, again, the processor just looks at the syntax and substitutes your macro values. I would be surprised if any of the data-step values were assigned to these variables.
Reference
Macro Processing: How the Macro Processor Executes a Compiled Macro
@pink_poodle its a good idea to link to the newest version of SAS documentation as options can change and sometimes they change the wording to make things more clear. I think it's the same in this case though, but just in general. The newer pages also load faster so that's mainly why I try to use them.
PS Awesome to see you answering questions though 🙂
Don't mix macro code and regular SAS code. The macro processor evaluates the macro code first and passes the resulting text onto SAS to evaluate as if you had typed out the program that way to begin with.
For your first problem just use a little smarter macro code.
%macro mysort(in,out, bylist);
%local i word;
proc sort data = &in out=&out ;
by &bylist ;
run;
data &out;
set &out;
by &bylist;
%do i=1 %to %sysfunc(countw(&bylist));
%let word=%scan(&bylist,&i);
total_&i + 1;
if first.&word then total_&i = 0;
%end;
run;
%mend mysort;
For your second one it is not clear what you are trying to do, but your %DO loop is running 12 times so it generates twelve IF/THEN/ELSE statements. If there even exists a data step variable named I it has nothing to do with the macro variable named I that the %DO loop is using. Perhaps you meant to generate these 12 assignment statements instead?
var01 = xyz;
var02 = xyz;
var03 = xyz;
var04 = xyz;
var05 = xyz;
var06 = xyz;
var07 = xyz;
var08 = xyz;
var09 = xyz;
var10 = abc;
var11 = abc;
var12 = abc;
Not sure why you would want that, but you could do it one of these macros?
%macro zeroes_ones();
%local i;
%do i = 1 %to 12;
var%sysfunc(putn(&i,z2)) = %if &i < 10 %then %do; xyz %end; %else %do; abc %end; ;
%end;
%mend zeroes_ones;
%macro zeroes_ones();
%local i ;
%do i = 1 %to 12;
%if &i < 10 %then %do;
var0&i = xyz;
%end;
%else %do;
var&i = abc;
%end;
%end;
%mend zeroes_ones;
But if your data set really has a variable named I and you need to decide which variable to change based on the value of that variable then you probably want to use an ARRAY instead of macro code.
array out var01-var12 ;
if 1 < i < 10 then out(i)=xyz;
else if i <= dim(out) then out(i)=abc;
else put 'Note: Invalid value for I.' i= ;
Regarding the first question, you are thinking that macro logic and DATA step logic are interchangeable. There are not. They are always different. A very small recommended change would be to change this logic:
if "&sort2" ~= "" then do; if first.&sort2 then total_2 = 0; end;
One possible replacement:
%if %length(&sort2) %then %do;
if first.&sort2 then total_2=0;
%end;
That way, macro language determines whether or not "first&sort2" appears within your DATA step.
Regarding the second question, again it appears that you use macro language and DATA step language interchangeably. They are always different, and "i" is always different than "&i". You would find it easier to use two loops:
%do i=1 %to 9;
%end;
%do i=10 %to 12;
%end;
Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.
Register today!SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.
Find more tutorials on the SAS Users YouTube channel.