SAS offers an array of products to help you understand your data and gain insight for data-driven decision making. Many of the pre-packaged solutions provide graphical point-and-click interfaces that allow users from different backgrounds apply complex statistical algorithms to find the story hidden underneath the surface of the data.
But what happens when you encounter a unique case not covered by your SAS solution or when you simply want to roll up your sleeves and run an analysis in your own particular way? Fortunately, SAS also provides a flexible programming language called SAS Macros that you can use to accomplish those exact goals.
Next thing you know, you understand all the relationships between your variables and have gained valuable insight. However, when you look back, you have 5,000 lines of non-reusable spaghetti code that does not follow a clear structure, mixes functionality, and can be extremely tedious to maintain.
This post covers one way of creating portable, organized SAS programs that are easy to maintain and expand.
Set up a Portable Directory Structure with SAS Macros
Select a directory structure generic enough that can be applied to all your projects. The goal is to enforce standardization to facilitate switching from one project to another and collaborating with other team members. A clear directory layout makes it really easy to find specific programs, reports, and sources even if you have never worked in that project before. This layout is going to depend on your preferences, but here is an example that you can use as reference and tweak to meet your goals:
Figure 1: Sample directory structure
Connecting Code Components With the Main.SAS Program
Now that we have a directory layout, we need to glue all the parts together in a way that makes the project easy to grow and to move to a different location without updating the code. To accomplish this, we need to make our code smart enough to create librefs, set SAS system options, and execute external programs without including hardcoded paths. Let’s take a look at how we can use Main.sas to set up the environment and achieve these goals:
/* ---------------------------------------------------------- */
/* Name : SAS Macro - Project Setup */
/* Author : Ivan Gomez, Zencos */
/* Purpose : Setup the environment for project */
/* ---------------------------------------------------------- */
%macro Get_Project_Path; %global project_path; %let project_path=Unknown; %let file_path=Unknown; /* Get the path of to the SAS file */ %if %sysfunc(SYSEXIST(SAS_EXECFILEPATH)) = 1) %then %do; %let file_path = %sysget(SAS_EXECFILEPATH); %end; %else %do; %let file_path = %sysfunc(getoption(sysin)) ; %end; /* Check the file to the SAS file was retrieved */ %if "&file_path"="Unknown" %then %do; %put %STR(ERROR:) Unable to determine file path. Aborting execution; %Abort CANCEL; %end; %let program_name = %sysfunc(SCAN(&file_path, -1, \)); %let project_path = %qsubstr(&file_path, 1, levai(%length(&file_path); %put %STR(NOTE:) Project root path is &project_path; %mend; %macro Setup_Log; /* Route the logs to the Logs/ directory */ %let log_file = &project_path.Logs\%sysfunc(date(), yymmdddlO.).log; PROC PRINTTO LOG="&log_file"; RUN; %mend; %macro Assign_librefs; /* Assign librefs */ libname data_in "&project_path.Source_Data"; libname data_out "&project_path.Data"; %mend; %macro Set_Options; /* Enable SAS to search for Macros in the /Macros/ directory */ options mautosource sasautos=("&project_path.Macros" sasautos); /* Search for Formats in the Project Formats directory */ options fmtsearch=("&project_path.Formats"); %mend; %macro SetUpEnvironment; %Get_Project_Path %Setup_Log %Assign_Librefs %Set_0ptions %mend;
%SetUpEnvironment /* Execute the SAS programs stored in the Programs directory */ %include "&project_path\Programs\Programl.sas"; %include "&project_path\Programs\Program2.sas";
This snippet meets all the requirements by executing a %SetupEnvironment macro that splits the process into four steps followed by a series of %include statements:
1. %Get_Project_Path
Relies on system and macro variables to determine the location of MAIN.SAS. This snippet works in the Windowing Environment in Base SAS and when submitting programs in batch mode, but it can easily be extended to work with other SAS programming interfaces.
2.%Setup_Log
Routes the log output to a timestamped file in the Logs directory.
3.%Assign_librefs
Creates SAS LIBREFs for our source_data and data directories.
4.%Set_Options
Instructs SAS to search for macros and formats in our project.
%include Statements
Finally, through %include statements, we bring the SAS programs from the Programs directory.
Keep Your Code Organized
You can find other sources that take different approaches using AUTOEXEC files. However, that requires additional configuration steps and most articles show hardcoded paths. This approach, on the contrary, does not need any pre-setup before executing the code.
Additionally, the greatest benefit of this method is that all the components that make up the SAS project are connected together without hardcoding a single path. Next time the project is moved to a new directory or shared with a new team member, it will just work saving time otherwise would be spent updating the programs to run from a new location.
Using the SAS Programming Language opens a world of possibilities, but it also introduces new challenges inherent to any codebase. It is easy to start a new SAS file, create your fist data step, add some procedures, and let the excitement of slowly discovering the story hidden in the data take over while completely forgetting about organizing your code.
Structuring code is a broad topic and here we are just scratching the surface of what you can do to go from chaotic code to organized and portable programs. Don’t treat this post as the ultimate guide to SAS programs, but instead let your imagination spark and think about other ways you can optimize your code.