Pass Function Logic to PROC FORMAT in SAS
PROC FORMAT is one of my favorite procedures in SAS. I can solve so many problems with PROC FORMAT that would be far more complicated otherwise. Today, I will demonstrate how to utilize the flexibility of formats to the maximum. I will do so by combining the procedure with one of my other favorite (though sadly not as frequently used) procedures PROC FCMP.
Using PROC FORMAT and PROC FCMP Together
The Function Compiler Procedure (FCMP) lets you create custom functions and Call Routines. A ruthlessly underused procedure in my book. Suppose that we want a format that determines whether a person is a teenager or not. I can easily write such a function with PROC FCMP.
Next is where the magic happens. I use PROC FORMAT and create the Teenager format as a numeric format in the value statement. I use the Other Keyword and put the newly created function Teen() in squared brackets. This means that SAS is to apply the format to the entire range of values.
Finally, I use the Teenager Format in the bottom data step. If you look in the created data set, you will see that Age is now displayed as text. Either ‘Teenager’ or ‘Not Teenager’.
proc fcmp outlib=work.functions.fun; function Teen(Age) $; length r $12; if 13 le Age le 19 then r='Teenager'; else r='Not Teenager'; return (r); endsub; run; options cmplib=work.functions; proc format; value TeenAger (default=12) other=[Teen()]; run; data class; set sashelp.class; format Age TeenAger.; run;
Obviously, we can easily create this format without using the above trick. We can create it directly in PROC FORMAT, which would be easier in this case. However, I will let the above code serve as an introduction to the concept. Next up is a real life example of when this technique comes in handy.
A real life example
Not too long ago, I came across a thread at the SAS Community where the questionnaire wanted to combine the BESTD and the COMMA formats. In other words he wanted a format with a period as thousand separator, a comma for decimal separator and that prints integers without decimals. Such an format does not exist out-of-the-box.
Instead, we have to build it ourselves. And the PROC FORMAT itself does not support function logic, so we have to use the FCMP trick instead. The code below follows the exact same pattern as above. The only difference is that the function logic is different (of course). In PROC FCMP, I use an If-Else Statement to control the two cases: Integer and Non-Integer. If the number is an integer, I do not want decimals. If it is, I want them. Furthermore, I build both cases with periods as thousand separators.
proc fcmp outlib=work.functions.fun; function fnc(num) $; if num=int(num) then r=scan(strip(put(num, commax32.10)), 1, ','); else r=tranwrd(strip(tranwrd(strip(put(num, commax32.10)), "0", " "))," ","0"); return (r); endsub; run; options cmplib=work.functions; proc format; value fmt (default=32) other=[fnc()]; run; data test; x=1234.567;output; x=-1234.00;output; x=1234567 ;output; x=1.23 ;output; format x fmt.; run;
If you look at the test data set, you will see that the data is correctly formatted. This would not be possible with PROC FORMAT alone.
You can see the entire thread with the problem here.
In this post, I demonstrate how to use PROC FORMAT and PROC FCMP to apply function logic to build custom formats in SAS. A very nice trick, that lets you utilize the flexibility of PROC FORMAT to the maximum. We see that it allows us to build formats, which were not possible with PROC FORMAT alone.
If you have read my blog before, you know that I love PROC FORMAT. Check out a few related posts here
You can download the entire code from this post here.