Can anyone please suggest why do the below two codes have different outputs?
I have a macro var FILETYPE which resolves to ,File // missing first value.
%let b=%scan("&filetype",1,",",M);
%put &b;
%let c=%scan("&filetype",2,",",M);
%put &c;
%let d=%scan("&filetype",3,",",M);
%put &d;
%mend;
%tt;`
I get the following output:
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable B resolves to
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable C resolves to
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable D resolves to File
File
When macro var C should have resolved to File , but its still NULL and D gets the value File.
The same logic in a plain datastep works correctly.
data a;
b=scan("&filetype",1,',','M');
c=scan("&filetype",2,',','M');
run;
Output :
b=blank
c=File
Can someone please suggest how come the SCAN function with M modifier work okay in data step but not in macro?
It works exactly the same in macro code as in data step code. The problem is that you added double quotes to both the string to be scanned and to the list of delimiters to use. So the equivalent data step would be:
%let filetype=A,B;
data _null_;
length string word $100;
string=quote(symget('filetype'));
do i=1 to 4;
word=scan(string,i,'","','M');
put i= word=:$quote.;
end;
run;
Which produces this result:
i=1 word=""
i=2 word="A"
i=3 word="B"
i=4 word=""
What you probably meant to do in macro code was:
%scan(%superq(filetype),1,%str(,),M)
In normal SAS code you need to add quotes around string literals so that the parser knows that you didn't mean a variable name. In macro code that is not needed, everything is a string. You use macro triggers, & and %, to let the parser know when to treat it differently.
When generating a delimited list to be used in macro code then generate it with a different delimiter and avoid all of the macro quoting needed to deal with the commas.
... into :filetype separated by '|' ...
%scan(&filetype,1,|,m)
Don't double quote the arguments in macro invocations. You DO need to literalize the comma valued argument as %str(,) in order to have it NOT be considered an argument separator.
%scan ( &filetype , 1, %str(,), M )
If the filetype value contains macro important symbols, you may have to macro quote or superq the argument. If the return can contain macro important symbols that are not to be resolved you will want to use %QSCAN to mask them.
Related
When I run the following, it works fine:
%let mname = ABC<2>;
%put &mname;
ABC<2>
%let mname2 = %scan("&mname.", 2, '<>');
%put &mname2;
2
However, when I changed mname2 to the following, it gives the error:
%let mname2 = %scan("&mname.", 1, '<');
%put &mname2;
ERROR: Open code statement recursion detected.
Any idea what is causing it?
Since you added quotes around the value passed to the %SCAN() function and then only selected one of the quotes as the output you have generated unbalanced quotes.
In regular SAS code (and most programming languages) to allow the compiler to tell the difference between a variable name or a keyword and a string literal you add quotes around the string
But the macro processor language is different. To the macro processor everything is a string and it just looks for the & or % triggers to tell whether there is anything it needs to operate on.
49 %let mname = ABC<2>;
50 %put &=mname;
MNAME=ABC<2>
51 %put %scan(&mname,1,<>);
ABC
52 %put %scan(&mname,2,<>);
2
If you think it is possible the %SCAN() function will return unbalanced quotes or other things that might need macro quoting use the %QSCAN() function instead.
I am new in SAS and am currently working at a rather large data set. I'm getting a error of the type:
NOTE: Invalid numeric data, 'dev' , at line 1304 column 173.
I want to create an %if conditional statement of this type:
if input data is numerical then continue
else if format.20 .
How would I go about doing this, thank you very much in advance and please note this is my first 3 hours in using SAS. I have experience in python.
code currently looks like this:
dateVar=year(&dateVar.)*100+month(&dateVar.);
if &devSampleStart.<=dateVar<=&devSampleEnd. then
sample='dev';
else if &baseSampleStart.<=dateVar<=&baseSampleEnd. then
sample='base';
else if &recSample.=dateVar then
sample='rec ';
else sample=' ';
Copy of the log:
MPRINT(CALCMIGRATIONMATRIX): dateVar=year(snapshot_date1)*100+month(snapshot_date1);
SYMBOLGEN: Macro variable DEVSAMPLESTART resolves to 200709
SYMBOLGEN: Macro variable DEVSAMPLEEND resolves to 201809
MPRINT(CALCMIGRATIONMATRIX): if 200709<=dateVar<=201809 then sample='dev';
SYMBOLGEN: Macro variable BASESAMPLESTART resolves to 201909
SYMBOLGEN: Macro variable BASESAMPLEEND resolves to 201909
MPRINT(CALCMIGRATIONMATRIX): else if 201909<=dateVar<=201909 then sample='base';
SYMBOLGEN: Macro variable RECSAMPLE resolves to 202009
MPRINT(CALCMIGRATIONMATRIX): else if 202009=dateVar then sample='rec ';
MPRINT(CALCMIGRATIONMATRIX): else sample=' ';
SYMBOLGEN: Macro variable DATEVAR resolves to snapshot_date1
SYMBOLGEN: Macro variable BASESAMPLEEND resolves to 201909
MPRINT(CALCMIGRATIONMATRIX): if mod(month(snapshot_date1),100)=mod(201909,100) then yearlyTimeSlice=1;
MPRINT(CALCMIGRATIONMATRIX): else yearlyTimeSlice=0;
SYMBOLGEN: Macro variable ACCOUNTID resolves to account_id
SYMBOLGEN: Macro variable RATINGVAR resolves to rating
MPRINT(CALCMIGRATIONMATRIX): keep account_id rating dateVar sample yearlyTimeSlice;
MPRINT(CALCMIGRATIONMATRIX): run;
NOTE: Character values have been converted to numeric values at the places given by: (Line):(Column).
1304:173 1304:34 1304:120 1304:159
NOTE: Invalid numeric data, 'dev' , at line 1304 column 173.
If your date variables are really numeric with a date format attached (like DDMMYY10.) then the error is not coming from the testing of the date values.
So it sounds like SAMPLE is either an existing numeric variable. Or some earlier code in your data step is referring to SAMPLE and it is causing SAS to define the variable as numeric.
To change the code you generate based on the type of an existing variable you need to test the variable before starting to generate the code that uses it. For example you could set a macro variable that your macro could test and decide what code to generate.
If you want to create SAMPLE as a new variable then perhaps you just want to conditionally drop any existing variable with that name.
data want;
set have (
%if %varexist(have,sample) %then %do;
drop=sample
%end;
);
length sample $4 ;
if &devSampleStart.<=dateVar<=&devSampleEnd. then sample='dev';
else ...
I have defined macro variable
%let data_names = fuzzy_Data_segment EMWS2.Clus_TRAIN;
Then I have written a macro to extract and print the values from the above macro variable as:
%macro calling_data;
%do i = 1 %to 2;
%let data_name&i = %qscan(&data_names,&i);
%put &&data_name&i;
%end;
%mend;
%calling_data;
My macro code is able print the first name(fuzzy_Data_segment), but, it is only printing the part of the second name(EMWS2). what should I do to print the entire second name
Your issue is that SAS considers a period to be one of the default delimiters in macro variables. In this case, it looks like you want to be using a space to delimit items in data_names, so specify that:
%let data_name&i= %qscan(&data_names,&i., %str( ));
You're also missing semicolons in your %let statement and in your call to calling_data.
I want to create something in SAS that works like an Excel lookup function. Basically, I set the values for macro variables var1, var2, ... and I want to find their index number according to the ref table. But I get the following messages in the data step.
NOTE: Variable A is uninitialized.
NOTE: Variable B is uninitialized.
NOTE: Variable NULL is uninitialized.
When I print the variables &num1,&num2, I get nothing. Here is my code.
data ref;
input index varname $;
datalines;
0 NULL
1 A
2 B
3 C
;
run;
%let var1=A;
%let var2=B;
%let var3=NULL;
data temp;
set ref;
if varname=&var1 then call symput('num1',trim(left(index)));
if varname=&var2 then call symput('num2',trim(left(index)));
if varname=&var3 then call symput('num3',trim(left(index)));
run;
%put &num1;
%put &num2;
%put &num3;
I can get the correct values for &num1,&num2,.. if I type varname='A' in the if-then statement. And if I subsequently change the statement back to varname=&var1, I can still get the required output. But why is it so? I don't want to input the actual string value and then change it back to macro variable to get the result everytime.
Solution to immediate problem
You need to wrap your macro variables in double quotes if you want SAS to treat them as string constants. Otherwise, it will treat them the same way as any other random bits of text it finds in your data step.
Alternatively, you could re-define the macro vars to include the quotes.
As a further option, you could use the symget or resolve functions, but these are not usually needed unless you want to create a macro variable and use it again within the same data step. If you use them as a replacement for double quotes they tend to use a lot more CPU as they will evaluate the macro vars once per row by default - normally, macro vars are evaluated just once, at compile time, before your code executes.
A better approach?
For the sort of lookup you're doing, you actually don't need to use a dataset at all - you can instead define a custom format, which gives you much more flexibility in how you can use it. E.g. this creates a format called lookup:
proc format;
value lookup
1 = 'A'
2 = 'B'
3 = 'C'
other = '#N/A' /*Since this is what vlookup would do :) */
;
run;
Then you can use the format like so:
%let testvar = 1;
%let testvar_lookup = %sysfunc(putn(&testvar, lookup.));
Or in a data step:
data _null_;
var1 = 1;
format var1 lookup.;
put var1=;
run;
I have a macro variable, &myvar, but it won't resolve when I try to put it in a data step variable. Why won't it, and what can I do to fix this?
%let myvar=Hello, world;
data _null_;
x='&myvar.';
put x=;
run;
Macro variables in SAS won't resolve when they are in single quotes, '&myvar'. They need to be in double quotes, "&myvar", in order to resolve properly.
If you need to have single quotes and a resolved macro variable, you have a few options, but the simplest is:
%str(%'&myvar.%')
The %' inside of %str will place a single quote character (or apostrophe) in the text string by itself without causing it to be quoted.
data _null_;
x="%str(%'&myvar.%')";
put x=;
run;
or
%let myvar2 = %str(%'&myvar.%');
In SAS 9.4M6 or higher version, you can use %tslit() to achieve the same function.
%let myvar=Hello, world;
data _null_;
x=%tslit(%superq(myvar));
put x=;
run;
%put %tslit(%superq(myvar));
x=Hello, world
'Hello, world'
This is a macro pre-defined in SAS. Here is the documentation about it:
https://documentation.sas.com/?docsetId=lebaseutilref&docsetTarget=n1phgnraoodvpln1bm941n44yq7q.htm&docsetVersion=9.4&locale=en