The purpose of this post is to explain and demonstrate the usefulness of the SAS Function Compiler procedure (FCMP, BASE/SAS) and related functionality in the context of processing timeseries data. The FCMP procedure lets you create and store SAS functions and subroutines. The previous posts in this series explained the fundamental ideas of processing timeseries as arrays and introduced some tools, syntax and data that will reappear here. If you haven’t had a chance to read them, links are below.
Functions and subroutines are useful for implementing blocks of code that can be reused. They are similar in purpose to SAS Macros. The difference between functions and subroutines can be confusing. Functions return a value based on input arguments, so they aren’t suited for array operations. However, functions are useful in pretty much any programming scenario, and they provide a straightforward introduction to the FCMP procedure.
Subroutines can return multiple values, in this context arrays, based on an input array. We’ll illustrate the usefulness of subroutines by converting some array processing code that was presented in the previous post into a compact, reusable block that can be stored and called during processing. The FCMP procedure functionality is available in both SAS 9 and SAS Viya. The final demonstration in this post shows how a subroutine can be compiled and then called in CAS using the TSMODEL procedure.
Demonstration 1, creating and using a custom function
SAS provides many pre-defined functions, and you’re probably already familiar with several focused on timeseries processing; HOLIDAY, INTNX and INTCK are some popular examples. Sooner or later, programmers will run into a situation where a function would be useful for accomplishing a programming task, but a predefined one doesn’t exist. We’ll start with a user defined function that converts prices denominated in British pounds (GBP) into dollars (USD) using a specified exchange rate.
First, we’ll need a pointer to a storage location for the custom function. The compile library or CMPLIB option specifies that new functions can be compiled to the WORK library in a table named TIMEFUNC.
options cmplib = work.timefnc;
The new function is created in this call to the FCMP procedure.
proc fcmp outlib=work.timefnc.fxfunc;
function exchg_convert(pounds, rate);
dollars = (rate)*pounds;
return(dollars);
endsub;
run;
It’s useful to understand how functions are compiled and stored, and the portion of the TIMEFNC table shown confirms the execution of this process. A compile library can contain multiple packages that contain a variety of functions and subroutines.
Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.
Now that the EXCHG_CONVERT function is ready for use, we’ll call it in a Data Step. Because the RATE argument will change over time, we’ve coded it using a MACRO variable to make the process of updating easier and less error prone.
%let gbprate=1.2;
data work.dollars;
set work.items_in_GBP;
format USD_price dollar10.2;
USD_price=exchg_convert(GBP_price, &gbprate);
run;
The DOLLARS table contains the results of running the EXCHG_CONVERT function. Recall that a function returns a value. There are multiple values of USD_PRICE in the table, but these were generated line by line, or from a sequence of function calls, as the data step ran.
Demonstration 2, creating and calling a subroutine in SAS 9
In the previous post in this series, a new array (feature) that flags the week that contains Easter Sunday was created for a project. Since it’s reasonable to expect that other projects with week interval data may also have an Easter effect, we’ll convert that syntax into to a reusable block of code using a subroutine. Subroutines can be created and called in both SAS 9 and SAS Viya. Since the syntax is not identical, we’ll focus on SAS 9 for this demonstration, and then cover the SAS Viya process in the next one.
*Bonus extra credit challenge; generalize the definition of the subroutine below so that it can create Easter based on common date interval (DAY, WEEK, MONTH or QUARTER) input arrays.
proc fcmp outlib=work.timefnc.evntfncs;
subroutine Easter_evnt(DateID[*], easter[*], yr[*]);
outargs easter, yr;
actlen = DIM(DateID);
do i = 1 to actlen;
yr[i]=year(DateID[i]);
easter[i] = (week(DateID[i])=week(holiday('EASTER', yr[i])));
end;
endsub;
run;
The previous post showed identical syntax in a SUBMIT block in the TSMODEL procedure. The syntax in the SUMIT block was local to that call of TSMODEL. This subroutine will be available to any SAS functionality that accommodates it.
In this example, we’ll call the subroutine in a the TIMEDATA procedure. Recall that this SAS 9 procedure is tuned for processing timeseries as arrays. In addition to Data Step and TIMEDATA, other SAS 9 functionality accommodates functions and subroutines created in the FCMP procedure. See the Base SAS Procedures Guide for more details on the FCMP procedure; https://support.sas.com/documentation/cdl/en/proc/65145/PDF/default/proc.pdf
proc timedata data=work.wineco_sorted outarray=winecoarrays
print=(arrays);
id date interval=week;
by region type;
var sales / accumulate=total;
var baseprice promotion / accumulate=average;
outarrays easter yr;
call Easter_evnt(date, easter, yr);
title "Create a feature for each BY group that flags Easter";
run;
The OUTARRAY table, WINECOARRAYS, contains an EASTER event variable. The portion shown below is for REGION1, Table Red (TBLRE) type wines.
Demonstration 3, creating and calling a subroutine in SAS Viya
Conceptually, this demonstration is the same as the previous one; subroutine syntax is created, compiled and then called to create the EASTER and YR arrays for the 16 BY groups in the WINECO data. Here, the data has been loaded into memory and the subroutine syntax will be compiled and called in a SUBMIT block in the TSMODEL procedure.
This demonstration follows a TSMODEL procedure documentation example. See: https://go.documentation.sas.com/doc/en/pgmsascdc/default/casforecast/casforecast_tsmodel_examples02...
First, the subroutine code is put into a SAS macro named FCMPCODE.
%macro fcmpcode;
subroutine Easter_evnt(DateID[*], easter[*], yr[*]);
outargs easter, yr;
actlen = DIM(DateID);
do i = 1 to actlen;
yr[i]=year(DateID[i]);
easter[i] = (week(DateID[i])=week(holiday('EASTER', yr[i])));
end;
endsub;
%mend;
Next, the EASTER_EVNT subroutine syntax is inserted into a TSMODEL procedure SUBMIT block with a call to the FCMPCODE Macro. Once the code is substituted in, it is compiled and then called within the submit block.
proc tsmodel data = mylib.wineco outarray=mylib.regtypeseries
outsum=mylib.regtypesum;
by region type;
id date interval=week;
var sales /acc = sum;
var baseprice promotion/acc = avg;
outarrays easter yr;
submit;
%fcmpcode;
call Easter_evnt(date, Easter, yr);
endsubmit;
run;
A portion of the OUTARRAY table REGTYPESERIES is shown below.
The SAS Viya method for subroutine creation and compilation looks different from the FCMP procedure approach, but it preserves the primary advantages of custom subroutines for array processing.
This post was intended to be a basic introduction to the FCMP procedure and associated functionally in the context of processing timeseries arrays. Hopefully, you already have some ideas for custom subroutines and functions to create that will make your work easier and more efficient. Interested readers will find many more SAS FCMP examples online.
Find more articles from SAS Global Enablement and Learning here.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning and boost your career prospects.