SAS: How do I put entries into a catalog? - macros

I have a catalog into which I would like to place uncompiled macros stored as .sas files. For example, I would like a catalog called "myMacros" which contains "Macro1.sas", "Macro2.sas", etc.
I am using SAS 9.4 on Windows.
Everywhere I have looked only tells me how to access a catalog once it already exists. I cannot find how to assign objects to a catalog. I have spent hours scouring the documentation, having read most of the SAS 9.4 Macro Language Reference and trying to make sense of FILENAME Statement, CATALOG Access Method and CATALOG Procedure.

The gist of the idea is you create a FILE definition to the catalog and then PUT the code in the catalog.
FILENAME CODE CATALOG "WORK.TEST.SOURCE";
DATA _NULL_;
FILE CODE ;
PUT "PROC PRINT";
RUN;
or
1. Define where macros are stored via MSTORED SASMSTORE options.
2. Assign the library
3. Use the SOURCE option on the macro code to store the code.
Example from Michael Raithels Paper (link below):
OPTIONS MSTORED SASMSTORE=MYSTORE;
LIBNAME MYSTORE "S:\PROJECTS\SGF2015\COMPILED_MACROS";
2
%MACRO RUNCONTS (IN=, OUT=) / STORE SOURCE DES="Run Contents and Test Print";
TITLE1 "CONTENTS AND TEST PRINT OF DS &IN. - &SYSDATE - &SYSTIME";
PROC CONTENTS DATA = &IN. VARNUM;
RUN;
PROC PRINT DATA= &IN. (OBS=5) NOOBS;
RUN;
%MEND;
Useful references:
http://analytics.ncsu.edu/sesug/2005/AD07_05.PDF
http://support.sas.com/resources/papers/proceedings15/3459-2015.pdf

Related

SAS macros to import and apply macro from text file on csv dataset

I have a text file "Macro definition" which has two SAS macros definition. I would like to import them and apply on HTWT.csv data set. This dataset has 20 observations and 6 variables ID, Gender, Age,Height,Weight,Year. All are numeric except gender variable. I have the code below to import and apply the macros from the txt file to csv file. I am getting an error message on running this code as below.
outcsvv is the name of HTWT dataset imported in SAS.
%include "C:\Users\komal\Desktop\Advanced SAS\Macro definition.txt";
%contents_of(outcsvv)
%print_data(outcsvv)
Warning:Apparent Invocation of macro "contents_of" not resolved
Error: Unable to complete processing of INCLUDE. Expected a filename or fileref
Expected a statement keyword: found "("
The second error I am getting is probably due to the macro definition(s) from the text file which are as follows.
%macro contents_of(name);
proc contents data=&name;
run;
%mend;
%macro print_data(name);
proc print data=&name;
run;
%mend;
Please let me know your advice on how to solve it. Thank you for your time.
You can setup your own macro Autocall Library. Just split your file; save one macro per-file. more.
or you can add this code to the begining of your program:
options insert=(sasautos="/C:\Users\komal\Desktop\Advanced SAS") ;
this will search for the macros in this directory.

SAS macros to save and list all macros in a session

I would like to save all the macros permanently defined as part of an exercise and list all the macros. I have 6 macros which I want to save and retrieve in this session:
%macro one(a,b,c)
%macro gchart(dseti,Weight,Gender)
%macro plot(dsetin,height,weight)
%macro one(a,b,c,strtpt,endpt)
%macro test
%macro name(dsetin,year,revenue)
%macro import_myfile(i=)
I have the code below and the error message follows it.
options mstored sasmstore=macross;
libname mjstore "C:\Users\komal\Desktop\Advanced SAS";
proc catalog cat=mjstore.macross;
contents;
title "Default Storage of SAS Macros";
quit
Error: Catalog "MJSTORE.MACROSS" not found
Please let me know your advice on how to solve it. Thank you for your time.
The default catalog is WORK.SASMACR
%macro one(a,b,c); %mend;
%macro gchart(dseti,Weight,Gender); %mend;
%macro plot(dsetin,height,weight); %mend;
%macro one(a,b,c,strtpt,endpt); %mend;
%macro test; %mend;
%macro name(dsetin,year,revenue); %mend;
%macro import_myfile(i=); %mend;
proc catalog cat=work.SASMACR;
contents;
title "Default Storage of SAS Macros";
quit;
The best way to find out which macros were defined during your program is to not use stored macro catalog options. Instead just let SAS default to storing them a WORK catalog. You can then use PROC CATALOG to see what macros have been compiled. Normally that will be WORK.SASMACR catalog, but if you are using alternative ways of running SAS (Enterprise Guide, SAS/Studio, stored process servers etc) it might be WORK.SASMACR1 catalog instead.
proc print data= sashelp.vcatalg width=min;
where libname='WORK' and memname='SASMACR'
and memtype='CATALOG' and objtype='MACRO'
;
var objname modified objdesc ;
run;
If you are using autocall macros newer versions of SAS will store the filename in the OBJDESC field in the metadata. Otherwise here is a link to a macro that will generate a list of the compiled macros and try to match them to a corresponding source file from the autocall library or other directories you might provide it. https://github.com/sasutils/macros/blob/master/maclist.sas

SAS: Rename a Macro Catalog

I want to store macros in a catalog. Doing so allows many macros to be shared with just a single file, as well as introduces a degree of separation from the user.
To store my macro, I run a program such as
/* HelloWorld.sas */
libname pwd "."; /* assign current directory */
option mstored sasmstore=pwd; /* set pwd as storage directory */
%macro HelloWorld()
/ store source; /* store compiled macro along with its source */
data _null_;
put "Hello, World!";
run;
%mend;
This creates a sasmacr.sas7bcat file in the directory in which HelloWorld.saslives. I can then move that file to another directory, such as C:\myMacrosand run the following program:
/* CallHelloWorld.sas */
libname myMacros 'C:\myMacros';
option mstored sasmstore=myMacros;
%HelloWorld();
The macro HelloWorld() is called without error.
However, if I want to consider the HelloWorld() macro as part of a "HelloWorld" macro suite, I cannot simply change the catalog name in Windows Explorer from sasmacr.sas7bcat to HelloWorld.sas7bcat. When I do this and try running CallHelloWorld.sas again (after closing and reopening SAS), the macro is not resolved.
1 /* CallHelloWorld.sas */
2 libname myMacros 'C:\myMacros';
NOTE: Libref MYMACROS was successfully assigned as follows:
Engine: V9
Physical Name: C:\myMacros
3 option mstored sasmstore=myMacros;
4
5 %HelloWorld();
-
180
NOTE: The SAS System was unable to open the macro library referenced by the SASMSTORE = libref
MYMACROS.
WARNING: Apparent invocation of macro HELLOWORLD not resolved.
ERROR 180-322: Statement is not valid or it is used out of proper order.
ERROR: Catalog MYMACROS.SASMACR does not exist.
NOTE: The SAS System was unable to open the macro library referenced by the SASMSTORE = libref
MYMACROS.
ERROR: An error occurred during the execution of the %COPY statement.
How do I change the name of a catalog which contains macros so that those macros may be called in various programs? Is it possible to name the catalog something different than sasmacr up front?
At least on Unix with SAS 9.4 you can use the SASAUTOS option to point at a FILEREF that uses the ZIP engine so that all of the macro definitions are stored in a single ZIP file.
The one "trick" is that you need to change the names of the member files in the ZIP file. Normally on unix SASAUTOS requires that the source files are named using the macro name in lowercase with a .sas extension (helloworld.sas). But for SASAUTOS to work with a ZIP file the members should be named using the macro name in uppercase with NO extension (HELLOWORLD).
filename mymacros zip '~/mymacros.zip';
options insert=(sasautos=(mymacros)) ;
EDIT
Unfortunately this method causes SAS to generate ERROR: messages when the macro source file is not found in the ZIP file, even if it is ultimately found in another file in the SASAUTOS option search path.
I don't think you can rename the catalog away from SASMACR and still use it directly. However, you could use PROC CATALOG potentially to manage the SASMACR catalog.
What you'd want to do is, when you wanted to include a particular macro, copy it via proc catalog from its source location into your chosen SASMSTORE location.
Something like:
libname myMacros 'C:\temp';
libname pwd '.';
options mstored sasmstore=pwd;
proc catalog catalog=myMacros.HelloWorld;
copy out=pwd.sasmacr;
run;
quit;
%HelloWorld();
Now - I would suggest this is probably overkill; there's really no reason to have separate macro catalogs this way. If you like the idea of separately including files, you may want to consider Autocall macros (where you don't store them compiled, but store their source and compile-on-demand); really compiling a macro costs nearly nothing in SAS anyway. But if using stored-compiled macros is what you like, this method is probably the best way to do it.
Of course, I think an easier way would be to leave the catalog SASMACR.SAS7BCAT and use the directory name to determine what it is, and then append librefs to your sasmstore option value.
From DATA_NULL_ I have received the answer to how to use to existing catalogs with different names. Use the CATNAME statement. So if you have catalogs TEST.CAT1 and TEST.CAT2 you can use the CATNAME statement to make TEST.SASMACR be the concatenation of the two macros.
CATNAME test.sasmacr
(test.cat1 (ACCESS=READONLY)
test.cat2 (ACCESS=READONLY)
)
;
You can now point the SASMSTORE option the the TEST libref.
option mstored sasmstore=test;
Here is an example of how you might create such separate catalogs using PROC CATALOG to copy members that have been compiled into a SASMACR catalog into catalogs with different names.
libname templib '~/test/cat1';
libname permlib '~/test';
options mstored sasmstore=templib;
%macro HelloWorld1() / store source;
data _null_;
put "Hello, World! &sysmacroname";
run;
%mend;
%macro HelloWorld2() / store source;
data _null_;
put "Hello, World! &sysmacroname";
run;
%mend;
proc catalog cat=templib.sasmacr ;
copy out=permlib.cat1;
select helloworld1 /et=macro;
run;
copy out=permlib.cat2;
select helloworld2 /et=macro;
run;
quit;
Now use the CATNAME command to chain the catalogs together.
options mstored sasmstore=permlib;
CATNAME permlib.sasmacr
(permlib.cat1 (ACCESS=READONLY)
permlib.cat2 (ACCESS=READONLY)
);
%helloworld1;
%helloworld2;

SAS Macro not detecting a file

CMS released a SAS macro that checks for the existence of a file:
** check existance of dataset**;
%macro CHECKDS(FILE,LONGFILE);
%if %sysfunc(exist(&FILE)) %then;
%else %do;
data _null_;
file print ls=255;
&MSG30 put "ERROR : [Msg30] Program halted, file &LONGFILE does not exist";
abort; run;
%end;
%mend CHECKDS;
Now when I call it with this:
LIBNAME IN1 "/folders/myfolders/";
%CHECKDS(&STPERSON.TXT,PERSON)
run;
I get this error: ERROR : [Msg30] Program halted, file PERSON does not exist.
I know that the files exists and is in that location. Any ideas?
The first argument to the exist function should be in the format libname.memname. The second argument to the exist function specifies the member type; since you didn't specify the member type, the default, DATA, is assumed. This implies a file with a SAS data file with a sas7bdat extension.
See here for a list of member types.
Since your file is a .txt file, I don't think it can be considered a library member. Anyone is welcome to correct me if I'm wrong.
DWal got it right above. Here's a more complete answer for what I had to do. I had to convert the .txt file to a sas data set (particularly .sas7bdat).
proc import datafile="/folders/myfolders/PERSON"
dbms=dlm
out=person
replace;
delimiter=' ';
getnames=yes;
run;
Then that had to be written to my library and I was able to use it.
data IN1.person ;
set person;
run;

How to write macro for importing multiple excel files(xlsx) in sas and append it

I am having some 50 excel files (xlsx format) to be imported to sas and then need to append it for analysis. All the excel files header are same i.e., the variable names are same for all the file. I need macro for importing and appending all of them at a time rather than importing all the files one by one and then later append it. Your help is much appreciated.
The other issue with the excel file is that there is a blank column next between the variable name and data points. I have written a code remove it using data step but came we write this also in the macro while importing.
Data XXX.yyy;
Set XXX.yyy;
if missing(coalesceC(of ASC Brand Cdesc1 Cust_ DGM Desc Family Grp1 High_Level_Product_Desc
Issf Name Prod_Desc Product__Code RVP SA_Desc Terr_ UOM Yr
)) and missing(coalesce(of Acc Int_Margin M_Cost Mth Net_Sales Sls__ Uts )) then delete;
run;
It sounds as though your existing code already does what you need it to do. I doubt there will be much of a performance gain from attempting to import all 50 files in one data step (which is possible via dde, but rather fiddly).
If your existing code is set up to process just one hard-coded file, I'd suggest using it to write a simple macro that takes one excel file as input, imports that file, and appends it to the master dataset. Then you can call the macro 50 times.
e.g. You could write the macro as something like this, incorporating all the relevant bits of your code, and replacing all references to specific files with macro variables:
%macro import_and_append(excel_file,base_dataset);
proc import datafile = "&excel_file" dbms = excel out = t_import;
run;
proc append base = &base_dataset data = t_import;
run;
proc datasets lib = work nolist nowarn;
delete t_import;
run;
quit;
%mend;
Then you can call the macro like so:
%import_and_append(c:\excel_file_01.xls,work.master_dataset)
Another way to do this would be to use the Excel LIBNAME Engine. You declare a library to each of your files, and then read all the sheets in 1 Data Step.
In this example, I have 2 workbooks (Book1.xlsx and Book2.xlsx) in C:\temp. All data is in Sheet1. 3 variables -- X, Y, and Z. Modify as needed for your purpose.
data files;
format file $12.;
input file $;
datalines;
Book1.xlsx
Book2.xlsx
;
run;
%macro read_excel(dir,outdata,files);
data _null_;
set &files end=last;
call execute("libname t" || strip(put(_n_,8.)) || " excel '&dir\" || strip(file) || "';");
if last then
call symput("n",_n_);
run;
data &outdata;
set
%do i=1 %to &n;
t&i.."Sheet1$"n
%end;
;
a = sum(x,y,z);
if missing(a) then delete;
run;
%do i=1 %to &n;
libname t&i clear;
%end;
%mend;
%read_excel(c:\temp,data_from_excel,files);