Dynamically selecting an autoexec file for batch processing in SAS - sas

Can anyone please provide advice or a step by step solution to the below problem.
The issue I am faced with is one where I have 2 sets of raw data in one study, one set for MRD and one for Neuro. I have created 2 separate autoexec files to read in the correct data into the interactive SAS session. But when it comes to batching the programs SAS does not assign the correct library locations for the data.
What I need to do is scan the directory path of the file that is being batched for MRD or Neuro and then direct the batching process to use the associated autoexec.sas file for either MRD or Neuro. All of this needs to happen before the batching process starts so that the correct libraries are assigned.
The process that my company follows to load the autoexec.sas files is as follow;
1. The SAS shortcut includes a custom call to a file called biosetup.inc.
2. Biosetup.inc includes the study path directory of the autoexec.sas file.
3. Autoexec.sas assigns the library names and paths to be used in the interactive SAS session as well as setting up the paths for macros etc.
The way I think I need to approach this is to create a macro inside of the biosetup.inc that will assign the correct autoexec file.
Is this at all possible?
So far I have been able to determine the directory path etc, and now what I still need to do is make sure that the correct autoexec file is being used.
%macro MAIN;
%let proctype = %scan(&SYSPROCESSNAME,1,%str( ));
/*Define macro variable for the path and filename*/
%if &proctype = DMS
%then %let pathpgmref=%sysget(SAS_EXECFILEPATH);
%else %let pathpgmref=%sysfunc(GetOption(SYSIN));
/*Define separate macro variables for the filename.extension, filename, and extension*/
/*Reverse path and filename to scan from the front (back)*/
%let pathpgmref = %sysfunc(reverse(&pathpgmref));
%let pgmextref = %scan(&pathpgmref,1,\);
%let extref = %scan(&pgmextref,1,.);
/*Reverse back to proper order*/
%let pathpgmref = %sysfunc(reverse(&pathpgmref));
%let pgmextref = %sysfunc(reverse(&pgmextref));
%let extref = %sysfunc(reverse(&extref));
%let pgmref = %scan(&pgmextref,1,.);
/*Remove filename to create program directory macro variable*/
%let temp = %eval(%index(&pathpgmref,&pgmref) - 1);
%let pathref = %substr(&pathpgmref,1,&temp);
%let PATH = %sysfunc(quote(&pathref));
%let EXTN = %sysfunc(quote(&extref));
%let PGMN = %sysfunc(quote(&pgmref));
Data _Null_;
if index(&PATH,"MRD") and lowcase(&EXTN) = "sas" then call symput("DATATYPE","MRD");
else if index(&PATH,"NEURO") and lowcase(&EXTN) = "sas" then call symput("DATATYPE","NEURO");
else if index(&PATH,"Listings") or index(&PATH,"Tables") and lowcase(&EXTN) = "sas" then do;
if lowcase(substr(reverse(&PGMN),1,1)) = "a" then call symput("DATATYPE","MRD");
if lowcase(substr(reverse(&PGMN),1,1)) = "b" then call symput("DATATYPE","NEURO");
end;
Run;
%if &DATATYPE = "MRD" %then %do;
%include "MRD_Setup.sas";
%end;
%if &DATATYPE = "NEURO" %then %do;
%include "NEU_Setup.sas";
%end;
%put ProcType: &proctype;
%put FullPath: &pathpgmref;
%put DirePath: &pathref;
%put FullFile: &pgmextref;
%put FileName: &pgmref;
%put ExtnType: &extref;
%put DataType: &DATATYPE;
%mend;
%MAIN;

I'd recommend simply passing in a parameter via the -SYSPARM command line option.
sas mysasfile.sas -AUTOEXEC biosetup.inc -SYSPARM "path_of_autoexec_to_run"
And then in your sas program I think you should be able to do this:
%include "&sysparm";
This assumes that you're not currently using -SYSPARM for anything else. It also assumes you have 2 shortcuts, 1 for each study. From your question it sounds like that's how it is currently setup.
I haven't tested this but in theory this should work.
Here's a working example (from the documentation).

Related

Run Macros based on conditions in SAS

I have tried a few ways to set up these macros. Bascially for each year there are 10 different versions of the macro, and I need to create a way to use the let statement at top to run only a single macro based on those conditions.
*What I have now:
%Let year=2001;
%Let Version=9;
%IF &YEAR=2000 and &Version=1 %THEN %DO;
%INCLUDE "macro location";
%ELSE %IF &YEAR=2000 and &Version=2 %THEN %DO;
%INCLUDE "macro location";
.....goes on until I get all of the macros included.
When I run this code it is running all of the Macros and I need it to choose the correct macro to run based on the conditions.
When used within the macro facility, your logic should work as expected.
When used in the DATA step, the %INCLUDE statement cannot be used in
conditional logic. However, you can use the %INCLUDE statement with
conditional logic when used with the macro facility. For example, you
can specify the following %IF-%THEN macro statement:
Source
However, there are alternative ways you can achieve the same goal.
If these macros are all compiled or stored in a single sasautos location, then you can call them directly and dynamically compile based off of the values of year and version:
options sasautos=(sasautos '/mylocation1' '/mylocation2' ...);
%if(&year = 2000 AND &version = 1) %then %do;
%macro1;
%end;
%else %if ...
Another alternative method would be to use call execute to conditionally execute with a data _null_ step:
data _null_;
select;
when(&year. = 2000 AND &version. = 1) call execute('%include /mylocation/macro1.sas');
when(&year. = 2000 AND &version. = 2) call execute('%include /mylocation2/macro2.sas');
....
otherwise;
end;
run;
Instead placing the %include within a conditional block, consider naming all the locations at the top and include the one that matches the parameters.
Example:
This makes the versions and locations readily available for knowing without scanning lots of code. Requires a consistent macro variable naming scheme.
%let macro_2000_v1 = /mylocation/macro1.sas ;
%let macro_2000_v2 = /mylocation/macro2.sas ;
%let macro_2000_v3 = /mylocation/macro3.sas ;
%let macro_2000_v4 = /mylocation/macro4.sas ;
%let macro_2000_v5 = /mylocation/macro5.sas ;
%let macro_2001_v6 = /mylocation/macro6_ext.sas ;
%let macro_2001_v7 = /mylocation/macro7.sas ;
%let macro_2001_v8 = /mylocation/macro8.sas ;
%let macro_2001_v9 = /mylocation/macro9.sas ;
%let macro_2001_v10 = /mylocation/macro_10_validated.sas ;
%Let year=2001;
%let version=9;
%include "&&macro_&year._v&version";

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"

Running SAS code on multiple files with DO LOOP and a MACRO

I am very new to SAS and have received my first work assignment. Basically, I need to pull all of the patient ID's (patid) and procedure code (proc_cd) from multiple SAS files and put it into an excel file.
From my research, I believe I need a MACRO with a do loop that will run this search for all of the files
Below is the code I've put together. Again I'm very new to SAS so any help will be appreciated!
libname sas 'P:\H3.2018.DH_StressQuery\dat';
libname optum 'C:\OPTUM Data\Zip5';
data libname.filename;
set libname.filename;
%MACRO LOOP * I don't know what to put here.
%DO i = 1 %TO
("zip5_r2018q1.sas7bdat","16.2GB","Sas7bdat","C:\OPTUM
Data\Zip5\zip5_r2018q1.sas7bdat","11Jul2018:20:07:01"
)
(data sas.query file;
set optum.zip5_m2007q1
(keep = patid, Proc_Cd);
if Proc_Cd = '94621');
proc print data= data.query file
%END;
%MEND LOOP;
%LOOP;

sas macro loop to rename variable

Hi I am trying to rename variables using SAS Macro loop.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data MilkNew;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
%Re(6)
This loop is mean to rename MT...Sp to MTSp.... Eg:MT20SP to MTSp20.
When I run my loop, there was no error but the variable names were not changed in MilkNew at all.
Where does the problem come? Thanks!
If the only purpose of the macro is to rename the variables in the data set, then why read the data with a set statement. Your data set is probably really small so you don't even realize the inefficiency of doing that. Instead use the modify statement in proc datasets to accomplish the same thing, but more efficiently. Here's an alternative macro for you.
%macro renamevar(dsname, time);
%local lib ds i;
%let lib = %sysfunc(coalescec(%scan(&dsname, -2, %str(.)), work));
%let ds = %scan(&dsname, -1, %str(.));
proc datasets lib=&lib nolist;
modify &ds;
rename
%do i = 1 %to &time;
mt&&t&i..Sp=MTSp&&t&i.
%end;
;
quit;
%mend;
%renamevar(milk, 6);
Here's the log after the macro call:
NOTE: Renaming variable mt12MthSp to MTSp12Mth.
NOTE: Renaming variable mt20Sp to MTSp20.
NOTE: Renaming variable mt30Sp to MTSp30.
NOTE: Renaming variable mt40Sp to MTSp40.
NOTE: Renaming variable mt50Sp to MTSp50.
NOTE: Renaming variable mt60Sp to MTSp60.
NOTE: MODIFY was successful for WORK.MILK.DATA.
NOTE: PROCEDURE DATASETS used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds
You should move the loop so that it only generates just the RENAME statement (or even just the old=new name pairs). What is happening now is that you keep overwriting MilkNew so only the last RENAME has any effect.
%macro Re(time);
data MilkNew;
set Milk;
%do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
run;
%mend Re;
%Re(6)
You should have seen the last variable name in the loop (so the 6th) changed. That's because you repeated the same data step with the same source dataset but a different destination - so each time you 'forgot' the changes made in the earlier step.
So, this would've worked, though I'll get in a minute to why this isn't a good way to do this.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data Milk;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
data milk;
input
MT12mthSP
MT20SP
MT30SP
MT40SP
MT50SP
MT60SP
;
datalines;
12 20 30 40 50 60
;;;;
run;
%Re(6)
Here I had it make all changes to Milk and save them back in that dataset. If you want to preserve Milk then first make Milk_New then have that in both set and data statements.
Second, you should not do a new data step for each change. Macros don't have to have a data step in them; they can be run inside the datastep.
So for example:
%macro Re(time);
%Do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
%mend Re;
data milk_new;
set milk;
%Re(6);
run;
Even better would be generating this list outside of a macro entirely - look up "generating code SAS" for suggestions on that.
If you didn't see any renames at all, you also may have an issue where a label is present on the column(s). That won't affect your usage of the variable name, but it will make it confusing. Use
label _all_;
Or include a label-clearing statement (label <varname>; where you pop in the same variable name as the original variable name before rename) inside your macro loop to fix that.

Listing all files in directory: How to detect if a result is a file or a subdirectory?

I have a small STP which list all files/subdirectories in a given directory on our Unix-Server.
Problem is, my datastep only returns the name of the object, but not an information if this is a file or directory.
I tried some things and had some Ideas, but non is really foolproof, maybe someone can give me a hint how i can get this.
What i have tried so far:
using fileexists: This returns true for files and directories, so this does not work here
Checking for a dot. There could be files without extensions (and so dont have a dot) and also directories which have a dot in name, so this is not foolproof
using unixfunctionalities in sas: This is forbidden on our server, so no option for me
open an filehandle and do a finfo, this returns a warning if it is not a file, but
a)maybe it could be something else then a directory or file, or a warning because of other reasons
b)checking for a warning to detect type is not a good solution imo
Here a part of my code which i am using:
filename _folder_ "%bquote(&mydirectory/)";
data x (keep=filepath);
handle=dopen( '_folder_' );
if handle > 0 then do;
count=dnum(handle);
do i=1 to count;
filepath="&mydirectory"||dread(handle,i);
output;
end;
end;
rc=dclose(handle);
run;
filename _folder_ clear;
/*this part just makes a macrovariable with all results*/
proc sql noprint;
select filepath into: fpath separated by '#' from x;
quit;
/* this macro collects some additional fileinformation from all file in macrovariable fpath and creates a HTML-Ouput*/
%FileAttribs;
Use FILENAME() function to define a fileref to the file. This will return a code to indicate if the file exists or not. Then use the DOPEN() function to attempt to open the fileref as a directory. This will tell you if the file is a directory.
For example here is the function style macro that I use.
%macro direxist
/*----------------------------------------------------------------------
Test if directory exists
----------------------------------------------------------------------*/
(path /* Name of directory to test for existance */
);
/*----------------------------------------------------------------------
Test if directory exists. Returns value 1 or 0.
1 - Directory exists
0 - Directory does not exist
Global macro variable SYSRC will be set to 1 when a file is found
instead of a directory.
----------------------------------------------------------------------*/
%local return rc did fileref;
%*----------------------------------------------------------------------
Set up return values as normal failure to find path.
-----------------------------------------------------------------------;
%let return=0;
%let sysrc=0;
%*----------------------------------------------------------------------
If path is not specified or does not exist then return normal failure.
-----------------------------------------------------------------------;
%if (%bquote(&path) = ) %then %goto quit;
%if ^%sysfunc(fileexist(&path)) %then %goto quit;
%*----------------------------------------------------------------------
Try to open it using DOPEN function.
Return 1 if it can be opened.
Otherwise set SYSRC=1 to mean that PATH exists but is not a directory.
-----------------------------------------------------------------------;
%if (0=%sysfunc(filename(fileref,&path))) %then %do;
%let did=%sysfunc(dopen(&fileref));
%if (&did) %then %do;
%let return=1;
%let rc=%sysfunc(dclose(&did));
%end;
%else %let sysrc=1;
%let rc=%sysfunc(filename(fileref));
%end;
%quit:
%*----------------------------------------------------------------------
Return the value as the result of the macro.
-----------------------------------------------------------------------;
&return
%mend;