/**************** This program tweaks the stock mhbounds program in two ways: 1. It removes the restriction on having a maximum of 99 strata. 2. It uses Rosenbaum's exact formulae for expectation and variance (as opposed to asymptotic formulae). These are more appropriate for small strata. One can use this iteration of mhbounds to conduct sensitivity analyses within strata of any size (not required to be 1:1 matched). ********************/ cap program drop rmhbounds program define rmhbounds, rclass version 9.0 syntax varname [if/], Gamma(numlist >=1 sort) [treated(varname) weight(varname) support(varname) stratum(varname) stratamat] tokenize `gamma' if `1'~=1 { local gamma = "1 `gamma'" } local rownum = wordcount("`gamma'") preserve if `"`treated'"' != `""' { /* if */ cap qui gen _treated = `treated' if _rc!=0 { di as error "Treatment variable _treated already defined" exit 198 } } /* if */ else if `"`treated'"' == `""' { /* else */ cap qui sum _treated if _rc!=0 { di as error "mhbounds.ado expects the variable _treated from psmatch2.ado or a user-provided variable" exit 198 } } /* else */ if `"`weight'"' != `""' { /* if */ cap qui gen _weight = `weight' if _rc!=0 { di as error "Weighting variable _weight already defined" exit 198 } } /* if */ else if `"`weight'"' == `""' { /* else */ cap qui sum _weight if _rc!=0 { di as error "mhbounds.ado expects the variable _weight from psmatch2.ado or a user-provided variable" exit 198 } } /* else */ if `"`support'"' != `""' { /* if */ cap qui gen _support = `support' if _rc!=0 { di as error "Support variable _support already defined" exit 198 } } /* if */ else if `"`support'"' == `""' { /* else */ cap qui sum _support if _rc!=0 { di as error "mhbounds.ado expects the variable _support from psmatch2.ado or a user-provided variable" exit 198 } } /* else */ tempvar _stratum if `"`stratum'"' != `""' { /* if */ // qui inspect `stratum' // local nstrata = r(N_unique) egen `_stratum' = group(`stratum') quietly levelsof `_stratum' local nstrata=r(r) // if `nstrata' == . { // di as error "Too many strata: maximum 99" // exit 198 // } } /* if */ else if `"`stratum'"' == `""' { /* else */ gen `_stratum'=1 local nstrata = 1 } /* else */ if "`if'"~="" { qui keep if `if' } qui drop if `varlist'==. qui keep if (_weight>0 & _weight!=.) qui keep if _support==1 foreach stratval of numlist 1/`nstrata' { /* strata */ if `nstrata'>1 { qui count if `_stratum'==`stratval' } else if `nstrata'==1 { qui count } if r(N)==0 { di as error "No treated in support, cannot compute Mantel-Haenszel (1959) statistic" exit 198 } qui sum `varlist' [aw=_weight] if _treated ==1 & `varlist'==1 & `_stratum'==`stratval' scalar y_1 = r(N) qui sum `varlist' [aw=_weight] if _treated ==0 & `varlist'==1 & `_stratum'==`stratval' scalar y_0 = r(N) qui sum _treated [aw=_weight] if _treated==1 & `_stratum'==`stratval' scalar n_1 = r(N) qui sum _treated [aw=_weight] if _treated==0 & `_stratum'==`stratval' scalar n_0 = r(N) qui sum `varlist' [aw=_weight] if `varlist'==1 & `_stratum'==`stratval' scalar y = r(N) qui sum _treated [aw=_weight] if `_stratum'==`stratval' scalar n = r(N) scalar lowbound = max(0,scalar(y)+scalar(n_1)-scalar(n)) scalar uppbound = min(scalar(y),scalar(n_1)) local j = 0 matrix outmat_`stratval' = J(`rownum',9,.) matrix colnames outmat_`stratval' = e_y mhplus mhminus p_mhplus p_mhminus E_mhplus E_mhminus V_mhplus V_mhminus foreach i of numlist `gamma' { /* grid points */ local j = `j' + 1 scalar e_y = `i' matrix outmat_`stratval'[`j',1] = `i' /* assumption: overestimation: grid points >= 1 */ forvalues k = `=lowbound'/`=uppbound' { if `k'>0 { local l = `k' - 1 } if `k'==`=lowbound' & `=lowbound'!=0 { scalar numsum`l' = 0 scalar densum`l' = 0 } scalar numexp`k' = `k'*comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y)^`k') scalar denexp`k' = comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y)^`k') if `k'==0 { scalar numsum0 = scalar(numexp0) scalar densum0 = scalar(denexp0) } else if `k'>0 { scalar numsum`k' = scalar(numsum`l') + scalar(numexp`k') scalar densum`k' = scalar(densum`l') + scalar(denexp`k') } } scalar Ebar_over=scalar(numsum`=uppbound')/scalar(densum`=uppbound') forvalues k = `=lowbound'/`=uppbound' { if `k'>0 { local l = `k' - 1 } if `k'==`=lowbound' & `=lowbound'!=0 { scalar Vnumsum`l' = 0 } scalar Vnumexp`k' = (`k'^2)*comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y)^`k') if `k'==0 { scalar Vnumsum0 = scalar(Vnumexp0) } else if `k'>0 { scalar Vnumsum`k' = scalar(Vnumsum`l') + scalar(Vnumexp`k') } } scalar Vbar_over=scalar(Vnumsum`=uppbound')/scalar(densum`=uppbound') - (scalar(Ebar_over)^2) // scalar b = -((scalar(e_y) - 1) * (scalar(n_1) + scalar(y)) + scalar(n)) // scalar a = (scalar(e_y) - 1) // scalar c = (scalar(e_y)*scalar(y)*scalar(n_1)) // scalar E_over1 = (-scalar(b) + (scalar(b)^2-4*scalar(a)*scalar(c))^(0.5))/(2*scalar(a)) // scalar E_over2 = (-scalar(b) - (scalar(b)^2-4*scalar(a)*scalar(c))^(0.5))/(2*scalar(a)) // // scalar Var_E_over1 = ( 1/scalar(E_over1) + 1/(scalar(y)-scalar(E_over1)) + 1/(scalar(n_1)-scalar(E_over1)) + 1 / (scalar(n)-scalar(y)-scalar(n_1)+scalar(E_over1)))^(-1) // scalar Var_E_over2 = ( 1/scalar(E_over2) + 1/(scalar(y)-scalar(E_over2)) + 1/(scalar(n_1)-scalar(E_over2)) + 1 / (scalar(n)-scalar(y)-scalar(n_1)+scalar(E_over2)))^(-1) * scalar mh_over1 = (scalar(y_1) - scalar(E_over1))^2 / scalar(Var_E_over1) * scalar mh_over2 = (scalar(y_1) - scalar(E_over2))^2 / scalar(Var_E_over2) scalar mh_over = (abs(scalar(y_1)-scalar(Ebar_over))-0.5)/sqrt(scalar(Vbar_over)) // scalar mh_over2 = (abs(scalar(y_1) - scalar(E_over2))-0.5) / sqrt(scalar(Var_E_over2)) // if (scalar(lowbound) <= scalar(E_over1)) & (scalar(E_over1)<=scalar(uppbound)) { matrix outmat_`stratval'[`j',2] = scalar(mh_over) matrix outmat_`stratval'[`j',4] = 1-normal(scalar(mh_over)) matrix outmat_`stratval'[`j',6] = scalar(Ebar_over) matrix outmat_`stratval'[`j',8] = scalar(Vbar_over) // } // else { // matrix outmat_`stratval'[`j',2] = scalar(mh_over2) // matrix outmat_`stratval'[`j',4] = 1-normal(scalar(mh_over2)) // matrix outmat_`stratval'[`j',6] = scalar(E_over2) // matrix outmat_`stratval'[`j',8] = scalar(Var_E_over2) // } /* assumption: underestimation: inverse values */ scalar e_y2 = 1/`i' forvalues k = `=lowbound'/`=uppbound' { if `k'>0 { local l = `k' - 1 } if `k'==0 & `=lowbound'!=0 { scalar numsum`l' = 0 scalar densum`l' = 0 } scalar numexp`k' = `k'*comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y2)^`k') scalar denexp`k' = comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y2)^`k') if `k'==0 { scalar numsum0 = scalar(numexp0) scalar densum0 = scalar(denexp0) } else if `k'>0 { scalar numsum`k' = scalar(numsum`l') + scalar(numexp`k') scalar densum`k' = scalar(densum`l') + scalar(denexp`k') } } scalar Ebar_under=scalar(numsum`=uppbound')/scalar(densum`=uppbound') forvalues k = `=lowbound'/`=uppbound' { if `k'>0 { local l = `k' - 1 } if `k'==0 & `=lowbound'!=0 { scalar Vnumsum`l' = 0 } scalar Vnumexp`k' = (`k'^2)*comb(scalar(y),`k')*comb(scalar(n)-scalar(y),scalar(n_1)-`k')*(scalar(e_y2)^`k') if `k'==0 { scalar Vnumsum0 = scalar(Vnumexp0) } else if `k'>0 { scalar Vnumsum`k' = scalar(Vnumsum`l') + scalar(Vnumexp`k') } } scalar Vbar_under=scalar(Vnumsum`=uppbound')/scalar(densum`=uppbound') - (scalar(Ebar_under)^2) // scalar b2 = -((scalar(e_y2) - 1) * (scalar(n_1) + scalar(y)) + scalar(n)) // scalar a2 = (scalar(e_y2) - 1) // scalar c2 = (scalar(e_y2)*scalar(y)*scalar(n_1)) // // scalar E_under1 = (-scalar(b2) + (scalar(b2)^2-4*scalar(a2)*scalar(c2))^(0.5))/(2*scalar(a2)) // scalar E_under2 = (-scalar(b2) - (scalar(b2)^2-4*scalar(a2)*scalar(c2))^(0.5))/(2*scalar(a2)) // // scalar Var_E_under1 = ( 1/scalar(E_under1) + 1/(scalar(y)-scalar(E_under1)) + 1/(scalar(n_1)-scalar(E_under1)) + 1 / (scalar(n)-scalar(y)-scalar(n_1)+scalar(E_under1)))^(-1) // scalar Var_E_under2 = ( 1/scalar(E_under2) + 1/(scalar(y)-scalar(E_under2)) + 1/(scalar(n_1)-scalar(E_under2)) + 1 / (scalar(n)-scalar(y)-scalar(n_1)+scalar(E_under2)))^(-1) * scalar mh_under1 = (scalar(y_1) - scalar(E_under1))^2 / scalar(Var_E_under1) * scalar mh_under2 = (scalar(y_1) - scalar(E_under2))^2 / scalar(Var_E_under2) scalar mh_under = (abs(scalar(y_1) - scalar(Ebar_under))-0.5) / sqrt(scalar(Vbar_under)) // scalar mh_under2 = (abs(scalar(y_1) - scalar(E_under2))-0.5) / sqrt(scalar(Var_E_under2)) // if (scalar(lowbound) <= scalar(E_under1)) & (scalar(E_under1)<=scalar(uppbound)) { matrix outmat_`stratval'[`j',3] = scalar(mh_under) matrix outmat_`stratval'[`j',5] = 1-normal(scalar(mh_under)) matrix outmat_`stratval'[`j',7] = scalar(Ebar_under) matrix outmat_`stratval'[`j',9] = scalar(Vbar_under) // } // else { // matrix outmat_`stratval'[`j',3] = scalar(mh_under2) // matrix outmat_`stratval'[`j',5] = 1-normal(scalar(mh_under2)) // matrix outmat_`stratval'[`j',7] = scalar(E_under2) // matrix outmat_`stratval'[`j',9] = scalar(Var_E_under2) // } if `j'==1 { matrix outmat_`stratval'[`j',6] = (scalar(n_1)*scalar(y)) / scalar(n) matrix outmat_`stratval'[`j',7] = (scalar(n_1)*scalar(y)) / scalar(n) matrix outmat_`stratval'[`j',8] = scalar(n_1)*scalar(n_0)*scalar(y)*(scalar(n)-scalar(y)) / ( scalar(n)^2*(scalar(n)-1) ) matrix outmat_`stratval'[`j',9] = scalar(n_1)*scalar(n_0)*scalar(y)*(scalar(n)-scalar(y)) / ( scalar(n)^2*(scalar(n)-1) ) matrix outmat_`stratval'[`j',2] = (abs(scalar(y_1) - outmat_`stratval'[`j',6])-0.5) / sqrt(outmat_`stratval'[`j',8]) matrix outmat_`stratval'[`j',4] = 1-normal(outmat_`stratval'[`j',2]) matrix outmat_`stratval'[`j',3] = (abs(scalar(y_1) - outmat_`stratval'[`j',7])-0.5) / sqrt(outmat_`stratval'[`j',9]) matrix outmat_`stratval'[`j',5] = 1-normal(outmat_`stratval'[`j',3]) } } /* grid points */ } /* strata */ matrix outmat0 = outmat_1[1...,6..9] if `nstrata'>1 { foreach stratval of numlist 2/`nstrata' { /* strata */ matrix outmat0 = outmat0 + outmat_`stratval'[1...,6..9] } } matrix outmat0 = outmat_1[1...,1..1] , outmat0[1...,1..4] matrix outmat = outmat0 local j = 0 matrix colnames outmat = e_y mhplus mhminus p_mhplus p_mhminus qui count if `varlist'==1 & _treated==1 local ytot = r(N) foreach i of numlist `gamma' { /* grid points */ local j = `j' + 1 matrix outmat[`j',2] = (abs(`ytot'-outmat0[`j',2])-0.5) / sqrt(outmat0[`j',4]) matrix outmat[`j',3] = (abs(`ytot'-outmat0[`j',3])-0.5) / sqrt(outmat0[`j',5]) matrix outmat[`j',4] = 1-normal(outmat[`j',2]) matrix outmat[`j',5] = 1-normal(outmat[`j',3]) } matrix drop outmat0 if `"`stratamat'"' == `""' { foreach stratval of numlist 1/`nstrata' { matrix drop outmat_`stratval' } } restore di di in green "Mantel-Haenszel (1959) bounds for variable " in yellow "`varlist'" di di in green "Gamma Q_mh+ Q_mh- p_mh+ p_mh-" di in green "-------------------------------------------------" local k = rowsof(outmat) forval i = 1 (1) `k' { di in yellow %5.3g outmat[`i',1] " " _c forval j = 2 (1) 5 { di in yellow %8.6g outmat[`i',`j'] " " _c } di } di di in green "Gamma : odds of differential assignment due to unobserved factors" di in green "Q_mh+ : Mantel-Haenszel statistic (assumption: overestimation of treatment effect)" di in green "Q_mh- : Mantel-Haenszel statistic (assumption: underestimation of treatment effect)" di in green "p_mh+ : significance level (assumption: overestimation of treatment effect)"" di in green "p_mh- : significance level (assumption: underestimation of treatment effect)"" end