In SAS I can use this handy snippet to do something like this.
%let listofvars = work.apples work.bananas work.oranges;
%let var_no = 1;
%let var = %scan(&listofvars, &var_no, ' ');
%do %while (&var ne);
proc sort data = &var;
by id;
run;
%let var_no = %eval(&var_no +1);
%let var = %scan(&listofvars, &var_no, ' ');
%end;
To sort each of those datasets.
I'd quite like to reduce the snippet to a loop macro, so I can do something like this:
%let setlist = work.apples work.bananas work.oranges;
%macro mymacro(dataset);
proc sort data = &dataset.
by id;
run;
%mend;
%loop(&setlist, mymacro);
/*the loop macro will know to pass the &var. in as a arguement to the macro*/
This will make for much better code readability.
Is this possible?
Yes. The name macro routine can be a macro. Macros "write" SAS code for you.
%macro create(dataset);
data &dataset;
do i=1 to 10;
id=rannor(0);
output;
end;
run;
%mend;
%macro sort(dataset);
proc sort data=&dataset;
by id;
run;
%mend;
%macro loop(list,mcr);
%local i n val ;
%let n=%sysfunc(countw(&list));
%do i=1 %to &n;
%let val = %scan(&list,&i);
%&mcr(&val);
%end;
%mend;
%let sets = apples oranges pears;
options mprint;
%loop(&sets,create);
%loop(&sets,sort);
Related
I am having trouble with the following %macro for a regression.
Basically, I want it to run whenever i=j, and i=1 to 12 and j=1 to 12.
%macro reg(num=);
%do i=1 %to #
%do j=1 %to #
proc reg data=ccy.eur; *Specify Currency Data Set Here;
model Ft_leadSt&i = Ft_St&j;
where &i=&j;
ods output parameterestimates (persist) =dpara1;
run;
%end;
%end;
%mend;
%reg(num=12)
The problem is my output seems to stop when i=9 and j=9. I haven't been able to figure out why?
Screen Shot of data
I would rewrite this without the double loop and only 1 PROC REG statement:
%macro reg(num=);
proc reg data=ccy.eur; *Specify Currency Data Set Here;
ods output parameterestimates (persist) =dpara1;
var FT_leads1 - FT_leads&n FT_St1 - FT_St&n;
%do i=1 %to #
_&i: model Ft_leadSt&i = Ft_St&i;
run;
%end;
quit;
%mend;
%reg(num=12)
Make sure you have those variables in the data set.
My loop is only making 1 iteration. I am supposed to create three macro variables: var1 = Month1, var2 = Month2, and var3 = Month3 if qtr = qtr1. My loop is only creating var1 = Month1 and I = 1 when I checked it with a Put statement. It is only making one iteration, so I'm not sure what I am doing wrong.
%Let qtr = qtr1;
%Macro Firstqtr(qtr);
%Let I = 1;
%If &qtr = qtr1 %then %do %until (&I > 3);
%Let var&I = Month&I;
%let I = %eval(&I + 1);
%end;
%Mend Firstqtr;
%Firstqtr(qtr);
Your %DO loop will never run given the input you made for the QTR parameter to your macro. You can turn on MLOGIC to see this.
1228 options mlogic;
1229 %Firstqtr(qtr);
MLOGIC(FIRSTQTR): Beginning execution.
MLOGIC(FIRSTQTR): Parameter QTR has value qtr
MLOGIC(FIRSTQTR): %LET (variable name is I)
MLOGIC(FIRSTQTR): %IF condition &qtr = qtr1 is FALSE
MLOGIC(FIRSTQTR): Ending execution.
If you want to pass in qtr1 as the value either hard code it in the macro call.
%Firstqtr(qtr1);
Or you could make your call pass in the macro variable you defined earlier.
%let qtr=qtr1;
%Firstqtr(&qtr);
It might make this distinction between the parameter's value and the value of an external macro variable with the same name clearer if you call the macro using named parameters. Note: you can use parameter names in the macro call even for parameters that were defined as positional in the macro definition.
%Firstqtr(qtr=&qtr);
option mprint;
%global qtr;
%Let qtr = qtr1;
%Macro Firstqtr(qtr);
%Let I = 1;
%If &qtr = &qtr %then %do %until (&I > 3);
%Let var&I = Month&I;
%let I = %eval(&I + 1);
%end;
%put &var1 &var2 &var3;
%Mend Firstqtr;
%Firstqtr(qtr);
You have to declare qtr as Global variable then only the if condition will be pass.
The issue is one of macro variable scope. qtr is defined both globally (line1) and locally (as a macro parameter) so the local (empty) one is used instead.
Try passing it through in your parameter as follows:
%Let qtr = qtr1;
%Macro Firstqtr(qtr);
%Let I = 1;
%If &qtr = qtr1 %then %do %until (&I > 3);
%global var&i;
%Let var&I = Month&I;
%put var&i=&&var&i;
%let I = %eval(&I + 1);
%end;
%mend Firstqtr;
%Firstqtr(&qtr);
Be aware that the variables you are creating would have local scope - to make them global, you declare them as such (%global statement).
I started to learn %macro in SAS and now I'm trying to implement simple bootstrap with histogram as an output.
/*Create K data sets(vectors)*/
%macro datasets(K);
%do i=1 %to &K;
data indata&i;
%do j = 1 %to 50;
x=(rand('normal',2,9));
output;
%end;
run;
%end;
%mend datasets;
%datasets(3);
/*Bootstrap and hist*/
%macro boot (data,res);
%do i=1 %to &res;
%let x = (sample(&data,50));
%let m = (mean(&x));
%end;
proc iml;
read &m into A;
create DataM from A;
append from A;
close Data1;
quit;
proc univariate data=Data1;
histogram m;
run;
%mend boot;
%boot(Indata1,100);
It doesn't work and I can't understand why. Can you point me the mistake?
Use PROC SURVEYSELECT to generate bootstrap samples then do bootstrap analysis by Replication (a variable created by SURVEYSELECT). Your macro idea will be far too slow.
As mentioned use Proc SurveySelect and Proc Means. You can select all 100 samples in one Proc SurveySelect and then apply Proc Means with a BY statement to calculate the means in one step. Macro's don't add anything to the solution here.
I'm posting both solutions - the macro solution does take longer as well.
*Without macro;
proc surveyselect data=indata1 out=rsample method=srs n=50 reps=100;
run;
proc means data=rsample noprint;
by replicate;
var x;
output out=Data1 mean(x)=m;
run;
proc univariate data=Data1;
histogram m;
run;
*Macro solution;
%macro boot(data, res);
%do i=1 %to &res;
%*Currently pulls the same sample every time but you can fix that part;
proc surveyselect data=&data out=x method=srs n=50 reps=1 seed=343434;
run;
proc means data=x noprint;
var x;
output out=m mean(x)=m;
run;
proc append base=DataM data=m;
run;
%end;
%mend;
%boot(Indata1,10);
Perhaps it will help if we outline some of the ways that the posted macro code does NOT work. If nothing else then as examples of things to avoid.
If the first macro , %datasets(), you are using a macro %DO loop where you should use a normal data step DO loop. Also make sure to define your local macro variables as local. This will prevent the macro from modifying the value of an existing macro variable with the same name.
/*Create K data sets(vectors)*/
%macro datasets(K);
%local i ;
%do i=1 %to &K;
data indata&i;
do j = 1 to 50;
x=(rand('normal',2,9));
output;
end;
drop j;
run;
%end;
%mend datasets;
In the second macro you have a %DO loop that does nothing.
%do i=1 %to &res;
%let x = (sample(&data,50));
%let m = (mean(&x));
%end;
You repeat the exact same %LET statements multiple times. The result does not change since the loop variable i is not referenced at all. If you called the macro with data=indata1 then the result of the two statements will be that X=(sample(indata1,50)) and that M=(mean((sample(indata1,50)))). I think that perhaps you intended that the strings sample and mean might take some action, but since they have no macro triggers (& or %) they are just streams of characters to the macro processor.
I am not an expert on IML, but those statements also do not look like they are doing much.
I have a problem with SAS. More precisely with calling macro, which is inside another macro. Here is the example.
data TEST_1;
do i = 1 to 100;
a=i**2;
output;
end;
run;
data TEST_2;
do i = 1 to 100;
b=i**3;
output;
end;
run;
%macro macro_in(file_a);
data result1;
set &file_a;
c=a+1;
run;
%mend;
%macro_in(TEST_1);
%macro macro_out(file_b);
data result2;
set &file_b._2;
d=a-1;
run;
data _null_;
do i = 1 to 2;
call execute(COMPRESS('%macro_in(' || &file_b || '_' || i || ')'));
output;
end;
run;
%mend;
%macro_out(TEST);
First macro works completely fine, however there is a slight problem with variable file_b i the second macro (Code cannot use it as an argument to the inner macro). Thanks for any help!
When calling macro_in from macro_out you do not need a datastep, you can use the macro language:
%macro macro_out(file_b);
data result2;
set &file_b._2;
d=a-1;
run;
%do i = 1 %to 2;
%macro_in(&file_b._&i);
%end;
%mend;
I have two simple macros:
%macro One(mvStr);
/*some code here to replace varOne -> stringOne*/
%mend One;
%macro Two;
%local mvStr;
%let mvStr = "String varOne StringvarOneString";
%One(&mvStr);
%mend Two;
So, How to replace all substrings varOne with stringOne?
This should do what you want...
%macro One(mvStr);
/*some code here to replace varOne -> stringOne*/
%Sysfunc(tranwrd(&mvStr, varOne, StringOne));
%mend One;
%macro Two;
%local mvStr;
%let mvStr = "String varOne StringvarOneString";
%let mvStr = %One(&mvStr);
%put &mvStr;
%mend Two;
%two;
Did not know if you have to have two macros, but would the following suffice?
%macro one(mvStr, From, To);
%local mvStr From To;
%let &mvStr. = "String &From. String&From.String";
%put %Qsysfunc(prxchange(s/&From./&To./,-1,&mvStr.));
%*put &mvStr.;
%mend;
options mprint;
%one(mvStr,varOne,StringOne)