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
Related
Very new to sas, need to perform export procedure for many datasets called data1, data2, data3 ... data10.
Is there a way to operationalize this? I've tried the following without success
LIBNAME input '/home/.../input';
LIBNAME output '/home/.../output';
%macro anynumber(number);
proc export data=input.data&number file="/home/.../output/data&number..dta" dbms=dta replace;
run;
%mend;
do i = 1 to 10;
%anynumber(i)
end;
run;
CALL EXECUTE is the recommended option but a macro loop is also a good option here.
You could loop it within the same macro as well. All options are illustrated below.
Usually when I see this type of coding though, my first thought is that someone isn't familiar with BY group processing.
data demo;
do i=1 to 10;
*make sure this string matches macro call;
str = catt('%anynumber(', i, ');');
*write records to log to check;
put str;
call execute(str);
end;
run;
Another option is a macro loop itself.
%macro loop_export(numLoops=10);
%do i=1 %to &numLoops;
%anywhere(&i);
%end;
%mend;
%loop_export(numLoops=10);
Putting them together:
%macro anynumber(number);
%do i=1 %to &number;
proc export data=input.data&number file="/home/.../output/data&i..dta" dbms=dta
replace;
run;
%end;
%mend;
*will export all 10;
%anyNumber(10);
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.
I have a table like this:
Lista_ID 1 4 7 10 ...
in total there are 100 numbers.
I want to call each one of these numbers to a macro i created. I was trying to use 'scan' but read that it's just for character variables.
the error when i runned the following code was
there's the code:
proc sql;
select ID INTO: LISTA_ID SEPARATED BY '*' from
WORK.AMOSTRA;
run;
PROC SQL;
SELECT COUNT(*) INTO: NR SEPARATED BY '*' FROM
WORK.AMOSTRA;
RUN;
%MACRO CICLO_teste();
%LET LIM_MSISDN = %EVAL(NR);
%LET I = %EVAL(1);
%DO %WHILE (&I<= &LIM_MSISDN);
%LET REF = %SCAN(LISTA_ID,&I,,'*');
DATA WORK.UP&REF;
SET WORK.BASE&REF;
FORMAT PERC_ACUM 9.3;
IF FIRST.ID_CLIENTE THEN PERC_ACUM=0;
PERC_ACUM+PERC;
RUN;
%LET I = %EVAL(&I+1);
%END;
%MEND;
%CICLO_TESTE;
the error was that:
VARIABLE PERC IS UNITIALIZED and
VARIABLE FIRST.ID_CLIENTE IS UNITIALIZED.
What I want is to run this macro for each one of the Id's in the List I showed before, and that are referenced in work.base&ref and work.up&ref.
How can I do it? What I'm doing wrong?
thanks!
Here's the CALL EXECUTE version.
%MACRO CICLO_teste(REF);
DATA WORK.UP&REF;
SET WORK.BASE&REF;
BY ID_CLIENTE;
FORMAT PERC_ACUM 9.3;
IF FIRST.ID_CLIENTE THEN PERC_ACUM=0;
PERC_ACUM+PERC;
RUN;
%CICLO_TESTE;
DATA _NULL_;
SET amostra;
*CREATE YOUR MACRO CALL;
STR = CATT('%CLIO_TESTE(', ID, ')');
CALL EXECUTE(STR);
RUN;
First you should note that SAS macro variable resolve is intrinsically a "text-based" copy-paste action. That is, all the user-defined macro variables are texts. Therefore, %eval is unnecessary in this case.
Other miscellaneous corrections include:
Check the %scan() function for correct usage. The first argument should be a text string WITHOUT QUOTES.
run is redundant in proc sql since each sql statement is run as soon as they are sent. Use quit; to exit proc sql.
A semicolon is not required for macro call (causes unexpected problems sometimes).
use %do %to for loops
The code below should work.
data work.amostra;
input id;
cards;
1
4
7
10
;
run;
proc sql noprint;
select id into :lista_id separated by ' ' from work.amostra;
select count(*) into :nr separated by ' ' from work.amostra;
quit;
* check;
%put lista_id=&lista_id nr=&nr;
%macro ciclo_teste();
%local ref;
%do i = 1 %to &nr;
%let ref = %scan(&lista_id, &i);
%*check;
%put ref = &ref;
/* your task below */
/* data work.up&ref;*/
/* set work.base&ref;*/
/* format perc_acum 9.3;*/
/* if first.id_cliente then perc_acum=0;*/
/* perc_acum + perc;*/
/* run; */
%end;
%mend;
%ciclo_teste()
tested on SAS 9.4 win7 x64
Edited:
In fact I would recommend doing this to avoid scanning a long string which is inefficient.
%macro tester();
/* get the number of obs (a more efficient way) */
%local NN;
proc sql noprint;
select nobs into :NN
from dictionary.tables
where upcase(libname) = 'WORK'
and upcase(memname) = 'AMOSTRA';
quit;
/* assign &ref by random access */
%do i = 1 %to &NN;
data _null_;
a = &i;
set work.amostra point=a;
call symputx('ref',id,'L');
stop;
run;
%*check;
%put ref = &ref;
/* your task below */
%end;
%mend;
%tester()
Please let me know if you have further questions.
Wow that seems like a lot of work. Why not just do the following:
data work.amostra;
input id;
cards;
1
4
7
10
;
run;
%macro test001;
proc sql noprint;
select count(*) into: cnt
from amostra;
quit;
%let cnt = &cnt;
proc sql noprint;
select id into: x1 - :x&cnt
from amostra;
quit;
%do i = 1 %to &cnt;
%let x&i = &&x&i;
%put &&x&i;
%end;
%mend test001;
%test001;
now in variables &x1 - &&x&cnt you have your values and you can process them however you like.
In general if your list is small enough (macro variables are limited to 64K characters) then you are better off passing the list in a single delimited macro variable instead of multiple macro variables.Remember that PROC SQL will automatically set the count into the macro variable SQLOBS so there is no need to run the query twice. Or you can use %sysfunc(countw()) to count the number of entries in your delimited list.
proc sql noprint ;
select id into :idlist separated by '|' from .... ;
%let nr=&sqlobs;
quit;
...
%do i=1 %to &nr ;
%let id=%scan(&idlist,&i,|);
data up&id ;
...
%end;
If you do generate multiple macro variables there is no need to set the upper bound in advance as SAS will only create the number of macro variables it needs based on the number of observations returned by the query.
select id into :idval1 - from ... ;
%let nr=&sqlobs;
If you are using an older version of SAS the you need set an upper bound on the macro variable range.
select id into :idval1 - :idval99999 from ... ;
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;
I've got the following issue and I'm not sure on how to do this.
I'm trying to merge 6000 variables through the code below
Please find below the piece of code I've written for two of the variables
data big_aat_1;
merge Aat_1(rename=(var14=var14_t0 var28=var28_t_0))
Aat_2(rename=(var14=var14_t_1 var28=var28_t_1))
Aat_3(rename=(var14=var14_t_2 var28=var28_t_2))
Aat_4(rename=(var14=var14_t_3 var28=var28_t_3))
Aat_5(rename=(var14=var14_t_4 var28=var28_t_4))
Aat_6(rename=(var14=var14_t_5 var28=var28_t_5));
by nouv_date;
run;
My aim is to try to automate my piece of code for the 6000 variables I have and keep the way I'm doing it e.g. with the merge.
The result will all the variables would be like the one below. The ...represent the rest of the variables
data big_aat_1;
merge Aat_1(rename=(var14=var14_t0 var28=var28_t_0 var37=var37_t_0 ...))
Aat_2(rename=(var14=var14_t_1 var28=var28_t_1 var37=var37_t_1 ...))
Aat_3(rename=(var14=var14_t_2 var28=var28_t_2 var37=var37_t_2 ...))
Aat_4(rename=(var14=var14_t_3 var28=var28_t_3 var37=var37_t_3 ...))
Aat_5(rename=(var14=var14_t_4 var28=var28_t_4 var37=var37_t_4 ...))
Aat_6(rename=(var14=var14_t_5 var28=var28_t_5 var37=var37_t_5 ...));
by nouv_date;
run;
There are 2 things I need to state
1) I have a dataset / table that contains all the distinct variable names (e.g. var14, var28 ...). It would be great if I can use it. The name of the dataset is dicoAg
2) I need to keep the merge for some reasons I cannot talk about here.
If you have any insight
I started creating test data sets (you obviously already have them):
%MACRO P;
%DO I=1 %TO 6;
data aat_&I;
%DO J=1 %TO 6000;
var&J=&J;
%END;
nouv_date=1;output;
run;
%END;
%MEND;
%P;
and then I used proc contents to have a list of the variables (you can skip this step and use dicoAg):
proc contents data=aat_1 varnum out=vars;run;
and then you have sas write the rename code for you:
data _NULL_;
set vars /*dicoAg*/(where=(NAME^="nouv_date")) end=fine;
file "MyPath\Rename.sas";
if _N_=1 then do;
put '%MACRO RENAME(J=); ';
put '(rename=( ';
end;
/*intead of NAME use the variable in dicoAg which contains all the variables' names*/
put ' ' NAME '=' NAME +(-1) '_&J';
if fine then do;
put ' )) ';
put '%MEND; ';
end;
run;
you include the code:
%include "MyPath\Rename.sas";
and at the end you write the macro to do the merge:
%MACRO P;
data big_aat_1;
merge
%DO D=1 %TO 6;
aat_&D. %RENAME(J=&D)
%END;
;
by nouv_date;
run;
%MEND;
%P;
Everyone,
Without going into full details, my man-a and I did that
data big_aat_1;
merge %do j=1 %to 6 ; Aat_&j(rename=(%do i=1 %to &&&&nvar&&pays&l ; &&&&var&&pays&l.._&i=&&&&var&&pays&l.._&i.._t%eval(&j-1) %end ; )) %end ; ;
by nouv_date;
run;
Not perfect nor superbly efficient but doing the trick.
Explanation :
&&&&nvar&&pays&lis the max number of variables
&&&&var&&pays&l.._&iis the variable
The results will give you something like this
merge Aat_1(rename=( var1=var1_t0 var31=var31_t0 var60=var60_t0 var90=var90_t0 var119=var119_t0 ...
Aat_6(rename=( var1=var1_t5 var31=var31_t5 var60=var60_t5 var90=var90_t5 var119=var119_t5...
Best.