Tuesday 18 May 2010

SAS Macro Programming is a pain in the butt!

I've been playing around with SAS macros these past few days and I must say I am totally shattered and depressed. It took me ages to find out why the SAS macros were not working in the first place - apparently macros are not meant to be run in "Open Space". They are meant to be placed inside data or proc steps... except that's not how you make global variables. However, it IS possible to run macros in open space though you need to add a function or two to get it working. Then I had issues with formatting and dates not being interpreted correctly. Finally I couldn't run a system command line command except on the local machine. Gah!

First. let's set some variables:

%let var_name = value;

The value can be a number or a string, but you're not meant to quote the string, like you usually do with SAS strings. If you want to see the value of var_name in the log (helps a great deal with debugging!):

%put &var_name.;

OK. Let's say you want to run a function to assign a value to a variable. Not straightforward - if you're in open space you need to call a special macro function %sysfunc:

%let var_name = %sysfunc( mdy(05, 18, 2010) ); * var_name holds date int value;

Note the command mdy() is in American Month-Day-Year mode.
What if we want to format this for output?

%let var_name = %sysfunc( putn( %sysfunc( mdy(05, 18, 2010) ), DATE7.) ); *holds 18MAY2010;

OK. Let's say I want to write a macro procedure/function or whatever they call it, and run it. Check out the silly if statements and loops.

%macro chk;
    * Check Sunday - yes, it counts the week days from Sunday - How annoying;
    * &yesterDate is already declared intnx(Day, "&sysdate"d, -1);
    %let weekday = %sysfunc( WEEKDAY( &yesterDate. ) );
    %if &weekday. = 1 %then %do;
        * intnx() is a great function - if its Sunday it sets yesterDate to Friday;
        %let yesterDate = %sysfunc( intnx(Day, &yesterDate., -2) );
    %end;
    * Now I want to declare an array... except macros don't do arrays, apparently!
    * Recommended guidlines say to do this:
    %let year = %sysfunc( YEAR(&yesterDate.) );

    %let Xmas1 = %sysfunc( mdy(12, 24, &year.) );
    %let Xmas2 = %sysfunc( mdy(12, 25, &year.) );
    %let Xmas3 = %sysfunc( mdy(12, 26, &year.) );
    %Do i = 1 %to 3;
        %if &yesterDate. = &&Xmas&i %then %do;
            %let yesterDate = %sysfunc( mdy(12, 23, &year.) );
            * Check its not Sunday again - Recursive Macro calling;
            %chk;
        %end;
    %end;
    * I have another loop to check for more holidays but I'm ommiting that;
%mend;
 
Now to run the macro I write:
 
%chk; 
 
but it would still work if I missed the ending semicolon - which is shocking because nothing else except multi-line comments work without an ending semicolon. Even single line comments and half finished statements need a semicolon on the end of a line.
 
OK. Now I had to run a command on the system. I had two options - the X command and the call system() command. X is a unixy command line thinga-ma-jig and can be called from anywhere - open space or not, while call system() can only be called inside a step. They are used like so:
 
X "move /path/to/file1.txt path/to/file2.txt"; *this is how you rename a file under unix/linux btw;
data _null_;
    call system("move /path/to/file1.txt /path/to/file2.txt"); *while windows does have the rename command, move works fine;
run;
 
An interesting thing is that you can send multiple commands through X seperated by semicolons while with call system() it does one command at a time.
 
Here's the problem... X didn't work on the remote server but call system() only worked on the local system. The rest of the script is meant to work on the remote server though. So this means that I'll need to run this part locally, comment it out temporarily, then run the whole script again remotely. Or I can skip that mess which took me ages to figure out why it wasn't working and manually change the file names.
 
SAS Sucks.

Saturday 8 May 2010

CSS3 is the bomb and HTML5 forms in Opera

I recently redesigned one of my personal websites and used some CSS3 coding. It works great in Firefox and Opera but not in Internet Explorer. IE7+, at least, looks acceptable, but with the dreaded IE6 it is a bit broken up. Now, if I were the type of person who had an inkling of respect for anyone who still uses IE6 I would have solved this problem for IE6 users but instead I've left it as it is and written a bit of JavaScript to tell (off) IE6 users to upgrade.

However, if I ever need to make CSS3 work in IE or need to remember the cool CSS3 effects, here's where I'd go: http://www.smashingmagazine.com/2010/04/28/css3-solutions-for-internet-explorer/

It covers Opacity / Transparency, Rounded Corners, Box Shadow, Text Shadow, Color Gradients, Transparent Background Colors, Multiple Backgrounds and Element Rotation, and the options available for IE. It's an awesome resource.

On another note, I've been looking at HTML5 from a distance for quite a while now but really haven't got my hands dirty yet. I found this great HTML5 slide show, which covers almost everything, but then I found something else, an example of HTML5 form widget, something that Firefox (3.6.3) doesn't show but Opera (10.51) gets working.

Basically, without Javascript or anything, just plain HTML5, for date and time fields, Opera shows a widget where you can pick the date and time with your mouse or keyboard. It's just so surprising to find something like this without needing heavy JS libraries and frameworks. Hopefully, the rest of the browsers will follow suite and build better such widgets and in the future we'll be using far less JS (or there will be some wicked abstraction going on).