In the previous episode, we built our own custom SAS function - a masterful trick indeed. Gordon Keener, a developer here at SAS, responded exuberantly "You think that's cool? - try THIS!" and proceeded to demonstrate prodigious powers with the SAS by using a custom function in a custom informat to elegantly resolve a programming problem. And another episode of Jedi SAS Tricks was launched...
First, if you haven't experienced the joy of bringing order to chaotic data using your own custom formats and informats, an excellent place to start is with Jonas Bilenas' SGF paper titled "I Can Do That With PROC FORMAT". But Gordon was talking about a new (SAS9.3) capability - the ability create a custom format that performs a function on a value. And because we know how to build our own user-defined functions with PROC FCMP, can't we build a custom format leveraging a custom function? Yes, we can! So let's dive in. If you want to click along, download the code here and fire up your SAS session. Just FYI, the code includes segments we don't discuss here (like producing the data sets, etc.)
Our data is read in from a raw text file. US data has temperatures recorded in °F, World data in °C. How can we compare these values? After reading it in, the original SAS data looks like this:
First, we'll build custom functions to convert °F to °C and °C to °F:
proc fcmp outlib=sasuser.MyFunc.MyPack; function c2f(Tc); return(((Tc*9)/5)+32); endsub; function f2c(Tf); return((5/9)*(Tf-32)); endsub; run;
Next, we could use these functions to "enrich" the data, adding a temp_c variable to the US data and a temp_f variable to the World data:
options cmplib=sasuser.Myfunc; data us_both; set us; Temp_c=f2c(temp_f); run; data world_both; set world; Temp_F=c2f(temp_c); run;
But - wouldn't it be cool to just print our °F temperatures in °C without having to modify the data? For that, let's write a custom format using our temperature conversion functions and use it in PROC PRINT:
proc format; value c2f (default=5) other=[c2f()]; value f2c (default=5) other=[f2c()]; run; title 'Printing with Formats'; proc print data=us label noobs; label temp_f='Temp (°C)'; format temp_f f2c5.1; run;
Even more intriguing, as Gordon originally suggested, we could build custom informats based on our temperature conversion functions and use them to read the temperature data from the raw data file directly into the units we desired. We could have read the Fahrenheit data in as Celsius or vice-versa, right from the start. For example:
data US_F_and_C; input @1 Place $14. @19 Date date7. @15 Temp_f @15 Temp_c f2c3.; format date date9.; datalines; Alaska -80 23jan71 Colorado -60 01jan79 Idaho -60 18jan43 North Dakota -60 15feb36 Wyoming -63 09feb33 ; run; data World_F_and_C; input @1 Place $14. @15 Date date7. @23 Temp_f c2f3. @23 Temp_c ; format date date9.; datalines; Antarctica 21jul83 -89 Siberia 06feb33 -68 Greenland 09jan54 -66 Yukon 03feb47 -63 Alaska 23jan71 -67 ; run;
And the results:
And that's a wrap! Until next time, may the SAS be with you!
hanks for the plug. Check out my SAS Press book, "The Power of PROC FORMAT" for more FORMAT/INFORMAT tricks. Maybe it is time for a new edition to include PROC FCMP.
Anywhere this square-bracket syntax is documented?
Sure - check out the PROC FORMAT documentation for the VALUE statement under the topic "value-range-set(s)". It's specified in the "existing-format" section.
Informats are my personal favourite to read in unusual data! Love your example, well thought out & well done!