SAS - Macro calling another macro - macros

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;

Related

Merging datasets only if they exist

So I'm trying to create a macro in sas and I'm attempting to merge multiple data sets in one data step. This macro also creates a variety of different data sets dynamically so I have no idea what data sets are going to be created and which ones aren't going to. I'm trying to merge four data sets in one data step and I'm trying to only merge the ones that exist and don't merge the ones that don't.
Haven't really tried anything but what I'm trying to do kind of be seen below.
DATA Something;
MERGE Something SomethingElse AnotherThing EXIST(YetAnotherThing)*YetAnotherThing;
RUN;
Well obviously that doesn't work because SAS doesn't work like that but I'm trying to do something like that where YetAnotherThing is one of the data sets that I am testing to see whether or not it exists and to merge it to Something if it does.
If you have a systematic naming convention this can be simplified. For example if you have a common prefix it becomes:
data want;
merge prefix: ;
run;
If they're all in the same library it's also easy. But otherwise you're stuck checking every single name as above.
Something along these lines:
data test1;
do i = 1 to 10;
val1 = i;
output;
end;
run;
data test2;
do i = 1 to 10;
val2 = i*2;
output;
end;
run;
data test3;
do i = 1 to 10;
val3 = i*3;
output;
end;
run;
data test5;
do i = 1 to 10;
val5 = i*4;
output;
end;
run;
%macro multi_merge(varlist);
%local j;
data test_merge;
set %scan(&varlist,1);
run;
%put %sysfunc(countw(&varlist));
%if %sysfunc(countw(&varlist)) > 1 %then %do;
%do j = 2 %to %sysfunc(countw(&varlist));
%if %sysfunc(exist(%scan(&varlist,&j))) %then %do;
data test_merge;
merge test_merge %scan(&varlist,&j);
by i;
run;
%end;
%end;
%end;
%mend;
%multi_merge(test1 test2 test3 test4 test5);
Test4 does not exist.
Same thing with no loop:
if you don't want to loop, you can do this:
%macro if_exists(varlist);
%if %sysfunc(exist(%scan(&varlist,1))) %then %scan(&varlist,1);
%mend;
data test_merge2;
merge test1
%if_exists(test2)
%if_exists(test3)
%if_exists(test4)
%if_exists(test5)
%if_exists(test6);
by i;
run;
I can think of two options:
Loop through the list of input datasets, check if each exists, then merge only those that do.
At the start of your macro, before you conditionally create each of the potential input datasets, create a dummy dataset with the same name containing no rows or columns. Then when you attempt to merge them, they will always exist, without messing up the output with lots of missing values.
Sample code for creating an empty dataset:
data want;
stop;
run;

SAS Macro Query

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.

Macro Do Until Loop in SAS

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).

Bootstrap macro in SAS

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.

Is it possible to pass a macro into a macro?

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);