SAS - create macro-name from variable - sas

I am wondering how I can set a macro-name from a variable.
Like this:
%Macro test(name);
...
%Macro new_&name;
...
%Mend;
...
%Mend test
Or if this is not possible:
%macro one(name);
%let mname=&name;
%mend one;
%macro two_&name;
...
%mend;
Any ideas? Many thanks.

First thing that pops into my mind is to use a temporary fileref to build your macros. Then include that fileref.
I think this does what you are looking for:
%macro test(x,file);
data _null_;
file &file;
format outStr $2000.;
outStr = ('%macro test_' || strip("&x") || "();");
put outStr;
put '%put hello world;';
outStr = '%put Passed in value is x:' || "&x and file: &file;";
put outStr;
put "proc means data=sashelp.class(obs=&x) mean; var age; run;";
put '%mend;';
run;
%include &file;
%mend;
filename tempfile temp;
%test(2,tempfile);
%test_2;
filename tempfile clear;

Yes you can do such a thing:
%macro macroFunc();
%put hi there;
%mend;
%macro macroCall(macroName);
%&macroName.();
%mend;
%mcr2(macroFunc);
But I'm really curious in what context this makes sense.
Seems like it will in no time result into a coding mess.

I never knew that you could not use a variable in a %MACRO statement...but that appears to be the case. As it says in the SAS documentation (http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#macro-stmt.htm) "you cannot use a text expression to generate a macro name in a %MACRO statement."
My next thought was that you might be able to create the %MACRO statement as a variable, but I couldn't find a way to mask %MACRO in the creation of the variable.
I finally figured out a work around, but it is likely not the best way to do this (and it may not work for what you're trying to do). I found that I could compile the macro statement in a data step. Unfortunately though, I could only run the macro from the variable when the entire macro code (from the %MACRO to the %MEND statement) was saved in the variable. See the code below.
%MACRO test(name);
data test;
*COMPILE MACRO STATEMENT;
pct=%nrstr('%');
name="new_&name";
beginning=pct||'MACRO '||strip(name)||'();';
*CODE TO BE INSIDE MACRO;
/*Note: SAS will encounter errors if you try to assign text containing macro
functions (e.g., %PUT, %IF, etc.) to a variable. To get around this, you must
put hide the % in the following syntax, %nrstr('%'), and concatenate/join the
syntax with the rest of the string */
code=pct||'PUT HELLO!;';
*COMPILE MEND STATEMENT;
end=pct||'MEND;';
call symput('MacroStatement',beginning||code||end); *Output var containing macro;
call symput('Execute',pct||strip(name)||';'); *Output var containing statement to run macro;
output;
run;
&MacroStatement
&Execute
%MEND;
%test(name1);

options mprint mlogic symbolgen nospool;
%let definition=abc;
%let mdef=macro &definition.;
%&mdef.;
%put TEST;
%mend;
%abc;

Related

How to trigger a macro for every data set containing a keyword

I'm doing a crash course on SAS macros and I'm stuck at one exercise. I have to create a macro, that will create a proc contents tables for every data set, that contains a keyword. I know how to do that using call execute, but I need this using proc sql and %do loop.
My attempt:
%macro contents(data=&syslast);
proc contents data=&data;
title "&data";
run;
%mend contents;
%macro ContentsAll(keyword);
select libname||'.'||memname
into :dsn1-
from sashelp.vstabvw
where upcase(memname) like %upcase("%quote(%)&&keyword%")
;
quit;
%do i=1 %to &sqlobs;
%contents(data=&&dsn&i);
%end;
%mend ContentsAll;
options mlogic mprint;
%ContentsAll(class);
options nomlogic nomprint;
I know there is some issue with a select statement, but I have no idea how to fix it. And where statement has an unprotected variable (my attempts at fixing it just break the where clause alltogether.
First of all, good job. It's so good that I'm almost sorry you're only missing the Proc SQL Statement :-)
%macro contents(data=&syslast);
proc contents data=&data;
title "&data";
run;
%mend contents;
%macro ContentsAll(keyword);
proc sql noprint;
select libname||'.'||memname
into :dsn1-
from sashelp.vstabvw
where upcase(memname) like %upcase("%quote(%)&&keyword%")
;
quit;
%do i=1 %to &sqlobs;
%contents(data=&&dsn&i);
%end;
%mend ContentsAll;
options mlogic mprint;
%ContentsAll(class);
options nomlogic nomprint;
There is no need to create all of those macro variables. Just keep the list of names in actual data. You can use CALL EXECUTE() to generate the code you want to run for each member in the list.
Note that the variables LIBNAME and MEMNAME will already be in uppercase when pulled from the DICTIONARY.MEMBERS metadata that the view SASHELP.VSTABVW uses. But the user passing in a value for the KEYWORD parameter might not have entered uppercase letters.
%macro ContentsAll(keyword);
data _null_;
set sashelp.vstabvw ;
where memname contains "%qupcase(&keyword)" ;
call execute(cats('%nrstr(%contents)(data=',libname,'.',memname,')'));
run;
%mend ContentsAll;

How to concatenate string and numeric SAS Macro and use it in where statement

so I have a code like below
%let THIS_YEAR=2020;
%macro programall;
%do i = 2016 %to &THIS_YEAR;
%let num2 =%eval(&i-2000);
%let xxx= CAT("MP",&num2);
data t_&i.;
set table1;
where GROUP in ("&xxx");
run;
%end;
for example
when i=2016
num2 = 2016-2000;
num2 = 16;
and try to concatenate with "MP", so it should create xxx=MP16.
and try to use in where statement.
but it is causing error.
how can I create Macro Variable like "MP16"correctly, so I can use it in where clause?
Thanks
You cannot use functions in macro code, they are just treated as any other text to the macro processor. But there is no need to use a function to concatenate text in macro code. Just expand the macro variable where you want to use the text it contains.
%let xxx= MP&num2 ;
Macro variables are just text (not strings, text as in the thing you type in). So to concatenate macro variables, just put them next to each other.
%let var1=Banana;
%let var2=Pepper;
%let var3=&var1. &var2.;
%put &=var3;
You don't actually have to use the third variable of course, you could just use "&var1. &var2." or whatever in your code directly.
Try
%let THIS_YEAR=2020;
%macro programall;
%local year;
%do year = 2016 %to &THIS_YEAR;
data t_&year.;
set table1;
where GROUP in ("MP%eval(&year-2000)");
run;
%end;
%mend;
options mprint;
%programall

Rename a external file in a SAS macro

I am getting a generic 'Statement not valid or out of order' message with the below:
%macro test;
data _null_;
%if %sysfunc(fileexist("C:\report_201809.xlsx")) = 1 %then %do;
rc=%sysfunc(rename("C:\report_201809.xlsx",
"C:\report_201809.xlsx"_old.xlsx",'file'));
%end;
%mend;
%test;
The code below should get you what you need. While you can use %if statements in a data step you generally won't need to. I'm guessing the error is coming from the %sysfunc function around the fileexist and rename functions. %sysfunc allows you to call data step functions outside of a data step so it is not needed here.
%macro test;
data _null_;
if fileexist("C:\file.txt") then do;
rc = rename("C:\file.txt", "C:\file2.txt", 'file');
end;
run;
%mend;
Alternatively, you could use an X Command that allows you to execute Windows commands. You could replace the rename function with the following statement.
x move C:\file.txt C:\file2.txt;
Remove the DATA _NULL_ or proceed per #J_Lard.
Macro arguments used in %sysfunc invoked function calls are implicitly quoted and do not need additional ' or "
%macro test;
%local rc;
%if %sysfunc(fileexist(C:\report_201809.xlsx)) = 1 %then %do;
%let rc = %sysfunc(rename(C:\report_201809.xlsx,C:\report_201809_old.xlsx,file));
%end;
%test;
You original code may have worked (by way of non-obvious side effect) if the filename "C:\report_201809.xlsx"_old.xlsx" (having an extraneous ") was corrected to "C:\report_201809_old.xlsx"

sas macro loop to rename variable

Hi I am trying to rename variables using SAS Macro loop.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data MilkNew;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
%Re(6)
This loop is mean to rename MT...Sp to MTSp.... Eg:MT20SP to MTSp20.
When I run my loop, there was no error but the variable names were not changed in MilkNew at all.
Where does the problem come? Thanks!
If the only purpose of the macro is to rename the variables in the data set, then why read the data with a set statement. Your data set is probably really small so you don't even realize the inefficiency of doing that. Instead use the modify statement in proc datasets to accomplish the same thing, but more efficiently. Here's an alternative macro for you.
%macro renamevar(dsname, time);
%local lib ds i;
%let lib = %sysfunc(coalescec(%scan(&dsname, -2, %str(.)), work));
%let ds = %scan(&dsname, -1, %str(.));
proc datasets lib=&lib nolist;
modify &ds;
rename
%do i = 1 %to &time;
mt&&t&i..Sp=MTSp&&t&i.
%end;
;
quit;
%mend;
%renamevar(milk, 6);
Here's the log after the macro call:
NOTE: Renaming variable mt12MthSp to MTSp12Mth.
NOTE: Renaming variable mt20Sp to MTSp20.
NOTE: Renaming variable mt30Sp to MTSp30.
NOTE: Renaming variable mt40Sp to MTSp40.
NOTE: Renaming variable mt50Sp to MTSp50.
NOTE: Renaming variable mt60Sp to MTSp60.
NOTE: MODIFY was successful for WORK.MILK.DATA.
NOTE: PROCEDURE DATASETS used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds
You should move the loop so that it only generates just the RENAME statement (or even just the old=new name pairs). What is happening now is that you keep overwriting MilkNew so only the last RENAME has any effect.
%macro Re(time);
data MilkNew;
set Milk;
%do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
run;
%mend Re;
%Re(6)
You should have seen the last variable name in the loop (so the 6th) changed. That's because you repeated the same data step with the same source dataset but a different destination - so each time you 'forgot' the changes made in the earlier step.
So, this would've worked, though I'll get in a minute to why this isn't a good way to do this.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data Milk;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
data milk;
input
MT12mthSP
MT20SP
MT30SP
MT40SP
MT50SP
MT60SP
;
datalines;
12 20 30 40 50 60
;;;;
run;
%Re(6)
Here I had it make all changes to Milk and save them back in that dataset. If you want to preserve Milk then first make Milk_New then have that in both set and data statements.
Second, you should not do a new data step for each change. Macros don't have to have a data step in them; they can be run inside the datastep.
So for example:
%macro Re(time);
%Do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
%mend Re;
data milk_new;
set milk;
%Re(6);
run;
Even better would be generating this list outside of a macro entirely - look up "generating code SAS" for suggestions on that.
If you didn't see any renames at all, you also may have an issue where a label is present on the column(s). That won't affect your usage of the variable name, but it will make it confusing. Use
label _all_;
Or include a label-clearing statement (label <varname>; where you pop in the same variable name as the original variable name before rename) inside your macro loop to fix that.

How do I use a value that is returned from a Macro Variable in another macro

I have simplified this a lot so it can be repeated
%macro macro_one(dt2);
%let var1 = &dt2;
%mend;
Then I have another macro and I want to use the output from macro one in macro 2
%macro macro_print(dt2);
/*call macro 1*/
%macro_one(&dt2);
%put &var1;
%mend;
/call macro/
%macro_print('purple');
It should print purple in the logs but I get an error
I get an error though - i suspect I need to assign the macro variable from macro one when I call macro two.
First, I suspect you have a typo between your code and here. Proper way to define a macro is:
%macro blah(x);
<do stuff>
%mend;
not:
%macro_blah(x);
<do stuff>
%mend;
The macro is created in %macro_one and defaults to a local scope. You can fix this by declaring it %global.
%macro macro_one(dt2);
%global var1;
%let var1=&dt2;
%mend;
Also, use %put not put in %macro_two.