Hi all, seeking help on how to write a macro to call another macro for different period.
I have different setting to run each month. Im wondering can I write a macro to auto repeat run the same macro.
%macro repeat(n);
%do i = 1 %to &n.;
%if &i. = 1 %then
%do;
%let month = 4;
%let lag = 2;
%end;
%if &i. = 2 %then
%do;
%let month = 7;
%let lag = 2;
%end;
%if &i. = 3 %then
%do;
%let month = 4;
%let lag = 1;
%end;
%month_setting;
%setting;
%end;
%mend;
%repeat(3);
%macro month_setting;
%global begin end;
%if %sysevalf(&lag = 2) %then
%do;
%let begin = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)),-&month.),date9.)"d;
%let end = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)), +&lag.),date9.)"d;
%end;
%else %if %sysevalf(&lag = 1) %then
%do;
%let begin = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)),-&month.+1),date9.)"d;
%let end = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)), +&lag.+1),date9.)"d;
%end;
%let end_YM = %sysfunc(inputn(%sysfunc(substr(%sysfunc(putn(&end., yymmddn8.)),1, 6)), 6.));
%let beg_YM = %sysfunc(inputn(%sysfunc(substr(%sysfunc(putn(&begin., yymmddn8.)),1, 6)), 6.));
%mend;
/*macro sample*/
%macro setting;
Data working.M&Month._Setting;
month = &month.;
lag = &lag.;
begin = put(&begin.,yymmddn8.);
end = put(&end.,yymmddn8.);
run;
%mend;
Sloppy macro programming. Always (as in ALWAYS) define variables used in the macro as local (%LOCAL statement).
Your macro datecreate uses i for the loop, which is also used in repeat; after returning from datecreate, &i will have a large enough value to terminate the loop in repeat.
Did you try the code? Did it work? If not what was wrong?
You can call another macro inside a macro.
But you have to have defined it before you try to CALL it.
So make sure to move the call to %REPEAT() to after the definition of all three macros.
No error pop up.
It only able to run one times. I have write %repeat(3). But, it only able to run &i.= 1
Run your macro with
options mprint mlogic symbolgen;
set and post the log.
Apologies. Can ignore the previous coding i asked. The issue seems like appear after the %datecreate macro.
If only run until %setting then it works. However, when run one more macro (i.e. %datecreate) then it won't auto generate the second and third run.
So, i have create delete specific global macro.
%macro repeat(n);
%do i = 1 %to &n.;
%if &i. = 1 %then
%do;
%let month = 4;
%let lag = 2;
%end;
%if &i. = 2 %then
%do;
%let month = 7;
%let lag = 2;
%end;
%if &i. = 3 %then
%do;
%let month = 4;
%let lag = 1;
%end;
%month_setting;
%setting;
%datecreate;
%deleteALL;
%end;
%mend;
%repeat(3);
%macro month_setting;
%global begin end;
%if %sysevalf(&lag = 2) %then
%do;
%let begin = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)),-&month.),date9.)"d;
%let end = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)), +&lag.),date9.)"d;
%end;
%else %if %sysevalf(&lag = 1) %then
%do;
%let begin = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)),-&month.+1),date9.)"d;
%let end = "%sysfunc(intnx(month,%sysfunc(inputN(&period.01,yymmdd10.)), +&lag.+1),date9.)"d;
%end;
%let end_YM = %sysfunc(inputn(%sysfunc(substr(%sysfunc(putn(&end., yymmddn8.)),1, 6)), 6.));
%let beg_YM = %sysfunc(inputn(%sysfunc(substr(%sysfunc(putn(&begin., yymmddn8.)),1, 6)), 6.));
%mend;
/*macro sample*/
%macro setting;
Data working.M&Month._Setting;
month = &month.;
lag = &lag.;
begin = put(&begin.,yymmddn8.);
end = put(&end.,yymmddn8.);
run;
%mend;
%macro datecreate;
%global dif;
%let dif = %sysfunc(intck(MONTH,&begin,&end));
%do i= 1 %to &dif;
%global date_&i;
%let date_&i=%sysfunc(intnx(month,&begin,&i,B),yymmn6.);
%put &&date_&i;
%end;
%mend;
%macro deleteALL;
options nonotes;
%local vars;
proc sql noprint;
select name into: vars separated by ' '
from dictionary.macros
where scope = 'GLOBAL' and (name contains 'DATE_' or name in ('MONTH', 'LAG', 'BEGIN','END', 'BEG_YM', 'END_YM', 'DIF'));
quit;
%symdel &vars;
options notes;
%put NOTE: Macro variables deleted.;
%mend;
But, the above coding not working also. It didn't pop any errors. However, it will not generate the results for Month 7 + Lag 2 and Month 4 + lag 1.
Sloppy macro programming. Always (as in ALWAYS) define variables used in the macro as local (%LOCAL statement).
Your macro datecreate uses i for the loop, which is also used in repeat; after returning from datecreate, &i will have a large enough value to terminate the loop in repeat.
Kurt has identified the problem, I want to share an example. Suppose you have macros OUTER and INNER where OUTER invokes INNER. You want to have OUTER loop 2 times, and INNER loop 3 times per iteration of OUTER.
If you code it like:
%macro outer(n=) ;
%do i=1 %to &n ;
%put %nrstr(%%)&sysmacroname ;
%put _user_ ;
%put ;
%inner(n=3) ;
%end ;
%mend outer ;
%macro inner(n=) ;
%do i=1 %to &n ;
%put %nrstr(%%)&sysmacroname ;
%put _user_ ;
%put ;
%end ;
%mend inner ;
%outer(n=2)
Your log will show that OUTER is loops once, not twice as intended. This is the problem you're having:
22 %outer(n=2) %OUTER OUTER I 1 OUTER N 2 %INNER INNER N 3 OUTER I 1 OUTER N 2 %INNER INNER N 3 OUTER I 2 OUTER N 2 %INNER INNER N 3 OUTER I 3 OUTER N 2
When OUTER executes, it creates the macro variables i and N.
When INNER executes, it creates a new macro variable N which is a different macro variable than the variable N which exists in outer. It does this because N is defined as macro parameter to INNER.
But when the %DO loop in INNER executes, it does NOT create a new macro variable i for INNER. Instead it uses the macro variable i that already exists in OUTER. So this is a naming collision. INNER has changed the values of a macro variable that belongs to OUTER. And because INNER loops three times, you end up with the macro variable i having the value of 3. Then the %DO loop in OUTER does not loop a second time, because i is already 3.
The way to avoid this problem is to declare the macro variable i to be local in INNER. That way when INNER executes, the %LOCAL statement will create a new local macro variable i for INNER that is separate from the macro variable i in OUTER.
%macro outer(n=) ;
%local i ;
%do i=1 %to &n ;
%put %nrstr(%%)&sysmacroname ;
%put _user_ ;
%put ;
%inner(n=3) ;
%end ;
%mend outer ;
%macro inner(n=) ;
%local i;
%do i=1 %to &n ;
%put %nrstr(%%)&sysmacroname ;
%put _user_ ;
%put ;
%end ;
%mend inner ;
%outer(n=2)
Log:
22 %outer(n=2) %OUTER OUTER I 1 OUTER N 2 %INNER INNER I 1 INNER N 3 OUTER I 1 OUTER N 2 %INNER INNER I 2 INNER N 3 OUTER I 1 OUTER N 2 %INNER INNER I 3 INNER N 3 OUTER I 1 OUTER N 2 %OUTER OUTER I 2 OUTER N 2 %INNER INNER I 1 INNER N 3 OUTER I 2 OUTER N 2 %INNER INNER I 2 INNER N 3 OUTER I 2 OUTER N 2 %INNER INNER I 3 INNER N 3 OUTER I 2 OUTER N 2
With that, outer loops two times, so you get the expected 2*3=6 executions of INNER. And you can see in the log that the macro variables i and N in INNER are independent of the macro variables i and N in OUTER.
you can do it much simpler and much more readable using data step:
libname working (work);
%macro setting(period, i);
data working.P&period._Setting;
period = input(cats(symget('period'),'01'),yymmdd8.);
i = symgetn("i");
select(i);
when(1)
do;
month = 4;
lag = 2;
end;
when(2)
do;
month = 7;
lag = 2;
end;
when(3)
do;
month = 4;
lag = 2;
end;
otherwise put "ERROR: I should be in 1, 2, 3.";
end;
select(lag);
when(2)
do;
b = intnx("month", period,-month);
e = intnx("month", period, +lag);
end;
when(1)
do;
b = intnx("month", period,-month+1);
e = intnx("month", period, +lag+1);
end;
otherwise put "ERROR: LAG shoild be in 1, 2.";
end;
begin = put(b,yymmddn8.);
end = put(e,yymmddn8.);
/* you didnot used those 2 in the code at all */
beg_YM = put(b,yymmn6.);
end_YM = put(e,yymmn6.);
keep month lag begin end beg_YM end_YM ;
run;
%mend;
%setting(202308, 1)
proc print;
run;
%setting(202308, 2)
proc print;
run;
%setting(202308, 3)
proc print;
run;
Bart
Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!
Check out this tutorial series to learn how to build your own steps in SAS Studio.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.