Date: Sat, 26 Sep 2009 21:47:02 -0700
Reply-To: xlr82sas <xlr82sas@AOL.COM>
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: xlr82sas <xlr82sas@AOL.COM>
Organization: http://groups.google.com
Subject: Re: Who has got a clinical reporting system that runs from a
Content-Type: text/plain; charset=ISO-8859-1
On Sep 25, 10:39 pm, RolandRB <rolandbe...@hotmail.com> wrote:
> On Sep 26, 3:50 am, "Lou" <lpog...@hotmail.com> wrote:
>
>
>
>
>
> > ""Data _null_;"" <iebup...@GMAIL.COM> wrote in message
>
> >news:ce1fb7450909251350j15e6bc52g15836c37f4fa2783@mail.gmail.com...
>
> > > On 9/25/09, RolandRB <rolandbe...@hotmail.com> wrote:
> > > > But if you
> > > > have, say, a "core" macro to do descriptive stats and category counts
> > > > with percentages and it is flexible enough to supply its output in a
> > > > dataset then that gives you the chance to "validate" that macro and
> > > > then the other reporting macros can call on its services.and avoid the
> > > > complexity. It then allows you to have different sets of sas code/
> > > > reporting macros for different clients that call these "core" macros
> > > > to do the work for them.
>
> > > Your "core" macros are called PROCS. PROC SUMMARY, FREQ and REPORT
> > > come to mind. You will also need FORMAT and probably TRANSPOSE.
>
> > > No macros required.
>
> > It will probably come as no surprise that I agree with Data _null_ on this -
> > I've said repeatedly here and elsewhere that 90% or more of the macros I've
> > run up against in pushing 30 years using SAS are rubbish. They're
> > overcomplicated, slow to execute, impossible to debug, etc.
>
> That matches with my own experience.
>
> > As far as validating a core set of macros is concerned, in theory I suppose
> > that's possible. In practice, what I've seen is a set of validated macros
> > in a global library. For a particular project, the global library is copied
> > into the project macro library. And then happily amended to the point where
> > there are dozens of versions of each macro, all with the same name, all
> > doing something slightly different, and of course none of these variants
> > were ever "validated". In some cases, the validated version is never used.
>
> I've seen that done.
>
> > And when the organization updates the SAS version, or adopts a new release
> > of MS Office, or upgrades the OS, or worse yet makes a platform change (e.g.
> > from Windows to Unix) the macros have to be validated all over again because
> > all of these changes in the background can and do cause macros that worked
> > fine to fail, or worse yet to appear to function but give results different
> > from those before the change.
>
> They really need a test platform such that if any changes to SAS or
> the platform software occur then the test suite is run again and
> compared directly against the results of the previous run.- Hide quoted text -
>
> - Show quoted text -
Over the last few years I have moved away from complicated macros and
set up what I call program templates. A program template is code
designed so that a programmer can update the code and wrap the code in
a macro. Wrapping is sometmes needed for mutiple reports. I alslo make
a concerted effort to never write a macro over 60 lines long(I don't
always succeed). If more than 60 lines consider a program template,
FCMP or rarely two macros.
Even with 10,000 lines standard macros often cannot do these simple
functions, which my program templates can easily do:
1. Different footnotes on each page.
2. Customized page splits (you decide manually or programatically when
to create a new page.
3. Nesting with categories (multilabel)
4. Subscripting within cells
5. Easily change the oder or position of any item on any page. I could
even put the Mean with the N(PCT) section
I have seen pharma demographic macros that generate 10,000 lines of
code when mprint is turned on.
For a cleaner version of the code below see:
http://homepage.mac.com/magdelina/.Public/utl.html
UTL_TIPWEB
T000130 EASY WAY TO DO N(PCT) MEAN STD MEDIAN MIN MAX REPORTS - NOTE
PRELOADFMT AND MULTILABEL FORMATS DUE TO RACE 48 LINES OF CODE
I do use macros for ods templates, so that output is standardized.
Incidentall I strongly suggest programmers use the 100% width
(outputwidth=100%) macro templates for landscape and portrait reports.
I think a set of reports that do not constantly change width more
esthetic. 100% width allows alignment of ods rtf text= or pretext=
with the body of your report. The code below could be simplified using
proc report line statements, however I had to box the body of the
report and the titles and footnotes had to be outside of report. SAS
does not provide a frame for boxing just the body of the report. Also
it is very frustrating because compute blocks in proc report can only
have one justification.
I wish I could make this easier to read. This is instructional code. I
do use the hidden dragon, but you can work around that.
/* DUAL INDENTAION AND WHITE/NON-WHITE PLUS ALL OTHER RACE CODES (ZERO
FILL FOR MISSING RACES) */
/* PAGE = THE PAGE WHERE RACE
APPEARS
*/
/* ORDER= THE ORDER IS THE RANK ON THE
PAGE
*/
/* THIS IS FOR INSTRUCTIONAL PURPOSES - CAN BE
SIMPLIFIED */
/* THIS CAN BE SIMPLIFIED BY NORMALIZING THE DATA EARLIER SEE OTHER
TEMPLATES */
options validvarname=upcase; /* important for some program
templates that use transpose
*/
proc
format;
value
sex
1 ='Page @01 @Order @01 @Sex - n (%) @\li360
Male'
2 ='Page @01 @Order @02 @Sex - n (%) @\li360
Female'
;
/* race code note the dual indentation
*/
value rcd
(multilabel)
1 ="Page @01 @Order @03 @Race - n (%) @\li360
White or Caucasian"
2, 3, 4, 5, 6, 7, 88 ="Page @01 @Order @04 @Race - n (%) @\li360
Non-White or Caucasian"
2 ="Page @01 @Order @05 @Race - n (%) @\li720
Black or African American"
3 ="Page @01 @Order @06 @Race - n (%) @\li720
Hispanic or Latino"
4 ="Page @01 @Order @07 @Race - n (%) @\li720
Asian"
5 ="Page @01 @Order @08 @Race - n (%) @\li720
Japanese"
6 ="Page @01 @Order @09 @Race - n (%) @\li720
American Indian or Alaska Native"
7 ="Page @01 @Order @10 @Race - n (%) @\li720
Native Hawaiian or Other Pacific Islander"
88 ="Page @01 @Order @11 @Race - n (%) @\li720
Other"
;
value
$univ
'WEIGHT_MEAN_STD' ="Page @02 @Order @01 @Weight @\li360
Mean(SD)"
'WEIGHT_MEDIAN' ="Page @02 @Order @02 @Weight @\li360
Median"
'WEIGHT_MIN_MAX' ="Page @02 @Order @03 @Weight @\li360
Min, Max";
;run;
/* COMPUTE THE BIG Ns AND USE TABULATE TO GET N AND PERCENT AND
UNIVARIATE STATISTICS
*/
proc sql;select resolve(catx(' ','%let',trt,'=%str({',trt,'} \line
{ N =',put(count(pat),2. -l),'});')) from utlinp.T000120_demog
group by
trt;quit;
ods output table(match_all=matall)
=TabOut;
proc tabulate
data=utlinp.T000120_demog;
format sex sex. rcd
rcd.;
var
weight;
class
trt;
class sex rcd/ preloadfmt
mlf;
tables trt sex rcd,trt*(n='Count'*f=7.
colpctn='Percent'*f=pctfmt9.);
tables trt*weight*(mean median std min max) /
printmiss;
run;quit;
/* CLEAN UP FOR PROC RDrgRT
*/
data
Fix;
retain Pge
0;
keep Pge Odr Trt Mjr Mnr Ans; /* THIS IS ALL WE NEED FOR PROC
REPRORT */
length Mjr Mnr Que Ans
$100;
set &matall
end=dne;
if dne then call symputx('MaxPge',put(Pge,
1.));
if _table_=1 then
do;
/* Count and Percent
*/
Que=coalescec(sex,rcd); /*
Question */
Ans=catx(' ',put(n,5.),compress('('!!put(pctn_100,4.)!!')')); /*
Answer */
Ans=translate
(Ans,'0','.');
Link
MjrMnr;
end;
else if _table_=2 then
do;
/* univariate statistics
*/
Que=put('WEIGHT_MEAN_STD',
$univ.);
Ans=cats(put(WEIGHT_MEAN,7.1),' (',put(WEIGHT_STD,
8.2),')');
Link
MjrMnr;
Que=put('WEIGHT_MEDIAN',
$univ.);
Ans=put(WEIGHT_MEDIAN,
7.1);
Link
MjrMnr;
Que=put('WEIGHT_MIN_MAX',
$univ.);
Ans=cats('(',put(WEIGHT_MIN,5.),', ',put(WEIGHT_MAX,5.),')'); /*
hidden dragon */
Link
MjrMnr;
end;
return;
MjrMnr:
if lengthn(Que)
>0;
Odr=input(scan(Que,2,'@'),2.)*100+input(scan(Que,4,'@'),
2.);
Pge=int(Odr/
100);
Mjr=left(scan(Que,
5,'@'));
Mnr=left(scan(Que,
6,'@'));
Output;
return;
run;
proc sort data=Fix out=FixSrt; by Odr Mjr Mnr Trt;
run;
proc transpose data=FixSrt out=FixXpo(drop=_name_); by Pge Odr Mjr
Mnr; var Ans; id Trt; run;
ods rtf file="/home/regusers/local/utl/rtf/&sysuserid._t000130.rtf"
style=amg_rtflan100;
ods
escapechar='^';
%macro twopge
(PgeMax);
%do Pge=1 %to
&PgeMax;
ods rtf prepage="^S={outputwidth=100% just=c font_size=11pt
font_face=arial} {\tc Title line} ^R/RTF'\line' {(Safety
Set)}";
proc rDrgrt data=FixXpo(where=(Pge=&Pge)) missing
split='#';
cols Pge Mjr Mnr Drg
Placebo;
define Pge / order noprint
order=data;
define Mjr / order noprint
order=data;
define Mnr / display "\ql\li180 {Category}" style
(column)={just=l
cellwidth=40%};
define Drg / display "\qc {&Drg}" style
(column)={just=c
cellwidth=29%};
define Placebo/ display "\qc {&Placebo}" style
(column)={just=c
cellwidth=29%};
compute before Mjr / style={just=l
pretext="\li180"}; ;
Lyn=Mjr;
line Lyn
$88.;
endcomp;
run;quit;
ods rtf text="^S={outputwidth=100% just=r font_size=9pt} Page
&Pge of
&PgeMax";
%if &pge=2 %then
%do;
ods rtf text="^S={outputwidth=100% just=l font_size=9pt}
{This footnote only appears on page
two}";
%end;
ods rtf text="^S={outputwidth=100% just=l font_size=8pt
font_style=italic} {Standard Next to Last
Footnote}";
ods rtf text="^S={outputwidth=100% just=l font_size=8pt
font_style=italic} {Standard Last
Footnote}";
%end;
%mend
twopge;
%twopge
(&MaxPge);
ods rtf close;
There are other templates on my site.
|