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

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;

Related

SAS libref not recognized in macro loop

I've run into an odd SAS quirk that I can't figure out - hopefully you can help.
I have a simple macro loop that imports CSV files and for some reason if I use a libref statement in the "out=" part of the import procedure, SAS doesn't recognize the libref as a valid name. But if I use the same libref in a data step, it works just fine.
The specific error it gives is: "ERROR: "TESTDB." is not a valid name."
I'd like to figure this out because I work with pretty big files and want to avoid reading through them more times than is necessary.
Here's the code that works, with some comments in it. I got around the issue by reading in the files, then writing them to permanent SAS datasets in a second step, but ideally I'd like to import the files directly into the "TESTDB" library. Any idea how to get SAS to recognize a libref in the "out=" statement of the import procedure?
libname testdb "C:\SAS test";
%let filepath = C:\SAS test\;
%macro loop(values);
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value = %qscan(&values,&i,%str(,));
proc import datafile = "&filepath.&value..csv"
out = &value dbms=csv replace; getnames=yes;
/*"out=testdb.&value" in the line above does not work*/
run;
data testdb.&value; set &value; run;
/*here the libref testdb works fine*/
%end;
%mend;
%loop(%str(test_a,test_b,test_c));
Thanks in advance for your help!
john
Perhaps try:
out=testdb.%unquote(&value)
Sometimes the macro language does not unquote values automatically. With result that the extra quoting characters introduced by a quoting function (%qscan %str %bquote %superq etc) cause problems.
Strange error. I am not able to pin it. My guess is that it has something to do with how the value macro variables are being created. When I moved the value variable creation to a data step and used Call Symputx, it works.
%macro loop(files);
/* Create macro variables for files.*/
data _null_;
count = countw("&files.",",");
call symputx("count",count,"L");
do i = 1 to count;
call symputx(cats("file",i),scan("&files.",i,","),"L");
end;
run;
/* Read and save each CSV as a sas table. */
%do i=1 %to &count.;
proc import datafile = "&filepath.&&file&i...csv"
out = testdb.&&file&i. dbms=csv replace; getnames=yes;
run;
%end;
%mend;
%loop(%str(test_a,test_b));

Dynamically selecting an autoexec file for batch processing in 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).

Get data filtered by dynamic column list in SAS stored process

My goal is to create a SAS stored process is to return data for a single dataset and to filter the columns in that dataset based on a multi-value input parameter passed into the stored process.
Is there a simple way to do this?
Is there a way to do this at all?
Here's what I have so far. I'm using a macro to dynamically generate the KEEP statement to define which columns to return. I've defined macro variables at the top to mimic what gets passed into the stored process when called through SAS BI Web Services, so unfortunately those have to remain as they are. That's why I've tried to use the VVALUEX method to turn the column name strings into variable names.
Note - I'm new to SAS
libname fetchlib meta library="lib01" metaserver="123.12.123.123"
password="password" port=1234
repname="myRepo" user="myUserName";
/* This data represents input parameters to stored process and
* is removed in the actual stored process*/
%let inccol0=3;
%let inccol='STREET';
%let inccol1='STREET';
%let inccol2='ADDRESS';
%let inccol3='POSTAL';
%let inccol_count=3;
%macro keepInputColumns;
%if &INCCOL_COUNT = 1 %then
&inccol;
%else
%do k=1 %to (&INCCOL_COUNT);
var&k = VVALUEX(&&inccol&k);
%end;
KEEP
%do k=1 %to (&INCCOL_COUNT);
var&k
%end;
;
%mend;
data test1;
SET fetchlib.Table1;
%keepInputColumns;
run;
/*I switch this output to _WEBOUT in the actual stored process*/
proc json out='C:\Logs\Log1.txt';
options firstobs=1 obs=10;
export test1 /nosastags;
run;
There are some problems with this. The ouput uses var1, var2 and var3 as the column names and not the actual column names. It also doesn't filter by any columns when I change the output to _webout and run it using BI Web Services.
OK, I think I have some understanding of what you're doing here.
You can use KEEP and RENAME in conjunction to get your variable names back.
KEEP
%do k=1 %to (&INCCOL_COUNT);
var&k
%end;
;
This has an equivalent
RENAME
%do k=1 %to (&INCCOL_COUNT);
var&k = &&inccol&k.
%end;
;
and now, as long as the user doesn't separately keep the original variables, you're okay. (If they do, then you will get a conflict and an error).
If this way doesn't work for your needs, and I don't have a solution for the _webout as I don't have a server to play with, you might consider trying this in a slightly different way.
proc format;
value agef
11-13 = '11-13'
14-16 = '14-16';
quit;
ods output report=mydata(drop=_BREAK_);
proc report data=sashelp.class nowd;
format age agef.;
columns name age;
run;
ods output close;
The first part is just a proc format to show that this grabs the formatted value not the underlying value. (I assume that's desired, as if it's not this is a LOT easier.)
Now you have the data in a dataset a bit more conveniently, I think, and can put it out to JSON however you want. In your example you'd do something like
ods output report=work.mydata(drop=_BREAK_);
proc report data=fetchlib.Table1 nowd;
columns
%do k=1 %to (&INCCOL_COUNT);
&&inccol&k.;
%end;
;
run;
ods output close;
And then you can send that dataset to JSON or whatever. It's actually possible that you might be able to go more directly than that even, but I don't know almost anything about PROC JSON.
Reading more about JSON, you may actually have an easier way to do this.
On the export line, you have the various format options. So, assuming we have a dataset that is just a subset of the original:
proc json out='C:\Logs\Log1.txt';
options firstobs=1 obs=10;
export fetchlib.Table1
(
%do k=1 %to (&INCCOL_COUNT);
&&inccol&k.;
%end;
)
/ nosastags FMTCHARACTER FMTDATETIME FMTNUMERIC ;
run;
This method doesn't allow for the variable order to be changed; if you need that, you can use an intermediate dataset:
data intermediate/view=intermediate;
set fetchlib.Table1;
retain
%do k=1 %to (&INCCOL_COUNT);
&&inccol&k.;
%end;
;
keep
%do k=1 %to (&INCCOL_COUNT);
&&inccol&k.;
%end;
;
run;
and then write that out. I'm just guessing that you can use a view in this context.
It turns out that the simplest way to implement this was to change the way that the columns (aka SAS variables) were passed into the stored process. Although Joe's answer was helpful, I ended up solving the problem by passing in the columns to the keep statement as a space-separated column list, which greatly simplified the SAS code because I didn't have to deal with a dynamic list of columns.
libname fetchlib meta library="lib01" metaserver="123.12.123.123"
password="password" port=1234
repname="myRepo" user="myUserName";"&repository" user="&user";
proc json out=_webout;
export fetchlib.&tablename(keep=&columns) /nosastags;
run;
Where &columns gets set to something like this:
Column1 Column2 Column3

How to import multiple .xls files into SAS?

I have a folder that contains a number of .xls files. The names of the file could be random. The exact number is unknown. How can I import these datasets to SAS by knowing only the folder's directory? I would have to iterate ... i have done this using Java ... I am curious can SAS do this?
Once you get a list of excel files in the folder (using techniques suggested above), you can put it into a macrovariable and loop through them in a macro assigning them one-by-one to a library:
%DO i=1 %TO %SYSFUNC(COUNTW(&list_of_files));
LIBNAME xlibr EXCEL "&your_folder\%scan(&list_of_files,&i)";
DATA imported_file_&i;
SET xlibr.'Sheet1$'n;
RUN;
%END;
If name(s) and number of sheets in each file may vary, then you'll need to add one more, nested, loop to iterate through all sheets of each file. Something like this:
%DO i=1 %TO %SYSFUNC(COUNTW(&list_of_files));
LIBNAME xlibr EXCEL "&your_folder\%scan(&list_of_files,&i)";
PROC SQL noprint;
SELECT memname into :sheets separated by ' '
FROM sashelp.vtable
WHERE libname="XLIBR";
QUIT;
%DO j=1 %TO %SYSFUNC(COUNTW(&sheets));
DATA imported_file_&i&j;
SET xlibr."%scan(&sheets,&j)$"n;
RUN;
%END;
%END;

Opening SAS datasets for viewing from within a .sas program

Is there a way to open a SAS dataset for viewing (i.e., in the "ViewTable" window) from within a .sas file?
I think this will do what you want:
dm log "vt sashelp.air";
Just change the "sashelp.air" part to your lib.table combo.
dw.mackie's answer is right on the money. That works great when submitted from the SAS editor window.
But I just want to caution you to be careful if you attempt it in batch mode (that is, having SAS run a .sas program directly from command-line using the -sysin option). It will indeed attempt to pop open the interactive SAS window environment upon execution.
But, if your batch code also attempts to build some graphs/charts, you'll be required to use the -noterminal option. And the -noterminal option isn't compatible with the dm command. You'd spot it right away in the log, but I just wanted to give you a heads-up.
Because of the size of some of my datasets I just do a simple proc print and limit the output to only 50 observations. I do this so often that I created the following macro that dumps the output to a html file.
%Macro DPrt(Dset, obs=50, vars=, w=, Path="C:\output\");
%LET BKPATH = &Path;
%PUT BKPATH= &BKPATH;
options obs = &obs.;
title;
ods listing close;
ods html
path = &BKPATH.
body = "Debug-&Dset..htm"
style = THEME;
proc print data = &Dset n u split=' ';
%if &vars NE %THEN %DO;
var &vars.;
%END;
%if &w NE %THEN %DO;
&w;
%END;
Run;
ods html close;
ods listing;
options obs = MAX;
%Mend Dprt;
Sample call for dataset test looks like
%dprt(test)