/*
/ Program : labncfb.sas
/ Version : 1.0
/ Author : Roland Rashleigh-Berry
/ Date : 05-Dec-2011
/ Purpose : To produce a table of lab (normalized) change from baseline
/ SubMacros : %unistats %popfmt %words %mkformat
/ Notes : This is an example macro that is meant to serve as an illustration
/ of how %unistats is called within a lab reporting macro and the
/ output dataset displayed using proc report. This macro produces a
/ LAB table of (optionally Normalized) Change From Baseline.
/
/ Normalization is done by adjusting the lab value based on its
/ reference range converted to a standardized reference range. You
/ must supply a dataset to the innorm= parameter for normalization
/ values to be calculated.
/
/ Baseline is defined to be the last value before the start of the
/ period specified by the analno= number supplied. Only the first
/ value within the same "visno" is kept. If you have different
/ requirements then you will need to change the macro logic which
/ should be clear in the code.
/
/ You will need to change this macro to get it to work on your
/ standard datasets and variable naming conventions. This macro is
/ only meant to be an illustration of how the %unistats macro can be
/ used with lab data.
/
/ There are two styles of reports: style=1 has the timepoints as the
/ across variable; style=2 has the treatment arm as the across
/ variable. If you set style= to null then both reports will be
/ produced.
/
/ If you set debug=yes then the intermediate datasets will be kept
/ which you can then use to debug the logic.
/
/ Usage : options nocenter nobyline;
/ title "Functional Group: #byval(labgrpx)";
/
/ %labncfb(inlab=lab,inpopu=popu,popu=TS,intrt=gentrt,analno=3,
/ descstats=N Min Mean SD Max,innorm=labref(where=(type="RR")),
/ ingrp=labref(where=(type="LG")));
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ inlab Input lab dataset
/ labval Lab value variable
/ labunit Lab unit variable
/ repeat=first By default, use the first non-missing value within visit.
/ Valid values are first, last and all.
/ rangelo Lab range low variable
/ rangehi Lab range high variable
/ innorm Input normalization dataset (should only have one entry per
/ lab unit). Normalization will only be done if this dataset
/ is specified.
/ normlo Normalized range low variable
/ normhi Normalized range high variable
/ intrt Input treatment dataset
/ inpopu Input population dataset
/ popu Population identifier string
/ analno Analysis number in intrt dataset to use
/ ingrp Dataset that contains the lab grouping variables LABGRP,
/ LABGRPX and LABNMOR
/ descstats=N Min Mean SD Max Descriptive statistics labels
/ style=1 Report style. 1=timepoint across, 2=treatment across,
/ null=show both
/ trtvar Treatment variable
/ trtdc Treatment decode variable
/ labparm Lab parameter variable
/ labparmdc Lab parameter decode variable
/ spacing=2 Spacing between descriptive statistics columns
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb 05Dec11 New (v1.0)
/===============================================================================
/ This is public domain software. No guarantee as to suitability or accuracy is
/ given or implied. User uses this code entirely at their own risk.
/=============================================================================*/
%put MACRO CALLED: labncfb v1.0;
%macro labncfb(inlab=,
labval=labstd,
labunit=labstdu,
repeat=first,
rangelo=llc,
rangehi=ulc,
innorm=,
normlo=lln,
normhi=uln,
intrt=,
inpopu=,
popu=,
analno=,
ingrp=,
descstats=N Min Mean SD Max,
style=1,
trtvar=atrsort,
trtdc=atrlbl,
labparm=labnm,
labparmdc=labnmx,
spacing=2,
debug=no
);
%local i key spac err errflag style1 style2 workds;
%let err=ERR%str(OR);
%let errflag=0;
%if not %length(&inlab) %then %do;
%let errflag=1;
%put &err: (labncfb) No lab dataset specified to inlab=;
%end;
%if not %length(&intrt) %then %do;
%let errflag=1;
%put &err: (labncfb) No gentrt dataset specified to intrt=;
%end;
%if not %length(&inpopu) %then %do;
%let errflag=1;
%put &err: (labncfb) No population dataset specified to inpopu=;
%end;
%if not %length(&popu) %then %do;
%let errflag=1;
%put &err: (labncfb) No population code specified to popu=;
%end;
%if not %length(&analno) %then %do;
%let errflag=1;
%put &err: (labncfb) No analno specified to analno=;
%end;
%if not %length(&descstats) %then %do;
%let errflag=1;
%put &err: (labncfb) No descriptive statistics labels specified to descstats=;
%end;
%if &errflag %then %goto exit;
%if not %length(&style) %then %do;
%let style1=Y;
%let style2=Y;
%end;
%else %if &style EQ 1 %then %let style1=Y;
%else %if &style EQ 2 %then %let style2=Y;
%let popu=%upcase(%sysfunc(dequote(&popu)));
%if not %length(&repeat) %then %let repeat=first;
%let repeat=%upcase(&repeat);
%if not %length(&debug) %then %let debug=no;
%let debug=%upcase(%substr(&debug,1,1));
*- create needed formats -;
proc format;
value tpfmt
1="_Baseline_"
2="_Last Value on Treatment_"
3="_Difference from Baseline_"
;
value tpind
1=" Baseline"
2=" Last Value on Treatment"
3=" Difference from Baseline"
;
run;
*- extract the data -;
proc sql noprint;
*- lab grouping variables -;
create table _labgrp as (
select labnm, labgrp, labgrpx, labnmor from &ingrp
);
*- patients in the population -;
create table _pop as (
select study, ptno from &inpopu(where=(popu="&popu" and popuny=1))
);
*- treatment arm -;
create table _tmtarm as (
select study, ptno, &trtvar, &trtdc
from &intrt(where=(analno=&analno))
);
*- patients and their treatment arm -;
create table _poptmt as (
select a.study, a.ptno, &trtvar, &trtdc
from _pop as a
left join _tmtarm as b
on a.study=b.study and a.ptno=b.ptno
);
*- on treatment start and stop -;
create table _ontrt as (
select a.study, a.ptno, atrstdt, atrsttm, atrspdt, atrsptm,
&trtvar, &trtdc
from &intrt(where=(analno=&analno)) as a
inner join _pop as b
on a.study=b.study and a.ptno=b.ptno
);
sasfile _ontrt load;
*- baseline (actually pre-treatment) lab values -;
create table _labbl as (
select a.study, a.ptno, &labparm, &labparmdc,
&labval, &labunit, &rangelo, &rangehi, labdt, labtm, visno, subevno
from &inlab as a
inner join _ontrt as b
on a.study=b.study and a.ptno=b.ptno and a.usable=1
and not missing(&labval)
and (labdtatrstdt or (labdt=atrstdt and labtm>atrsttm)))
) order by study, ptno, &labparm, visno, subevno, labdt;
quit;
*- Create two formats based on the coded and decoded -;
*- treatment arm values (one is undented, the other not). -;
%mkformat(_tmtarm,&trtvar,&trtdc,$atrind,indent=1);
%mkformat(_tmtarm,&trtvar,&trtdc,$atrfmt,indent=0);
*- style 2 needs a format with the population total shown so call popfmt -;
%if &style2 EQ Y %then %do;
%popfmt(dsin=_poptmt,trtvar=&trtvar,trtfmt=$atrfmt.,uniqueid=study ptno,underscore=yes);
%end;
*- keep the first, last or all within visno for baseline and on-treat -;
data _labbl2;
set _labbl;
by study ptno &labparm visno;
%if &repeat NE ALL %then %do;
if &repeat..visno;
%end;
run;
data _labont2;
set _labont;
by study ptno &labparm visno;
%if &repeat NE ALL %then %do;
if &repeat..visno;
%end;
run;
*- keep only the last for overall baseline and on-treat -;
data _labbl3;
set _labbl2;
by study ptno &labparm;
if last.&labparm;
run;
data _labont3;
set _labont2;
by study ptno &labparm;
if last.&labparm;
run;
*- bring the baseline and on-treatment data together -;
data _laball;
set _labbl3 _labont3;
by study ptno &labparm;
run;
*- Get rid of the cases where you only have one observation and -;
*- hence you do not have both a baseline and a last treatment value. -;
data _laball2;
set _laball;
by study ptno &labparm;
if first.&labparm and last.&labparm then delete;
run;
%let workds=_laball2;
*- calculate normalized values if dataset is specified -;
%if %length(&innorm) %then %do;
proc sql noprint;
create table _labnorm as (
select a.*, b.&normlo as _rnglo, b.&normhi as _rnghi, b.&labunit as _normunit,
b.&normlo+(&labval-a.&rangelo)*(b.&normhi-b.&normlo)/(a.&rangehi-a.&rangelo) as _normval
from _laball2 as a
left join &innorm as b
on a.&labparm=b.&labparm
) order by study, ptno, &labparm, visno, subevno, labdt;
quit;
%let labval=_normval;
%let labunit=_normunit;
%let workds=_labnorm;
%end;
*- generate change from baseline observations -;
data _laball3;
length tpx $ 4 labparmstr $ 80;
retain bl .;
set &workds;
by study ptno &labparm;
labparmstr=trim(&labparmdc)||" ["||trim(&labunit)||"]";
if first.&labparm then do;
bl=&labval;
tp=1;
tpx="BASE";
output;
end;
else do;
tp=2;
tpx="LAST";
output;
tp=3;
tpx="DIFF";
&labval=&labval-bl;
output;
end;
drop bl;
run;
*- merge in the treatment arm -;
proc sql noprint;
create table _laball4 as (
select a.*, &trtvar, &trtdc
from _laball3 as a
left join _tmtarm as b
on a.study=b.study and a.ptno=b.ptno
);
quit;
*- Call unistats to calculate descriptive statistics with -;
*- a transposed-by-statistic output dataset produced. -;
%unistats(dsin=_laball4,print=no,varlist=&labval,
trtvar=&trtvar,trtfmt=$atrfmt.,
byvars=tp &labparm labparmstr,
descstats=&descstats,dstranstat=_transtat);
*- add in the lab group info -;
proc sql noprint;
create table _transtat2 as (
select a.*, b.labgrp, b.labgrpx, b.labnmor
from _transtat as a
left join _labgrp as b
on a.&labparm=b.&labparm
) order by labgrp, labgrpx;
quit;
*- style=1 report with timepoint as the across variable -;
%if &style1 EQ Y %then %do;
proc report missing headline headskip nowd split="@" data=_transtat2 spacing=&spacing;
by labgrp labgrpx;
columns ( "__" labnmor labparmstr &labparm &trtvar tp,(&_statkeys_) _foolrep);
define labnmor / group noprint;
define labparmstr / group noprint;
define &labparm / group noprint;
define &trtvar / group order=internal "Parameter/" " Treatment"
format=$atrind. width=20 spacing=0;
define tp / across " " order=internal format=tpfmt. ;
%do i=1 %to %words(&_statkeys_);
%let key=%scan(&_statkeys_,&i,%str( ));
%let spac=;
%if &i EQ 1 %then %let spac=spacing=3;
define &key / display &spac;
%end;
define _foolrep / noprint;
compute before &labparm;
line @1 labparmstr $char60.;
endcompute;
break after &labparm / skip;
run;
%end;
*- style=2 report with treatment arm as the across variable -;
%if &style2 EQ Y %then %do;
proc report missing headline headskip nowd split="@" data=_transtat2 spacing=&spacing;
by labgrp labgrpx;
columns ( "__" labnmor labparmstr &labparm tp &trtvar,(&_statkeys_) _foolrep);
define labnmor / group noprint;
define labparmstr / group noprint;
define &labparm / group noprint;
define tp / group order=internal "Parameter/" " Visit/" " Difference from Baseline"
format=tpind. width=30 spacing=0 left;
define &trtvar / across " " order=internal format=&_popfmt_ ;
%do i=1 %to %words(&_statkeys_);
%let key=%scan(&_statkeys_,&i,%str( ));
%let spac=;
%if &i EQ 1 %then %let spac=spacing=3;
define &key / display &spac;
%end;
define _foolrep / noprint;
compute before &labparm;
line @1 labparmstr $char60.;
endcompute;
break after &labparm / skip;
run;
%end;
sasfile _ontrt close;
*- tidy up -;
%if &debug NE Y %then %do;
proc datasets nolist;
delete _ontrt _transtat _transtat2 _labbl _labbl2 _labbl3
_labont _labont2 _labont3
_laball _laball2 _laball3 _laball4 _labgrp
_pop _poptmt _tmtarm
%if %length(&innorm) %then %do;
_labnorm
%end;
;
quit;
%end;
%goto skip;
%exit: %put &err: (labncfb) Leaving macro due to problem(s) listed;
%skip:
%mend labncfb;