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

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

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

@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

11 REPLIES 11
PaigeMiller
Diamond | Level 26

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
hua
Obsidian | Level 7 hua
Obsidian | Level 7
Yes, The logic is like this, but I tried several sentences, it doesn't work. Could you please give me more details? Thank you!
Reeza
Super User

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;
hua
Obsidian | Level 7 hua
Obsidian | Level 7
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!
Astounding
PROC Star

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;

hua
Obsidian | Level 7 hua
Obsidian | Level 7
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.
Tom
Super User Tom
Super User

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
hua
Obsidian | Level 7 hua
Obsidian | Level 7
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?
Tom
Super User Tom
Super User

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

hua
Obsidian | Level 7 hua
Obsidian | Level 7
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!
Tom
Super User Tom
Super User

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

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

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

What is Bayesian Analysis?

Learn the difference between classical and Bayesian statistical approaches and see a few PROC examples to perform Bayesian analysis in this video.

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
  • 11 replies
  • 7271 views
  • 3 likes
  • 5 in conversation