Spectre Macros

(Author: Roland Rashleigh-Berry                                                                                              Date: 28 Apr 2013)

Latest Updates

Introduction

The macros in Spectre are there to help you do the bulk of safety reporting and to provide you with a complete set of utility macros to help you in complex cases. It is important to be aware of them so that you do not "re-invent the wheel" and write macros that effectively already exist. Many of these macros are public domain macros, written my myself in the past, that I have made available for a number of years. When I wrote Spectre, I often had a need for these macros so I simply copied them from my web site. Sometimes I needed to amend them, other times not. You can link to these macros from "Roland's SAS Macros".

Many of the macros are very short and yet they are useful. If you can be aware of them then they should be of enormous help to you, especially in the field of clinical reporting. I spent many years building up a collection of macros which you will find here and on the web site linked to above. If there were any more macros that needed writing, I would have thought of them and written them.

If you become familiar with all these macros then it might even change the way you code. I was heavily influenced with the philosophy of Unix when I wrote them so most of them take the form of simple utilities. Many of the macros are "function style" macros that "return" a value when called. Not many programmers are aware of this type of macro and yet they are commonly used. The %lowcase macro is one of these, though most programmers think it a macro function.

Apart from %npcttab and %unistats, which are introduced using many demonstrations, you are expected to learn about the macros from reading their headers. This is also true of %npcttab and %unistats - the demonstration programs for these two complex macros are just there to get you started until you have the confidence to learn from the headers. If your system is set up then you can see a list of all the macros with brief purpose and usage using the command "showme macros". You can then look at this list and if a macro interests you then you can view the macro header using a command like "showme zerogrid.sas" which will show you the header of the macro "zerogrid.sas". Because these macro headers are easy to access, you are expected to learn from the headers and call the macros correctly, so there is much less error checking done inside the macros than is normal for the industry. The only checking done in most cases is to ensure you have supplied enough parameter values for it to continue. Reducing the number of checks also saves a little CPU time and this is helpful where pharmaceutical companies use low-powered servers for their SAS® work.

A great help to learning about these macros would be to have SAS® Learning Edition (assuming you have not got a full copy of SAS® software) and to try out the macros. The recommendation on the main page is to download this web site to your laptop or home PC and to have SAS Learning Edition installed as well so you can try out examples. You will learn more about the macros if you use them for real instead of just reading about them. SAS Learning Edition will allow most, but not all, macros to run. It lacks "proc printto", for example, and this will prevent %openrep from working.

(When you use your browser to view the macros you will see a first line that is a narrow "/*" followed by two blank lines. If you view the same macro as "Source", from the "View" pull-down menu, you will see the first line is "/*<pre><b>" and that there are no following blank lines. The "<pre>" tells the browser that what follows is "pre-formatted" and the "<b>" tells it to display it as "bold". I have put the "<pre><b>" there so that your browser will display the macro correctly. This will not affect the operation of the macro as the entire header is treated as a comment. Note that one of the macros %quotelst must be viewed as "Source" as the browser changes some of the characters. Certainly, if you make a copy of it by copying and pasting, you must make that copy from the "Source" window. This is clearly stated in the header of the macro and a warning about this is repeated below).

The Main Reporting Macros

The main reporting macros are %popfmt, %unistats and %npcttab. Most large pharmaceutical companies (as well as some small ones) have such macros, though they might be named differently. %popfmt creates a "population format" that takes as input a treatment arm format and creates a new format with population totals at the end. %unistats creates a report of summary statistics (for numeric variables) and category counts and percentages (for categorical variables). %npcttab creates a report of patient counts and percentages of the population total with optional event counts. It seems that no reporting system is complete without these macros. How to use them has been fully described elsewhere, so they they are simply linked to here so you can look at the code, if you are interested.
%popfmt
%unistats
%npcttab

The Main Reporting Sub-Macros

The main reporting macros use some subordinate macros. In the case of calculating p-values, there is an advantage in doing this outside of the main macro. The thinking is that the main macro can be stable and "validated" while the subordinate macros, that calculates p-values, can be updated independently. I am expecting the following two p-value macros to change. I have no statistics qualification, so as I learn more about the subject, I expect to make further amendments.
%unipvals
%npctpvals

The macro %unistats, when first written, was not intended to produce a report since the output values could be presented in many ways. One common way of presenting output was identified, however, so a macro was written to do this. %unistats has the appearance of being able to produce a report but in reality it calls another macro named %unicatrep to do this. Keeping this as a separate macro is useful, as it allows the report be deferred until after some manipulation of the output dataset. You can read about the structure of the output datasets on the detailed page for %unistats.
%unicatrep

Another sub-macro for %unistats is %unimap. This is another macro that will likely need changing in the future so I kept it separate. It links statistics labels to statistics keywords. For example, a label for standard deviation could be "SD", "sd", "STD" or it could have a footnote marker attached like "sd*" but they all have to link to "STD" which is a keyword. There may be cases I have not thought of, so it made sense to me to keep this separate. This is another case where the main macro can be stable and validated while its submacro can be subject to change. It is a very good idea to organize major macros in this way such that easily identifiable changeable code is performed in submacros.
%unimap

%titles, %openrep and %closerep

%titles is the macro that calls user-defined titles (and footnotes) into your program. %openrep creates a temporary file ready to receive report output and %closerep takes this temporary file and adds "Page X of Y" labels. Note that %closerep will also substitute the character "A0"x with a space. This is very useful. Sometimes, using "proc report", you need to maintain the alignment of text string in a column such as keeping a decimal point in the same place and if you use the "center" or "right" options then the values will get shifted in some cases unless you do something to stop it happening. To get around this, the character "A0"x is placed at the end of the field to prevent the text getting shifted and it needs to be removed for the final report. %unistats uses this technique, although you are probably not aware of it. You can use this technique as well - suppose you wanted a variable to have a blank label, you can not set it to a blank because SAS software will then add a label that is the same as the variable name, but if you set the label to the "A0"x character then %closerep will convert it to a space character. Actually it is not %closerep that does this, it is the script it calls to add page numbers named "pagexofy".
%titles
%openrep
%closerep

%titles sub-macros

A call to the %titles macro spawns calls to several macros that get information about the job and program running. It also calls a titles macro corresponding to the client. Assuming the client is company "XY", as defined in the member "protocol.txt", then it will call a macro named %xytitles and this is included in the list below as an example client titles macro. It also calls a macro named %maxtitle to tell you how many titles and footnotes have been set up, but you will want to use this elsewhere, so it will be described later.
%jobinfo
%protinfo
%proginfo
%xytitles

%zerogrid and %allfmtvals

I use these two macros a lot, especially %zerogrid, which has been around for a few years. Sometimes you need all possible combinations of different values and to merge your data on top of it to make sure everything is represented. Usually, you need to merge your data on top of a grid of zero values and that is what %zerogrid is for. It creates the grid of all combinations of values you specify plus whatever you choose set to zero (or set to something else). To give a meaningful example, suppose you had to produce a report of what equipment all the stores had but you had to show all categories of equipment for each store, even if some stores lacked some items of equipment. This is where you would use %zerogrid to make sure all equipment types were represented. Sometimes, the complete set of possible items does not exist in your data. You could create a dummy dataset which includes these categories, of course, and use that as input to the %zerogrid macro, but sometimes you know that all these values are defined to a format. You could then use %allfmtvals to create a dataset containing every "start" value for the format and then use the resulting dataset as input to %zerogrid. I couldn't imagine doing my job without these two macros. %unistats calls them as well.
%zerogrid
%allfmtvals

%quotelst and %commas

Suppose you write a macro that requires the user to provide a list of variables. To keep things simple, the list is best requested as a space delimited list. But once you have that list you might need to use it in an "in()" function in your code. You will then need each variable in the list to be in quotes. That is what the %quotelst macro is for. But it goes beyond the "in()" function. Suppose a list of variables has been supplied and you want to search the list of a particular variable. Using %index() might let you down because the variable you are searching for might match part of the name of a longer different variable. One safe way of coping with this is to quote the list of variables and quote the variable you are searching for and then use %index() on the quoted version of the list and the variable. That way you will be sure you got a good match. I use %quotelst a lot when I am writing macros.

The %commas macro is a variation on %quotelst. It calls it but sets the quote marks to nothing and asks for a comma delimiter. This is what you will need if you are writing a macro that supplies a variable list to "proc sql". You will need to use comma delimiters for "proc sql" and so I wrote the %commas macro.
%quotelst # You must view this macro as "Source" from the "View" pull-down menu for reasons stated in the header
%commas

%words

Suppose you wrote a macro where the user had to input a list of variables. You might have to do something for each variable in turn. Your code will be shorter if you know how many items there are and so I wrote %words to return the number of items so you could code something like:
 
%do i=1 %to %words(&varlist);
  whatever..
%end;

It is a function-style macro just like %quotelst and %commas. If you look inside the code you will see that the value it "returns" does not have a semicolon after it. This is deliberate and is normal for a function-style macro.
%words

%nobs and %attrn

First a warning about %nobs. This is a very common macro found at many sites that gives the number of observations in a dataset but they tend to work in different ways for different sites. Mine is a function-style macro that returns a positive number of observations if the dataset is OK and has observations but it returns "0" if not so that I can use the zero in a boolean expression such as:
 
%if %nobs(test) %then %do;

I have had this macro for many years and have become aware that most sites have variations on this macro so I tend to use %attrn (short for "attribute numeric") instead where it makes sense to do so. This, again, is a function-style macro. It makes sense to use it when you want to avoid any conflict with a different %nobs macro and you know the dataset exists. You could use it like this:
 
%let nobs=%attrn(dset,nobs);

...or like this as for the %nobs example above:
 
%if %attrn(dset,nobs) %then %do;

Here they are. Note that when they return their values the line is not ended with a semicolon. This is normal for function-style macros:
%nobs
%attrn

%attrc and %attrv

Before we forget about %attrn I thought I would give %attrc and %attrv a mention. They are short for "attribute character" and "attribute for a variable". %attrn and %attrc work at the dataset level but %attrv works at the variable level. I will leave you to work out how to use them from the headers.
%attrc
%attrv

%getvalue

Sometimes I have a dataset with unique values in it and I need to produce a report for each value. If "by" processing is not convenient I can use %nobs (or %attrn) to find out how many observations are in it and then extract each value in turn and produce a report. I don't use it that often but you might need it one day.
%getvalue

%maxtitle

I use this macro a lot when I am using Spectre. Or I at least use what it creates for me because I know the %titles macro calls it at the end of its processing to tell me how many titles and footnotes there are. It creates two global macro variables named _maxtitle_ and _maxfoot_ that you can use in a titles or footnotes statement like this:
 
title%eval(&_maxtitle_+1) "My Title";

It normally displays the values of _maxtitle_ and _maxfoot_ in the log but you can call it in "quiet" mode if you like if you do not want a message written to the log. If you are setting up extra titles and footnotes in your reporting macro then you will need to know these values of _maxtitle_ and _maxfoot_ so that you don't overwrite a title or footnote that has already been set up
%maxtitle

%lafootnote and %latitle

Every pharmaceutical company that I know of requires the footnotes on their reports to be left-aligned. This is very simple to do. All you need to do is to follow your footnote with a lot of space characters, usually in a second string, so that your first string does not get more than 262 characters (or whatever it is that SAS software complains about) so I wrote the %lafootnote macro to do this. Sometimes you will need to left-align a titles. This is often a "by line" that you want to be left-aligned so I wrote %latitle for this. Spectre normally put a page marker "FF"x in the rightmost position of the top line of each page of a report to receive the "Page X of Y" labels but it could be that you are creating an ad-hoc report and you want to add these labels afterwards. %latitles has the ability to put this page marker in. %ctitlepgmrk is another macro I wrote that will keep titles centred but add this page mark.
%lafootnote
%latitle
%ctitlepgmrk

%lookahead

You might know of the lag() function for storing the last value of a variable. But sometimes you will want to look ahead in a dataset. "Time to Event" calculations come to mind. For this I wrote the %lookahead macro. It isn't very efficient so use it sparingly.
%lookahead

%nodata

Usually you need to produce a report, even if you have no data. The report will normally have titles, in this case, but no footnotes. And if one of your titles was your "by line" then you will need to suppress it. You would think that producing a report with nothing in it is simple but actually it is quite complicated. So I wrote a macro to do that named %nodata and this has to call other macros which will be listed below.
%nodata
%titlelen
%titlegen
%bytitle

%nodupkey

You will often have sorted "nodupkey" to drop surplus observations but which ones were dropped? You sometimes need to know this and go back and check the data to see if there is something wrong. I wrote %nodupkey for this so that it lists the observations it is dropping.
%nodupkey

%varlist

Sometimes you need to get a list of variable from a dataset. They might be all the variables, the character variables or the numeric variables. I wrote three macros for this:
%varlist
%varlistn
%varlistc

%nvars

Sometimes you need a count of variables in a a dataset. They might be all the variables, the character variables or the numeric variables. I wrote three macros for this. Now if you are paying attention and thinking the same way as I do, you will already know how I am going to do this for the last two cases.
%nvars
%nvarsn
%nvarsc

%qreadpipe

Sometimes you will need to run a Unix command and need to use the result in your program. I wrote a macro named %qreadpipe that will assign the results of a Unix command to a macro variable. And if you are wondering why the name starts with a "q" it is because it returns the result "macro quoted". To use it in ordinary sas code the you can surrounfd it with the %unquote() function.
%qreadpipe

%remove

You might need to remove occurrences of a string from a macro variable and assign the result to a new macro variable. I haven't used this one in a while.
%remove

%complibs

Sometimes you need to compare whole libraries to find out what changes to datasets have occurred. This could be a new study increment or perhaps you are doing some sort of test and you want to make sure the server configuration change is going to give you the same datasets with the same code as before. I wrote %complibs for this a few years ago and it has been updated since. It calls the macros %supasort, %dsall and %dslist.
%complibs
%supasort
%dsall
%dslist

Conclusion

You have been introduced to many of the Spectre macros.
 
 

Use the "Back" button of your browser to return to the previous page.

contact the author








SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration.