I wrote a proc SQl normally first without any macro variables and it works, now I would like to convert it to macro and it doesnt work, can you see where the problem is ?
%macro macrova(LIB=, DATA=);
proc sql noprint;
/*creating an in memory variable with all char variables from the dataset*/
select name into :names separated by ' '
from dictionary.columns where libname="&lib" and memname="&data" and type='char';
/*to make sure we have the same order of the variables an in-memory variable ORDER is created*/
SELECT NAME INTO: ORDER SEPARATED BY ' '
from sashelp.vcolumn where LIBNAME= "&lib" and memname="&datA" ;
quit;
%MEND;
OPTIONS MLOGIC SYMBOLGEN;
%macrova(LIB=SASHELP,DATA=CLASS)
%PUT &NAMES;
%PUT ℴ
LOG:
55 %PUT &NAMES;
WARNING: Apparent symbolic reference NAMES not resolved.
&NAMES
56 %PUT ℴ
WARNING: Apparent symbolic reference ORDER not resolved.
&ORDER
You need to either define the macro variables before calling the macro or add %GLOBAL statement to the macro. Otherwise the macro variabless will be created as local and disappear when the macro exits.
Note that there is no variable named ORDER in dictionary.columns, I assume you meant to use the VARNUM variable.
Also there is no need to run two queries to generate two macro variables.
%macro macrova(LIB=, DATA=);
%global names order ;
proc sql noprint;
select name
, varnum
into :names separated by ' '
, :order separated by ' '
from dictionary.columns
where libname=%upcase("&lib")
and memname=%upcase("&data")
and type='char'
order by 2
;
quit;
%mend macrova;
%macrova(LIB=SASHELP,DATA=CLASS)
%put &NAMES;
%put ℴ
The names are being created as LOCAL values inside the macro and are not available outside it. Try adding
%global NAMES ORDER;
to the macro before the SELECT statements.
Related
I have a directory of csv files, each with names that begin with the letter m and end with a number. There are twelve files - m6 to m17.
I'd like to read them in and process them as separate data sets. I've written two macros attempting to do so. Macro1 works. Macro2 breaks. I would prefer Macro2 if I can get it to work, to avoid unnecessary bits like my creation of %rawfiles, invocation of %sysfunc, etc.
Macro 1:
%let rawcsv = C:\ALL\dat\;
%let rawfiles = m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17;
%macro1;
%do i = 1 %to %sysfunc(countw(&rawfile));
%let rawfile = %scan(&rawfiles, &i);
proc import datafile="&&rawcsv.&&rawfile.csv"
out=&rawfile replace
dbms=csv;
guessingrows=500;
run;
%end;
%mend;
%macro1;
Macro 2:
%let rawcsv = C:\ALL\dat\;
%macro macro2(first=6, last=19);
%do i=&first. %to &last. %by 1;
proc import datafile="&&rawcsv..m&&i.csv"
out=m&i replace
dbms=csv;
guessingrows=500;
run;
%end;
%mend;
%macro2;
%macro2 is my bad imitation of this solution. It returns the following errors:
MPRINT(MACRO2): proc import datafile="C:\ALL\dat\m.6.csv" out=m.6 replace
dbms=csv;
MPRINT(MACRO2): ADLM;
MPRINT(MACRO2): guessingrows=500;
MPRINT(MACRO2): run;
ERROR: Library name is not assigned. /*repeats this error 14 times, once per file*/
Two questions:
What am I missing in %macro2?
Do you see a better solution that I am not using? The files are structured differently and not stackable, just a heads up.
From your log we can see a period is being inserted into the output dataset name. Just remove that extra period in your macro definition.
MPRINT(MACRO2): proc import datafile="C:\ALL\dat\m.6.csv" out=m.6 replace dbms=csv;
The extra & in the code is probably confusing you. When the macro processor sees two & it converts them to one and then reprocesses the string to further resolve the resulting macro variable references.
The period after a macro variable name is not required when the macro processor can tell that the name has ended. But the periods are needed in some places.
One place in your code is where it is required to make sure the macro processor knows where the name ends (the macro variable is named readcsv not readcsvm ). Another is where you want to place an actual period after the value of a macro variable. You will need to place two periods there since the first will be used by the macro processor when it evaluates the macro variable value.
In this version of macro2 I have removed the periods after the macro variable names in the places where they are not required just to emphasize the places where the period is required.
%let rawcsv = C:\ALL\dat\;
%macro macro2(first, last);
%local i ;
%do i=&first %to &last ;
proc import dbms=csv
datafile="&rawcsv.m&i..csv"
out=m&i replace
;
guessingrows=500;
run;
%end;
%mend macro2;
%macro2(first=6, last=19)
Small typo here, you need to use an & in front of LAST not the %.
%do i=&first. %to %last. %by 1;
Should be:
%do i=&first. %to &last. %by 1;
Unless you're using a separate macro called last to determine your end of the loop. But in that case you likely wouldn't also have a parameter called last.
If you're looking for alternate options I usually recommend reading all at once using a data step or CALL EXECUTE instead of macro loops as they're infinitely easier to debug in my opinion.
https://communities.sas.com/t5/SAS-Communities-Library/How-do-I-write-a-macro-to-import-multiple-text-files-that-have/ta-p/223627
I have a SAS dataset with values
yyyymm
201605
201606
201607
201608
201609
I am trying to find a way to pass these values one at a time to macro such that
do while dataset still has value
%macro passdata(yyyymm);
end
How can I do this in SAS. Can someone please help with a sample/code snippet.
As mentioned by the prior comment, a way to pass parameters is through call execute routine. Note that this must be done in datastep environment. The lines are read from the set you input.
You can input multiple variables. Just add more variables in '||' separators. Note that the variables may have a lot of whitespaces in them. (==Do comparisons with care.)
Here is a small sample code. Tested.
data start_data;
input date_var ;
datalines;
201605
201606
201607
201608
201609
;
run;
%macro Do_stuff(input_var);
%put 'Line generates value ' &input_var;
%mend do_stuff;
data _null_;
set start_data;
call execute('%do_stuff('||date_var||')' );
run;
Try this example and try modifying to meet your needs... From the "source" dataset we can use call symput() to assign a macro token to each observation (differentiated by the SAS automatic dataset variable n so My_token1, My_token2, etc.) Once you have a set of macro variables defined, just loop through them! This program will print all the individual records from source to the SAS log:
data source;
do var=1000 to 1010;
output;
end;
run;
data _null_;
set source;
call symput(compress("My_token"||_n_),var);
run;
%put &my_token1 &my_token4;
%Macro neat;
%do this=1 %to 11;
*Check the log.;
%put &&My_token&this;
%end;
%mend;
%neat;
I need to create sum of 4 variables multiple times each time with new set of variables. For e.g. A1=sum(a1,a2,a3,a4),B1=sum(b1,b2,b3,b4) & so on. So , I am trying to write a macro that will help me do it easily. Following is the code:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
I am using SAS EG for the same & when I run the macro I get the following note:
NOTE: Writing TAGSETS.SASREPORT13(EGSR) Body file: EGSR
& obviously when I try to execute the macro it throws an error.
Can some one help me out?
when calling a macro, you need to precede the macro name with a % symbol, eg as follows:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
The NOTE is harmless. It is ERRORs and WARNINGs in general that you should be concerned with.
I'd point out that this will probably still throw an error, as you are trying to replace characters in a variable (&var1) that appears as though it should contain a numeric field (being part of a sum function). Given your description of what you are trying to achieve, I'd suggest adding the new variable name as another macro parameter - as follows:
%macro SUM2(VAR1,var2,var3,VAR4,varname);
data Subs_60_new;
set Subs_60;
&varname=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2
,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4
,MyNewVariable);
I am looking for a way to resolve the parameters of an already defined SAS MACRO. I know where to locate session defined macros (WORK.SASMACR/WORK.SASMAC1), but now I want to know go a step further and determine its parameters. So for example:
%macro test(val1, val2);
%put &val1 &val2;
%mend test;
/* With proc catalog I can find the defined macro... */
proc catalog cat=work.SASMAC1;
contents out=macros;
run;
/* How to create function/macro which takes the macro name and returns its arguments, eg:*/
%resolveMacroParams(test);
/* ...returns: */
val1 val2
Any help would be greatly appreciated!
Kind Regards,
Herman
You can do this if the macro was compiled with the SOURCE option. The following gives you a dataset with the macro text from which you could trivially obtain the arguments:
libname dummy 'c:\temp\';
options mstored sasmstore=dummy;
%macro test(val1, val2)/store source;
%put &val1 &val2;
%mend test;
filename mymacr temp;
%copy TEST/lib=dummy source out=mymacr;
data test;
infile mymacr truncover;
input #1 line $100.;
run;
You want something like values supplied as parameters to a function at its last call?
The definition of macro doesn't store it's values. Macro params are in local scope of that macro and their values are not anywhere AFTER the macro ends.
During execution you can find it in dictionary.macros:
So to keep it you'd have to change your macro like this:
%macro test(val1, val2);
proc sql;
create table TEST_VALS as
select * from dictionary.macros
where scope='TEST'
;
quit;
%mend test;
%test(11, 22);
or put those values into some global macro variables.
I'm creating a macro variable but when use the same macro variable in my Proc Report this macro is generating a space in front of the value
Select COUNT(DISTINCT USUBJID) into: N1 from DMDD where ARMN=1;
How do I rectify it in the same code??
This is actually 'working as designed' for PROC SQL SELECT INTO. While all of the other answers are, in some ways, correct, this is a special case as opposed to normal macro variables, such as
%let var= 5 ;
%put [&var];
where that will return just
[5]
while this is not doing that. That is a behavior of PROC SQL SELECT INTO, and is intentional.
These two statements:
proc sql;
select name into :name from sashelp.class where name='Alfred';
select name into :shortname separated by ' ' from sashelp.class where name='Alfred';
quit;
%put `&name` `&shortname`;
are non-identical. separated by ' ' (or any other separated by) will always trim automatically unless notrim is included, and if you have 9.3 or newer, you have a new option, trimmed, which you can use if you intend to select a single variable. I think this behavior was introduced in 9.2 (the non-trimming of select into without a separated by, by default).
If you are only selecting a single value, adding separated by ' ' will have no impact on your result other than to cause the trimming to occur.
This is because any macro variable is stored as a character. If the source data is numeric then SAS uses the best12. format to convert to character, therefore the result is padded with leading blanks. I get around this by using the CATS function which strips out leading and trailing blanks. You can't use the LEFT or STRIP functions as these only work against character variables.
Select cats(COUNT(DISTINCT USUBJID)) into: N1 from DMDD where ARMN=1;
Use the %cmpres() macro to remove blanks.
http://support.sas.com/documentation/cdl/en/mcrolref/64754/HTML/default/viewer.htm#n0tvdbcgr9xc6dn14wmx9hpd6h51.htm
Select trim(put(COUNT(DISTINCT USUBJID), 16. -L)) into: N1 from DMDD where ARMN=1;
Use PUT() to format the output string with -L (left) alignement.