Hi,
I want to write a macro to run a model multiple times, each time setting the seed to a randomly selected value greater than 1. Right now my code looks something like this:
%macro RandBetween(min, max);
(&min + floor((1+&max-&min)*rand("uniform")))
%mend;
%macro DoLoop;
%DO j = 1 %TO 2 ;
%LET x = %RandBetween(1,50);
%LET Seed = %EVAL(&x+&j);
proc ... data=... ;
...
seed &seed;
run;
%END;
%MEND DoLoop;
%DoLoop;
However there is an issue assigning x a macro value. Any ideas how to assign a macro a random value?
Thanks!
When you do
%LET x = %RandBetween(1,50);
The macro call is resolved to
%LET x = (1 + floor((1+50-1)*rand("uniform")));
So that x will now contain the text string
(1 + floor((1+50-1)*rand("uniform")))
%LET Seed = %EVAL(&x+&j);
will then resolve to
%LET Seed = %EVAL((1 + floor((1+50-1)*rand("uniform")))+1);
(for &j=1) where the macro processor can't resolve floor and rand (as they are not macro functions), and will throw an error because of non-numeric data.
If you want to create a value, I suggest to use a data _null_ step:
%macro RandBetween(min, max);
(&min + floor((1+&max-&min)*rand("uniform")))
%mend;
%macro DoLoop;
%DO j = 1 %TO 2 ;
data _null_;
x = %RandBetween(1,50);
call symput('Seed',put(x+&j,best.));
run;
proc ... data=... ;
...
seed &seed;
run;
%END;
%MEND DoLoop;
%DoLoop;
You also have to question if macro randbetween is necessary at all.
When you do
%LET x = %RandBetween(1,50);
The macro call is resolved to
%LET x = (1 + floor((1+50-1)*rand("uniform")));
So that x will now contain the text string
(1 + floor((1+50-1)*rand("uniform")))
%LET Seed = %EVAL(&x+&j);
will then resolve to
%LET Seed = %EVAL((1 + floor((1+50-1)*rand("uniform")))+1);
(for &j=1) where the macro processor can't resolve floor and rand (as they are not macro functions), and will throw an error because of non-numeric data.
If you want to create a value, I suggest to use a data _null_ step:
%macro RandBetween(min, max);
(&min + floor((1+&max-&min)*rand("uniform")))
%mend;
%macro DoLoop;
%DO j = 1 %TO 2 ;
data _null_;
x = %RandBetween(1,50);
call symput('Seed',put(x+&j,best.));
run;
proc ... data=... ;
...
seed &seed;
run;
%END;
%MEND DoLoop;
%DoLoop;
You also have to question if macro randbetween is necessary at all.
Thank you @Kurt_Bremser that's running now. I just have one question to help my understanding of the code - what does the code 'best.' do?
put(x+&j,best.)
converts the numeric values derived from the summation to a character value, as call symput expects two strings as arguments; otherwise you'd get a NOTE about the type conversion, and I don't like them in my codes (I even have measures to search for such NOTEs in log files and throw errors in batch jobs, as undetected type mismatches can cause logical havoc).
best. is just a default format that will work basically all the time and create a string that SAS will always recognize as a number when the macro variable is resolved.
Thanks @Kurt_Bremser for all your help!
To resolve data step functions such as Floor or Rand in macros you must use the macro function %sysfunc for each function called. Also the macro language doesn't use the the quoted literal strings such as the parameter to Rand quite the same way since it expects text.
This does what you were attempting:
%macro RandBetween(min, max); %let x = %eval(&min + %sysfunc ( floor((1+&max-&min)* %sysfunc(rand(uniform)) )) ); %put &x; %mend; %RandBetween(1,50);
You can see that complex operations involving multiple functions can get quite ugly quickly. I agree with @Kurt_Bremser that datasteps often are much easier to understand and debug than doing things directly in the macro language for many operations.
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!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.
Select SAS Training centers are offering in-person courses. View upcoming courses for: