SAS 9.4 Macro Eval Function issues - sas

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;

Related

SAS macro if exists

If the dataset dataset_1&x._&y. exists, I want to get data from it, but if it doesn't exist i want to get data from dataset_2.
I have tried the following macro but it doesn't work:
%macro test(x,y);
%if %sysfunc(exist(dataset_1_&x._&y.)) %then %do;
data final_data;
set dataset_1_&x,_&y.;
run;
%end;
%else %do;
data final_data;
set dataset_2;
run;
%end;
%mend;
Try this - no need to define a macro:
%let exist=%sysfunc(exist(work.dataset_1&x._&y.));
%let inds=%sysfunc(ifc(&exist=1,work.dataset_1&x._&y.,work.dataset_2));
data work.final_data;
set &inds;
run;
If you did want a macro to see if a dataset (or view) exists, you could use this one: https://core.sasjs.io/mf__existds_8sas.html

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"

Two statements in one code (choosing by PROMT variable

I have to different statements in one code. For example:
First:
data step2;
set ste1;run;
proc print data=step2;quit;
Second:
data step6;
set ste5;run;
proc print data=step5;quit;
Is it possible to choose by PROMPT variable which one of them I need to Execute?
Assuming you mean a stored process prompt variable, you can simply build a macro:
%macro choose;
%if &mypromptvar=1 %then %do;
data step2;
set ste1;run;
proc print data=step2;quit;
%end;
%if &mypromptvar=2 %then %do;
data step6;
set ste5;run;
proc print data=step5;quit;
%end;
%mend;
%choose;
where mypromptvar is the name of your promptvariable...

SAS conditional logic to execute another sas program based on condition

I have a dataset naming error_table as follows. All the variables are character
Errorno Error Resolution
001 login check
002 datacheck check
I wanted a logic that executes a sas program If the Errorno is not in 001 and 002. Else stop execution and display the error_table.
I tried the following
%macro test();
proc sql;
select trim(Error_No) into: num from error_table;
quit;
%if &num. not in ("001","002") %then %do;
%include "/path/dev/program.sas";
%end;
%else %do;
proc print data = error_table;
run;
%end;
%mend;
%test;
But, it is throwing an error.
Can anyone please correct the logic.
You need to watch out for the case when the SELECT returns zero rows. You should set a default value to the macro variable NUM.
Is your dataset variable numeric or character? Use the TRIMMED or SEPARATED BY clause instead of the TRIM() function to prevent spaces in the macro variable that is generated by the INTO clause.
%let num=NONE;
select Error_No into: num trimmed from error_table;
Remember that to the macro processor everything is a string so don't but quotes around the values you are trying to match unless they are actually part of the value.
%if NOT (&num. in (001,002)) %then %do;
Also to use the IN operator in macro code you need to make sure you have set the MINDELIMITER option.
I would sugest moving condition with error codes to proc sql.
proc sql;
select count(*) into :num_errors
from error_table
where Errorno in ("001", "002");
quit;
Then in macrovariable you have number of errors that are 001 or 002.
Next step is to check macro-condition:
%if &num_errors. > 0 %then %do;
%include "/path/dev/program.sas";
%end;
%else %do;
proc print data = error_table;
run;
%end;
%mend;

How to detect how many observations in a dataset (or if it is empty), in SAS?

I wonder if there is a way of detecting whether a data set is empty, i.e. it has no observations.
Or in another saying, how to get the number of observations in a specific data set.
So that I can write an If statement to set some conditions.
Thanks.
It's easy with PROC SQL. Do a count and put the results in a macro variable.
proc sql noprint;
select count(*) into :observations from library.dataset;
quit;
There are lots of different ways, I tend to use a macro function with open() and attrn(). Below is a simple example that works great most of the time. If you are going to be dealing with data views or more complex situations like having a data set with records marked for deletion or active where clauses, then you might need more robust logic.
%macro nobs(ds);
%let DSID=%sysfunc(OPEN(&ds.,IN));
%let NOBS=%sysfunc(ATTRN(&DSID,NOBS));
%let RC=%sysfunc(CLOSE(&DSID));
&NOBS
%mend;
/* Here is an example */
%put %nobs(sashelp.class);
Here's the more complete example that #cmjohns was talking about. It will return 0 if it is empty, -1 if it is missing, and has options to handle deleted observations and where clauses (note that using a where clause can make the macro take a long time on very large datasets).
Usage Notes:
This macro will return the number of observations in a dataset. If the dataset does not exist then -1 will be returned. I would not recommend this for use with ODBC libnames, use it only against SAS tables.
Parameters:
iDs - The libname.dataset that you want to check.
iWhereClause (Optional) - A where clause to apply
iNobsType (Optional) - Either NOBS OR NLOBSF. See SASV9 documentation for descriptions.
Macro definition:
%macro nobs(iDs=, iWhereClause=1, iNobsType=nlobsf, iVerbose=1);
%local dsid nObs rc;
%if "&iWhereClause" eq "1" %then %do;
%let dsID = %sysfunc(open(&iDs));
%end;
%else %do;
%let dsID = %sysfunc(open(&iDs(where=(&iWhereClause))));
%end;
%if &dsID %then %do;
%let nObs = %sysfunc(attrn(&dsID,nlobsf));
%let rc = %sysfunc(close(&dsID));
%end;
%else %do;
%if &iVerbose %then %do;
%put WARNING: MACRO.NOBS.SAS: %sysfunc(sysmsg());
%end;
%let nObs = -1;
%end;
&nObs
%mend;
Example Usage:
%put %nobs(iDs=sashelp.class);
%put %nobs(iDs=sashelp.class, iWhereClause=height gt 60);
%put %nobs(iDs=this_dataset_doesnt_exist);
Results
19
12
-1
Installation
I recommend setting up a SAS autocall library and placing this macro in your autocall location.
Proc sql is not efficient when we have large dataset. Though using ATTRN is good method but this can accomplish within base sas, here is the efficient solution that can give number of obs of even billions of rows just by reading one row:
data DS1;
set DS nobs=i;
if _N_ =2 then stop;
No_of_obs=i;
run;
The trick is producing an output even when the dataset is empty.
data CountObs;
i=1;
set Dataset_to_Evaluate point=i nobs=j; * 'point' avoids review of full dataset*;
No_of_obs=j;
output; * Produces a value before "stop" interrupts processing *;
stop; * Needed whenever 'point' is used *;
keep No_of_obs;
run;
proc print data=CountObs;
run;
The above code is the simplest way I've found to produce the number of observations even when the dataset is empty. I've heard NOBS can be tricky, but the above can work for simple applications.
A slightly different approach:
proc contents data=library.dataset out=nobs;
run;
proc summary data=nobs nway;
class nobs;
var delobs;
output out=nobs_summ sum=;
run;
This will give you a dataset with one observation; the variable nobs has the value of number of observations in the dataset, even if it is 0.
I guess I am trying to reinvent the wheel here with so many answers already. But I do see some other methods trying to count from the actual dataset - this might take a long time for huge datasets. Here is a more efficient method:
proc sql;
select nlobs from sashelp.vtable where libname = "library" and memname="dataset";
quit;