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

I'm having trouble getting If/Then statements to work correctly with my macro variables inside a datastep. I'm writing a macro to handle two different cases: calculating stat tests with no transformation, and then calculating stat tests after a natural log transformation. If my data fails the test of normality, I log transform and test again. If it passes, I set my global flag, `log_flag`, to 1. I then want to test the status of this flag in data steps in order to correctly handle the transformed (or not) variables. In this case 'Log_flag' should be set to 0, but the program runs as if it were a 1.  I've tried variations of the following:

Data want;
set have;
if symget("log_flag")=1 then do;
if &log_flag. = 1 then do;
if resolve("log_flag")=1 then do;
%if &log_flag. = 1 %then %do; xxx; %end; test=symget("log_flag"); if test=1 then do; end

No matter what I try, the if/then statement is essentially ignored and all code following it is processed as if the if/then were true, even when it is false. I know that the `log_flag` is being correctly assigned a value of zero because the `%if` `%then` statements work and execute correctly in open code.  I'm just having trouble getting it to resolve correctly inside a datastep.  Also, when I set the "test" variable to the value of symget("log_flag"), the test variable appears in the dataset with the correct value (0 in this case).  The if/then statement just seems to always evaluate to true and I have no idea why.

Please let me know if there is any other information you need to help me figure this out. Thanks guys!

 

1 ACCEPTED SOLUTION

Accepted Solutions
Reeza
Super User

Yeah, that makes sense because you're not using macro logic there, you're using data step logic so the variables will always exist. 

You're misunderstanding how the macro and data step processor work here. You need macro logic and obviously this needs to be in a a macro as well. I think in 9.4 M5 that may have changed.

 

data outliersTable2;
set outliersTable;
where &var.>Upper_limit or &var.<lower_Limit;
%if &log_flag=1 %then %do;
&var.=exp(&var);
q1_2=exp(q1);
median_2=exp(median);
q3_2=exp(q3);
iqr=exp(iqr);
lower_limit=exp(lower_limit);
upper_limit=exp(upper_limit);
drop resp;
%end;
run;

View solution in original post

13 REPLIES 13
RW9
Diamond | Level 26 RW9
Diamond | Level 26

Macro code is resolved before the datastep is compiled and executed.  Depending on what other code you have:

%let log_flag=0;

data want;
if &log_flag. = 1 then do;
a="abc";
end;
else do;
a="def";
end;
run;

 Works fine, as when the datastep is compiled &log_flag gets replaced by 1 (or 0).  Note how do needs to have an associated end with semicolon.  Please provide full code of an example you see for better information.

KevinJChristie
Fluorite | Level 6

Ok, here is the exact code I'm running:

%let log_flag=0;
/*unimportant code that works correctly here */

%if &norm_check. > 0 %then %do;
/* natural log transformation
%let log_flag=1 */
%end;

data outliersTable2; set outliersTable; where &var.>Upper_limit or &var.<lower_Limit; if &log_flag.=1 then do; &var.=exp(&var); q1_2=exp(q1); median_2=exp(median); q3_2=exp(q3); iqr=exp(iqr); lower_limit=exp(lower_limit); upper_limit=exp(upper_limit); drop resp; end; run;

Log_flag is 0 because I'm using non-transformed data.  I've attached the table that shows that the variable "resp" gets dropped, and q1_2, median_2, and q3_2 all get created when they shouldn't.  Everything after the do in the final section of code is just to back transform the natural log data.  Funny thing is the Q1_2, Median_2, and Q3_2 variables all get created, but not populated.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

I need to see some sort of working example, there are too many open parts here.  For instance you have this:

%if &norm_check. > 0 %then %do;

This code does not work in open code, needs to be in a macro.  This is never defined:

&var.

A good idea is also to show the log, will show what things decode to, so to debug turn on:

options mlogic mprint symbolgen;

You will then see the resolved code in the log which will help you debug.

Astounding
PROC Star

Any variable mentioned in a DATA step automatically gets created:

 

data want;

if 5=4 then do;

   name='Fred';

end;

run;

 

The variable NAME is created (but not populated since 5 never equals 4).  That's normal behavior for a DATA step.

Quentin
Super User

A DATA step IF statement cannot control the creation of data step variables.  When the DATA step compiles, all data step variables referenced in the code are created in the program data vector.  The DATA step IF is not evaluated until the step executes.  So below sample code:

 

%let log_flag=0 ;
data want ;
  set sashelp.class (keep=name age);
  if &log_flag=1 then do ;
    age2=age**2 ;
  end ;
  put _all_ ;
run ;

Will always create the variable AGE2.  Because the step that compiles is:

data want ;
  set sashelp.class (keep=name age);
  if 0=1 then do ;
    age2=age**2 ;
  end ;
  put _all_ ;
run ;

Because 0=1 is false, the AGE2 assignment statement will never be executed.  But it is compiled.  And therefore AGE2 is created (with a missing value).

 

If you want to control the generation of the DATA step assignment statements, that is the job of the macro language %IF.  It controls what SAS code is generated.  

 

%macro try() ;
%local log_flag ; %let log_flag=0 ; data want ; set sashelp.class (keep=name age); %if &log_flag=1 %then %do ; age2=age**2 ; %end ; put _all_ ; run ; %mend try ; options mprint ; %try()

With that code, the AGE2 assignment statement is never generated, so it is not part of the DATA step code that is compiled.  MPRINT shows the code generated by the macro:

 

MPRINT(TRY):   data want ;
MPRINT(TRY):   set sashelp.class (keep=name age);
MPRINT(TRY):   put _all_ ;
MPRINT(TRY):   run ;
BASUG is hosting free webinars Next up: Mike Raithel presenting on validating data files on Wednesday July 17. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Kurt_Bremser
Super User

symget() and resolve() return character values, and so the results should be tested as such.

resolve() expects a text expression, not a macro variable name, so you need to use &log_flag there.

%let log_flag=1;

Data _null_;
if symget("log_flag") = "1" then put "A";
if &log_flag. = 1 then put "B";
if resolve("&log_flag") = "1" then put "C";
test = symget("log_flag");
if test = "1" then put "D";
run;
Astounding
PROC Star

One of your suggestions is simplest but not foolproof:

 

data want;

set have;

if &log_flag = 1 then do;

....

end;

run;

 

Just to be sure you can handle values other than 1 or 0, I would suggest changing it to:

 

data want;

set have;

if "&log_flag" = "1" then do;

....

end;

run;

 

Also, it wouldn't hurt to add this statement before the DATA step:

 

%put &log_flag;

ballardw
Super User

There are some timing issues between data step and macro values.

 

Since your example code has so many unclosed DO / END blocks it is pretty hard to see what you may be trying or where the issue crops up.

 

In this case the macro variable value is resolved at compile time and becomes part of the code for the data step and returns the expected result.

%let log_flag=0;

Data _null_;
if &log_flag = 1 then put "Log_flag = 1";
else if &log_flag = 0 then put "Log_flag = 0";

run;

 

 

so log_flag is always ne 1 for the duration of the step.

 

Are you attempting to set Log_flag macro variable in the same data step? Generally that isn't going to work. And if so, there would be no reason for a macro variable at all. Since you did not show how or when you set the macro variable perhaps that is an issue.

 

If your macro variable log_flag were equal to 0 then this line:

%if &log_flag. = 1 %then %do; xxx; %end;

would yield a data step error of "ERROR 180-322: Statement is not valid or it is used out of proper order."   and the step would not execute at all.

KevinJChristie
Fluorite | Level 6

Thanks to everyone's replies so far.  Just to answer a few questions:

I never try to assign 'Log_Flag' in a data step, only in open code. 

All code that I'm posting comes from within a macro.  From start to finish.

Below is a screen shot of my log where the variable resolves correctly in open code

Pic1.JPGs

All of my do/end statements are closed, and there are no errors being thrown there.

 

My whole goal is just to check the value of this flag within a data step, and then either perform or skip certain actions.  The problem is it doesn't seem to be performing the logic check.

 

If you think I missed your suggestion, or am still not trying something in the way that you think will work please let me know.  I'm trying to keep up with everyone that's posted so far.

 

Thanks again!

Reeza
Super User
  • SYMGET() will return a character variable.
  • RESOLVE() will return a character variable, but it needs the & in the parameter.
  • &log_flag will resolve as numeric

You need to treat them correctly depending on your reference method.

Here's an example of testing each one independently and then you can test them together via nesting if desired.

%let log_flag=1;
Data want;
set sashelp.class;
if symget("log_flag")='1' then do;
  put "Test #1 is True";
end;

if &log_flag. = 1 then do;
  put "Test #2 is True";
end;


if resolve("&log_flag")="1" then do;
  put "Test #3 is True";
end;

test=symget("log_flag");
if test='1' then do;
  put "Test #4 is True";
end;

run;

 


@KevinJChristie wrote:

I'm having trouble getting If/Then statements to work correctly with my macro variables inside a datastep. I'm writing a macro to handle two different cases: calculating stat tests with no transformation, and then calculating stat tests after a natural log transformation. If my data fails the test of normality, I log transform and test again. If it passes, I set my global flag, `log_flag`, to 1. I then want to test the status of this flag in data steps in order to correctly handle the transformed (or not) variables. In this case 'Log_flag' should be set to 0, but the program runs as if it were a 1.  I've tried variations of the following:

Data want;
set have;
if symget("log_flag")=1 then do;
if &log_flag. = 1 then do;
if resolve("log_flag")=1 then do;
%if &log_flag. = 1 %then %do; xxx; %end; test=symget("log_flag"); if test=1 then do; end

No matter what I try, the if/then statement is essentially ignored and all code following it is processed as if the if/then were true, even when it is false. I know that the `log_flag` is being correctly assigned a value of zero because the `%if` `%then` statements work and execute correctly in open code.  I'm just having trouble getting it to resolve correctly inside a datastep.  Also, when I set the "test" variable to the value of symget("log_flag"), the test variable appears in the dataset with the correct value (0 in this case).  The if/then statement just seems to always evaluate to true and I have no idea why.

Please let me know if there is any other information you need to help me figure this out. Thanks guys! 

 



 PS. Not a guy.

KevinJChristie
Fluorite | Level 6

Thanks for your suggestion of making sure I'm treating the variables correctly based on reference method.  I just tried:

data outliersTable2;
set outliersTable;
where &var.>Upper_limit or &var.<lower_Limit;
if SYMGET("log_flag")="1" then do;
&var.=exp(&var);
q1_2=exp(q1);
median_2=exp(median);
q3_2=exp(q3);
iqr=exp(iqr);
lower_limit=exp(lower_limit);
upper_limit=exp(upper_limit);
drop resp;
end;
run;

And I still get a table with columns that shouldn't be there (I.e. Q1_2, Median_2, and Q3_2).

Pic2.JPG

 

I had tried each one of your tests as a solution to my problem, but the problem is they're ALWAYS evaluating to true.  I can't get them to evaluate to false for some reason. 

Reeza
Super User

Yeah, that makes sense because you're not using macro logic there, you're using data step logic so the variables will always exist. 

You're misunderstanding how the macro and data step processor work here. You need macro logic and obviously this needs to be in a a macro as well. I think in 9.4 M5 that may have changed.

 

data outliersTable2;
set outliersTable;
where &var.>Upper_limit or &var.<lower_Limit;
%if &log_flag=1 %then %do;
&var.=exp(&var);
q1_2=exp(q1);
median_2=exp(median);
q3_2=exp(q3);
iqr=exp(iqr);
lower_limit=exp(lower_limit);
upper_limit=exp(upper_limit);
drop resp;
%end;
run;
KevinJChristie
Fluorite | Level 6

I could have sworn I tried using %if and %then in the data step with no success, but this finally worked for me.  Thank you Reeza.

Ready to join fellow brilliant minds for the SAS Hackathon?

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!
How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 13 replies
  • 1523 views
  • 3 likes
  • 7 in conversation