Help using Base SAS procedures

How to use IF Then to end a loop in a macro

Accepted Solution Solved
Reply
Frequent Contributor
Frequent Contributor
Posts: 81
Accepted Solution

How to use IF Then to end a loop in a macro

[ Edited ]

Hello,

 

( I'm sorry I didn't clearly describe my questions, so I edit it a little bit, hope this time it's much clear.)

I'm trying to do a iteration in a macro with do loop. But still have problems about using IF THEN to end the loop.

The Logic here is: I have a initial K value, and after several calculations I got the Kt. If abs(Kt-K)>0.0001 then let the Kt be the K and do the calculation again until the last step to get the Kt, if abs(Kt-K)<=0.0001 then end the loop and get the Kt.

The equations I used to get Kt is Kt = 1/n * sum(log(k*x/sigma-k*theta/sigma+1)), where x is my sample, theta and sigma are constant. And at first what i was trying to do is to give a initial value of K, then calculate the Kt, and let the Kt be the new K, then do the loop for 100 times. As a result, I could get 100 values of Kt. And The code I use is as following:

 

 

* First clean up my dataset to get my sample X, constant theta and sigma, and give the initial K ;
data test1;
set test;
  x=prep_ad;
  theta=P75_pow;
  sigma=1/f75_pow;
  K=0.5;
keep station x theta sigma K;
run;

* Then I use macro to let it run 100 times ;

%macro EstimateK;
  %do i = 2 %to 100;
    * Calculate the value (log(K*x/sigma-K*theta/sigma+1)) for each X; 
    data test_after&i;
    set test_after1;
       LN =LOG((K / sigma) * x + 1 - K * theta / sigma);
    run;
    * Get the sum of LN of the former step and the total number of X (which is n in the equation). And then calculate Kt; 
    proc means data=test&i noprint;
       var LN;
       by station;
       output out=test_sum sum=SumLN;
    run;
    data test_sum;
    set test_sum;
       N=_FREQ_;
       Kt=SumLN/N;
    keep station Kt;
    run;
    * Record Kt for each run;
    data test_processK&i;
    set test_sum;
       K&i=K;
    keep station K&i;
    run;
    * Merge the new Kt to the old dataset;
    data test&i;
    set test&i;
       if _n_ =1 then set test_sum;
    run;
quit;
%end;
%mend;
%EstimateK;

* After run the macro, I merge all the Kt into one dataset;
data test_processK;
  merge test_processK2-test_processK100;
  by station;
run;

 

This code force the loop run 100 times, but I'm trying to give it a condition, which is that the difference between K and Kt is less than 0.0001. Once the condition is met, then jump out of the loop, else do it until 100 time.

 

My question is I don't know where should I put the IF condition in the macro.

 

Hope anyone could help me with this problem!

Thank you in advance!

 

Best,

Hua

 

 


Accepted Solutions
Solution
‎11-10-2017 04:59 PM
Super User
Super User
Posts: 8,116

Re: How to use IF Then to end a loop in a macro


hua wrote:
Thank you, Tom. You are right, My code doesn't work, I'm not sure where and how should I put the IF sentences. But if I put "%if abs(kt-k)>0.0001 %then K=Kt;" at the begin of macro, how could the code know where is the Kt and K variables?

First the macro code needs to test macro variables. It cannot test the values of variables in the data set.

So if your success criteria depends on values of dataset variables then you need to test it with data step code and then generate a macro variable that the macro conditions can test.

One way to do this might be to code your loop as a %DO %WHILE() or %DO UNTIL() and increment your counter yourself. Something like this.

%macro test ;
%let success=0;
%let i=0;
%do %until(&success or &i > 100);
  %let i=%eval(&i+1);
  %put Inside loop &=i ;

data _null_;
   if &i > 3 then call symputx('success',1);
run;

%end ;
%put Outside loop &=i ;
%mend test ;
%test;

So basically you set a macro variable flag as false and then when your criteria is met you set it to true.  It is good to also include a loop counter even if you don't use it for anything just to set an upper bound on the number of times the loop will run to prevent infinite looping.

 

Your test of K and KT would occur in whatever SAS code you are using to calculate them like the data step in the simple example above.

View solution in original post


All Replies
Respected Advisor
Posts: 3,043

Re: How to use IF Then to end a loop in a macro

You want a structure something like this

 

%macro whatever;
     ... do some stuff ...
    %if condition %then %do;
         ... do more stuff when condition is true ...
    %end;
%mend;
         
--
Paige Miller
Frequent Contributor
Frequent Contributor
Posts: 81

Re: How to use IF Then to end a loop in a macro

Posted in reply to PaigeMiller
Yes, The logic is like this, but I tried several sentences, it doesn't work. Could you please give me more details? Thank you!
Super User
Posts: 23,754

Re: How to use IF Then to end a loop in a macro

1. Paste your code into a code box using the { i } icon

2. Format!

3. Why are you using macro logic in the data step? Use normal if/then

4. To evaluate logic with decimal points in macro use SYSEVALF. Otherwise everything is text and you'll get results you don't expect.

5. Why is there a quit? 

6. I'm assuming this is simplified code, because as written it doesn't seem to accomplish anything...

7. Add comments - then we may have an idea of what you expect the code to do, rather than what it's actually doing.

8. Are you absolutely sure (I'm not) that this isn't better done in a data step or PROC OR than in a macro?

 

%macro EstimateK;
	%do i = 2 %to 100;

		data mydata&i;
			set mydata1;
			Kt=LOG((K / sigma) * x + 1 - K * theta / sigma);
		run;

		data mydata1;
			set mydata&i;

			%if abs(kt-k)>0.0001 %then
				K=Kt;
			%else %return;
		run;

		proc delete data=mydata&i;
		run;

		quit;

	%end;
%mend;

%EstimateK;
Frequent Contributor
Frequent Contributor
Posts: 81

Re: How to use IF Then to end a loop in a macro

Thank you for your recommendation, I will format the code next time. Yes, it is a simplified code. The reason why I don't think I could use the single step with data step is there has several steps in the process, and I was trying to use macro and do loop to let it calculate 100 times and output Kt for each run. But I don't know how to use a if condition to jump out the loop (if it meet the condition before 100 times). I had thought the quit is used for quit a do loop, isn't it?
Thank you for your patient reply!
Super User
Posts: 6,781

Re: How to use IF Then to end a loop in a macro

I could be wrong, but this sounds like a problem that a single DATA step can handle:

 

data want;

set mydata1;

do j=1 to 1000 until (abs(kt-k)>0.0001);
   Kt=LOG((K / sigma) * x + 1 - K * theta / sigma);

end;
run;

Frequent Contributor
Frequent Contributor
Posts: 81

Re: How to use IF Then to end a loop in a macro

Posted in reply to Astounding
Thank you! I'm sorry! I simplified the code. Actually, it has several more steps before I get Kt, so a single DATA step may not help to do so.
Super User
Super User
Posts: 8,116

Re: How to use IF Then to end a loop in a macro

Your %IF statement doesn't make any sense and is not even going to compile.  

7     %macro EstimateK;
8     %if abs(kt-k)>0.0001 %then K=Kt;
9     %else otherstring;
10    %mend;
11    %put %EstimateK;
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is
       required. The condition was: abs(kt-k)>0.0001
ERROR: The macro ESTIMATEK will stop executing.

Even if you got it to compile it wouldn't do what you need.  You are just comparing characters strings.

12    %macro test ;
13    %if abs > 100 %then %put abs > 100 ; %else %put abs <= 100;
14    %if k > 100 %then %put k > 100; %else %put k <= 100;
15    %mend test ;
16    %test;
abs > 100
k > 100
Frequent Contributor
Frequent Contributor
Posts: 81

Re: How to use IF Then to end a loop in a macro

Thank you, Tom. You are right, My code doesn't work, I'm not sure where and how should I put the IF sentences. But if I put "%if abs(kt-k)>0.0001 %then K=Kt;" at the begin of macro, how could the code know where is the Kt and K variables?
Solution
‎11-10-2017 04:59 PM
Super User
Super User
Posts: 8,116

Re: How to use IF Then to end a loop in a macro


hua wrote:
Thank you, Tom. You are right, My code doesn't work, I'm not sure where and how should I put the IF sentences. But if I put "%if abs(kt-k)>0.0001 %then K=Kt;" at the begin of macro, how could the code know where is the Kt and K variables?

First the macro code needs to test macro variables. It cannot test the values of variables in the data set.

So if your success criteria depends on values of dataset variables then you need to test it with data step code and then generate a macro variable that the macro conditions can test.

One way to do this might be to code your loop as a %DO %WHILE() or %DO UNTIL() and increment your counter yourself. Something like this.

%macro test ;
%let success=0;
%let i=0;
%do %until(&success or &i > 100);
  %let i=%eval(&i+1);
  %put Inside loop &=i ;

data _null_;
   if &i > 3 then call symputx('success',1);
run;

%end ;
%put Outside loop &=i ;
%mend test ;
%test;

So basically you set a macro variable flag as false and then when your criteria is met you set it to true.  It is good to also include a loop counter even if you don't use it for anything just to set an upper bound on the number of times the loop will run to prevent infinite looping.

 

Your test of K and KT would occur in whatever SAS code you are using to calculate them like the data step in the simple example above.

Frequent Contributor
Frequent Contributor
Posts: 81

Re: How to use IF Then to end a loop in a macro

I understand "the macro code needs to test macro variables. It cannot test the values of variables in the data set." But I still don't know how to pass the value of variable to the macro variable. For example, I have the value of Kt-K in my dataset, how to make the macro variable change? Thank you!
Super User
Super User
Posts: 8,116

Re: How to use IF Then to end a loop in a macro

Use CALL SYMPUTX() to write to a macro variable.

Your macro will only be able to test the value between data/proc steps.

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 11 replies
  • 364 views
  • 3 likes
  • 5 in conversation