Calling a Macro in a loop? - sas

Here is a simple SAS program I created ...
%MACRO SCANLOOP();
%DO I=1 %TO 5;
%put &I;
%END;
%MEND;
%MACRO TEST();
%DO I=1 %TO 3;
%SCANLOOP();
%END;
%MEND;
%TEST();
RUN;
I was expecting this SAS code to produce the following output:
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
but instead I just got ...
1
2
3
4
5
Can anyone explain to me why?
Thanks
Brian

You need to define your macro variables as LOCAL. Otherwise SAS will use the existing macro variable with the same name in the outer scope. For your particular example you must make I local in the SCANLOOP macro. But you should really do it in both.
%MACRO SCANLOOP();
%LOCAL I;
%DO I=1 %TO 5;
%put &I;
%END;
%MEND;

Oh those variables are not scoped the way I expected. If I change the variable in the first macro from I to J then it works.

Related

How to receive and filter array data coming from a report to a stored process is SAS

I've a parameter of named _ID who accepts multiple values from a list and send them to my stored process, lets say I've sent four values (1,2,3,4) in it, I'll receive them in my store process as,
_ID0 = 4
_ID1 = 1
_ID2 = 2
_ID3 = 3
_ID4 = 4
_ID_COUNT = 4
I'm receiving and filtering them as following.
%let ID = "&_ID";
%let Count = "&_ID_COUNT";
%macro IDs;
%global _ID0;
/* If more than one ID value was selected then cycle through the values */
%if %eval(&_ID0 ge 2) %then %do;
%do i=1 %to &_ID0;
&&_ID&i
%end;
%end;
/* If only one ID value was selected */
%else &_ID
%mend;
****************************;
%macro filter;
%if &Count ne "0" %then %do;
%stpbegin;
proc sql noprint;
create table users as
select *
from work.users
where id in(%IDs);
run;
%stpend;
%end;
%mend; %filter;
there is not any error in log above one is my code but It's not filtering anything. if user table has values 1-10 in id column user should be update with only 1,2,3,4
user
id
1
2
3
4
5
6
7
8
9
10
after filter i want
user
id
1
2
3
4
I don't know What's wrong and did I miss any better approach.
Your code should work. The %IDS macro could be more concise and might need more logic to deal with the inconsistency of how macro variables are created when count is less than 2. So this macro will make sure that the 0 and 1 variables are populated (at least while the macro is running) before trying to loop over them.
%macro IDs;
%local i ;
%let _id0 = &_id_count ;
%if &_id0 = 1 %then %let _id1 = &_id ;
%if &_id0 = 0 %then -99999 ;
%do i=1 %to &_id0;
&&_ID&i
%end;
%mend IDs;
Based on your example data it should work like this:
1071 %put (%ids);
(1 2 3 4)
What value do you want to emit if they don't select any values? I have set this example up to generate -9999, but your other macro should already be skipping the call in that case so it shouldn't matter.

Execution of data step despite of the errors

I am executing the following code to combine a large number of data sets . Here is code:
%macro combine;
data modelfit;
set
%do i = 30 %to 116 %by 3 ;
stat&i
%end;
;
%end;
run;
%mend;
%combine;
However, few of the data sets are not present. Hence, the data set is failing to execute. How can I prevent the data step from stopping to execute and combine the data sets that are there . Can anybody please help me with this
Check if the files exist before adding them in the loop:
%macro combine;
data modelfit;
set
%do i = 30 %to 116 %by 3 ;
%if %sysfunc(exist(stat&i)) %then %do ; stat&i %end ;
%end;
;
run;
%mend;
%combine;

SAS: Running macros inside do loop does not seem to work

I need to run a series of macros multiple times. To do so, I've built a new macro that has inside of it a do-loop, which is supposed to run "i" number of times, and with each iteration of the do-loop, the series of macros referenced above are supposed to run.
Here is the essence of code (note that the first proc sql essentially takes from a dataset called "DatesRange" and placed a range of dates into the variable "varlist_Dates"; the range of dates within this variable is used for Macro1, Macro2, Macro3).
%macro MultipleTimes;
proc sql noprint;
select distinct Date
into :varlist_Dates separated by ' '
from DatesRange
order by Date;
quit;
%do i = 1 %to 5;
%let date = %scan(&varlist_Dates.,&i.);
%Macro1;
%Macro2;
%Macro3;
%end;
%mend;
I'm finding that it stops at i=1 and never proceeds. I'm completely unclear on why. I've experimented by playing around with removing some macros and keeping others but nothing seems to work. I feel like there may be something more fundamental about my methodology that is off, because there's something I don't know about SAS and about the way a macro inside of a do-loop inside of a macro works.
Any help would be much appreciated. Thanks!
First: check if i is used in any of those macros. I bet it is. It's probably being changed to something higher than 5 (thus exiting after one loop).
In SAS, when you reference a macro variable that already exists, it is defined as having the scope of the most local symbol table that contains it, unless you use %local to specify it as local to the current macro. So if you throw %let i=1 to 5; around in multiple nested macros, it will be using the same &i.
As an example, see the following code:
%let i=A;
%macro outer;
%put &=i;
%do i=1 %to 5;
%put OUTER FIRST: &=i;
%inner;
%put OUTER LAST: &=i;
%end;
%mend outer;
%macro inner;
%do i=1 %to 5;
%put INNER: &=i.;
%end;
%mend;
%put GLOBAL FIRST: &=i;
%outer;
%put GLOBAL LAST:&=i;
Notice how &i is always the same value. That's not what you mean, now is it?
Now, on the other hand, &i does get a different value in each bit if you do it right:
%let i=A;
%macro outer;
%local i;
%put &=i;
%do i=1 %to 5;
%put OUTER FIRST: &=i;
%inner;
%put OUTER LAST: &=i;
%end;
%mend outer;
%macro inner;
%local i;
%do i=1 %to 5;
%put INNER: &=i.;
%end;
%mend;
%put GLOBAL FIRST: &=i;
%outer;
%put GLOBAL LAST:&=i;
And if you %put _all_ in INNER you will see this in action.
Second: Don't write it this way. You're going to so much effort, just write it a better way to begin with.
%Macro RunMyMacros(date=);
%Macro1; *I hope you use &date as a parameter in these and not as a global;
%Macro2;
%Macro3;
%mend RunMyMacros;
proc sql noprint;
select distinct cats('%RunMyMacros(date=',Date,')')
into :calllist_Dates separated by ' '
from DatesRange
order by Date;
quit;
&calllist;
That's far easier to use, troubleshoot, and run, and doesn't require hardcoding the number of valid dates or anything else in it.

SAS: Creating lag variables with a do macro

I'm trying to create 4 new lag variables, each one adding an additional lag. The code below produces only the final lag variable, i.e. after running this code there is a new variable called lag_4, but lag_1, lag_2, and lag_3 are not created. Thanks
%macro makelags;
%do i=1 %to 4;
data work.test1;
set work.dataset;
lag_&i = lag&i(id_number);
run;
%end;
%mend makelags;
%makelags;
You need to loop inside the data step, not outside of it.
If you were to loop:
data work.test1;
set work.dataset;
%do i = 1 %to 4;
lag_&i. = lag&i.(id_number);
%end;
run;
(The whole datastep can be inside a macro, or just the %do loop).
The way I'd do it, if I needed a macro (Because, say, the number of lags varies):
%macro lagme(num_lags=);
%do _i = 1 %to &num_lags.;
lag_&_i. = lag&_i.(id_number);
%end;
%mend lagme;
data mydata;
set olddata;
%lagme(num_lags=4);
run;
Your code is overwriting dataset test1 4 times keeping only the version created by the last %do iteration.
Try moving the %do cycle inside the data step:
data work.test1;
set work.dataset;
%do i=1 %to 4;
lag_&i = lag&i(id_number);
%end;
run;

sas macro call macro

Hi I have a program to use one macro to call another one.
I have two month(jun12 and jul12) and each month has two parts(1 & 2), I want to do a loop which I construct a macro called"Loop", Inside it, I constructed a Array, and used Do comment do call a macro "try".
Seems like it doesn't work. Can someone help me with it? Thank you!
LIBNAME EC100006 "G:\sample";
%MACRO try(month=,part=);
...FROM EC100006.monthitsum&month.lag&part AS t1
%MEND try;
%Macro test;
ARRAY Mon(2) jun12 jul12;
%Do i=1 %to 2;
%Do j=1 %to 2
%try(month=Mon(i),part=j)
%End
%End
%Mend test;
%test
You can't have an array of macro variables.
The simplest way to repeatedly call a macro with a list of parameters is to make a dataset with those parameters and call it from the dataset, either with CALL EXECUTE or using PROC SQL to create a macro list of macro calls.
data call;
input month $ part;
datalines;
jun12 1
jul12 2
;;;;
run;
proc sql;
select cats('%try(month=',month,',part=',part,')') into :mcalllist
separated by ' '
from call;
quit;
&mcalllist;
That only works if you have less than 20,000 characters worth of calls or so - if it's more than that you need to try a different option (%include file or call execute).
So right now it's something like this
LIBNAME EC100006 "G:\sample";
%MACRO try(month=,part=);
...FROM EC100006.monthitsum&month.lag&part AS t1
%MEND try;
Data Array
ARRAY Mon{2} jun12 jul12;
RUN;
%Macro test;
%Do i=1 %to 2;
%Do j=1 %to 2
%try(month=Mon(i),part=j)
%End
%End
%Mend test;
%test