IFC function throwing error in IN operator - sas

Has anyone encountered the below error:
options symbolgen mprint mlogic minoperator;
%macro test /minoperator mindelimiter=',';
%let var1 = EXPORT;
%if &var1 in (EXPORT, TEST,TEST1) %then %let var_final1=1;
%put &=var_final1.;
%let var_final2 = %sysfunc(ifc(&var1= EXPORT,1,2));
%put &=var_final2;
%let var_final3 = %sysfunc(ifc(&var1 in (EXPORT, TEST,TEST1),1,2));
%put &var_final3;
%mend;
%test;
var_final1 and var_final2 are working okay, whereas, for var_final3 where we have ifc function, i get the below error:
ERROR: Argument 1 to function IFC referenced by the %SYSFUNC or %QSYSFUNC macro function is not a number.
ERROR: Invalid arguments detected in %SYSCALL, %SYSFUNC, or %QSYSFUNC argument list. Execution of %SYSCALL statement or %SYSFUNC
or %QSYSFUNC function reference is terminated.
Not sure why the IN operator does not work with IFC or IFN functions?
Can anyone please suggest.
Thanks

It is a very tricky job %SYSFUNC() has been given. It has to figure out how to convert your macro text into variables/expressions it can pass to the function it is trying to call. In this case it cannot figure out the IN operator. Perhaps it doesn't know about the MINOPERATOR option.
You can fix it by making its problem easier by adding an explicit call to %EVAL() so that all that it needs to pass to the IFN() function is the resulting zero or one.
%let var_final3 = %sysfunc(ifc(%eval(&var1 in (EXPORT, TEST,TEST1)),1,2));

I think you just need to evaluate your 'in' expression so that the ifn or ifc function sees it as a boolean value:
options symbolgen mprint mlogic minoperator;
%macro test /minoperator mindelimiter=',';
%let var1 = EXPORT;
%if &var1 in (EXPORT, TEST,TEST1) %then %let var_final1=1;
%put &=var_final1.;
%let var_final2 = %sysfunc(ifc(&var1= EXPORT,1,2));
%put &=var_final2;
%let var_final3 = %sysfunc(ifc(%eval(&var1 in (EXPORT, TEST,TEST1)),1,2));
%put &var_final3;
%mend;
%test;

Related

SAS Macro Statement %If and %Let

I am trying to solve a problem in which, based on certain conditions, it assigns you a parameter with the let function. For this exercise I am using %if with %let conditions on the code. The code I have written so far in simplified way is the following:
%let anio = 2022;
%let base = 2;
%Macro Data;
%if &anio = 2022 %then %do;
%Let year_add = %Str(&Base.C);
%Let year_add1 = %Str(&Base.B);
%mend;
%Data;
%put &=year_add;
%put &=year_add1;
The problem is that apparently the macro is not assigning any value to me in the second let statement
The first %put = &year_add gives me the correct result 2C.
Unfortunately with the second %put = &year_add1 it appears the following message: apparent symbolic referenc yeard_add1 not resolved
Can anyone can give me a hand or advise on how I can assign different let statements based on a condition?
Thanks in advance.
Your macro definition is missing an %END for the %DO.
%macro data;
%if &anio = 2022 %then %do;
%let year_add = &Base.C;
%let year_add1 = &Base.B;
%end;
%mend;
If the target macro variables, YEAR_ADD and YEAR_ADD1 do not already exist then your macro will create them as LOCAL to the DATA macro. So once the macro finishes they will be removed.
The easiest solution is just to make sure the macro variables exist before you call the macro.
%let anio = 2022;
%let base = 2;
%let year_add=;
%let year_add1=;
%data;
%put &=year_add;
%put &=year_add1;
If you are certain the macro variables do not already exist in some other macro that is calling %DATA() then you could add a %GLOBAL statement to define them in the GLOBAL macro scope so they will not be removed when the macro finishes by adding this to the macro definition:
%global year_add year_add1 ;
But that will generate an error if they have been defined as LOCAL to some other macro that called %DATA. So to be safe only force them into the GLOBAL scope if they do not already exist.
%if not %symexist(year_add) %then %global year_add;
%if not %symexist(year_add1) %then %global year_add1;
But the logic does not require you to define a macro. Just use the %IF/%THEN/%DO/%END block in open code. Then you won't have any macro variable scoping issues.
%if &anio = 2022 %then %do;
%let year_add = &Base.C;
%let year_add1 = &Base.B;
%end;
That works fine unless you are running on some really old version of SAS.

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 : definition of variables in if loop

I am new to SAS macro writing and I have been struggling with writing a code for the following instance.
%let DateOfInterest= "15jul2016"d;
%let yearyyyy=%sysfunc(putn(&DateOfInterest,year4.));
%let yearyyyy2=eval(yearyyyy+1);
data _null_;
if "01JAN2016"d<=&DateOfInterest<="31MAR2016"d then do;
%let reportdate="31MAR2016"d;
%let reportdate2="01APR2016"d;
%let reportdate3="01JAN2016"d;
%let QuarterOfInterest=Q1;
if "31MAR2016"d<&DateOfInterest<="30JUN2016"d then do;
%let reportdate="30JUN2016"d;
%let reportdate2="01JUL2016"d;
%let reportdate3="01APR2016"d;
%let QuarterOfInterest=Q2;
if "30JUN2016"d<&DateOfInterest<="30SEP2016"d then do;
%let reportdate="30SEP2016"d;
%let reportdate2="01OCT2016"d;
%let reportdate3="01JUL2016"d;
%let QuarterOfInterest=Q3;
if "30SEP2016"d<&DateOfInterest<="31DEC2016"d then do;
%let reportdate="31DEC2016"d;
%let reportdate2="01JAN2017"d;
%let reportdate3="01OCT2016"d;
%let QuarterOfInterest=Q4;
end;
end;
end;
end;
run;
The code runs without any problem. However, whatever DateOfInterest I choose, the reportdate variables come out to be the ones specified in the last if loop. is there a way to change the code in order to have reportdates variable in line with the DateOfInterest?
Thanks.
You're combining macro with data step in a way that doesn't work. Macro language and data step language are basically unrelated: macro language can write data step code, and the other way around, but not affect each other generally.
In particular, macro code is compiled and executed first, before any dataset is opened or any data step code is compiled or executed. That's the point really - it lets you write datastep code pre-compilation.
So
if ... then do;
%let something
end;
That doesn't work, because the macro %let happens first, then later the data step happens.
%if ... %then %do;
%let something
%end;
That works, because it's all in the macro language. Generally speaking, if it doesn't have a % at the start, it's not a macro statement/function, and won't work on the macro language.
What you're doing is going to have some more complications, though. You have to be in a macro to use %if, but you also have scoping issues.
So a general small macro like this would be:
%let mval=1;
%macro set_things;
%if &mval=1 %then %do;
%let mval1=1;
%end;
%else %if &mval=2 %then %do;
%let mval2=1;
%end;
%else %do;
%let mval0=1;
%end;
%mend;
%set_things();
%put &=mval &=mval0 &=mval1 &=mval2;
Notice that doesn't work: because it's not global, so you need one more line inside the macro:
%global mval0 mval1 mval2;
That tells SAS to make them available in the global area.

SAS 9.4 Macro Eval Function issues

I have a data set called monthlypayments, which is located in a folder I have assigned ‘training’ and it has a variable payments.
I want to output ‘payment type’ which is “high payment” if the payment>400 and “low payment” otherwise.
I keep getting this error
ERROR: DS-00075 : Parsing error occurred while trying to %EVAL an
expression: Invalid syntax found in call to %EVAL**
Can someone tell me what I'm doing wrong?
%LET root=D:\Users\Data;
libname training "&root.";
%LET dataset=training.monthlypayments;
%LET outlib=out;
%LET outfile=monthlypaymentsclassified;
%LET variable=payment;
%IF %EVAL(&VARIABLE.>400) %THEN %DO;
data &outlib..&outfile.;
set &dataset.;
paymenttype="high payment";
run;
%ELSE %DO;
data &outlib..&outfile.;
set &dataset.;
paymenttype="low payment";
run;
%END;
If you want to make a subset of the data based on values of the variables in the data then you need to use normal SAS code and not macro logic statements. If looks like your macro variable just tells you which data step variable to use in your IF statement.
data &outlib..&outfile.;
set &dataset.;
if &variable > 400 then paymenttype="high payment";
else paymenttype="low payment";
run;

SAS macro for creating multiple proq sql tables. Error: numeric opperand required

I have a list of IDs that contain multiple "XO codes". I want to create a macro that will loop through these IDs and create a table for each using a where statement that corresponds to the appropriate XO code. EX:
%let ID_77= '35X02','35X04';
%let DnO_IDs= &ID_77; /intends to add more &ID_ numbers/
%macro loop;
proc sql;
%do k=1 %to %sysfunc(countw(&DnO_IDs_Ids.));
%let ID= %scan(&DnO_IDs.,&k.);
create table EP_&ID as
select * from table
where XO in ("&ID.") and AY>=(&CurrY-14);
%end;
quit;
%mend;
%loop;
I am receiving this error:
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was:
'35X04'
ERROR: Argument 2 to macro function %SCAN is not a number.
ERROR: The macro LOOP will stop executing.
The quotes are an issue and should not be needed. Using comma as your delimiter can also cause trouble. It would work better to use spaces.
%let ID_77= 35X02 35X04;
%let DnO_IDs= &ID_77; /intends to add more &ID_ numbers/
%let CurrY=2015;
%macro loop;
%local k id ;
proc sql;
%do k=1 %to %sysfunc(countw(&DnO_IDs));
%let ID= %scan(&DnO_IDs,&k);
create table EP_&ID as
select * from table
where XO in ("&ID") and AY>=(&CurrY-14)
;
%end;
quit;
%mend;
%loop;
If you do want to use the values as quoted comma separated lists then you need to modify your COUNTW() and %SCAN() function calls appropriately and add a call to DEQUOTE() to remove the quotes.
%let ID_77= '35X02','35X04';
... %sysfunc(countw(%superq(DnO_IDs),%str(,)));
%let ID= %sysfunc(dequote(%scan(%superq(DnO_IDs),&k,%str(,))));
I think this should resolve the issue:
%do k=1 %to %eval(%sysfunc(countw(&DnO_IDs_Ids.)));
Might be %sysfunc return value is considered non integer.