/*
/ Program : locf.sas
/ Version : 1.0
/ Author : Roland Rashleigh-Berry
/ Date : 13-Feb-2007
/ Purpose : To perform "Last Observation Carried Forward" processing.
/ SubMacros : %words %vartype %commas
/
/ Notes : READ THE FOLLOWING NOTES CAREFULLY.
/
/ Note that "last observation carried forward" does NOT mean last
/ dataset observations carried forward. It is the last non-missing
/ observation (in the sense of "as observed") for possibly multiple
/ measures being independently carried forward and assigned to
/ visits where no measure was available.
/
/ The only data you should feed in to this macro is data eligible to
/ be carried forward. If, for example, baseline data be not eligible
/ for carrying forward then do not supply baseline data to this
/ macro. If you only want data carrying forward for a subset of the
/ measures taken then only supply that subset of data to this macro.
/
/ Note that if you only want to keep carried forward data for one or
/ two specific timepoints then subset the output dataset AFTERWARDS.
/ Data you want to keep and data eligible to be carried forward are
/ two different things. The data you feed into this macro is the
/ data ELIGIBLE for carrying forward. This is typically all
/ on-treatment measures (not including baseline) up to and including
/ the end-point.
/
/ This macro will assume that a numeric value is missing if it
/ equals missing values and a character value is missing if it
/ equals a space. If, for example, a "0" signified a missing value
/ in the input data then you should reset it to what this macro
/ recognises as a missing value before feeding it in.
/
/ The output dataset you get out of this macro is pure LOCF data. It
/ is up to you what you do with it. If you merge it in with your
/ original data then it is up to you to signify what is LOCF data
/ and what is original data.
/ Usage :
/
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ dsin= Input dataset name containing LOCF eligible data only
/ dsout= Output dataset containing pure generated LOCF data
/ vars= Variable or variables for which you want to carry forward
/ data for. Multiple variable names to be separated by spaces.
/ bygroup= Variable or variables signifying the grouping for values to
/ be carried forward. It could be something like "subject" but
/ it could be an extra grouping variable as well like "subject
/ parameter".
/ visitvars= Visit variables. This will most often be a simple variable
/ like "visit". Multiple variables must be separated by spaces.
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb 01Apr04 Allow for multiple values per visit window and take the
/ latest.
/ rrb 13Feb07 "macro called" message added
/===============================================================================
/ 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: locf v1.0;
%macro locf(dsin=,
dsout=,
vars=,
bygroup=,
visitvars=
);
%local error i var vartype;
%let error=0;
/*-------------------------------------------------*
Check we have enough parameters set to continue
*-------------------------------------------------*/
%if not %length(&dsin) %then %do;
%let error=1;
%put ERROR: (locf) No input dataset name supplied to dsin= parameter;
%end;
%if not %length(&vars) %then %do;
%let error=1;
%put ERROR: (locf) No variables to carry forward supplied to vars= parameter;
%end;
%if not %length(&bygroup) %then %do;
%let error=1;
%put ERROR: (locf) No grouping variables supplied to bygroup= parameter;
%end;
%if not %length(&visitvars) %then %do;
%let error=1;
%put ERROR: (locf) No visit variables supplied to visitvars= parameter;
%end;
%if &error %then %goto error;
%if not %length(dsout) %then %let dsout=%scan(&dsin,1,%str(%());
/*-------------------------------------------------*
Start processing the data
*-------------------------------------------------*/
proc sort data=&dsin out=_locfin(keep=&bygroup &visitvars &vars);
by &bygroup &visitvars;
run;
/*--------------------------------------------------------*
Create an empty grid of bygroup/visitvars combinations
*--------------------------------------------------------*/
proc sort nodupkey data=_locfin(keep=&bygroup) out=_locfby;
by &bygroup;
run;
proc sort nodupkey data=_locfin(keep=&visitvars) out=_locfvis;
by &visitvars;
run;
proc sql noprint;
create table _locfgrid as
select * from _locfby, _locfvis
order by %commas(&bygroup &visitvars);
quit;
proc datasets nolist;
delete _locfby _locfvis;
run;
quit;
/*-------------------------------------------------*
Build up the output dataset one var at a time
*-------------------------------------------------*/
%do i=1 %to %words(&vars);
%let var=%scan(&vars,&i,%str( ));
%let vartype=%vartype(_locfin,&var);
%*- extract information specific to this variable -;
data _locfval;
set _locfin(keep=&bygroup &visitvars &var
where=(&var NE
%if "&vartype" EQ "C" %then %do;
' '
%end;
%else %do;
.
%end;
));
run;
data _locfval;
set _locfval;
by &bygroup &visitvars;
if last.%scan(&bygroup &visitvars,-1,%str( ));
run;
data _locfgrid;
retain _seq 0;
merge _locfgrid _locfval(in=_val drop=&var);
by &bygroup &visitvars;
if first.%scan(&bygroup,-1,%str( )) then _seq=0;
if _val then _seq=_seq+1;
run;
data _locfval;
retain _seq 0;
set _locfval;
by &bygroup;
if first.%scan(&bygroup,-1,%str( )) then _seq=0;
_seq=_seq+1;
run;
data _locfgrid;
merge _locfval _locfgrid;
by &bygroup _seq;
drop _seq;
run;
%end;
/*-------------------------------------------------*
Tidy up and exit
*-------------------------------------------------*/
data &dsout;
set _locfgrid;
run;
proc datasets nolist;
delete _locfval _locfgrid _locfin;
run;
quit;
%goto skip;
%error:
%put ERROR: (locf) Leaving locf macro due to error(s) listed;
%skip:
%mend;