how do I get so inside the loop I get: var1, var2? I know it does not work to dereference j but the meaning gets more clear to what I want to do (see below)
%let var1 = apple;
%let var2 = pear;
data _null_;
do j=1 to j=2;
put &var&j; //<---?
end;
run;
in the log:
apple
pear
As noted above, J is not a macro variable so you cannot use it as such. You can use the SYMGET function to retrieve the value though. Assuming you want data step logic for some reason:
data _null_;
do i=1 to 2;
x= symget(catt('var', i));
put x;
end;
run;
Sounds like you want to resolve a macro variable whose name you are creating by appending the value of another macro variable to some constant prefix.
If you try to use code like this:
%let var1 = apple;
%let var2 = pear;
%let j=1 ;
%put &var&j;
You will get an error message that the macro variable named VAR does not exist.
You need to signal to the macro processor that it needs to delay trying to evaluate &var until after the suffix has been appended. The way to do this is to double the first &.
%put &&var&j;
The presence of double &'s will cause the macro processor to replace them with a single & and set a reminder to itself the re-scan the result for more macro variable references.
So the first pass will replace && with & and replace &j with 1. Then the second pass will replace &var1 with apple.
Related
I have 5 separate datasets(actually many more but i want to shorten the code) named dk33,dk34,dk35,dk51,dk63, each dataset contains a numeric field: surv_probs. I would like to load the values into 5 arrays and then use the arrays in a datastep(result), however, I need advice what is the best way to do it.
I am getting error when I use the macro: setarrays: (code below)
WARNING: The quoted string currently being processed has become more than 262 characters long. You might have unbalanced quotation
marks.
WARNING: The quoted string currently being processed has become more than 262 characters long. You might have unbalanced quotation
marks.
ERROR: Illegal reference to the array dk33_arr.
Here is the main code.
%let var1 = dk33;
%let var2 = dk34;
%let var3 = dk35;
%let var4 = dk51;
%let var5 = dk63;
%let varN = 5;
/*put length of each column into macro variables */
%macro getlength;
%do i=1 %to &varN;
proc sql noprint;
select count(surv_probs)
into : &&var&i.._rows
from work.&&var&i;
quit;
%end;
%mend;
/*load values of column:surv_probs into macro variables*/
%macro readin;
%do i=1 %to &varN;
proc sql noprint;
select surv_probs
into: &&var&i.._list separated by ","
from &&var&i;
quit;
%end;
%mend;
data _null_;
call execute('%readin');
call execute('%getlength');
run;
/* create arrays*/
%macro setarrays;
%do i=1 %to 1;
j=1;
array &&var&i.._arr{&&&&&&var&i.._rows};
do while(scan("&&&&&&var&i.._list",j,",") ne "");
&&var&i.._arr = scan("&&&&&&var&i.._list",j,",");
j=j+1;
end;
%end;
%mend;
data result;
%setarrays
put dk33_arr(1);
* some other statements where I use the arrays*
run;
Answer to toms question:
*macro getlength(when executed) creates 5 macro variables named: dk33_rows,dk34_rows,dk35_rows,dk51_rows,dk63_rows
*the macro readin(when executed):creates 5 macro variables dk33_list,dk34_list,dk35_list,dk51_list,dk63_list. Each containing a string which is comma separates the values from the column: eg.: 0.99994,0.1999,0.1111
*the macro setarrays creates 5 arrays,when executed, dk33_arr,dk34_arr,... holding the parsed values from the macro variables created by readin
I find that "macro arrays" like VAR1,VAR2,.... are generally more trouble than they are worth. Either keep your list of dataset names in an actual dataset and generate code from that. Or if the list is short enough put the list into a single macro variable and use %SCAN() to pull out the items as you need them.
But either way it is also better to avoid trying to write macro code that needs more than three &'s. Build up the reference in multiple steps. Build a macro variable that has the name of the macro you want to reference and then pull the value of that into another macro variable. It might take more lines of code, but you can more easily understand what is happening.
%let i=1 ;
%let mvarname=var&i;
%let dataset_name=&&&mvarname;
Before you begin using macro code (or other code generation techniques) make sure you know what code you are trying to generate. If you want to load a variable into a temporary array you can just use a DO loop. There is no need to macro code, or copying values, or even counts, into macro variables. For example instead of getting the count of the observations you could just make your temporary array larger than you expect to ever need.
data test1 ;
if _n_=1 then do;
do i=1 to nobs_dk33;
array dk33 (1000) _temporary_;
set dk33 nobs=nobs_dk33 ;
dk33(i)=surv_probs;
end;
do i=1 to nobs_dk34;
array dk34 (1000) _temporary_;
set dk34 nobs=nobs_dk34 ;
dk34(i)=surv_probs;
end;
end;
* What ever you are planning to do with the DK33 and DK34 arrays ;
run;
Or you could transpose the dataset first.
proc transpose data=dk33 out=dk33_t prefix=dk33_ ;
var surv_probs ;
run;
Then your later step is easier since you can just use a SET statement to read in the one observation that has all of the values.
data test;
if _n_=1 then do;
set dk33_t ;
array dk33 dk33_: ;
end;
....
run;
I have created a SAS macro, macro A, that takes in a variable name and returns transformed versions of that name i.e. if you run %A(asdf) you get out asdf_log asdf_exp asdf_10. I want to write another macro, macro B, that takes the output from the first macro and appends it together into a new macro variable.
%macro B(varList, outputName);
%let &outputName =
%A(var1);
%A(var2);
;
%mend
Is almost what I want to do, except that it obviously doesn't compile.
I am also not sure if this is possible in SAS.
As a further complication, the input to macro B is a list of variable that I want to run macro A for and append into one long list of variable names.
Why? Because I have a macro that runs on a list of variables and I want to run it on a transformed variable list.
Example:
I have %let varList = x y; and I want as an output x_log x_exp x_10 y_log y_exp y_10. To do this I want two macros one, macro A, that returns the transformed variables names:
%macro A(var);
&var._log
&var._exp
&var._10
%mend
I can't get the second macro (B as written above) to work properly.
So if the inner macro is just returning characters, that is it doesn't actually generate any non macro statements, then why not make the outer one work the same way?
%macro inner(x);
&x._log &x._exp &x._10
%mend;
%macro outer(list);
%local i;
%do i=1 %to %sysfunc(countw(&list));
%inner(%scan(&list,&i))
%end;
%mend outer;
%let want=%outer(X y Z);
This is not too hard. You need to loop over the values in varList, appending results to outputName. You also need to declare outputName as GLOBAL so it will be accessible outside %B
%macro B(varList, outputName);
%global &outputName;
%let &outputName = ;
%local i n var;
%let n = %sysfunc(countw(&varList));
%do i=1 %to &n;
%let var = %scan(&varList,&i);
%let &outputName = &outputName %A(&var);
%end;
%mend;
I need to pass a variable that contains spaces to a macro. And use this variable to make some logic and to buid a new column inside the macro.
I've tried something like:
%MACRO func(var);
if first.id then &var = 0;
retain &var;
if descr = %unquote(%str(%'&var%')) then &var = 1;
%MEND;
proc sort data=work.table5a;
by id;
run;
data temp;
set work.table5a;
by id;
%func(PLUTO)
%func(PAPERINO)
%func(BANDA BASSOTTI)
if last.id;
run;
ERROR is:
NOTE: Line generated by the macro variable "VAR".
37 BANDA BASSOTTI
_____
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
If i comment %prova(BANDA BASSOTTI) it works. Any suggestions ?
thanks
You're using &var to create a variable name, and if you want to have a variable name with spaces in it, you need to use the variable name literals, e.g. "BANDA BASSOTTI"n. I haven't done this myself, seems like it makes the code uglier and harder to write, but something like this seems to work:
options validvarname=any;
%MACRO func(var);
retain "&var"n;
if first.id then "&var"n = 0;
if descr = "&var" then "&var"n = 1;
%MEND;
How do I print out the data type of a macro variable in the log
%macro mymacro(dt2);
%LET c_mth = %SYSFUNC(intnx(month,&dt2.d,-1,e),date9.) ;
%put &c_mth;
%mend;
mymacro('01sep2014')
I have a bunch of macro variables assigned using a %let or into:
my problem is I'm trying to do a bunch of boolean condition on dates but I suspect that some of my variables are strings and some are dates
I have casted them in my code but to triple check there is surely a way to return something to the log
I want something similar to using str() or mode() or is.numeric() in R
H,
The SAS macro language is weird. : )
As Reeza said, macro variables do not have a type, they are all text.
But, if you use Boolean logic (%IF statement), and both operands are integers, the macro language will do a numeric comparison rather than a character comparison.
So you can use the INPUTN() function to convert the date strings to SAS dates (number of days since 01Jan1960), and then compare those. Here's an example, jumping off from your code:
%macro mymacro(dt1,dt2);
%local c_mth1 c_mth2 n_mth1 n_mth2;
%let c_mth1 = %sysfunc(intnx(month,&dt1.d,-1,e),date9.) ;
%let c_mth2 = %sysfunc(intnx(month,&dt2.d,-1,e),date9.) ;
%let n_mth1 = %sysfunc(inputn(&c_mth1,date9.)) ;
%let n_mth2 = %sysfunc(inputn(&c_mth2,date9.)) ;
%put &c_mth1 -- &n_mth1;
%put &c_mth2 -- &n_mth2;
%if &n_mth1<&n_mth2 %then %put &c_mth1 is before &c_mth2;
%else %put &c_mth1 is NOT before &c_mth2;
%mend;
Log from a sample call:
236 %mymacro('01feb1960','01mar1960')
31JAN1960 -- 30
29FEB1960 -- 59
31JAN1960 is before 29FEB1960
--Q.
Macro variables do not have a type, they are all text.
You have to make sure the variable is passed in a way that makes sense to the program and generates valid SAS code.
%let date1=01Jan2014;
%let date2=31Jan2014;
data _null_;
x = "&date1"d > "&date2"d;
y = "&date2"d > "&date1"d;
z = "&date2"d-"&date1"d;
put 'x=' x;
put 'y=' y;
put 'z=' z;
run;
Log should show:
x=0
y=1
z=30
If your macro variables resolve to date literals, you can use intck combined with %eval to compare them, e.g.
%let mvar1 = '01jan2015'd;
%let mvar2 = '01feb2015'd;
/*Prints 1 if mvar2 > mvar1*/
%put %eval(%sysfunc(intck(day,&mvar1,&mvar2)) > 0);
I would like to create variables containing lagged values of a given variable for a large number of lags. How could I do this? I try the following:
data out;
set in;
do i = 1 to 50;
%let j = i;
lag_&j = Lag&j.(x);
end;
run;
How can I get the loop variable i into the macro variable j or how to use it directly to create the appropriately named variable and for the Lag function?
Chris J answers the question, but here i'll provide my preferred way of doing this.
%macro lagvar(var=,num=);
%do _iter = 1 %to &num.;
lag_&_iter. = lag&_iter.(&var.);
%end;
%mend lagvar;
data out;
set in;
%lagvar(var=x,num=50); *semicolon optional here;
run;
This is a more modular usage of the macro loop (and more readable, assuming you use intelligent names - the above is okay, you could do even more with the name if you wanted to be very clear, and of course add comments).
You're mixing macro & datastep syntax incorrectly...
You need a macro-loop (%DO instead of do) to generate the datastep code (i.e. lag1-lag50), and macro-loops need to be within a macro.
%MACRO LAGLOOP ;
data out ;
set in ;
%DO J = 1 %TO 50 ;
lag_&J = lag&J(x) ;
%END ;
run ;
%MEND ;
%LAGLOOP ;