tirsdag den 16. juni 2009

Welcome - %isnum macro

Why this blog?


I work as a freelance SAS developer, and I have been doing so for the last 11 years. I often use a number of general macros, which I generally rewrite when I need them at a new customer site. This blog is going to be my repository of SAS macro utilities, so that I do not have to rewrite the stuff every time.

%IsNum macro


The first macro here is an alternative to the good old SAS Institute %DataTyp. There are 3 good reasons to replace it with something else:

  1. It is slow - mostly because it uses the very inefficient %verify macro
  2. It does not check for overflow or underflow - a number such as 2.3e256354 will be accepted, even though it is out of range for 64-bit floating point numbers
  3. I can never remember the return values - is it "num" and "char" as in dictionary.columns, or 1 and 2 as in PROC CONTENTS? Neither, if I remember correctly. But then, I probably don't.

What is the fast way of checking whether something is a valid number? Just use the input() function, with ?? to suppress error messages:
data _NULL_;
a=input("&var",?? best32.);
if a=. then call symput('NUMERIC','0');
else call symput('NUMERIC','1');
run;

Not quite what I want, though. The really good thing would be to have a boolean function-style macro, that you can call anywhere:
  %if %isnum(&parm1) %then %do; 

How to do that? You can't submit a datastep in such a macro. And %sysfunc() does not accept the input() function, only the inputn() function, which, on the other hand, does not accept ?? in front of the format. How can you submit a datastep without really submitting a datastep? By creating a view, that's how. My first version of this macro used a datastep view - but then I started testing WPS, and that does not (yet) accept datastep views. But SQL views are okay as well:
proc sql;
create view macview.isnum as
select input(symget('parm'),?? best32.) is not null as isnum
from dictionary.macros;
quit;

Why "from dictionary.macros"? Well, any old table with more than zero rows will do. And dictionary.macros is never empty. We can now write our simple and nifty macro:
%macro isnum(parm);
%local dsid rc;
%let dsid=%sysfunc(open(macview.isnum));
%let rc=%sysfunc(fetch(&dsid));
%do;%sysfunc(getvarn(&dsid,1))%end;
%let rc=%sysfunc(close(&dsid));
%mend;

Nothing fancy here, except maybe the line
 %do;%sysfunc(getvarn(&dsid,1))%end;

- which is the line that actually returns a value. You are probably more familiar with this way of returning a value
%let rval=something very useful;
&rval
%mend;

I am not using that style for efficiency reasons. It does not really matter when you are just returning a simple digit, but when you start building up return values like this
%do i=1 %to 999;
%let rval=&rval X&i;
%end;
&rval
%mend;

the style becomes rather inefficient. So we may as well get used to the other style right away:
%do i=1 %to 999; 
%do; X&i%end;
%end;
%mend;

the %do-loop in this last example could have been written as a one-liner (that holy grail of us veteran APL programmers!), but I prefer to display the control structure of the program clearly. But why not just write like this:
%do i=1 %to 999;
X&i
%end;
%mend;

Good question! The answer is that this version of the macro will output the indentation whitespace as blanks in the return value. So, remember this: when you see a
%do;(this is just some value, not macro statements)%end;

in one of my macros, it is the return value from the macro being built up.


And now for the endnotes. This macro has (so far) only been tested under WPS. I have no reason to think that it will not work under SAS version 9.x - but there may be a problem with SAS version 8. I am not going to create a write-around for that, as I suppose most people are moving away from SAS 8 by now. If you absolutely must, you can write your own datastep view. And then the usual disclaimers: the stuff presented here is free, but there are no guarantees that it will work, only my assurance that it probably will.

Where to find the stuff


I have created an open source sas-macro project, where all the macros will be stored. The macro itself is in the svn/trunk subdirectory, the definition for the SQL view is in the /Preprocess subdirectory under that. You will have to create and allocate a MACVIEW library in order to use the macro.

The project is under subversion control - I just finished downloading and installing the Subclipse plugin for the WPS (Eclipse) workbench - works great!

Ingen kommentarer:

Send en kommentar