**************************************************************************************************** * PROGRAM OVERVIEW **************************************************************************************************** * * PROGRAM: ms_createpov3.sas * * Created (mm/dd/yyyy): 02/02/2017 * Last modified: 02/01/2018 * Version: 1.2 * *-------------------------------------------------------------------------------------------------- * PURPOSE: * This program will identify all index dates meeting the inclusion/exclusion criteria * * Program inputs: * -Dataset with potential index dates (POV1) * -Dataset with inclusion/exclusion codes * * Program outputs: * -Dataset with index dates meeting the inclusion/exclusion criteria (POV3) * * PARAMETERS: * * * Programming Notes: * * * *-------------------------------------------------------------------------------------------------- * CONTACT INFO: * Mini-Sentinel Coordinating Center * info@mini-sentinel.org * *-------------------------------------------------------------------------------------------------- * CHANGE LOG: * * Version Date Initials Comment (reference external documentation when available) * ------- -------- -------- --------------------------------------------------------------- * 1.1 12/21/17 AP Added CODEDAYS parameter to only consider multiple codes during * lookback window * * 1.2 02/01/18 AP Removed post-index enrollment requirement for exclusions (QRP-496) * ***************************************************************************************************; %macro ms_createpov3(); %put =====> MACRO CALLED: ms_createpov3 v1.2; ********************************************************; * For Inclusions/Exclusions, the same algorithm ; * applies to all types of analyses ; ********************************************************; *Initialize for the case where No inclusion/exclusion criteria => keeping all patients; data _POV3; set _POV1; keep Patid Adate; run; %let INCL=0; %let EXCL=0; %ISDATA(dataset=&INCLUSIONCODES.); %IF %EVAL(&NOBS.>=1) %THEN %DO; *Are there any incl/excl conds to verify for this iteration?; data _IT&INCLUSIONCODES.; set &INCLUSIONCODES.; where group="&ITGROUP."; run; %ISDATA(dataset=_IT&INCLUSIONCODES.); %IF %EVAL(&NOBS.>=1) %THEN %DO; *Initialize list with all potential index dates; data _POV3; set _POV1; MustKickOutHard=0; *cumulative; MustKickOutElig=0; *cumulative; keep PatId Adate Enr_Start Enr_end MustKickOut:; run; *Uniqueness of _InclExcl; Proc Sort nodupkey data=_InclExcl out=_InclExcl; by Patid Adate Expiredt CondInclusion cond subcond SubCondInclusion CondFrom CondTo; run; *Get all in INCL/EXCL period claims (both incl and Excl) relative to each potential index dates; proc sql noprint; create table _POV3Claims(where=(InPeriod=1)) as select distinct index.Patid, index.Adate, Inc.CondInclusion, Inc.cond, Inc.Subcond, Inc.SubCondinclusion, Inc.codedays, Inc.adate as incdate, %MS_PeriodsOverlap(period1=COALESCE(index.Adate+CondFrom,-999999) COALESCE(index.Adate+CondTo,999999), period2=Inc.ADate Inc.Expiredt) as InPeriod from _POV1 as index, _InclExcl as Inc where index.Patid = Inc.Patid order by index.Patid, index.Adate; quit; data _null_; set _IT&INCLUSIONCODES.; if CondInclusion=0 then call symputx("EXCL",1); if CondInclusion=1 then call symputx("INCL",1); run; %put EXCL = &EXCL.; %put INCL = &INCL.; *Determine how many conditions; %let MAXCOND=; proc sql; select max(cond) into: maxcond from _IT&INCLUSIONCODES.; quit; data _NULL_; call symputx("MAXCOND",&MAXCOND.);*For sum issue; run; %put MAXCOND = &MAXCOND.; %DO COND=1 %TO &MAXCOND.; %let MAXSUBCOND=0; *Determine how many levels of subconditions needed; proc sql; select max(subcond) into: maxsubcond from _IT&INCLUSIONCODES. (where=(cond=&COND.)); quit; data _NULL_; call symputx("MAXSUBCOND",&MAXSUBCOND.);*For sum issue; run; %put MAXSUBCOND = &MAXSUBCOND.; *Determine whether this COND is an exclusion or inclusion; proc sql noprint; select max(CondInclusion) into: inclusion from _IT&INCLUSIONCODES. (where=(cond=&COND.)); select min(CondFrom) into: MinimumCondFrom from _IT&INCLUSIONCODES. (where=(cond=&COND.)); quit; %put &INCLUSION. &MinimumCondFrom.; *Set data for this cond; data _POV3_&cond.; set _POV3(keep=Patid ADate ENR_START ENR_END); ExclCond&COND.=0; run; %DO SUBCOND=1 %TO &MAXSUBCOND.; %let subinclusion=0; *Determine if SUBCOND is an exclusion or inclusion; proc sql noprint; select max(SubCondInclusion) into: SUBCONDINCLUSION from _IT&INCLUSIONCODES. (where=(cond=&COND. and subcond=&SUBCOND.)); select min(CondFrom) into: CondFrom from _IT&INCLUSIONCODES. (where=(cond=&COND. and subcond=&SUBCOND.)); select max(codedays) into: codedays from _IT&INCLUSIONCODES. (where=(cond=&COND. and subcond=&SUBCOND.)); quit; %put &SUBCONDINCLUSION. &CondFrom. &codedays.; *Only keep if codedays satisfied (number of ADates >= codedays); proc means data=_POV3Claims noprint nway missing; class patid Adate CondInclusion cond Subcond SubCondinclusion codedays / missing; output out=_POV3Incl_tmp(drop=_: where=(freq>=codedays)) n=freq; where cond=&COND. and subcond=&SUBCOND.; run; proc sort nodupkey data=_POV3Incl_tmp out=_POV3Incl_tmp(keep=PatId Adate); by PatId Adate; *Here Adate is of the episode, not the INCL; where cond=&COND. and subcond=&SUBCOND.; run; *Square; data _POV3_&cond.; merge _POV3_&cond.(in=a) _POV3Incl_tmp(in=b); by PatId Adate; if a and b then SubCond_&COND._&SUBCOND.=1; else SubCond_&COND._&SUBCOND.=0; if &SUBCONDINCLUSION.=1 then do; if a and b then SubCond_&COND._&SUBCOND.=1; else SubCond_&COND._&SUBCOND.=0; end; *If the subcondition is met but it is a subexclusion, then means that condition not satisfied; else do; if a and b then SubCond_&COND._&SUBCOND.=0; else SubCond_&COND._&SUBCOND.=1; end; run; *Check elig for the subconditions exclusions; *If the condition is a CondExclusion, then it will be checked again later with different assumptions; %if &SUBCONDINCLUSION.=0 and &inclusion.=1 %then %do; data _POV3_&cond.; set _POV3_&cond.; *check that the patient meets also meets elig for this subcondition; if not(Enr_Start <= Adate + COALESCE(&CondFrom.,0)) then do;*Open ended means -infty/infty; SubCond_&COND._&SUBCOND.=0; ExclCond&COND.=1; end; run; %end; %END; *loop subcond; *If all subconditions are satisfied, then condition is satisfied (at this point); data _POV3_&cond.; set _POV3_&cond.; Cond&cond.=sum(of SubCond_&COND._1-SubCond_&COND._&MAXSUBCOND.)=&MAXSUBCOND.; run; %let condFrom2=.; data _null_; set _IT&INCLUSIONCODES. (where=(cond=&COND.)); if CondInclusion=0 then do; call symputx('CondFrom2',"&MinimumCondFrom."); end; run; %put &CondFrom. &CondFrom2.; *Manage if an exclusion - if you have one hard exclusion or not enough elig, you are out; data _POV3_&cond.; set _POV3_&cond.; if &INCLUSION.=0 then do; *if the patient had cond&cond.=1 then can never be included; if Cond&cond.=1 then MustKickOutHard_&cond.=1; else MustKickOutHard_&cond.=0; *flip the dummy; Cond&cond.=Cond&cond.=0; *Because we can have CondInclusion=0 and SubInclusion=1, we still need to consider if enough elig in this case; MustKickOutElig_&cond.=0; if not(Enr_Start <= Adate + COALESCE(&CondFrom2.,0)) then do; MustKickOutElig_&cond.=1; ExclCond&COND.=1; *Open ended means -infty/infty; Cond&cond.=0; end; end; *Create a dummy to remember if this condition was an inclusion or and exlusion; InclCond&COND.=&INCLUSION.; run; *Put all together; data _POV3; merge _POV3(in=a) _POV3_&cond.(in=b keep=PatId Adate Cond: SubCond: Excl: InclCond: MustKickOut:); by PatId Adate; if MustKickOutHard_&cond.=1 then MustKickOutHard=1; if MustKickOutElig_&cond.=1 then MustKickOutElig=1; if a; run; %END;*loop cond; *Clean up; data _POV3; set _POV3; array _cond(*) Cond1-cond&maxcond.; array _InclCond(*) InclCond1-InclCond&maxcond.; *At least one inclusion criterion; %if &maxcond. >1 %then %do; inclusion=max(of InclCond1-InclCond&maxcond.); %end; %else %do; inclusion=InclCond1; %end; *Initialize AnyCond; if inclusion ne 1 then AnyCond=1; else do; AnyCond=0; *Find those with at least on inclusion met; do i=1 to &maxcond.; if _cond(i)=1 and _InclCond(i)=1 then AnyCond=1; end; end; *Kick out any index not meeting the elig criteria for at least one exlcusion or that has at least one hard exclusion (claim).; if MustKickOutElig or MustKickOuthard then AnyCond=0; drop inclusion; run; data _POV3Excl1; *for attrition; set _POV3; if MustKickOutHard then output _POV3Excl1; run; data _POV3Excl2; *for attrition; set _POV3; if MustKickOutElig then output _POV3Excl2; run; data _POV3Incl; *for attrition; set _POV3; If AnyCond then output _POV3Incl; run; data _POV3; set _POV3Incl; run; %END; *_ITINCLUSIONCODES; %END; *INCLUSIONCODES; %put NOTE: ********END OF MACRO: ms_createpov3 v1.2********; %mend ms_createpov3;