BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
RyanJB
Obsidian | Level 7

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.

1 ACCEPTED SOLUTION

Accepted Solutions
Astounding
PROC Star

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; 

View solution in original post

9 REPLIES 9
Kurt_Bremser
Super User

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.

RyanJB
Obsidian | Level 7
Thanks, the difference in how data and macro steps are evaluated is what I think I was really asking (without knowing it), so that's quite helpful.
pink_poodle
Barite | Level 11

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

Reeza
Super User

@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. 

https://documentation.sas.com/?docsetId=mcrolref&docsetTarget=p06bq1j5uf0sz4n1vd3wx36tf0ud.htm&docse...

 

PS Awesome to see you answering questions though 🙂

 

pink_poodle
Barite | Level 11
@Reeza, thank you for letting me know :). I like the tabs and the layout of the new version.
RyanJB
Obsidian | Level 7
That's a perfect reference. Thanks!
Tom
Super User Tom
Super User

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= ;

 

 

Astounding
PROC Star

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; 

RyanJB
Obsidian | Level 7
You're correct, I didn't really understand the difference between the two. Thanks for the help!

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Mastering the WHERE Clause in PROC SQL

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.

Discussion stats
  • 9 replies
  • 1391 views
  • 11 likes
  • 6 in conversation