**************************************************************************************************** * PROGRAM OVERVIEW **************************************************************************************************** * * PROGRAM: ms_cidadenom.sas * * Created (mm/dd/yyyy): 12/19/2014 * Last modified: 03/23/2018 * Version: 1.14 * *-------------------------------------------------------------------------------------------------- * PURPOSE: * This macro will compute denominators. * * Program inputs: * -Many datasets created by ms_cidanum * * Program outputs: * -The DPLocal.&RUNID._DenomCounts output table * -The DPLocal.&RUNID._&GEOG output tables * * 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.2 01/12/15 DM Defensive coding added to eliminate w.a.r.n.i.n.g.s. and errors triggeredby * mergenoby option values * * 1.3 01/22/15 DM Commented out the id parameter in the shaveoutside macro call * * 1.4 02/02/15 SL(DM) Corrected bug related to open ended condfrom/condto * * 1.5 01/15/16 SL(VC) Modify to include complex inclusion and Covariate enhancements * * 1.6 04/21/16 DM Added HDPS window parameters in denominators algorithm * * 1.7 11/14/16 AP Added denominator calculations for geographic location * * 1.8 05/05/17 AP - Added denominator calculations for race and hispanic vars * - Changed Priordays_af to Priordays_af-1 (see QRP-376) * * 1.9 07/12/17 AP - Added MFU window to enrollment requirements * - Changed Priordays_af -1 back to Priordays_af (see QRP-394) * * 1.10 10/03/17 AP Added HHS/Census region denominator calculations, * moved Geographic strata into main table (QRP-434), * recoded sex values (QRP-453), include blackout period in denom (QRP-319) * * 1.11 11/20/17 AP Removed optional blackout parameter (QRP-466) * * 1.12 12/21/17 AP 1. Removed comorbidity window enrollment criteria (QRP-435) * 2. Added CODEDAYS requirement to inclusion/exclusion (QRP-455) * 3. Initialized maxlookback macro var (QRP-475) * * 1.13 02/01/17 AP Removed post-index enrollment criteria (QRP-496) * * 1.14 03/23/18 RR Save denominator data for Never exposed group (QRP-495) * ***************************************************************************************************; %macro ms_cidadenom(); %put =====> MACRO CALLED: ms_cidadenom v1.14; /*---------------------------------------------------*/ /* Original enrollment periods */ /*---------------------------------------------------*/ * Extract highest CONDFROM value among exclusions if any; %let MaxExclCondFrom=0; %macro wrapper; %ISDATA(dataset=&INCLUSIONCODES.); %IF %EVAL(&NOBS.>=1) %THEN %DO; proc sql noprint; select min(condfrom) into :MaxExclCondFrom from &INCLUSIONCODES.(where=(group="&ITGROUP.")) where Condinclusion=0; quit; %END; %put &MaxExclCondFrom.; %mend wrapper; %wrapper; *initialize maxlookback; %let MaxLookBack = 0; *If death censoring is required, it was already applied (Enr_End was previously truncated by CIDAnum); data _DenomOrig&Group. (keep=PatId EligEpisode Enr_Start Enr_End) _Denom&Group.; set Enr_&Group.; where Enr_End>=&startfollowup.; format Enr_Start Enr_End DenomEnrStart DenomEnrEnd date9.; MaxLookBack=Max(0,&ENRDAYS.,&WASHPER.,&FUPWASHPER.,&MINCOVFROM.,&MINMFUFROM.,-&MaxExclCondFrom.,&L1HDPSFROM.); * Resctrict eligibility to Study period (minus maximum of required lookbacks); DenomEnrStart=Enr_Start; * Initially truncate start eligibility enrollment start by max of lookback periods; DenomEnrStart=DenomEnrStart + MaxLookBack; DenomEnrEnd=Enr_End; EligEpisode=episode; if DenomEnrEnd >= DenomEnrStart; drop DeathDt episode; *Assign MaxLookBack macro variable; call symputx('MaxLookBack',MaxLookBack); run; %put &MaxLookBack.; /*---------------------------------------------------*/ /* Inclusion required (POV3 Incl) for eligibility */ /*---------------------------------------------------*/ proc datasets library=work nowarn nolist; delete _denom1_stack:; quit; %macro wrapper; %ISDATA(dataset=&INCLUSIONCODES.); %IF %EVAL(&NOBS.>=1) %THEN %DO; data _IT&INCLUSIONCODES.; set &INCLUSIONCODES.; where group="&ITGROUP."; run; *For EACH condition, shave periods for all subconditions; %ISDATA(dataset=_IT&INCLUSIONCODES.); %IF %EVAL(&NOBS.>=1) %THEN %DO; %let MAXCOND=; proc sql; select max(cond) into: maxcond from _IT&INCLUSIONCODES.; quit; %put &MAXCOND.; %DO COND=1 %TO &MAXCOND.; *Determine how many levels of subconditions needed; %let MAXSUBCOND=; proc sql; select max(subcond) into: maxsubcond from _IT&INCLUSIONCODES. (where=(COND=&COND.)); quit; *Determine if condition is an inclusion; proc sql; select max(CondInclusion) into: inclusion from _IT&INCLUSIONCODES. (where=(COND=&COND.)); quit; %put &MAXSUBCOND. &INCLUSION.; *Determine if there is subexclusion in condition in order to calculate new maxlookback since one above is for CondInclusion=0; %let SUBEXCL=0; data _null_; set _IT&INCLUSIONCODES.(where=(COND=&COND.)); if subCondinclusion=0 then call symputx("SUBEXCL",1); run; %put SUBEXCL = &SUBEXCL.; *If there is subexclusion, determine new maxlookback for that specific condition before shaving; %IF &SUBEXCL.=1 %THEN %DO; %let NewMaxExclCondFrom=0; proc sql noprint; select min(condfrom) into :NewMaxExclCondFrom from _IT&INCLUSIONCODES. where COND=&COND. and SubCondInclusion=0; quit; %put &NewMaxExclCondFrom.; data _Denom&Group._&cond. (rename=(newmaxlookback=MaxLookBack)); set _Denom&Group.; *If the subcond condfrom is greater in absolute value than the maxlookback value, then readjust denom period; /* NewMaxLookBack=Max(-&MaxExclCondFrom.,-&NewMaxExclCondFrom.);*/ NewMaxLookBack=Max(0,&Maxlookback.,-&NewMaxExclCondFrom.); * Resctrict eligibility to Study period (minus maximum of required lookbacks); DenomEnrStart=Enr_Start; * Initially truncate start eligibility enrollment start by max of lookback periods; DenomEnrStart=DenomEnrStart + NewMaxLookBack; DenomEnrEnd=Enr_End; if DenomEnrEnd >= DenomEnrStart; drop maxlookback; run; %END;*If subexcl=1; %IF &SUBEXCL.^=1 %THEN %DO; data _Denom&Group._&cond.; set _Denom&Group.; run; %END; %DO SUBCOND=1 %TO &MAXSUBCOND.; *Determine if subcond is an inclusion or exclusion; %let SUBINCLUSION=0; proc sql; select max(SubCondInclusion) into: subinclusion from _IT&INCLUSIONCODES. (where=(COND=&COND. and SUBCOND=&SUBCOND.)); quit; %put &SUBINCLUSION.; *Merge time periods for each subcondition and shave for each; %IF &SUBINCLUSION. = 1 %THEN %DO; proc sort data=_InclExcl out=_EligIncl_&subcond.; by patid adate; where COND=&COND. and subcond=&SUBCOND.; run; data _EligIncl_&subcond.; set _EligIncl_&subcond.; format EligStart EligEnd date9.; EligStart=ADate-coalesce(CondTo,99999); EligEnd=Expiredt-coalesce(CondFrom,-99999); if EligStart<=EligEnd; id = _n_; keep PatId ADate EligStart EligEnd codedays id; run; *only keep time periods that meet codedays criteria; %macro evaluate_codedays_inc(); %let codedays = 1; data _null_; set _EligIncl_&subcond.; if _n_ = 1 then do; call symputx('codedays', codedays); end; run; %if %eval(&codedays.) = 1 %then %do; data _overlap_spans_&subcond.; set _EligIncl_&subcond.; run; %end; %else %do; proc sql noprint; create table _overlap_spans_&subcond.(where=(eligstart <=eligend) rename=overlap_start=eligstart rename=overlap_end=eligend) as select distinct a1.patid, max(a1.eligstart %do ref = 2 %to &codedays.; ,a&ref..eligstart %end;) as overlap_start format date9., min(a1.eligEnd %do ref = 2 %to &codedays.; ,a&ref..eligEnd %end;) as overlap_end format date9. from _EligIncl_&subcond. as a1 %do ref = 2 %to &codedays.; , _EligIncl_&subcond. as a&ref. %end; where a1.Patid %do ref = 2 %to &codedays.; = a&ref..PatId %end; and (a1.id %do ref = 2 %to &codedays.; < a&ref..id %end;); quit; %end; %mend; %evaluate_codedays_inc(); %MergeTimePeriods(dataset=_overlap_spans_&subcond., DateStart=EligStart, DateEnd=EligEnd); %ms_shaveoutside(reffile=_overlap_spans_&subcond., refstart=EligStart, refend=EligEnd, KeepPartBf=Y, ToShaveFile=_Denom&Group._&cond., ToShaveStart=DenomEnrStart, ToShaveEnd=DenomEnrEnd, outfile=_Denom&Group._&cond.); *Drop refstart and refend since _Denom&Group can be looped; data _Denom&Group._&cond.; set _Denom&Group._&cond.(drop=eligstart eligend); run; %END;*If subinclusion=1; %IF &SUBINCLUSION. = 0 %THEN %DO; proc sort data=_InclExcl out=_UneligExcl_&subcond.; by patid adate; where COND=&COND. and subcond=&SUBCOND.; run; data _UneligExcl_&subcond.; set _UneligExcl_&subcond.; where COND=&COND. and subcond=&SUBCOND.; format UneligStart UneligEnd date9.; UneligStart=ADate-coalesce(CondTo,99999); UneligEnd=Expiredt-coalesce(CondFrom,-99999); if UneligStart<=UneligEnd; id = _n_; keep PatId ADate UneligStart UneligEnd codedays id; run; *only keep time periods that meet codedays criteria; %macro evaluate_codedays_exl(); %let codedays = 1; data _null_; set _UneligExcl_&subcond.; if _n_ = 1 then do; call symputx('codedays', codedays); end; run; %if %eval(&codedays.) = 1 %then %do; data _overlap_spans_&subcond.; set _UneligExcl_&subcond.; run; %end; %else %do; proc sql noprint; create table _overlap_spans_&subcond.(where=(Uneligstart <=Uneligend) rename=overlap_start=Uneligstart rename=overlap_end=Uneligend) as select distinct a1.patid, max(a1.UneligStart %do ref = 2 %to &codedays.; ,a&ref..Uneligstart %end;) as overlap_start format date9., min(a1.UneligEnd %do ref = 2 %to &codedays.; ,a&ref..UneligEnd %end;) as overlap_end format date9. from _UneligExcl_&subcond. as a1 %do ref = 2 %to &codedays.; , _UneligExcl_&subcond. as a&ref. %end; where a1.Patid %do ref = 2 %to &codedays.; = a&ref..PatId %end; and (a1.id %do ref = 2 %to &codedays.; < a&ref..id %end;); quit; %end; %mend; %evaluate_codedays_exl(); %MergeTimePeriods(dataset=_overlap_spans_&subcond., DateStart=UneligStart, DateEnd=UneligEnd); *Verify if there is data or else e.r.r.o.r when shaving; %ISDATA(dataset=_UneligExcl_&subcond.); %IF %EVAL(&NOBS.>=1) %THEN %DO; %ms_shaveinside(reffile=_Denom&Group._&cond., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_overlap_spans_&subcond., ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group._&cond.); %END;*If there is data; %END;*If subinclusion=0; %END;*loop on subcond; %put &INCLUSION.; %IF &INCLUSION.=1 %THEN %DO; proc append base=_Denom&Group._STACK_INC data=_Denom&Group._&cond.(keep=patid denomenrstart denomenrend eligepisode) force; run; %END; %IF &INCLUSION.=0 %THEN %DO; proc append base=_Denom&Group._STACK_EXCL data=_Denom&Group._&cond.(keep=patid denomenrstart denomenrend eligepisode) force; run; %END; %END;*End Loop Cond; *Output dataset for inclusion=1 and for exclusions=0 and then (a) shaveoutside (b) shaveinside denom; *For inclusions, need to merge periods; *First shaveoutside denom file but need to merge inclusion periods together; %IF %SYSFUNC(exist(_Denom&Group._STACK_INC))=1 %THEN %DO; %MergeTimePeriods(dataset=_Denom&Group._STACK_INC, DateStart=DenomEnrStart, DateEnd=DenomEnrEnd); *_Denom&Group._STACK_INC includes all time periods where the potential index dates will meet at least one of the cond inclusions; data _Denom&Group._STACK_INC; set _Denom&Group._STACK_INC; rename DenomEnrStart=DTSTART DenomEnrEnd=DTEND ; run; %ms_shaveoutside(reffile=_Denom&Group._STACK_INC, refstart=DTSTART, refend=DTEND, /*id=EligEpisode,*/ KeepPartBf=Y, ToShaveFile=_Denom&Group., ToShaveStart=DenomEnrStart, ToShaveEnd=DenomEnrEnd, outfile=_Denom&Group./*_incl*/); %END; %IF %SYSFUNC(exist(_Denom&Group._STACK_EXCL))=1 %THEN %DO; *merge all periods of ineligibilities; %MergeTimePeriods(dataset=_Denom&Group._STACK_EXCL, DateStart=DenomEnrStart, DateEnd=DenomEnrEnd); *_Denom&Group._STACK_EXCL includes all time periods where the potential index dates will meet at least one of the cond exclusions; data _Denom&Group._STACK_EXCL; set _Denom&Group._STACK_EXCL; rename DenomEnrStart=DTSTART DenomEnrEnd=DTEND ; run; *remove all these periods from the ELIG; %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_Denom&Group._STACK_EXCL, ToShaveStart=DTSTART, ToShaveEnd=DTEND, outfile=_Denom&Group./*_excl*/); %END; proc sort data=_Denom&Group. out=_Denom&Group. nodupkey; by patid denomenrstart denomenrend eligepisode; run; %END;*If there are inclusion codes for GROUP; %END;*If there are inclusion codes; %mend wrapper; %wrapper; /*---------------------------------------------------*/ /* Group Index (POV1) to remove from eligibility */ /*---------------------------------------------------*/ %macro wrapper; %if %eval(&itt.=1) %then %do; %let InDataset=_groupindexitt; %end; %else %do; %let InDataset=_groupindex; %end; %ISDATA(dataset=&InDataset.); %IF %EVAL(&NOBS.>=1) & %EVAL(&WASHPER. ne 0) %THEN %DO; data _UneligGroupIndex; set &InDataset.; format UneligStart UneligEnd date9.; UneligStart=ADate + 1; * Added a +1 so a member can remain eligible the day of index date; UneligEnd=ExpireDt + min(&WASHPER.,99999); if UneligStart<=UneligEnd; keep PatId UneligStart UneligEnd; run; %MergeTimePeriods(dataset=_UneligGroupIndex, DateStart=UneligStart, DateEnd=UneligEnd); %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_UneligGroupIndex, ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group.); %END; %mend wrapper; %wrapper; /*---------------------------------------------------*/ /* Group Washout (POV2) to remove from eligibility */ /*---------------------------------------------------*/ %macro wrapper; %ISDATA(dataset=_groupwash); %IF %EVAL(&NOBS.>=1) %THEN %DO; data _UneligGroupWash; set _groupwash; format UneligStart UneligEnd date9.; UneligStart=ADate + 1; * Added a +1 so a member can remain eligible the day of index date; UneligEnd=ExpireDt + min(&WASHPER.,99999); if UneligStart<=UneligEnd; keep PatId UneligStart UneligEnd; run; %MergeTimePeriods(dataset=_UneligGroupWash, DateStart=UneligStart, DateEnd=UneligEnd); %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_UneligGroupWash, ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group.); %END; %mend wrapper; %wrapper; /*---------------------------------------------------*/ /* Follow-up Event (POV5) to remove from eligibility */ /*---------------------------------------------------*/ %macro wrapper; %ISDATA(dataset=_fupevent);*Will only apply to type 2 analysis; %IF %EVAL(&NOBS.>=1) %THEN %DO; data _UneligFupEvent; set _fupevent; format UneligStart UneligEnd date9.; UneligStart=sum(ADate, - &BLACKOUTPER., 1); * Added a +1 so a member can be eligible the day of event; UneligEnd=ExpireDt + min(&FUPWASHPER.,99999); if UneligStart<=UneligEnd; keep PatId UneligStart UneligEnd; run; %MergeTimePeriods(dataset=_UneligFupEvent, DateStart=UneligStart, DateEnd=UneligEnd); %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_UneligFupEvent, ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group.); %END; %mend wrapper; %wrapper; /*-----------------------------------------------------*/ /* Follow-up Washout (POV6) to remove from eligibility */ /*-----------------------------------------------------*/ %macro wrapper; %ISDATA(dataset=_fupwash); %IF %EVAL(&NOBS.>=1) %THEN %DO; data _UneligFupWash; set _fupwash; format UneligStart UneligEnd date9.; UneligStart=ADate + 1; * Added a +1 so a member can remain eligible the day of index date; UneligEnd=ExpireDt + min(&FUPWASHPER.,99999); if UneligStart<=UneligEnd; keep PatId UneligStart UneligEnd; run; %MergeTimePeriods(dataset=_UneligFupWash, DateStart=UneligStart, DateEnd=UneligEnd); %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_UneligFupWash, ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group.); %END; %mend wrapper; %wrapper; /*---------------------------------------------------*/ /* Remove ITT episode lengths from eligibility - */ /* patient no longer eligible to become incidence */ /* until ITT has expired */ /*---------------------------------------------------*/ %macro wrapper; %if %eval(&itt.=1) %then %do; data _UneligITT; set _ptsmasterlist; format UneligStart UneligEnd date9.; UneligStart=IndexDt + 1; UneligEnd=IndexDt + &ITTDays. - 1; if UneligStart<=UneligEnd; keep PatId UneligStart UneligEnd; run; %ISDATA(dataset=_UneligITT); %IF %EVAL(&NOBS.>=1) %THEN %DO; %ms_shaveinside(reffile=_Denom&Group., refstart=DenomEnrStart, refend=DenomEnrEnd, id=EligEpisode, ToShaveFile=_UneligITT, ToShaveStart=UneligStart, ToShaveEnd=UneligEnd, outfile=_Denom&Group.); %END; %end; %mend wrapper; %wrapper; /*-----------------------------------------------------*/ /* Make sure the number of enrollment days remaining */ /* after each potential elig days is sufficient */ /*-----------------------------------------------------*/ * Get first Index Dates and Event Dates to handle member days correctly according to CohortDef; proc sql noprint; create table _Denom as select den.PatId, den.EligEpisode, den.Enr_Start, den.Enr_End, min(lst.IndexDt) as FIndexDt format date9., min(lst.FEventDt) as FEventDt format date9. from _DenomOrig&Group. as den left join _ptsmasterlist as lst on den.PatId=lst.PatId group by den.PatId, den.EligEpisode, den.Enr_Start, den.Enr_end order by PatId, EligEpisode; quit; proc sort data = _Denom&Group.; by PatId EligEpisode; run; data _Denom&Group.; merge _Denom&Group.(in=a keep=PatId EligEpisode DenomEnrStart DenomEnrEnd) _Denom; by PatId EligEpisode; format AdjustedEnrEnd date9.; if a; * Adjust eligibility end date; AdjustedEnrEnd = Enr_End - Max(0,&MinEpisDur. - 1,&MinDaySupp. - 1,&BlackoutPer.,&EnrDaysAft.); DenomEnrEnd = min(DenomEnrEnd,AdjustedEnrEnd); if "&COHORTDEF."="01" then DenomEnrEnd=min(DenomEnrEnd,FIndexDt); if "&COHORTDEF."="03" then DenomEnrEnd=min(DenomEnrEnd,FEventDt); MemberDays = DenomEnrEnd - DenomEnrStart + 1; * Because DenomEnrEnd can have been truncated, keep only the observations with positive member days; if MemberDays>0; run; /*---------------------------*/ /* Add DOB/SEX/Race/Hispanic */ /*---------------------------*/ proc sort nodupkey data=_Denom&Group. out=_UniquePts(Keep=PatId); by PatId; run; proc sql noprint; /*Age and demogs*/ create table _DemoCount as select Dem.PatId, Count(Dem.PatId) as numdemo from indata.&demtable. as DEM inner join _UniquePts as pts on DEM.patid = pts.patid group by Dem.PatId; *restrict to unique pts; create table _Demo as select distinct pts.*, birth_date, case when sex = 'F' then 'F' when sex = 'M' then 'M' else 'O' end as sex, race, hispanic, zip, zip_date from indata.&demtable as DEM inner join _Denom&Group. as pts on DEM.patid = pts.patid inner join _DemoCount (where=(numdemo=1)) as cnt on cnt.patid = pts.patid; create table _Denom&Group. as select * from _demo; quit; /** Zip Codes **/ %ISDATA(dataset=infolder.&ZIPFILE.); %IF %EVAL(&NOBS.>0) %THEN %DO; proc sql noprint; create table _zip&Group. as select pts.*, lkup.statecode, lkup.hhs_region, lkup.cb_region from _Denom&Group. as pts left join infolder.&zipfile. as lkup on pts.zip = lkup.zip order by pts.patid; quit; data _Denom&Group.(drop=statecode cb_region hhs_region); length zip3 state hhs_reg cb_reg $7; set _zip&Group.; if missing(zip) then do; zip3 = 'Missing'; state = 'Missing'; hhs_reg = 'Missing'; cb_reg = 'Missing'; end; else if missing(statecode) then do; zip3 = 'Invalid'; state = 'Invalid'; hhs_reg = 'Invalid'; cb_reg = 'Invalid'; end; else do; zip3 = substr(strip(zip),1,3); state = statecode; cb_reg = cb_region; hhs_reg = hhs_region; if statecode ne '' and missing(hhs_region) then do; hhs_reg = 'Other'; end; if statecode ne '' and missing(cb_reg) then do; cb_reg = 'Other'; end; end; run; %END; %else %do; data _Denom&Group.; set _Denom&Group.; length zip3 state cb_reg hhs_reg $7 Zip_uncertain $1; zip3 = ''; state = ''; cb_reg = ''; hhs_reg = ''; Zip_uncertain = ''; run; %end; *truncate periods such that all interval lies in Minage-MaxAge; data _Denom&Group.; set _Denom&Group.; format MinAgeDate MaxAgeDate date9.; MinAgeDate = intnx(scan("&AGETYP.",1),birth_date,&MINAGE.,'sameday'); if &MAXAGE.=99999 then MaxAgeDate=intnx('Years',birth_date,110,'sameday'); else MaxAgeDate= intnx(scan("&AGETYP.",&NBSTRAT.),birth_date,&MAXAGE.+1,'sameday'); *Member not meetin minimum age yet; if MinAgeDate > DenomEnrStart then DenomEnrStart=MinAgeDate; *can be pushed beyond DenomEnrEnd; *Member has reached or will reach will reach maxage ; if MaxAgeDate <= DenomEnrEnd then DenomEnrEnd=MaxAgeDate-1; *can be pushed before DenomEnrStart; *Adjust; if DenomEnrStart <= DenomEnrEnd; run; *At this point, each day in the DenomEnrStart-DenomEnrEnd intervals represents a potential index date if the patients would have started one of the study drugs. Now, we need to restrict only to the startfollowup-enddate period. This will simplify accumulating days for year and year/Month strata, especially if the startfollowup day is not the first day of the month and or the first month of the year; data _Denom&Group.; set _Denom&Group.; sfup=input("&startfollowup.", best.); format sfup date9.; DenomEnrStart=max(DenomEnrStart,&startfollowup.); DenomEnrEnd=min(DenomEnrEnd,&enddate.); patient=1; MemberDays=DenomEnrEnd-DenomEnrStart+1; if DenomEnrStart<=DenomEnrEnd; run; *If Never exposed cohort is requested, create a dataset here for nvr_exposed.sas macro; %IF "&NEVEREXPOSEDCOHORT." EQ "Y" %then %do; data _nvrexp&Group.; set _Denom&Group.; run; %END; *Prepare dates for year/month loop; %global nummonths; %global numyears; data _null_; format startfollowup enddate date9.; startfollowup=intnx('month',&startfollowup.,0,'begin'); *in case not at the start of month; enddate=intnx('month',&enddate.,1,'begin')-1; *in case not at the end of month; nummonths=intck('month',startfollowup,enddate)+1; numyears=intck('year',startfollowup,enddate)+1; call symputx("StartYear",year(startfollowup)); call symputx("StartMonth",month(startfollowup)); call symputx("EndYear",year(enddate)); call symputx("EndMonth",month(enddate)); call symputx("nummonths",nummonths); call symputx("numyears",numyears); do i=1 to nummonths+1; call symput("PER"||strip(put(i,best.)),put(intnx('month',&startfollowup.,i-1,'begin'),best.)); end; do i=1 to numyears+1; call symput("YEAR"||strip(put(i,best.)),put(intnx('year',&startfollowup.,i-1,'begin'),best.)); end; run; %PUT &StartYear. &StartMonth. &EndYear. &EndMonth. &nummonths. &numyears.; *this macro is to avoid having to output one record per interval per stratification level which for certain partners, can become quite large.; options nosymbolgen; %macro CreateSums(infile=,outfile=,by=,dimout=); data _temp; set &infile.; By &by. PatId; array _NumPtsYM(*) _NumPtsYM1-_NumPtsYM&nummonths.; *Pts level temporary vector to avoid double counting patients in same year/month; array NumPtsYM(*) NumPtsYM1-NumPtsYM&nummonths.; *accumator of unique patient in the same year/month; array NumMemDaysYM(*) NumMemDaysYM1-NumMemDaysYM&nummonths.; *accumator of unique days in the same year/month; array _NumPtsY(*) _NumPtsY1-_NumPtsY&numyears.; *Pts level temporary vector to avoid double counting patients in same year/month; array NumPtsY(*) NumPtsY1-NumPtsY&numyears.; *accumator of unique patient in the same year; array NumMemDaysY(*) NumMemDaysY1-NumMemDaysY&numyears.; *accumator of unique days in the same year; %do p=1 %to &nummonths.; %let p1=%eval(&p.+1); if %MS_PeriodsOverlap(period1=DenomEnrStart DenomEnrEnd,period2=&&PER&p. &&PER&p1.-1) then do; _NumPtsYM(&p.)=1; NumMemDaysYM(&p.)=sum(NumMemDaysYM(&p.),Min(DenomEnrEnd,&&PER&p1.-1)-max(DenomEnrStart,&&PER&p.)+1); end; %end; %do p=1 %to &numyears.; %let p1=%eval(&p.+1); if %MS_PeriodsOverlap(period1=DenomEnrStart DenomEnrEnd,period2=&&YEAR&p. &&YEAR&p1.-1) then do; _NumPtsY(&p.)=1; NumMemDaysY(&p.)=sum(NumMemDaysY(&p.),Min(DenomEnrEnd,&&YEAR&p1.-1)-max(DenomEnrStart,&&YEAR&p.)+1); end; %end; if last.PatId then do; do i = 1 to &NUMYEARS.; NumPtsY(i)=sum(NumPtsY(i),_NumPtsY(i)); end; do i = 1 to &nummonths.; NumPtsYM(i)=sum(NumPtsYM(i),_NumPtsYM(i)); end; call missing(of _Num:); end; if last.&dimout. then do; output; call missing(of Num:); end; keep &by. Num:; retain Num: _Num:; run; %ISDATA(dataset=_temp); %IF %EVAL(&NOBS.=0) %THEN %DO; proc sql noprint; insert into _temp set NumPtsYM1=.; quit; %END; proc transpose data=_temp(drop=NumPts:) out=_Days prefix=NumMemDays; by &by.; run; proc transpose data=_temp(drop=NumMemDays:) out=_PTS prefix=NumPts; by &by.; run; /* Get system default value; store in macro variable of same name */ %let mergenoby=%sysfunc(getoption(mergenoby)); /* Set mergenoby to nowarn to avoid triggering an error or w.a.r.n.i.n.g. at this step */ options mergenoby=nowarn; data &outfile.; merge _Days(rename=_NAME_=_Days_) _PTS(rename=_NAME_=_PTS_); *No by; if _PTS_=:"NumPtsYM" then do; per=input(tranwrd(_PTS_, 'NumPtsYM' , ''),best.); date=intnx('month',&startfollowup.,per-1,'begin'); month=month(date); year=year(date); end; else do; per=input(tranwrd(_PTS_, 'NumPtsY' , ''),best.); date=intnx('year',&startfollowup.,per-1,'begin'); year=year(date); end; Rename NumMemDays1=DenNumMemDays NumPts1=DenNumPts; keep year month &by. Num:; run; /* Reset mergenoby to original value (as stored in macro variable) */ options mergenoby=&mergenoby.; %mend CreateSums; *create individual denom contribution to final table; *need to do this individually to make sur we dont multiple-count *patients in the presence of different strata; *Create Overall counts; proc means data=_Denom&Group. nway noprint; var MemberDays Patient; class PatId; output out=_ALL sum(MemberDays)= max(Patient)=; run; proc means data=_ALL noprint; var MemberDays Patient; output out=_ALL(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; proc sort data=_Denom&Group.; By Sex PatId; run; *Create sex level counts; proc means data=_Denom&Group. noprint; var MemberDays Patient; by Sex PatId; output out=_S sum(MemberDays)= max(Patient)=; run; proc means data=_S noprint; var MemberDays Patient; by Sex; output out=_S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Sex Year and Sex Year Month; %CreateSums(infile=_Denom&Group., outfile=_SYM, by=sex, dimout=Sex); *year only (will use _SYM as sex is mutually exclusive; proc means data=_SYM nway noprint; var DenNumMemDays DenNumPts; class year; where year ne . and month=.; output out=_Y (drop=_:) sum(DenNumMemDays)=DenNumMemDays sum(DenNumPts)=DenNumPts; run; *year month only (will use _SYM as sex is mutually exclusive; proc means data=_SYM nway noprint; var DenNumMemDays DenNumPts; class year month; where year ne . and month ne .; output out=_YM (drop=_:) sum(DenNumMemDays)=DenNumMemDays sum(DenNumPts)=DenNumPts; run; *Create race, race*sex, race*year, race*year*month level counts; %if "&race_out" = "Y" %then %do; /*race*/ proc sort data=_Denom&Group.; By race PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by race PatId; output out=_R sum(MemberDays)= max(Patient)=; run; proc means data=_R noprint; var MemberDays Patient; by race; output out=_R(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; /*race year month*/ %CreateSums(infile=_Denom&Group., outfile=_RYM, by=race, dimout=race); /*race sex*/ proc sort data=_Denom&Group.; By race sex PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by race sex PatId; output out=_R_S sum(MemberDays)= max(Patient)=; run; proc means data=_R_S noprint; var MemberDays Patient; by race sex; output out=_R_S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; *Create hispanic, hispanic*sex, hispanic*year, hispanic*year*month level counts; %if "&hispanic_out" = "Y" %then %do; /*Hispanic*/ proc sort data=_Denom&Group.; By hispanic PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by hispanic PatId; output out=_H sum(MemberDays)= max(Patient)=; run; proc means data=_H noprint; var MemberDays Patient; by hispanic; output out=_H(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; /*hispanic year month*/ %CreateSums(infile=_Denom&Group., outfile=_HYM, by=hispanic, dimout=hispanic); /*hispanic sex*/ proc sort data=_Denom&Group.; By hispanic sex PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by hispanic sex PatId; output out=_H_S sum(MemberDays)= max(Patient)=; run; proc means data=_H_S noprint; var MemberDays Patient; by hispanic sex; output out=_H_S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; /*-------------------------------------------------*/ /* further breaking down periods by age categories */ /*-------------------------------------------------*/ data _Denom&Group.Age(rename=AgeDenomEnrEnd=DenomEnrEnd rename=AgeDenomEnrStart=DenomEnrStart); set _Denom&Group.; format AgeGroup $9.; format threshdate AgeDenomEnrStart AgeDenomEnrEnd date9.; AgeDenomEnrStart=DenomEnrStart; AgeDenomEnrEnd=DenomEnrEnd; *OrigMemberDays=DenomEnrEnd-DenomEnrStart+1; do i=&NBSTRAT. to 1 by -1; threshdate=intnx(scan("&AGETYP.",i),birth_date,input(scan("&AGETHRESH.",i),best.),'sameday'); if threshdate <= AgeDenomEnrEnd then do; AgeGroup = scan("&AGESTRAT.",i,' '); AgeGroupSort = i; AgeDenomEnrStart=max(threshdate,DenomEnrStart); MemberDays=AgeDenomEnrEnd-AgeDenomEnrStart+1; output; AgeDenomEnrEnd=threshdate-1; if AgeDenomEnrEnd < DenomEnrStart then leave; end; end; keep PatId AgeDenomEnrEnd AgeDenomEnrStart sex race hispanic AgeGroup AgeGroupSort Patient MemberDays threshdate; run; *AgeGroup level counts; proc means data=_Denom&Group.Age nway noprint; var MemberDays Patient; class AgeGroupSort PatId; output out=_AG sum(MemberDays)= max(Patient)=; run; proc means data=_AG nway noprint; var MemberDays Patient; class AgeGroupSort; output out=_AG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; proc sort data=_Denom&Group.Age; By Sex AgeGroupSort PatId; run; *Sex AgeGroup level counts; proc means data=_Denom&Group.Age noprint; var MemberDays Patient; by Sex AgeGroupSort PatId; output out=_SAG sum(MemberDays)= max(Patient)=; run; proc means data=_SAG noprint; var MemberDays Patient; by Sex AgeGroupSort; output out=_SAG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Sex AgeGroup year and Sex AgeGroup year month level counts; %CreateSums(infile=_Denom&Group.Age, outfile=_SAGYM, by=Sex AgeGroupSort, dimout=AgeGroupSort); proc sort data=_Denom&Group.Age; By AgeGroupSort PatId; run; *AgeGroup year and AgeGroup year month level counts; %CreateSums(infile=_Denom&Group.Age, outfile=_AGYM, by=AgeGroupSort, dimout=AgeGroupSort); *Race*AgeGroup level counts; %if "&race_out" = "Y" %then %do; proc sort data=_Denom&Group.Age; By race AgeGroupSort PatId; run; *race AgeGroup level counts; proc means data=_Denom&Group.Age noprint; var MemberDays Patient; by race AgeGroupSort PatId; output out=_RAG sum(MemberDays)= max(Patient)=; run; proc means data=_RAG noprint; var MemberDays Patient; by race AgeGroupSort; output out=_RAG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; *Hispanic*AgeGroup level counts; %if "&hispanic_out" = "Y" %then %do; proc sort data=_Denom&Group.Age; By hispanic AgeGroupSort PatId; run; *race AgeGroup level counts; proc means data=_Denom&Group.Age noprint; var MemberDays Patient; by hispanic AgeGroupSort PatId; output out=_HAG sum(MemberDays)= max(Patient)=; run; proc means data=_HAG noprint; var MemberDays Patient; by hispanic AgeGroupSort; output out=_HAG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; /*-------------------------------------------------*/ /* Geographic categories */ /*----------------------------------%---------------*/ /*Produce Uncertain level stratifications, regardless of Geographic region specified*/ %IF %STR(&GEOG.) ne %STR() & %LENGTH(&GEOG.) > 0. %then %do; /*Need to adjust denominator for zip_date*/ data _Denom&Group._un(rename=ZipDenomEnrEnd=DenomEnrEnd rename=ZipDenomEnrStart=DenomEnrStart); set _Denom&Group.; format ZipDenomEnrStart ZipDenomEnrEnd date9.; ZipDenomEnrStart=DenomEnrStart; ZipDenomEnrEnd=DenomEnrEnd; /*1. if zip_date < DenomEnrStart, full enrollment is certain*/ if . < zip_date < DenomEnrStart then do; MemberDays=DenomEnrEnd-DenomEnrStart+1; zip_uncertain = 'N'; output; end; /*2. if zip_date is missing, full enrollment is uncertain*/ else if zip_date = . then do; MemberDays=DenomEnrEnd-DenomEnrStart+1; zip_uncertain = 'Y'; output; end; /*3. if zip_date > ZipEnrEnd, then no uncertain time*/ else if zip_date >= DenomEnrEnd then do; MemberDays=DenomEnrEnd-DenomEnrStart+1; zip_uncertain = 'Y'; output; end; /*4. if DenomEnrStart <= zip_date <= DenomEnrEnd, then calculate uncertain time*/ else do; zip_uncertain = 'Y'; /*Adjust Denom enrollment*/ ZipDenomEnrEnd=zip_date; MemberDays=zip_date-DenomEnrStart+1; output; zip_uncertain = 'N'; ZipDenomEnrStart=zip_date+1; ZipDenomEnrEnd = DenomEnrEnd; MemberDays=ZipDenomEnrEnd-ZipDenomEnrStart; /*no +1 because member is certain on zip_date*/ output; end; keep PatId ZipDenomEnrEnd ZipDenomEnrStart sex Patient MemberDays zip_uncertain zip_date birth_date race hispanic zip3 state hhs_reg cb_reg; run; /*Uncertain*/ proc sort data=_Denom&Group._un; By zip_uncertain PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by zip_uncertain PatId; output out=_U sum(MemberDays)= max(Patient)=; run; proc means data=_U noprint; var MemberDays Patient; by zip_uncertain; output out=_U(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *create uncertain*sex level counts; proc sort data=_Denom&Group._un; By zip_uncertain sex PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by zip_uncertain sex PatId; output out=_U_S sum(MemberDays)= max(Patient)=; run; proc means data=_U_S noprint; var MemberDays Patient; by zip_uncertain sex; output out=_U_S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Create age group*uncertain counts; data _Denom&Group.Age_U(rename=AgeDenomEnrEnd=DenomEnrEnd rename=AgeDenomEnrStart=DenomEnrStart); set _Denom&Group._un; format AgeGroup $9.; format threshdate AgeDenomEnrStart AgeDenomEnrEnd date9.; AgeDenomEnrStart=DenomEnrStart; AgeDenomEnrEnd=DenomEnrEnd; *OrigMemberDays=DenomEnrEnd-DenomEnrStart+1; do i=&NBSTRAT. to 1 by -1; threshdate=intnx(scan("&AGETYP.",i),birth_date,input(scan("&AGETHRESH.",i),best.),'sameday'); if threshdate <= AgeDenomEnrEnd then do; AgeGroup = scan("&AGESTRAT.",i,' '); AgeGroupSort = i; AgeDenomEnrStart=max(threshdate,DenomEnrStart); MemberDays=AgeDenomEnrEnd-AgeDenomEnrStart+1; output; AgeDenomEnrEnd=threshdate-1; if AgeDenomEnrEnd < DenomEnrStart then leave; end; end; keep PatId AgeDenomEnrEnd AgeDenomEnrStart sex AgeGroup AgeGroupSort Patient MemberDays threshdate zip_uncertain; run; proc sort data=_Denom&Group.Age_U; by zip_uncertain AgeGroupSort PatId; run; proc means data=_Denom&Group.Age_U noprint; var MemberDays Patient; by zip_uncertain AgeGroupSort PatId; output out=_U_AG sum(MemberDays)= max(Patient)=; run; proc means data=_U_AG noprint; var MemberDays Patient; by zip_uncertain AgeGroupSort; output out=_U_AG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Create Year*uncertain level counts; proc sort data=_Denom&Group._un; by zip_uncertain PatId; run; %CreateSums(infile=_Denom&Group._un, outfile=_U_Y, by= zip_uncertain, dimout=zip_uncertain); proc means data=_U_Y nway noprint; var DenNumMemDays DenNumPts; class zip_uncertain year; where year ne . and month=.; output out=_U_Y (drop=_:) sum(DenNumMemDays)=DenNumMemDays sum(DenNumPts)=DenNumPts; run; %end; %macro geogdenoms(strata=); *Create State/Zip/Census/HHS level counts; proc sort data=_Denom&Group.; By &strata. PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by &strata. PatId; output out=_&strata. sum(MemberDays)= max(Patient)=; run; proc means data=_&strata. noprint; var MemberDays Patient; by &strata.; output out=_&strata.(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *create sex level counts; proc sort data=_Denom&Group.; By &strata. Sex PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by &strata. sex PatId; output out=_&strata._S sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._S noprint; var MemberDays Patient; by &strata. sex; output out=_&strata._S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *race/hispanic level counts; %if "&race_out" = "Y" %then %do; proc sort data=_Denom&Group.; By &strata. race PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by &strata. race PatId; output out=_&strata._R sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._R noprint; var MemberDays Patient; by &strata. race; output out=_&strata._R(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; %if "&hispanic_out" = "Y" %then %do; proc sort data=_Denom&Group.; By &strata. hispanic PatId; run; proc means data=_Denom&Group. noprint; var MemberDays Patient; by &strata. hispanic PatId; output out=_&strata._H sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._H noprint; var MemberDays Patient; by &strata. hispanic; output out=_&strata._H(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; *Create Age Group level counts; data _Denom&Group.Age&strata.(rename=AgeDenomEnrEnd=DenomEnrEnd rename=AgeDenomEnrStart=DenomEnrStart); set _Denom&Group.; format AgeGroup $9.; format threshdate AgeDenomEnrStart AgeDenomEnrEnd date9.; AgeDenomEnrStart=DenomEnrStart; AgeDenomEnrEnd=DenomEnrEnd; *OrigMemberDays=DenomEnrEnd-DenomEnrStart+1; do i=&NBSTRAT. to 1 by -1; threshdate=intnx(scan("&AGETYP.",i),birth_date,input(scan("&AGETHRESH.",i),best.),'sameday'); if threshdate <= AgeDenomEnrEnd then do; AgeGroup = scan("&AGESTRAT.",i,' '); AgeGroupSort = i; AgeDenomEnrStart=max(threshdate,DenomEnrStart); MemberDays=AgeDenomEnrEnd-AgeDenomEnrStart+1; output; AgeDenomEnrEnd=threshdate-1; if AgeDenomEnrEnd < DenomEnrStart then leave; end; end; keep PatId AgeDenomEnrEnd AgeDenomEnrStart sex &strata. AgeGroup AgeGroupSort Patient MemberDays threshdate; run; proc sort data=_Denom&Group.Age&strata.; by &strata. AgeGroupSort PatId; run; proc means data=_Denom&Group.Age&strata. noprint; var MemberDays Patient; by &strata. AgeGroupSort PatId; output out=_&strata._AG sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._AG noprint; var MemberDays Patient; by &strata. AgeGroupSort; output out=_&strata._AG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Create Year level counts (using %createsums in order to preserve future option to stratify by month); proc sort data=_Denom&Group.; by &strata. PatId; run; %CreateSums(infile=_Denom&Group., outfile=_&strata._Y, by=&strata., dimout=&strata.); proc means data=_&strata._Y nway noprint; var DenNumMemDays DenNumPts; class &strata. year; where year ne . and month=.; output out=_&strata._Y (drop=_:) sum(DenNumMemDays)=DenNumMemDays sum(DenNumPts)=DenNumPts; run; *create uncertain level counts; proc sort data=_Denom&Group._un; By &strata. zip_uncertain PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by &strata. zip_uncertain PatId; output out=_&strata._U sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._U noprint; var MemberDays Patient; by &strata. zip_uncertain; output out=_&strata._U(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *create uncertain*sex level counts; proc sort data=_Denom&Group._un; By &strata. zip_uncertain sex PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by &strata. zip_uncertain sex PatId; output out=_&strata._U_S sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._U_S noprint; var MemberDays Patient; by &strata. zip_uncertain sex; output out=_&strata._U_S(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *create uncertain*race and uncertain*hispanic level counts; %if "&race_out" = "Y" %then %do; proc sort data=_Denom&Group._un; By &strata. zip_uncertain race PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by &strata. zip_uncertain race PatId; output out=_&strata._U_R sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._U_R noprint; var MemberDays Patient; by &strata. zip_uncertain race; output out=_&strata._U_R(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; %if "&hispanic_out" = "Y" %then %do; proc sort data=_Denom&Group._un; By &strata. zip_uncertain hispanic PatId; run; proc means data=_Denom&Group._un noprint; var MemberDays Patient; by &strata. zip_uncertain hispanic PatId; output out=_&strata._U_H sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._U_H noprint; var MemberDays Patient; by &strata. zip_uncertain hispanic; output out=_&strata._U_H(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; %end; *Create age group*uncertain counts; data _Denom&Group.Age&strata._U(rename=AgeDenomEnrEnd=DenomEnrEnd rename=AgeDenomEnrStart=DenomEnrStart); set _Denom&Group._un; format AgeGroup $9.; format threshdate AgeDenomEnrStart AgeDenomEnrEnd date9.; AgeDenomEnrStart=DenomEnrStart; AgeDenomEnrEnd=DenomEnrEnd; *OrigMemberDays=DenomEnrEnd-DenomEnrStart+1; do i=&NBSTRAT. to 1 by -1; threshdate=intnx(scan("&AGETYP.",i),birth_date,input(scan("&AGETHRESH.",i),best.),'sameday'); if threshdate <= AgeDenomEnrEnd then do; AgeGroup = scan("&AGESTRAT.",i,' '); AgeGroupSort = i; AgeDenomEnrStart=max(threshdate,DenomEnrStart); MemberDays=AgeDenomEnrEnd-AgeDenomEnrStart+1; output; AgeDenomEnrEnd=threshdate-1; if AgeDenomEnrEnd < DenomEnrStart then leave; end; end; keep PatId AgeDenomEnrEnd AgeDenomEnrStart sex &strata. AgeGroup AgeGroupSort Patient MemberDays threshdate zip_uncertain; run; proc sort data=_Denom&Group.Age&strata._U; by &strata. zip_uncertain AgeGroupSort PatId; run; proc means data=_Denom&Group.Age&strata._U noprint; var MemberDays Patient; by &strata. zip_uncertain AgeGroupSort PatId; output out=_&strata._U_AG sum(MemberDays)= max(Patient)=; run; proc means data=_&strata._U_AG noprint; var MemberDays Patient; by &strata. zip_uncertain AgeGroupSort; output out=_&strata._U_AG(drop=_:) sum(MemberDays)=DenNumMemDays sum(Patient)=DenNumPts; run; *Create Year*uncertain level counts; proc sort data=_Denom&Group._un; by &strata. zip_uncertain PatId; run; %CreateSums(infile=_Denom&Group._un, outfile=_&strata._U_Y, by=&strata. zip_uncertain, dimout=zip_uncertain); proc means data=_&strata._U_Y nway noprint; var DenNumMemDays DenNumPts; class &strata. zip_uncertain year; where year ne . and month=.; output out=_&strata._U_Y (drop=_:) sum(DenNumMemDays)=DenNumMemDays sum(DenNumPts)=DenNumPts; run; %mend; %if %index(&GEOG.,ZIP3) > 0 %then %do; %geogdenoms(strata=zip3); %end; %if %index(&GEOG.,STATE) > 0 %then %do; %geogdenoms(strata=state); %end; %if %index(&GEOG.,HHS_REG) > 0 %then %do; %geogdenoms(strata=hhs_reg); %end; %if %index(&GEOG.,CB_REG) > 0 %then %do; %geogdenoms(strata=cb_reg); %end; *combining all counts; *relevant denominator counts are: Overall (_ALL) Year(_Y) Sex (_S) AgeGroup (_AG) Sex AgeGroup (_SAG) Sex AgeGroup Year (_SAGYM) Sex AgeGroup Year Month (_SAGYM) AgeGroup Year (_AGYM) AgeGroup Year Month (_AGYM) Sex Year(_SYM) Sex Year Month (_SYM) race (_R) Race Sex (_R_S) Race AgeGroup (_RAG) Race year (_RYM) Race year month (_RYM) Hispanic (_H) Hispanic Sex (_H_S) Hispanic AgeGroup (_HAG) Hispanic year (_HYM) Hispanic year month (_HYM) ZIP3 (_ZIP3) ZIP3 Uncertain (_ZIP3_U) ZIP3 Sex (_ZIP3_S) ZIP3 Uncertain Sex (_ZIP3_U_S) ZIP3 AgeGroup (_ZIP3_AG) ZIP3 Uncertain AgeGroup (_ZIP3_U_AG) ZIP3 Year (_ZIP3_Y) ZIP3 Uncertain Year (_ZIP3_U_Y) ZIP3 Race (_ZIP3_R) ZIP3 Uncertain Race (_ZIP3_U_R) ZIP3 Hispanic (_ZIP3_H) ZIP3 Uncertain Hispanic (_ZIP3_U_H) STATE (_STATE) STATE Uncertain (_STATE_U) STATE Sex (_STATE_S) STATE Uncertain Sex (_STATE_U_S) STATE AgeGroup (_STATE_AG) STATE Uncertain AgeGroup (_STATE_U_AG) STATE Year (_STATE_Y) STATE Uncertain Year (_STATE_U_Y) STATE Race (_STATE_R) STATE Uncertain Race (_STATE_U_R) STATE Hispanic (_STATE_H) STATE Uncertain Hispanic (_STATE_U_H) HHS_REG (_HHS_REG) HHS_REG Uncertain (_HHS_REG_U) HHS_REG Sex (_HHS_REG_S) HHS_REG Uncertain Sex (_HHS_REG_U_S) HHS_REG AgeGroup (_HHS_REG_AG) HHS_REG Uncertain AgeGroup (_HHS_REG_U_AG) HHS_REG Year (_HHS_REG_Y) HHS_REG Uncertain Year (_HHS_REG_U_Y) HHS_REG Race (_HHS_REG_R) HHS_REG Uncertain Race (_HHS_REG_U_R) HHS_REG Hispanic (_HHS_REG_H) HHS_REG Uncertain Hispanic (_HHS_REG_U_H) CB_REG (_CB_REG) CB_REG Uncertain (_CB_REG_U) CB_REG Sex (_CB_REG_S) CB_REG Uncertain Sex (_CB_REG_U_S) CB_REG AgeGroup (_CB_REG_AG) CB_REG Uncertain AgeGroup (_CB_REG_U_AG) CB_REG Year (_CB_REG_Y) CB_REG Uncertain Year (_CB_REG_U_Y) CB_REG Race (_CB_REG_R) CB_REG Uncertain Race (_CB_REG_U_R) CB_REG Hispanic (_CB_REG_H) CB_REG Uncertain Hispanic (_CB_REG_U_H) Uncertain (_U) Uncertain Sex (_U_S) Uncertain AgeGroup (_U_AG) Uncertain Year (_U_Y) *Note: Uncertain refers to uncertain zip code; data _DenomCounts; set _ALL(in=a) _Y(in=b) _S(in=c) _AG(in=d) _SAG(in=e) _SAGYM(in=f) _AGYM(in=g) _SYM(in=h) _YM(in=i) %if "&race_out" = "Y" %then %do; _R(in=j) _R_S(in=k) _RAG(in=l) _RYM(in=m) %end; %if "&hispanic_out" = "Y" %then %do; _H(in=n) _H_S(in=o) _HAG(in=p) _HYM(in=q) %end; %if %index(&GEOG.,ZIP3) > 0 %then %do; _ZIP3(in=ab) _ZIP3_U(in=ac) _ZIP3_S(in=ad) _ZIP3_U_S(in=ae) _ZIP3_AG(in=af) _ZIP3_U_AG(in=ag) _ZIP3_Y(in=ah) _ZIP3_U_Y(in=ai) %if "&race_out" = "Y" %then %do; _ZIP3_R(in=aj) _ZIP3_U_R(in=ak) %end; %if "&hispanic_out" = "Y" %then %do; _ZIP3_H(in=al) _ZIP3_U_H(in=am) %end; %end; %if %index(&GEOG.,STATE) > 0 %then %do; _STATE(in=bb) _STATE_U(in=bc) _STATE_S(in=bd) _STATE_U_S(in=be) _STATE_AG(in=bf) _STATE_U_AG(in=bg) _STATE_Y(in=bh) _STATE_U_Y(in=bi) %if "&race_out" = "Y" %then %do; _STATE_R(in=bj) _STATE_U_R(in=bk) %end; %if "&hispanic_out" = "Y" %then %do; _STATE_H(in=bl) _STATE_U_H(in=bm) %end; %end; /*uncertain*/ %IF %STR(&GEOG.) ne %STR() & %LENGTH(&GEOG.) > 0. %then %do; _U(in=un1) _U_S(in=un2) _U_AG(in=un3) _U_Y(in=un4) %end; %if %index(&GEOG.,HHS_REG) > 0 %then %do; _HHS_REG(in=cb) _HHS_REG_U(in=cc) _HHS_REG_S(in=cd) _HHS_REG_U_S(in=ce) _HHS_REG_AG(in=cf) _HHS_REG_U_AG(in=cg) _HHS_REG_Y(in=ch) _HHS_REG_U_Y(in=ci) %if "&race_out" = "Y" %then %do; _HHS_REG_R(in=cj) _HHS_REG_U_R(in=ck) %end; %if "&hispanic_out" = "Y" %then %do; _HHS_REG_H(in=cl) _HHS_REG_U_H(in=cm) %end; %end; %if %index(&GEOG.,CB_REG) > 0 %then %do; _CB_REG(in=db) _CB_REG_U(in=dc) _CB_REG_S(in=dd) _CB_REG_U_S(in=de) _CB_REG_AG(in=df) _CB_REG_U_AG(in=dg) _CB_REG_Y(in=dh) _CB_REG_U_Y(in=di) %if "&race_out" = "Y" %then %do; _CB_REG_R(in=dj) _CB_REG_U_R(in=dk) %end; %if "&hispanic_out" = "Y" %then %do; _CB_REG_H(in=dl) _CB_REG_U_H(in=dm) %end; %end; ; format group $40. race hispanic $1. zip3 state hhs_reg cb_reg $7.; group="&itgroup."; if a then level="000"; if b then level="001"; if c then level="002"; if d then level="003"; if e then level="004"; if f and month eq . then level="005"; if f and month ne . then level="006"; if g and month eq . then level="007"; if g and month ne . then level="008"; if h and month eq . then level="009"; if h and month ne . then level="010"; if i then level="011"; %if %index(&GEOG.,ZIP3) > 0 %then %do; if ab then level="020"; if ac then level="021"; if ad then level="022"; if ae then level="023"; if af then level="024"; if ag then level="025"; if ah then level="026"; if ai then level="027"; %end; %else %do; zip3 = ''; %end; %if %index(&GEOG.,STATE) > 0 %then %do; if bb then level="040"; if bc then level="041"; if bd then level="042"; if be then level="043"; if bf then level="044"; if bg then level="045"; if bh then level="046"; if bi then level="047"; %end; %else %do; state = ''; %end; %if %index(&GEOG.,HHS_REG) > 0 %then %do; if cb then level="070"; if cc then level="071"; if cd then level="072"; if ce then level="073"; if cf then level="074"; if cg then level="075"; if ch then level="076"; if ci then level="077"; %end; %else %do; hhs_reg = ''; %end; %if %index(&GEOG.,CB_REG) > 0 %then %do; if db then level="090"; if dc then level="091"; if dd then level="092"; if de then level="093"; if df then level="094"; if dg then level="095"; if dh then level="096"; if di then level="097"; %end; %else %do; cb_reg = ''; %end; %if "&race_out" = "Y" %then %do; %if %index(&GEOG.,ZIP3) > 0 %then %do; if aj then level="028"; if ak then level="029"; %end; %if %index(&GEOG.,STATE) > 0 %then %do; if bj then level="048"; if bk then level="049"; %end; %if %index(&GEOG.,HHS_REG) > 0 %then %do; if cj then level="078"; if ck then level="079"; %end; %if %index(&GEOG.,CB_REG) > 0 %then %do; if dj then level="098"; if dk then level="099"; %end; if j then level="110"; if k then level="111"; if l then level="112"; if m and month eq . then level="113"; if m and month ne . then level="114"; %end; %else %do; race = ''; %end; %if "&hispanic_out" = "Y" %then %do; %if %index(&GEOG.,ZIP3) > 0 %then %do; if al then level="030"; if am then level="031"; %end; %if %index(&GEOG.,STATE) > 0 %then %do; if bl then level="050"; if bm then level="051"; %end; %if %index(&GEOG.,HHS_REG) > 0 %then %do; if cl then level="080"; if cm then level="081"; %end; %if %index(&GEOG.,CB_REG) > 0 %then %do; if dl then level="100"; if dm then level="101"; %end; if n then level="115"; if o then level="116"; if p then level="117"; if q and month eq . then level="118"; if q and month ne . then level="119"; %end; %else %do; hispanic = ''; %end; %IF %STR(&GEOG.) ne %STR() & %LENGTH(&GEOG.) > 0. %then %do; if un1 then level = "060"; if un2 then level = "061"; if un3 then level = "062"; if un4 then level = "063"; %end; %else %do; zip_uncertain = ''; %end; run; *Store patient denoms to DPLocal; %IF %EVAL(&group.=1) %THEN %DO; data DPLocal.&RUNID._DenomCounts; retain Level group sex agegroupsort year month race hispanic zip3 state hhs_reg cb_reg zip_uncertain DenNumPts DenNumMemDays; set _DenomCounts; run; %END; %ELSE %DO; proc append base=DPLocal.&RUNID._DenomCounts data=_DenomCounts force; run; %END; *delete datasets; proc datasets nowarn noprint lib=work; delete _ALL _Y _S _AG _SAG _SAGYM _AGYM _SYM _YM _R _R_S _RAG _RYM _H _H_S _HAG _HYM _zip3: _state: _U _U_S _U_AG _U_Y _HHS_REG: _CB_REG:; quit; %put NOTE: ******** END OF MACRO: ms_cidadenom v1.14 ********; %mend ms_cidadenom;