Base file: K:\TIDE Projects\FDA_Sentinel\07. Projects and Task Orders\01. Modular Programs\QRP\Distributed\QRP_3.3.3\inputfiles\macros\ms_attrition.sas
Compared file: K:\Sentinel\requests\mpl2\cder_mpl2p_wp001\packages\cder_mpl2p_wp001_nsdp_v01\WithRadar\inputfiles\macros\ms_attrition.sas
****************************************************************************************************
* PROGRAM OVERVIEW
****************************************************************************************************
*
* PROGRAM: ms_attrition.sas
*
* Created (mm/dd/yyyy): 12/19/2014
* Last modified: 08/15/2016
* Version: 1.8
*
*--------------------------------------------------------------------------------------------------
* PURPOSE:
* This macro will create the attrition table.
*
* Program inputs:
* -Many datasets created by the ms_cidanum macro
*
* Program outputs:
* - The MSOC.&RUNID._Attrition output table
*
* PARAMETERS:
*
* Programming Notes:
* -Unlike ms_cidanum that reports episode level data, this macro reports member level data
*
*
*--------------------------------------------------------------------------------------------------
* CONTACT INFO:
* Mini-Sentinel Coordinating Center
* info@mini-sentinel.org
*
*--------------------------------------------------------------------------------------------------
* CHANGE LOG:
*
* Version Date Initials Comment (reference external documentation when available)
* ------- -------- -------- ---------------------------------------------------------------
* 1.1 02/02/15 SL(DM) Corrected when age range span is shorter than the
* StartFollowup and EndDate span.
*
* 1.2 05/07/15 DM Added members lost to follow-up and still at risk in
* surveillance mode
*
* 1.3 06/26/15 DM Fixed days at risk post blackout when Analysis = PS or ADS
* Removed surveillance analysis related code
*
* 1.4 01/15/16 VC Remove CovariateWindow and replace with MinCovFrom MaxCovTo
* Removed surveillance analysis related code
*
* 1.5 04/21/16 DM Added HDPS window parameters in attrition algorithm
*
* 1.6 08/10/16 AP(SOC) Reordered AgeGroupMet rows (QCI-180)
*
* 1.7 08/15/16 DM Re-Added surveillance analysis related code
*
* 1.8 01/11/17 AP Fixed bug (QRP-296)
*
***************************************************************************************************;
%macro ms_attrition();
%put =====> MACRO CALLED: ms_attrition v1.8;
/******************/
/* Utility macros */
/******************/
%macro attrition(file=,ToExcl=);
proc sql noprint;
select count(distinct PatId) into: NumToExcl
from &file.
where not (&ToExcl.);
quit;
%put &NumToExcl.;
data &file.;
set &file.;
where not (&ToExcl.);
run;
%ISDATA(dataset=_Attrition);
%IF %EVAL(&NOBS.>=1) %THEN %DO;
data _Attrition;
set _Attrition end=eof;
output;
if eof then do;
Level=input("&level.",best.);
Num=input("&NumToExcl.",best.);
call symputx("level",&level.+1);
output;
end;
run;
%END;
%ELSE %DO;
data _Attrition;
format level best. num comma10.;
Level=input("&level.",best.);
Num=input("&NumToExcl.",best10.);
call symputx("level",&level.+1);
output;
run;
%END;
%put %eval(&level.-1) &NumToExcl.;
%mend;
/***************************************************************/
/* Prepare Raw Eligibility-type data for waterfall (all types) */
/***************************************************************/
*keep last look;
%if %eval(&type=3) %then %do;
data _look;
format startfollowup enddate date9.;
startfollowup=&ExpPeriodStartDt.;
enddate=&ExpPeriodEndDt.;
call symputx("startfollowup",startfollowup);
call symputx("enddate",enddate);
keep startfollowup enddate;
run;
%end;
%else %do;
data _look;
set &Monitoringfile. end=eof1;
if eof1 then output;
keep startfollowup enddate;
run;
%end;
proc sql noprint;
create table _enr(where=(InGap=1)) as
select *,
%MS_PeriodsOverlap(period1=enr.enr_start enr.enr_end,
period2=look.startfollowup look.enddate) as InGap
from indata.&EnrTable. as enr,
_look as look
order by enr.Patid,
enr.enr_start;
quit;
*Delete data from patients in DPs &PTSTOEXCLUDE. files;
%ms_delpatients(datafile=_enr,
ptsfile=&PTSTOEXCLUDE.,
Outfile=_enr);
%IF %EVAL(&chartvar.=0) %THEN %DO;
data _enr;
set _enr;
format chart $1.;
chart="N";
run;
%END;
*non-missing birth date/sex - mutliple demog entries (should be zero - defensive coding);
data _Demogsmissing;
set indata.&Demtable.;
where missing(BIRTH_DATE) or missing(Sex);
keep PatId;
run;
%ms_delpatients(datafile=_Demogsmissing,
ptsfile=&PTSTOEXCLUDE.,
Outfile=_Demogsmissing);
proc sort nodupkey data=_Demogsmissing;
by PatId;
run;
proc sort nodupkey data=indata.&Demtable.(keep=patId BIRTH_DATE Sex) out=_demogs
dupout=_MultiDemogs(keep=PatId);
by PatId;
run;
proc sort nodupkey data=_MultiDemogs;
by PatId;
run;
*shaving outside;
data _enr;
merge _enr(in=a)
_demogs(in=b)
_Demogsmissing(in=c)
_MultiDemogs(in=d);
by PatId;
if a and b and not c and not d;
if enr_start < &startfollowup. then enr_start = &startfollowup.;
if enr_end > &enddate. then enr_end = &enddate.;
if MEDCOV="Y" then meds=1;else meds=0;
if DRUGCOV="Y" then drugs=1;else drugs=0;
if chart="Y" then charts=1;else charts=0;
Both=0;
if drugs=1 and meds=1 then both=1;
run;
*One record per patient;
proc means data=_enr nway noprint;
var meds drugs both charts;
by Patid;
id BIRTH_DATE Sex startfollowup enddate; *unique at the PatId level;
output out=_enr(rename=_freq_=NumRec drop=_type_) sum=;
run;
*get Coverage for this group;
data _coverage;
set infolder.&cohortfile.;
where group="&ITGROUP.";
keep coverage CHARTRES;
run;
data _MedDrugCov(where=(female=1));
set _enr;
if _N_=1 then set _coverage;
all=1;
MedCovOnly=0;
DrugCovOnly=0;
DrugCovMixAlways=0;
if drugs=0 and meds=NumRec then MedCovOnly=1;
else if meds=0 and drugs=NumRec then DrugCovOnly=1;
else if both=0 then DrugCovMixAlways=1;
*trick for attrition table in the case where coverage = D or M;
if coverage='D' then do;
DrugCovOnly=0; *never delete those;
DrugCovMixAlways=0; *never delete those;
end;
if coverage='M' then do;
MedCovOnly=0; *never delete those;
DrugCovMixAlways=0; *never delete those;
end;
MeetChartReq=0;
if upcase(CHARTRES)='Y' then do;
if charts=NumRec then MeetChartReq=1; *all eligibility span have charts availability;
end;
else MeetChartReq=1;
if upcase(sex) = 'F' then female = 1;
run;
/** CALCULATE AGE and AGE STRATA SPECIFIED BY MSOC **/
%ms_agestrat(infile=_MedDrugCov,
outfile=_keep1,
startdt=birth_date,
enddt=startfollowup,
timestrat=&agestrat.);
%ms_agestrat(infile=_MedDrugCov,
outfile=_keep2,
startdt=birth_date,
enddt=enddate,
timestrat=&agestrat.);
*Patient eligible age must overlap at least startfollowup - enddate;
data _combine;
set _keep1
_keep2;
keep PatId;
if AgeGroup ne '' or %MS_PeriodsOverlap(period1=MinAgeDate MaxAgeDate,
period2=startfollowup enddate);
keep PatId AgeGroup;
run;
proc sort nodupkey data=_combine;
by PatId;
run;
data _keep;
merge _MedDrugCov(in=a)
_combine(in=b);
by PatId;
if a;
if b then AgeGroup="Yes";
else AgeGroup="";
run;
*Reset cumulative;
proc datasets library=work nowarn nolist;
delete _attrition;
quit;
%global level;
%let level=1;
%attrition(file=_keep,ToExcl=all=0);
%attrition(file=_keep,ToExcl=MedCovOnly=1);
%attrition(file=_keep,ToExcl=DrugCovOnly=1);
%attrition(file=_keep,ToExcl=DrugCovMixAlways=1);
%attrition(file=_keep,ToExcl=AgeGroup='');
%attrition(file=_keep,ToExcl=MeetChartReq=0);
/*************************************************************************/
/* Exclusion - Members must have at least one QUERYGROUP */
/* claim within the query period; */
/*************************************************************************/
proc sql noprint;
create table _DistinctPatIds as
select distinct Patid,
1 as AtLeastOneClaim
from _groupindex
where &startfollowup. <= Adate <= &enddate.
order by PatId;
quit;
data _keep;
merge _keep(in=a)
_DistinctPatIds;
by Patid;
if a;
if _N_=1 then set _Cohortdef;
run;
%attrition(file=_keep,ToExcl=AtLeastOneClaim=.);
/***************************************************************/
/* Prepare Episode/Claim-type data for waterfall (all types) */
/* Copied/Modified from CIDAnum */
/***************************************************************/
%if %eval(&type.=1) %then %do;
data _EventsInFupWash _WashEventsInFupWash;
set _POV1(obs=0 keep=PatId Adate rename=Adate=IndexDt);
run;
%end;
%if %eval(&type.<=2) %then %do;
*Note that all dummies are created from the "keep" perspective;
Data _MasterEpisode;
Merge _POV1(in=a rename=Adate=IndexDt)
_POV2(in=b rename=Adate=IndexDt drop=InGap)
_POV3(in=c rename=Adate=IndexDt keep=patid Adate)
_POV4(in=d rename=EpisodeStartDt=IndexDt)
_IOT(in=e rename=EpisodeStartDt=IndexDt)
_EventsInFupWash(in=f keep=PatId IndexDt)
_WashEventsInFupWash(in=g keep=PatId IndexDt);
by Patid IndexDt;
if a and d; *b and d later;
if a then pov1=1;
if b then pov2=1;
if c then pov3=1;
if d then pov4=1;
if e then iot=1;
*indexDate must overlap greater monitoring period;
if &startfollowup. <= IndexDt <= &enddate.;
*Trucate episode at OIT;
if e and TrunkDate then EpisodeEndDt=TrunkDate;
*ExpExtPer may have made the episode to go beyond elig.;
if EpisodeEndDt > enr_end then EpisodeEndDt = enr_end;
if blackoutper =. then blackoutper=0;
*Minimum look back enrollment duration;
if Enr_Start <= IndexDt-max(0,&enrdays.) then MinEnrdays=1;
if Enr_Start <= IndexDt-max(0,Washper) then MinWashper=1;
if Enr_Start <= IndexDt-max(0,T2FupWashPer) then MinT2FupWashPer=1;
if Enr_Start <= IndexDt-max(0,&MinCovFrom.) then MinCovariatewindowMet=1;
if Enr_Start <= IndexDt-max(0,&PRIORDAYS_bf.,&L1HDPSFROM.) then MinINCLINDEX1=1;
if Enr_End >= IndexDt+max(0,&PRIORDAYS_af.,&MaxCovTo.,&L1HDPSTO.) then MinINCLINDEX2=1;
if MinINCLINDEX1 and MinINCLINDEX2 then MinINCLINDEX=1;
*Minimum Episode Duration;
if EpisodeEndDt-IndexDt + 1 >= minepisdur then MinEpiDurMet=1;
*Patient must be enrolled throughout the at-risk period when minepisdur;
if minepisdur > 0 then do;
if Enr_Start <= indexDt and Indexdt + minepisdur - 1 <= Enr_end then FUEnrolMet=1;
end;
else FUEnrolMet=1;
*patients must have at least one day at risk post blackout (at least
one day to observe and event);
if blackoutper<=0 then PostBlackoutper=1;
else if "&Analysis."="ps" or "&Analysis."="ads" then do;
if blackoutper and min(EpisodeEndDt,IndexDtLookEndDt) - (IndexDt + blackoutper) >= 0 then PostBlackoutper=1;
end;
else if blackoutper and EpisodeEndDt - (IndexDt + blackoutper) >= 0 then PostBlackoutper=1;
if not f and not g then NoEventsInFUWAsh=1;
if not pov2 then NoPov2Claims=1;
MinDaySupMet=.;
NoExclClaim=.;
SuffExclElig=.;
HadInclClaim=.;
label IndexDt="IndexDt";
run;
%end;
%else %if %eval(&type.=3) %then %do;
*Note that all dummies are created from the "keep" perspective;
Data _MasterEpisode;
Merge _POV1(in=a rename=Adate=IndexDt)
_POV2(in=b rename=Adate=IndexDt drop=InGap)
_POV3(in=c rename=Adate=IndexDt keep=patid Adate)
_CtrlEventsInWin(in=d)
_RiskEventsInWin(in=e)
_Events(in=f)
_EventsBothWin(in=e);
by Patid IndexDt;
if a; *b later;
if a then pov1=1;
if b then pov2=1;
if c then pov3=1;
*indexDate must overlap greater monitoring period;
if &startfollowup. <= IndexDt <= &enddate.;
*Minimum look back enrollment duration;
if Enr_Start <= MinEligtHOIDt then MinLookBackLength=1;
if Enr_Start <= IndexDt-max(0,Washper) then MinWashper=1;
if Enr_Start <= IndexDt-max(0,&enrdays.) then MinEnrdays=1;
*Minimum enrollment duration after exposure;
if MaxEligtEnrAftDt <= Enr_End then MinEnrAft=1;
if not pov2 then NoPov2Claims=1;
if d or e then AtLeastOneEvent=1;
if f then EventInOnlyOneWindow=1;
NoExclClaim=.;
SuffExclElig=.;
HadInclClaim=.;
format temp date9.;
temp=IndexDt-max(0,MinWindowDt - LookBackLength);
temp2=max(0,MinWindowDt - LookBackLength);;
label IndexDt="IndexDt";
run;
*Events;
proc freq;tables MinLookBackLength MinWashper MinEnrdays;run;
%end;
*Calculate age at index date;
proc sort nodupkey data=_enr out=_BDates(keep=PatId birth_date);
by PatId;
run;
data _MasterEpisode;
merge _MasterEpisode (in=a)
_BDates;
by PatId;
if a;
run;
%ms_agestrat(infile=_MasterEpisode,
outfile=_MasterEpisode,
startdt=birth_date,
enddt=IndexDt,
timestrat=&agestrat.);
data _MasterEpisode;
set _MasterEpisode;
if agegroup ne "" then AgeGroupMet=1;
ExclCritMet=1;
HadInclClaim=.;
run;
%put &type. &EXCL. &INCL.;
%macro wrapper;
%if %eval(&type.=2) %then %do;
* NOTE: there is a need to recompute the TotRxSup for the mindaysupp criterion
before applying all other criterion as it is done in CIDANUM;
*Summarize utilization that are overlapping selected episodes;
%ms_shaveoutside(reffile=_MasterEpisode,
refstart=IndexDt,
refend=EpisodeEndDt,
KeepPartBf=Y,
ToShaveFile=_GroupIndex,
ToShaveStart=Adate,
ToShaveEnd=ExpireDt,
outfile=_GroupUtilFile);
*Summarize RxSup during episode (only to apply mindaysupp criterion);
Proc means data=_GroupUtilFile nway noprint;
var RxSup RxAmt;
class PatId IndexDt;
output out=_GroupUtilFile(keep=PatId IndexDt TotRxSup) sum(RxSup)=TotRxSup;
run;
data _MasterEpisode;
merge _MasterEpisode(in=a)
_GroupUtilFile;
by PatId IndexDt;
if a;
if TotRxSup >= mindaysupp then MinDaySupMet=1;
if MinDaySupMet;
run;
%end;
%IF %EVAL(&EXCL.=1) %THEN %DO;
data _MasterEpisode;
merge _MasterEpisode(in=a)
_POV3Excl1(in=b keep=PatId Adate rename=Adate=IndexDt) /*had an exclusion claim*/
_POV3Excl2(in=c keep=PatId Adate rename=Adate=IndexDt); /*had insufficient eligibility*/
by PatId IndexDt;
if a;
NoExclClaim=0;
SuffExclElig=0;
if not b then NoExclClaim=1;
if not c then SuffExclElig=1;
ExclCritMet=.;
if NoExclClaim=1 and SuffExclElig=1 then ExclCritMet=1;
run;
%end;
%IF %EVAL(&INCL.=1) %THEN %DO;
data _MasterEpisode;
merge _MasterEpisode(in=a)
_POV3Incl(in=b keep=PatId Adate rename=Adate=IndexDt); /*had an inclusion claim*/
by PatId IndexDt;
if a;
if b then HadInclClaim=1;
run;
%end;
%mend wrapper;
%wrapper;
%if %eval(&type.<=2) %then %do;
*One record per patient per indexdt - summarizing the "To Keep dummies (an absence of which will trigger a deletion)";
proc means data=_MasterEpisode noprint missing nway;
var MinEnrdays MinWashper MinT2FupWashPer MinCovariatewindowMet MinINCLINDEX MinEpiDurMet FUEnrolMet
PostBlackoutper NoPov2Claims AgeGroupMet MinDaySupMet NoEventsInFUWAsh NoExclClaim SuffExclElig HadInclClaim ExclCritMet;
class PatId IndexDt;
output out=_MasterPatient(drop=_:) max(MinEnrdays MinWashper MinT2FupWashPer MinCovariatewindowMet MinINCLINDEX MinEpiDurMet FUEnrolMet
PostBlackoutper NoPov2Claims AgeGroupMet MinDaySupMet NoEventsInFUWAsh HadInclClaim ExclCritMet)=
min(NoExclClaim SuffExclElig)=;
run;
%end;
%else %do;
*Same day deletion criterion;
data _MasterEpisode;
merge _MasterEpisode(in=a)
_samedaydeleted(in=b keep=PatID IndexDt);
by PatID IndexDt;
if a;
SameDayDeletedMet=1;
if b then SameDayDeletedMet=.;
run;
*One record per patient per indexdt - summarizing the "To Keep dummies (an absence of which will trigger a deletion)";
proc means data=_MasterEpisode noprint missing nway;
var NoPov2Claims AgeGroupMet
MinLookBackLength MinEnrAft MinWashper MinEnrdays
HadInclClaim ExclCritMet
AtLeastOneEvent EventInOnlyOneWindow SameDayDeletedMet
NoExclClaim SuffExclElig ;
class PatId IndexDt;
output out=_MasterPatient(drop=_:) max(MinEnrdays MinWashper MinLookBackLength MinEnrAft NoPov2Claims AgeGroupMet
HadInclClaim ExclCritMet AtLeastOneEvent EventInOnlyOneWindow SameDayDeletedMet)=
min(NoExclClaim SuffExclElig)=;
run;
%end;
*add dummies;
data _keep;
merge _keep(in=a)
_MasterPatient;
by PatId;
if a;
run;
%macro RunClaimToExcl;
%attrition(file=_keep,ToExcl=AgeGroupMet=.);
%attrition(file=_keep,ToExcl=NoPov2Claims=.);
%if %eval(&type<=2) %then %do;
%if %eval(&type=2) %then %attrition(file=_keep,ToExcl=MinDaySupMet=.);
%if %eval(&type=2 & &itt.=0) %then %attrition(file=_keep,ToExcl=MinEpiDurMet=.);
%if %eval(&type=2 & &itt.=1) %then %attrition(file=_keep,ToExcl=MinEpiDurMet=-1); *-1 to delete none, but still increment level;
%if %eval(&type=2) %then %attrition(file=_keep,ToExcl=PostBlackoutper=.);
%attrition(file=_keep,ToExcl=MinEnrdays=.);
%attrition(file=_keep,ToExcl=MinWashper=.);
%if %eval(&type=2) %then %attrition(file=_keep,ToExcl=MinT2FupWashPer=.);
%if %eval(&type=2) %then %attrition(file=_keep,ToExcl=NoEventsInFUWAsh=.);
%put &EXCL. &INCL.;
%IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and SuffExclElig=0);
%IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1); *MinWashper=-1 to delete none, but still increment level;
%IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and NoExclClaim=0);
%IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1);
%IF %EVAL(&INCL.=1) %THEN %attrition(file=_keep,ToExcl=HadInclClaim=.);
%IF %EVAL(&INCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1);
%end;
%else %do;
%attrition(file=_keep,ToExcl=MinWashper=.);
%attrition(file=_keep,ToExcl=MinEnrdays=.);
%put &EXCL. &INCL.;
%IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and SuffExclElig=0);
%IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1); *MinWashper=-1 to delete none, but still increment level;
%IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and NoExclClaim=0);
%IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1);
%IF %EVAL(&INCL.=1) %THEN %attrition(file=_keep,ToExcl=HadInclClaim=.);
%IF %EVAL(&INCL.=0) %THEN %attrition(file=_keep,ToExcl=MinWashper=-1);
*When cohortDef is 01, consider the first claim only for a member to assess the remaining criteria;
data _keep;
set _keep;
by PatId IndexDt;
if T3CohortDef ='01' then do;
if first.PatId;
end;
run;
%attrition(file=_keep,ToExcl=SameDayDeletedMet=.);
*Analytic cohort exclusion criteria;
%attrition(file=_keep,ToExcl=MinLookBackLength=.);
%attrition(file=_keep,ToExcl=MinEnrAft=.);
%attrition(file=_keep,ToExcl=AtLeastOneEvent=.);
%attrition(file=_keep,ToExcl=EventInOnlyOneWindow=.);
%end;
%mend RunClaimToExcl;
%RunClaimToExcl;
%macro InformationCrit;
%if %sysfunc(fileexist(_itdrugsexcl))=0 %then %do;
*Create dummies for Stockpiled excluded records;
data _StockInformation;
merge _keep(in=a)
_itdrugsexcl;
By PatId;
if a;
exclGroup=0;
exclEvent=0;
exclCond=0;
if upcase(T2_INDEX)="DEF" then exclGroup=1;
if upcase(T2_FUP)="DEF" then exclEvent=1;
if upcase(T2_INDEX) in('INC','EXC') then exclCond=1;
run;
*One record per patent;
proc means data=_StockInformation noprint;
var exclGroup exclEvent exclCond;
by PatId;
output out=_StockInformation(drop=_:) max=;
run;
data _keep_;
set _StockInformation;
run;
%attrition(file=_keep_,ToExcl=exclGroup=0);
data _keep_;
set _StockInformation;
run;
%attrition(file=_keep_,ToExcl=exclEvent=0);
data _keep_;
set _StockInformation;
run;
%attrition(file=_keep_,ToExcl=exclCond=0);
%end;
%else %do;
data _Attrition;
set _Attrition end=eof;
output;
if eof then do;
Num=.;
do i=1 to 3;
Level=input("&level.",best.);output;
call symputx("level",&level.+1);
end;
end;
drop i;
run;
%end;
%mend;
%InformationCrit;
/*********************************************************/
/* Calculate Exclusions variables and ADD extra variable */
/*********************************************************/
%put &type.;
data _Attrition;
retain group level descr num Excluded;
format group $30. descr $500.;
set _Attrition;
group="&ITGROUP.";
format ;
LastRemain=lag(num);
if (&type.=1 and 1 <=_N_<=14) or (&type.=2 and 1 <=_N_<= 19) or (&type.=3 and 1 <=_N_<= 19) then do;
Excluded=LastRemain-num;
end;
else if (&type.=1 and 15 <=_N_<=17) or (&type.=2 and 20 <=_N_<=22) or (&type.=3 and 20 <=_N_<=22) then do;
Excluded=num;
Num =.;
end;
if &type.=1 then do;
if level=1 then descr="Initial Member Count - Members with a non-missing birth date/sex at any enrollment episode overlapping the query period";
if level=2 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=N and MedCov=Y during the query period";
if level=3 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N during the query period";
if level=4 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N and DrugCov=N and MedCov=Y during the query period";
if level=5 then descr="Exclusion - Members must satisfy the age range condition within the query period";
if level=6 then descr="Exclusion - Members must meet chart availability criterion within the query period";
if level=7 then descr="Exclusion - Members must have at least one GROUP claim within the query period";
if level=8 then descr="Exclusion - Members must have at least one GROUP episode beginning within the age range condition";
if level=9 then descr="Exclusion - Members must have at least one GROUP episode that meets Query incidence criterion [no GROUP claim in the prior Query 'WashPer' days]";
if level=10 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the 'EnrDays' parameter";
if level=11 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the Query 'WashPer' parameter";
if level=12 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion enrollment requirement";
if level=13 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion conditions";
if level=14 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Inclusion conditions";
if level=15 then descr="Information - Members with at least one GROUP claim with supply and/or amount outside specified ranges";
if level=16 then descr="Information - Members with at least one EVENT claim with supply and/or amount outside specified ranges";
if level=17 then descr="Information - Members with at least one INCL/EXCL claim with supply and/or amount outside specified ranges";
end;
if &type.=2 then do;
if level=1 then descr="Initial Member Count - Members with a non-missing birth date/sex at any enrollment episode overlapping the query period";
if level=2 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=N and MedCov=Y during the query period";
if level=3 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N during the query period";
if level=4 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N and DrugCov=N and MedCov=Y during the query period";
if level=5 then descr="Exclusion - Members must satisfy the age range condition within the query period";
if level=6 then descr="Exclusion - Members must meet chart availability criterion within the query period";
if level=7 then descr="Exclusion - Members must have at least one GROUP claim within the query period";
if level=8 then descr="Exclusion - Members must have at least one GROUP episode beginning within the age range condition";
if level=9 then descr="Exclusion - Members must have at least one GROUP episode that meets Query incidence criterion [no GROUP claim in the prior Query 'WashPer' days]";
if level=10 then descr="Exclusion - members must have at least one GROUP episode with at least minimum days supplied (based on MinDaysSupp criterion)";
if level=11 then descr="Exclusion - members must have at least one GROUP episode with at least minimum days duration (based on MinEpisDur criterion)";
if level=12 then descr="Exclusion - Members must have at least one GROUP episode with more than blackout days duration";
if level=13 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the 'EnrDays' parameter";
if level=14 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the Query 'WashPer' parameter";
if level=15 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the Event 'WashPer' parameter";
if level=16 then descr="Exclusion - Members must have at least one GROUP episode that meets Event incidence criterion [no QUERYEVENTGROUP claim in the prior Event 'WashPer' days]";
if level=17 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion enrollment requirement";
if level=18 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion conditions";
if level=19 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Inclusion conditions";
if level=20 then descr="Information - Members with at least one GROUP claim with supply and/or amount outside specified ranges";
if level=21 then descr="Information - Members with at least one EVENT claim with supply and/or amount outside specified ranges";
if level=22 then descr="Information - Members with at least one INCL/EXCL claim with supply and/or amount outside specified ranges";
end;
if &type.=3 then do;
if level=1 then descr="Initial Member Count - Members with a non-missing birth date/sex at any enrollment episode overlapping the query period";
if level=2 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=N and MedCov=Y during the query period";
if level=3 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N during the query period";
if level=4 then descr="Exclusion – Members must be excluded if they only have episodes with DrugCov=Y and MedCov=N and DrugCov=N and MedCov=Y during the query period";
if level=5 then descr="Exclusion - Members must satisfy the age range condition within the query period";
if level=6 then descr="Exclusion - Members must meet chart availability criterion within the query period";
if level=7 then descr="Exclusion - Members must have at least one GROUP claim within the query period";
if level=8 then descr="Exclusion - Members must have at least one GROUP episode beginning within the age range condition";
if level=9 then descr="Exclusion - Members must have at least one GROUP episode that meets Query incidence criterion [no GROUP claim in the prior Query 'WashPer' days]";
if level=10 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the Query 'WashPer' parameter";
if level=11 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the 'EnrDays' parameter";
if level=12 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion enrollment requirement";
if level=12 then descr=" ";
if level=13 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Exclusion conditions";
if level=14 then descr="Exclusion - Members must have at least one GROUP episode satisfying the Inclusion conditions";
if level=15 then descr="Exclusion - Members must have only one exposure code on day 0";
if level=16 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the (calculated) HOI assessment window";
if level=17 then descr="Exclusion - Members must have at least one GROUP episode satisfying the enrollment criterion specified by the 'EnrDaysAft' parameter";
if level=18 then descr="Exclusion - Members must have at least one Event in one of the two HOI assessment windows";
if level=19 then descr="Exclusion - Members must have an event satisfying the incidence criterion";
if level=20 then descr="Information - Members with at least one GROUP claim with supply and/or amount outside specified ranges";
if level=21 then descr="Information - Members with at least one EVENT claim with supply and/or amount outside specified ranges";
if level=22 then descr="Information - Members with at least one INCL/EXCL claim with supply and/or amount outside specified ranges";
end;
rename num=remaining;
drop LastRemain;
run;
*Surveillance mode: report members lost to follow-up and still at risk;
%if &type.=2 %then %do;
%let MEMBERS_LOSTTOFOLLOWUP=.;
%let MEMBERS_STILLATRISK=.;
%VAREXIST(_ptsmasterlist,LastLookFollowed);
%if %eval(&VAREXIST.=1) %then %do;
proc means data=_ptsmasterlist;
class PatId;
var LastLookFollowed;
output out=_LastLookFollowed (drop=_:) max=;
run;
proc sql noprint;
select count(distinct PatId) into : MEMBERS_LOSTTOFOLLOWUP
from _ptsmasterlist
where 0 < LastLookFollowed <= &PERIODIDSTART.;
proc sql noprint;
select count(distinct PatId) into : MEMBERS_STILLATRISK
from _LastLookFollowed
where LastLookFollowed =0 and not missing(PatId);
quit;
%end;
%put &MEMBERS_LOSTTOFOLLOWUP. &MEMBERS_STILLATRISK.;
data _Attrition;
set _Attrition end=eof;
output;
if eof then do;
group="&ITGROUP.";
descr="Information - Members lost to follow-up up to this period.";
Excluded=&MEMBERS_LOSTTOFOLLOWUP.;
Remaining=.;
level= 23;
output;
descr="Information - Members still at risk at the end of this period.";
Excluded=.;
Remaining=&MEMBERS_STILLATRISK.;
level= 24;
output;
end;
run;
%end;
*Store patient episodes to MSOC;
%IF %EVAL(&group.=1) %THEN %DO;
data MSOC.&RUNID._Attrition;
retain group level descr remaining Excluded;
set _Attrition;
run;
%END;
%ELSE %DO;
proc append base=MSOC.&RUNID._Attrition
data=_Attrition force;
run;
%END;
%put NOTE: ******** END OF MACRO: ms_attrition v1.8 ********;
%mend ms_attrition;