**************************************************************************************************** * PROGRAM OVERVIEW **************************************************************************************************** * * PROGRAM: ms_attrition.sas * * Created (mm/dd/yyyy): 12/19/2014 * Last modified: 03/22/2018 * Version: 2.7 * *-------------------------------------------------------------------------------------------------- * 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) * * 1.9 05/15/17 AP Fixed 2 bugs (QRP-369, QRP-376) * * 2.0 07/07/17 AP Added attrition table for Type 5 * Harmonized levels across all analysis types * * 2.1 10/03/17 AP Added functionality to keep patients censored during blackout (QRP-319) * * 2.2 11/15/17 AP Removed functionality to keep patients censored during blackout (QRP-466) * * 2.3 11/20/17 AP Type 3 patients not excluded from CIDA due to HOI incidence criteria (QRP-456) * Changed language on level 10 (QRP-468) * * 2.4 12/07/17 AP Type 2 adjust enrollment for death date (QRP-470) * * 2.5 12/21/17 AP Removed comorbidity window enrollment requirement (QRP-435) * * 2.6 02/01/18 AP Removed post-index enrollment requirements (QRP-496) * * 2.7 03/22/18 RR Added analysis = ms (QRP-539) ***************************************************************************************************; %macro ms_attrition(); %put =====> MACRO CALLED: ms_attrition v2.7; /******************/ /* 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 Race Hispanic) 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 Race Hispanic 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; 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; *demographic criteria; MeetDemogs=1; if upcase(sex) not in (&DemogSex.) or upcase(Race) not in (&DemogRace.) or upcase(Hispanic) not in (&DemogHispanic.) then MeetDemogs=0; 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; /*1*/ %attrition(file=_keep,ToExcl=all=0); /*2*/ %attrition(file=_keep,ToExcl=MedCovOnly=1); /*3*/ %attrition(file=_keep,ToExcl=DrugCovOnly=1); /*4*/ %attrition(file=_keep,ToExcl=DrugCovMixAlways=1); /*5*/ %attrition(file=_keep,ToExcl=AgeGroup=''); /*6*/ %attrition(file=_keep,ToExcl=MeetChartReq=0); /*7*/ %attrition(file=_keep,ToExcl=MeetDemogs=0); /*************************************************************************/ /* Exclusion - Members must have at least one QUERYGROUP */ /* claim within the query period; */ /*************************************************************************/ /*Re-shave _groupindex for enrollment now truncated at death*/ %if &censor_dth.=1 %then %do; %ms_shaveoutside(reffile=ENR_&GROUP., refstart=Enr_Start, refend=Enr_End, KeepPartBf=N, ToShaveFile=_groupindex(drop=enr_start enr_end), ToShaveStart=adate, ToShaveEnd=ExpireDt, outfile=_groupindex); %end; 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; run; /*8*/ %attrition(file=_keep,ToExcl=AtLeastOneClaim=.); *Calculate age at index date; proc sort nodupkey data=_enr out=_BDates(keep=PatId birth_date); by PatId; run; proc sort data=_groupindex; by patid; run; data _groupindex_age; merge _groupindex (in=a) _BDates; by PatId; if a; if &startfollowup. <= Adate <= &enddate.; run; %ms_agestrat(infile=_groupindex_age, outfile=_groupindex_age, startdt=birth_date, enddt=adate, timestrat=&agestrat.); proc sql noprint; create table _DistinctPatIds_age as select distinct Patid, 1 as AgeGroupMet from _groupindex_age where agegroup ne "" order by PatId; quit; data _keep; merge _keep(in=a) _DistinctPatIds_age; by Patid; if a; if _N_=1 then set _Cohortdef; run; /*9*/ %attrition(file=_keep,ToExcl=AgeGroupMet=.); /***************************************************************/ /* Prepare Episode/Claim-type data for waterfall (all types) */ /* Copied/Modified from CIDAnum */ /***************************************************************/ %if %eval(&type.=1 | &type.=5) %then %do; data _EventsInFupWash _WashEventsInFupWash _EventsInBlackout; set _POV1(obs=0 keep=PatId Adate rename=Adate=IndexDt); run; %end; %if %eval(&type. ne 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) _POV4(in=d rename=EpisodeStartDt=IndexDt) _IOT(in=e rename=EpisodeStartDt=IndexDt) _EventsInFupWash(in=f keep=PatId IndexDt) _WashEventsInFupWash(in=g keep=PatId IndexDt) _EventsInBlackout(in=h 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.; *Adjust EpisodeEndDt if EpisodeEndDt > enddate; %if &censor_qryend = 1 %then %do; if EpisodeEndDt > &enddate. then EpisodeEndDt = &enddate.; %end; /*Adjust EpisodeEndDt if EpisodeEndDt > DeathDt*/ if EnrEpisodeCensoredAtDeath = 1 then do; if EpisodeEndDt > DeathDt then EpisodeEnddt = deathdt; if DeathDt <= Enr_End then Enr_End=DeathDt; if Enr_end < Enr_Start then delete; end; *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., Washper) then MinEnrdays1=1; if Enr_Start <= IndexDt-max(0,T2FupWashPer) then MinHoiEnrCrit=1; if Enr_Start <= IndexDt-max(0,&MinCovFrom.,&L1HDPSFROM.,&MinMFUFrom.) then MinINCLINDEX1=1; if Enr_End >= IndexDt+max(0,&EnrDaysAft.) then MinEnrAft=1; if MinINCLINDEX1 and MinEnrdays1 then MinEnrdays=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" or "&Analysis."="ms" 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 HOIIncidenceCrit=1; if not h then HOIBlackout = 1; if not pov2 then NoPov2Claims=1; MinDaySupMet=.; NoExclClaim=.; SuffExclElig=.; HadInclClaim=.; T3SameDayDeletedMet = 1; T3AtLeastOneEvent = 1; ExclCritMet=1; 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); 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 MinHoiEnrCrit=1; if Enr_Start <= IndexDt-max(0,&enrdays., Washper,&MinCovFrom., &MinMFUfrom.) then MinEnrdays=1; *Minimum enrollment duration after exposure; if MaxEligtEnrAftDt <= Enr_End then MinEnrAft=1; if not pov2 then NoPov2Claims=1; if f then T3AtLeastOneEvent=1; HOIIncidenceCrit=1; *if d and e then HOIIncidenceCrit = .; HOIBlackout=1; NoExclClaim=.; SuffExclElig=.; HadInclClaim=.; ExclCritMet=1; format temp date9.; temp=IndexDt-max(0,MinWindowDt - LookBackLength); temp2=max(0,MinWindowDt - LookBackLength);; label IndexDt="IndexDt"; run; %end; %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. ne 3) %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 MinEnrAft MinEnrdays MinHoiEnrCrit MinEpiDurMet FUEnrolMet PostBlackoutper NoPov2Claims MinDaySupMet HOIIncidenceCrit NoExclClaim SuffExclElig HadInclClaim ExclCritMet T3SameDayDeletedMet T3AtLeastOneEvent HOIBlackout; class PatId IndexDt; output out=_MasterPatient(drop=_:) max(MinEnrAft MinEnrdays MinHoiEnrCrit MinEpiDurMet FUEnrolMet PostBlackoutper NoPov2Claims MinDaySupMet HOIIncidenceCrit HadInclClaim ExclCritMet T3SameDayDeletedMet T3AtLeastOneEvent HOIBlackout)= 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; T3SameDayDeletedMet=1; if b then T3SameDayDeletedMet=.; 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 MinHoiEnrCrit MinEnrAft MinEnrdays HadInclClaim ExclCritMet T3AtLeastOneEvent HOIIncidenceCrit T3SameDayDeletedMet NoExclClaim SuffExclElig HOIBlackout; class PatId IndexDt; output out=_MasterPatient(drop=_:) max(MinEnrdays MinHoiEnrCrit MinEnrAft NoPov2Claims HadInclClaim ExclCritMet T3AtLeastOneEvent HOIIncidenceCrit T3SameDayDeletedMet HOIBlackout)= min(NoExclClaim SuffExclElig)=; run; %end; *add dummies; data _keep; merge _keep(in=a) _MasterPatient(in=b); by PatId; if a; MetIncWRTitself = 1; if a and not b then MetIncWRTitself = .; run; /*10*/ %attrition(file=_keep,ToExcl=MetIncWRTitself=.); /*11*/ %attrition(file=_keep,ToExcl=NoPov2Claims=.); /*12*/ %attrition(file=_keep,ToExcl=T3SameDayDeletedMet=.); /*applicable to type 3 only*/ /*13*/ %attrition(file=_keep,ToExcl=MinEnrdays=.); /*Meets EnrDays and Washper*/ /*14*/ %attrition(file=_keep,ToExcl=MinHoiEnrCrit=.); /*15*/ %attrition(file=_keep,ToExcl=HOIIncidenceCrit=.); %put &EXCL. &INCL.; /*16*/ %IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and SuffExclElig=0); /*16*/ %IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinEnrdays=-1); *MinEnrdays=-1 to delete none, but still increment level; /*17*/ %IF %EVAL(&EXCL.=1) %THEN %attrition(file=_keep,ToExcl=ExclCritMet=. and NoExclClaim=0); /*17*/ %IF %EVAL(&EXCL.=0) %THEN %attrition(file=_keep,ToExcl=MinEnrdays=-1); /*18*/ %attrition(file=_keep,ToExcl=MinEnrdays=-1); /*Inclusion enrollment criteria. Placeholder because not such criteria currently exist*/ /*19*/ %IF %EVAL(&INCL.=1) %THEN %attrition(file=_keep,ToExcl=HadInclClaim=.); /*19*/ %IF %EVAL(&INCL.=0) %THEN %attrition(file=_keep,ToExcl=MinEnrdays=-1); /*20*/ %attrition(file=_keep,ToExcl=MinEnrAft=.); %if %eval(&type=2) %then %do; /*21*/ %attrition(file=_keep,ToExcl=MinDaySupMet=.); /*22*/ %if %eval(&itt.=0) %then %attrition(file=_keep,ToExcl=MinEpiDurMet=.); /*22*/ %if %eval(&itt.=1) %then %attrition(file=_keep,ToExcl=MinEpiDurMet=-1); *-1 to delete none, but still increment level; /*23*/ %attrition(file=_keep,ToExcl=PostBlackoutper=.); %end; %else %do; /*Increment levels*/ /*21*/ %attrition(file=_keep,ToExcl=MinEnrdays=-1); /*22*/ %attrition(file=_keep,ToExcl=MinEnrdays=-1); /*23*/ %attrition(file=_keep,ToExcl=MinEnrdays=-1); %end; /*24*/ %attrition(file=_keep,ToExcl=HOIBlackout=.); %if %eval(&type.=3) %then %do; *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; %end; /*25*/ %attrition(file=_keep,ToExcl=T3AtLeastOneEvent=.); /*applicable to type 3 only*/ %macro InformationCrit; %if %sysfunc(fileexist(_itdrugsexcl))=0 %then %do; proc sort data=_itdrugsexcl; by patid; run; *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 */ /*********************************************************/ data _Attrition; retain group level descr num Excluded; format group $40. descr $500.; set _Attrition; group="&ITGROUP."; format ; LastRemain=lag(num); if 1 <=_N_<=25 then do; Excluded=LastRemain-num; end; else if 26 <=_N_<=28 then do; Excluded=num; Num =.; end; /*exclusion*/ 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 enrollment 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 enrollment 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 enrollment 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 satisfy the demographic (sex, race and hispanic) condition"; if level=8 then descr="Exclusion - Members must have at least one claim with cohort-identifying codes within the query period"; if level=9 then descr="Exclusion - Members must have at least one cohort episode beginning within the age range condition"; if level=10 then descr="Exclusion - Members must have at least one episode defining index claim during the query period"; if level=11 then descr="Exclusion - Members must have at least one cohort episode incident with respect to other criteria"; if level=12 then descr="Exclusion - Members must have only one exposure NDC on index date"; %if %eval(&type. ne 3) %then %do; if level=12 then descr=""; %end; if level=13 then descr="Exclusion - Members must have at least one cohort episode satisfying the pre-index enrollment criterion"; if level=14 then descr="Exclusion - Members must have at least one cohort episode satisfying the HOI-defined enrollment criterion"; %if %eval(&type.=1 | &type.=5) %then %do; if level=14 then descr=""; %end; if level=15 then descr="Exclusion - Members must have at least one cohort episode that meets HOI incidence criterion"; %if %eval(&type.=1 | &type.=3 | &type.=5) %then %do; if level=15 then descr=""; %end; if level=16 then descr="Exclusion - Members must have at least one cohort episode satisfying the exclusion enrollment requirement"; if level=17 then descr="Exclusion - Members must have at least one cohort episode satisfying the exclusion conditions"; if level=18 then descr="Exclusion - Members must have at least one cohort episode satisfying the inclusion enrollment requirement"; if level=19 then descr="Exclusion - Members must have at least one cohort episode satisfying the inclusion conditions"; if level=20 then descr="Exclusion - Members must have at least one cohort episode satisfying the post-index enrollment criterion"; if level=21 then descr="Exclusion - Members must have at least one cohort episode with at least minimum days supplied"; if level=22 then descr="Exclusion - Members must have at least one cohort episode with at least minimum days duration"; if level=23 then descr="Exclusion - Members must have at least one cohort episode with longer than blackout days duration"; if level=24 then descr="Exclusion - Members must have at least one cohort episode that meets HOI blackout criterion"; %if %eval(&type. ne 2) %then %do; if level=21 then descr=""; if level=22 then descr=""; if level=23 then descr=""; if level=24 then descr=""; %end; if level=25 then descr="Exclusion - Members must have at least one HOI in one of the two HOI assessment windows"; %if %eval(&type. ne 3) %then %do; if level=25 then descr=""; %end; /*information*/ if level=26 then descr="Information - Members with at least one cohort claim with supply and/or amount outside specified ranges"; if level=27 then descr="Information - Members with at least one HOI claim with supply and/or amount outside specified ranges"; %if %eval(&type. = 1 |&type.=5) %then %do; if level=27 then descr=""; %end; if level=28 then descr="Information - Members with at least one INCL/EXCL claim with supply and/or amount outside specified ranges"; 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 noprint; 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 end of monitoring period (Type 2, surveillance mode only)"; Excluded=&MEMBERS_LOSTTOFOLLOWUP.; Remaining=.; level= 29; output; descr="Information - Members still at risk at the end of monitoring period (Type 2, surveillance mode only)"; Excluded=.; Remaining=&MEMBERS_STILLATRISK.; level= 30; 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 v2.7 ********; %mend ms_attrition;