version 13 program define _eventols, rclass #d; syntax varlist(fv ts numeric) [aw fw pw] [if] [in], /* Proxy for eta and covariates go in varlist. Can add fv ts later */ Panelvar(varname) /* Panel variable */ Timevar(varname) /* Time variable */ POLicyvar(varname) /* Policy variable */ LWindow(string) /* Left window. */ RWindow(string) /* Right window. */ [ w_type(string) /* Window defined by the user (numeric) or define window based on the data time limits (string: max or balanced) */ nofe /* No fixed effects */ note /* No time effects */ TRend(string) /* trend(a -1) Include a linear trend from time a to -1. Method can be either GMM or OLS*/ SAVek(string) /* Generate the time-to-event dummies, trend and keep them in the dataset */ nogen /* Do not generate k variables */ kvars(string) /* Stub for event dummies to include, if they have been generated already */ nodrop /* Do not drop _k variables */ norm(numlist integer max=1) /* Coefficiente to normalize */ reghdfe /* Use reghdfe for estimation */ IMPute(string) /*imputation on policyvar*/ addabsorb(string) /* Absorb additional variables in reghdfe */ DIFFavg /* Obtain regular DiD estimate implied by the model */ cohort(string) /* create or variable varname, where varname is categorical variable indicating cohort */ control_cohort(string) /* dummy variable indicating the control cohort */ REPeatedcs /*indicate that the input data is a repeated cross-sectional dataset*/ * ] ; #d cr marksample touse tempvar mkvarlist qui gen byte `mkvarlist' = `touse' tempname delta Vdelta bb VV * delta - event coefficients * bb - regression coefficients tempvar esample tousegen * For eventgenvars and for cohort generation, ignore missings in varlist mark `tousegen' `if' `in' **** parsers *parse savek if "`savek'"!="" parsesavek `savek' foreach l in savek noestimate saveint kreplace { loc `l' = r(`l') if "``l''"=="." loc `l' "" return loc `l' = "``l''" } *error messages for incorrect specification of noestimate if "`noestimate'"!="" & "`diffavg'"!="" { di as err _n "{bf:noestimate} and {bf: diffavg} not allowed simultaneously" exit 301 } if "`noestimate'"!="" & "`trend'"!="" { di as err _n "{bf:noestimate} and {bf:trend} not allowed simultaneously" exit 301 } *parse trend if "`trend'"!="" parsetrend `trend' loc trcoef = r(trcoef) loc methodt = r(methodt) loc saveov = r(saveoverlay) if "`saveov'"=="." loc saveov "" return loc saveov = "`saveov'" *error messages for incorrect specification of the trend option if "`trend'"!="" { tempvar ktrend trendy trendx if "`w_type'"=="numeric" { if `trcoef'<`lwindow'-1 | `trcoef'>`rwindow'+1 { di as err "{bf:trend} is outside estimation window." exit 301 } } if `trcoef'>=0 { di as err "trend coefficient must be smaller than 0" exit 301 } if `trcoef'==-1 { di as err "Trend extrapolation requires at least two pre-treatment points" exit 301 } if !inlist("`methodt'","ols","gmm"){ di as err "{bf:method(`methodt')} is not a valid suboption" exit 301 } if "`methodt'"=="ols" { loc ttrend "_ttrend" } else loc ttrend "" } * error messages for sun_abraham loc sun_abraham "" if "`cohort'"!="" { di as text _n "You have specified the {bf:cohort} or the {bf:sunabraham} option" di as text "Event-time coefficients will be estimated with the Interaction Weighted Estimator of Sun and Abraham (2021)" loc sun_abraham "sun_abraham" } if "`saveint'"!="" & "`sun_abraham'"==""{ di as err _n "Suboption {bf:saveint} can only be specified with {bf:cohort}" exit 301 } *gen & kvars if ("`gen'"!="" & "`kvars'"=="") | ("`gen'"=="" & "`kvars'"!="") { di as err _n "Options -nogen- and -kvars- must be specified together" exit 301 } * Parse cohort and control_cohort if "`cohort'"!="" { * Parse to distinguish if cohort variable is given or should be created parsecohort `cohort' loc cohortvar = r(cohortvar) loc cohortforce = r(force) loc cohorttype = r(cohorttype) loc cohortsave = r(save) loc cohortreplace = r(replace) * Parse control_cohort if "`control_cohort'" == "" { loc control_cohorttype "create" loc control_cohortvar = "." } else { parsecontrol_cohort `control_cohort' loc control_cohortvar = r(control_cohortvar) loc control_cohorttype = r(control_cohorttype) loc control_cohortsave = r(save) loc control_cohortreplace = r(replace) } * Check consistency: if cohort and control_cohort are both given, the cohort variable should be missing if control_cohort is 1 unless force if "`cohorttype'"=="variable" & "`cohortforce'"=="." & "`control_cohorttype'"=="variable" { cap assert `cohortvar'==. if `control_cohortvar' == 1 & `touse' if _rc { di as err _n "Cohort variable `cohortvar' is not missing for the control cohort selected by `control_cohort'" exit 301 } cap assert `control_cohortvar'==0 | `control_cohortvar'==1 if `touse' if _rc { di as err _n "Control cohort indicator variable `control_cohortvar' is not binary" exit 301 } cap assert `control_cohortvar'==0 if `cohortvar'!=. & `touse' if _rc { di as err _n "Control cohort variable `control_cohortvar' is not zero for treated cohorts defined by `cohortvar'" exit 301 } } else if "`cohortvar'"!="" & "`cohortforce'"=="force" & "`cohorttype'"=="variable" { di as text _n "Treatment cohort variable `cohortvar' was not checked for consistency with the policy" di as text "variable `policyvar' or the control cohort variable `control_cohortvar'" } } *** loc i = "`panelvar'" loc t = "`timevar'" loc z = "`policyvar'" * Set norm to -1 if missing if "`norm'"=="" loc norm = -1 if "`gen'" != "nogen" { if "`impute'"!=""{ tempvar rr qui gen double `rr'=. } _eventgenvars if `tousegen', panelvar(`panelvar') timevar(`timevar') policyvar(`policyvar') lwindow(`lwindow') rwindow(`rwindow') w_type(`w_type') trcoef(`trcoef') methodt(`methodt') norm(`norm') impute(`impute') rr(`rr') mkvarlist(`mkvarlist') `repeatedcs' loc included=r(included) loc names=r(names) loc komittrend=r(komittrend) loc binnorev = r(binnorev) loc ambiguous = r(ambiguous) if "`komittrend'"=="." loc komittrend = "" *bring the imputed policyvar loc impute=r(impute) if "`impute'"=="." loc impute = "" *if imputation succeeded: if "`impute'"!="" { tempvar zimp qui gen double `zimp'=`rr' loc z="`zimp'" } else loc z = "`policyvar'" *if window was max or balanced, use calculated left and right window limits if "`w_type'"=="string" { loc lwindow = r(lwindow) loc rwindow = r(rwindow) } } else { loc kvstub "`kvars'" loc j=1 loc names "" loc included "" foreach var of varlist `kvstub'* { if `norm' < 0 loc kvomit = "m`=abs(`norm')'" else loc kvomit "p`=abs(`norm')'" if "`var'"=="`kvstub'_evtime" | "`var'" == "`kvstub'_eq_`kvomit'" continue if "`kvstub'"!="_k" { loc sub : subinstr local var "`kvstub'" "_k", all qui clonevar `sub' = `var' } else { loc sub = "`var'" } if `j'==1 loc names `""`sub'""' else loc names `"`names'.."`sub'""' * " loc included "`included' `sub'" loc ++ j } } loc komit "`norm'`komittrend'" loc komit = strtrim("`komit'") *split varlist (depvar and independentvars): change variables' order in the regression. Necessary for GMM matrix operations loc nvars: word count(`varlist') tokenize `varlist' loc depenvar `1' if `nvars'>1 { forval k=2(1)`nvars'{ loc indepvars "`indepvars' ``k''" } } else loc indepvars "" ******** SA ******* if "`sun_abraham'"!=""{ * Another consistency check: obs with cohort should have some treated values, obs with control_cohort should have some untreated values * Not checking that all cohorts have treated values, not checking that z is always zero for never treat, so these are minimal checks if "`cohorttype'"=="" { qui levelsof z if `cohortvar'!=. & `tousegen|' if r(levels)=="0" { di as err _n "Treated observations according to cohort variable `cohortvar' are inconsistent" di as err "with values of the policy variable `z'" exit 301 } if "`control_cohorttype'"=="variable" { qui count if `z'==0 & `control_cohortvar' & `tousegen' if r(N)==0 { di as err _n "Untreated observations according to control cohort variable `control_cohortvar'" di as err "are inconsistent with values of the policy variable `z'" } } } * Create the cohort variable if requested if "`cohorttype'"=="create" { cap assert `binnorev'==1 if _rc { di as err _n "The policy variable is not binary or treatment reverts" di as err "Cannot create treatment cohort variables" exit 301 } tempvar timet qui gen `timet'=`timevar' if `policyvar'==1 & `tousegen' cap confirm var _cohort if !_rc { di as err _n "You have a variable named _cohort. _cohort is reserved for internal -xtevent- variables." di as err _n "Please rename this variable before proceeding." exit 110 } qui bys `panelvar' : egen _cohort = min(`timet') if `tousegen' loc cohortvar "_cohort" } * If control_cohort is missing, take the values with cohort = . if "`cohortvar'"!="" & "`control_cohorttype'"=="create" { if "`control_cohort'"=="" di as txt _n "Control cohort not specified. Using values with cohort variable == . as the control cohort" else di as txt _n "Using values with cohort variable == . as the control cohort" cap confirm var _control_cohort if !_rc { di as err _n "You have a variable named _control_cohort. _control_cohort is reserved for internal -xtevent- variables." di as err _n "Please rename this variable before proceeding." exit 110 } gen _control_cohort = (`cohortvar' == .) loc control_cohortvar "_control_cohort" } * Parse the dependent variable local lhs = "`depenvar'" local rel_time_list = "`included'" * Convert the varlist of relative time indicators to nvarlist local nvarlist "" // copies of relative time indicators with control cohort set to zero local dvarlist "" // for display foreach l of varlist `rel_time_list' { local dvarlist "`dvarlist' `l'" tempname n`l' qui gen `n`l'' = `l' qui replace `n`l'' = 0 if `control_cohortvar' == 1 local nvarlist "`nvarlist' `n`l''" } * Get cohort count and count of relative time qui levelsof `cohortvar' if `control_cohortvar' == 0, local(cohort_list) local nrel_times: word count `nvarlist' local ncohort: word count `cohort_list' **** step 2 * Initiate empty matrix for weights tempname bb ff_w * Loop over cohort and get cohort shares for relative times local nresidlist "" foreach yy of local cohort_list { tempvar cohort_ind resid`yy' qui gen `cohort_ind' = (`cohortvar' == `yy') qui _regress `cohort_ind' `nvarlist' if `touse' & `control_cohortvar' == 0 [`weight'`exp'] , nocons mat `bb' = e(b) matrix `ff_w' = nullmat(`ff_w') \ `bb' *di "`yy'" *mat li `ff_w' qui predict double `resid`yy'', resid local nresidlist "`nresidlist' `resid`yy''" //list of variables of residuals } * Get VCV estimate for the cohort shares using avar * In case users have not set relative time indicators to zero for control cohort * Manually restrict the sample to non-control cohort tempname XX Sxx Sxxi S KSxxi Sigma_ff qui mat accum `XX' = `nvarlist' if `touse' & `control_cohortvar' == 0 [`weight'`exp'], nocons mat `Sxx' = `XX'*1/r(N) mat `Sxxi' = syminv(`Sxx') qui avar (`nresidlist') (`nvarlist') if `touse' & `control_cohortvar' == 0 [`weight'`exp'], nocons robust mat `S' = r(S) mat `KSxxi' = I(`ncohort')#`Sxxi' mat `Sigma_ff' = `KSxxi'*`S'*`KSxxi'*1/r(N) // Note that the normalization is slightly different from the paper // The scaling factor is 1/N for N the obs of cross-sectional units // But here estimates are on the panel, which is why it is 1/NT instead // Should cancel out for balanced panel, but unbalanced panel is a TODO // as of Jan 23, 2023, Sun and Abraham have not updated this code section. //According to the expression for the asymptotic variance in the proof of proposition 6 (https://arxiv.org/pdf/1804.05785v2.pdf#page=50), the scaling factor is 1/N. **** step 1 * Prepare interaction terms for the interacted regression local cohort_rel_varlist "" // hold the temp varnames local interact_varlist "" // fill in with name of interaction variables foreach l of varlist `rel_time_list' { foreach yy of local cohort_list { tempvar n`n`l''_`yy' qui gen `n`n`l''_`yy'' = (`cohortvar' == `yy') * `n`l' ' // TODO: might be more efficient to use the c. operator if format w/o missing local cohort_rel_varlist "`cohort_rel_varlist' `n`n`l''_`yy''" loc lnumber : subinstr local l "_k_eq_" "" loc interact_varlist "`interact_varlist' _interact_`lnumber'_c`yy'" if "`saveint'"!=""{ qui gen _interact_`lnumber'_c`yy' = `n`n`l''_`yy'' } } } local bcohort_rel_varlist "" // hold the interaction varnames foreach l of varlist `rel_time_list' { foreach yy of local cohort_list { local bcohort_rel_varlist "`bcohort_rel_varlist' `l'_x_`yy'" } } * Estimate the interacted regression as an xtevent regression: } ***** Main regression tempname reg_base if "`noestimate'"==""{ if "`te'" == "note" loc te "" else loc te "i.`t'" * If gmm trend run regression before adjustment quietly if ("`methodt'"=="gmm" | "`sun_abraham'"!="") loc q "quietly" else loc q "" if "`reghdfe'"=="" { if "`fe'" == "nofe" { loc abs "" loc cmd "regress" } else { loc abs "absorb(`i')" loc cmd "areg" } if "`sun_abraham'"=="" { `q' `cmd' `depenvar' `included' `indepvars' `te' `ttrend' [`weight'`exp'] if `touse', `abs' `options' } else { qui `cmd' `depenvar' `cohort_rel_varlist' `indepvars' `te' `ttrend' [`weight'`exp'] if `touse', `abs' `options' } } else { loc cmd "reghdfe" loc noabsorb "" *absorb nothing if "`fe'" == "nofe" & "`te'"=="" & "`addabsorb'"=="" { loc noabsorb "noabsorb" loc abs "" } *absorb only one else if "`fe'" == "nofe" & "`te'"=="" & "`addabsorb'"!="" { loc abs "absorb(`addabsorb')" } else if "`fe'" == "nofe" & "`te'"!="" & "`addabsorb'"=="" { loc abs "absorb(`t')" } else if "`fe'" != "nofe" & "`te'"=="" & "`addabsorb'"=="" { loc abs "absorb(`i')" } *absorb two else if "`fe'" == "nofe" & "`te'"!="" & "`addabsorb'"!="" { loc abs "absorb(`t' `addabsorb')" } else if "`fe'" != "nofe" & "`te'"=="" & "`addabsorb'"!="" { loc abs "absorb(`i' `addabsorb')" } else if "`fe'" != "nofe" & "`te'"!="" & "`addabsorb'"=="" { loc abs "absorb(`i' `t')" } *absorb three else if "`fe'" != "nofe" & "`te'"!="" & "`addabsorb'"!="" { loc abs "absorb(`i' `t' `addabsorb')" } * else { loc abs "absorb(`i' `t' `addabsorb')" } if "`sun_abraham'"=="" { `q' reghdfe `depenvar' `included' `indepvars' `ttrend' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' } else { qui reghdfe `depenvar' `cohort_rel_varlist' `indepvars' `ttrend' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' } } *** SA: combine cohort shares and output from the interacted regression if "`sun_abraham'"!=""{ tempname evt_bb b evt_VV V local bcohort_rel_varlist "`bcohort_rel_varlist' `covariates'" // TODO: does not catch the constant term if reghdfe includes a constant. mat `b' = e(b) mata st_matrix("`V'",diagonal(st_matrix("e(V)"))') * Convert the delta estimate vector to a matrix where each column is a relative time local end = 0 forval i = 1/`nrel_times' { local start = `end'+1 local end = `start'+`ncohort'-1 mat `b'`i' = `b'[.,`start'..`end'] mat `evt_bb' = nullmat(`evt_bb') \ `b'`i' mat `V'`i' = `V'[.,`start'..`end'] mat `evt_VV' = nullmat(`evt_VV') \ `V'`i' } mat `evt_bb' = `evt_bb'' mat `evt_VV' = `evt_VV'' * Take weighted average for IW estimators tempname w delta b_iw nc nr mata: `w' = st_matrix("`ff_w'") mata: `delta' = st_matrix("`evt_bb'") mata: `b_iw' = colsum(`w':* `delta') mata: st_matrix("`b_iw'", `b_iw') mata: `nc' = rows(`w') mata: `nr' = cols(`w') * Ptwise variance from cohort share estimation and interacted regression tempname VV wlong V_iw V_iw_diag * VCV from the interacted regression mata: `VV' = st_matrix("e(V)") mata: `VV' = `VV'[1..`nr'*`nc',1..`nr'*`nc'] // in case reghdfe reports _cons mata: `wlong' = `w'':*J(1,`nc',e(1,`nr')') // create a "Toeplitz" matrix convolution forval i=2/`nrel_times' { mata: `wlong' = (`wlong', `w'':*J(1,`nc',e(`i',`nr')')) } mata: `V_iw' = `wlong'*`VV'*`wlong'' * VCV from cohort share estimation tempname Vshare Vshare_evt share_idx Sigma_l mata: `Vshare' = st_matrix("`Sigma_ff'") mata: `Sigma_l' = J(0,0,.) mata: `share_idx' = range(0,(`nc'-1)*`nr',`nr') forval i=1/`nrel_times' { forval j=1/`i' { mata: `Vshare_evt' = `Vshare'[`share_idx':+`i', `share_idx':+`j'] mata: `V_iw'[`i',`j'] = `V_iw'[`i',`j'] + (`delta'[,`i'])'*`Vshare_evt'*(`delta'[,`j']) // mata: `Sigma_l' = blockdiag(`Sigma_l',`Vshare_evt') } } mata: `V_iw' = makesymmetric(`V_iw') // mata: `V_iw' = `V_iw'' // mata: st_matrix("`Sigma_l'", `Sigma_l') mata: st_matrix("`V_iw'", `V_iw') mata: `V_iw_diag' = diag(`V_iw') mata: st_matrix("`V_iw_diag'", `V_iw_diag') mata: mata drop `b_iw' `VV' `nc' `nr' `w' `wlong' `Vshare' `share_idx' `delta' `Vshare_evt' `Sigma_l' `V_iw' `V_iw_diag' matrix colnames `b_iw' = `dvarlist' matrix colnames `V_iw' = `dvarlist' matrix rownames `V_iw' = `dvarlist' matrix rownames `ff_w' = `cohort_list' matrix colnames `ff_w' = `dvarlist' matrix rownames `Sigma_ff' = `cohort_list' matrix colnames `Sigma_ff' = `cohort_list' matrix colnames `evt_bb' = `dvarlist' matrix rownames `evt_bb' = `cohort_list' matrix colnames `evt_VV' = `dvarlist' matrix rownames `evt_VV' = `cohort_list' *save b and V from the interacted regression tempname b_ir V_ir mat `b_ir' = e(b) mat `V_ir' = e(V) loc n_interact_varlist = wordcount("`interact_varlist'") loc b_ir_cl : colnames `b_ir' loc n_b_ir_cl = wordcount("`b_ir_cl'") loc b_ir_varlist = "" forvalues i=1/`n_b_ir_cl' { if `i' <= `n_interact_varlist' { loc j = word("`interact_varlist'" ,`i') loc h = word("`b_ir_cl'", `i') loc k = regexr("`h'", "__\w+", "`j'") loc b_ir_varlist = "`b_ir_varlist' `k'" } else { loc k = word("`b_ir_cl'" ,`i') loc b_ir_varlist = "`b_ir_varlist' `k'" } } matrix colnames `b_ir' = `b_ir_varlist' matrix rownames `V_ir' = `b_ir_varlist' matrix colnames `V_ir' = `b_ir_varlist' *replace b and V in the interacted regression tempname est_sun_abraham repostdelta `b_iw' `V_iw' * Display results if "`methodt'"=="gmm" loc qq "quietly" `qq' _coef_table_header `qq' _coef_table , bmatrix(e(b)) vmatrix(e(V)) _estimates hold `est_sun_abraham', copy } * Return coefficients and variance matrix of the delta k estimates separately mat `bb'=e(b) mat `VV'=e(V) mat `delta' = `bb'[1,`names'] mat `Vdelta' = `VV'[`names',`names'] loc df = e(df_r) gen byte `esample' = e(sample) } * Trend adjustment by GMM if "`methodt'"=="gmm" { tempname deltatoadj Vtoadj deltaadj Vadj bbadj VVadj loc gmmtrendsc = `trcoef' loc start = "_k_eq_m`=abs(`trcoef')'" * Notice that here I am requiring normalization in -1 mat `deltatoadj' = `delta'[1,"`start'".."_k_eq_m2"] mat `deltatoadj' = [`deltatoadj',0] mat `deltatoadj' = `deltatoadj'' mat `Vtoadj' = `Vdelta'["`start'".."_k_eq_m2","`start'".."_k_eq_m2"] mat `Vtoadj' = [`Vtoadj',J(`=abs(`trcoef')-1',1,0)] mat `Vtoadj' = (`Vtoadj'\J(1,`=abs(`trcoef')',0)) * Get vector of other coefficients, and their variance tempname Omegapsi_st Omegadeltapsi_st Valladj gmm_trcoefs * Check whether output included covariates local bnames : colnames e(b) loc covars: list bnames - included if "`covars'"!="" loc icovars 1 else loc icovars 0 if `icovars'==1 { loc deltanames : colnames(`delta') loc deltanames1: word 1 of `deltanames' loc deltanamesw: word count `deltanames' loc deltanamesl: word `deltanamesw' of `deltanames' loc Vnames : colnames(`VV') loc psinames: list Vnames - deltanames loc psinames1 : word 1 of `psinames' mat psi = `bb'[1,"`psinames1'"...] mat `Omegapsi_st' = `VV'["`psinames1'"...,"`psinames1'"...] mat `Omegadeltapsi_st' = `VV'["`deltanames1'".."`deltanamesl'","`psinames1'"...] } mata: adjdelta(`gmmtrendsc',`lwindow',`rwindow', `icovars',"`deltatoadj'","`Vdelta'","`Vtoadj'","`delta'","`Omegapsi_st'","`Omegadeltapsi_st'","`gmm_trcoefs'","`deltaadj'","`Vadj'","`Valladj'") * Post the new results loc dnames : colnames(`delta') *change column an row names mat colnames `deltaadj' = `dnames' mat colnames `gmm_trcoefs' = `dnames' mat colnames `Vadj' = `dnames' mat rownames `Vadj' = `dnames' mat `bbadj' = `bb' mat `VVadj' = `VV' *insert adjusted values foreach i in `dnames' { mat `bbadj'[1,colnumb("`bb'","`i'")]= `deltaadj'[1,"`i'"] foreach j in `dnames' { mat `VVadj'[rownumb("`VVadj'","`j'"),colnumb("`VVadj'","`i'")]= `Vadj'["`j'","`i'"] } } * Post the new results (V matrix for all coeffs) tempname VValladj loc allnames : colnames(`bb') mat colnames `Valladj' = `allnames' mat rownames `Valladj' = `allnames' mat `VValladj' = `VV' foreach i in `allnames' { foreach j in `allnames' { mat `VValladj'[rownumb("`VValladj'","`j'"),colnumb("`VValladj'","`i'")]= `Valladj'["`j'","`i'"] } } *reset delta & Vdelta so xteventplot will plot the right coefficients mat `delta' = `bbadj'[1,`names'] mat `Vdelta' = `VVadj'[`names',`names'] *reset b and V so the returned matrices are the adjusted ones mat `bb'=`bbadj' mat `VV'=`VValladj' *repostdelta `bbadj' `VVadj' repostdelta `bbadj' `VValladj' _coef_table_header _coef_table , bmatrix(e(b)) vmatrix(e(V)) } * DiD estimate if "`diffavg'"!=""{ *list of omitted coefficients loc komit_comma : subinstr local komit " " ",", all * fill in lists of pre and post coefficients loc pre_plus "" loc post_plus "" if "`trend'"!="" { di as txt _n "When trend is included, the endpoints are excluded from the calculation of the difference" di as txt "in average coefficients between the pre and post periods." loc llimit = `lwindow' loc rlimit = `rwindow' loc postden = `rwindow'+1 loc preden = -`lwindow' } else { loc llimit = `lwindow'-1 loc rlimit = `rwindow'+1 loc postden = `rwindow'+2 loc preden = -`lwindow'+1 } forvalues v = `llimit'/`rlimit' { if inlist(`v', `komit_comma') continue if `v'<0 { loc pre_plus "`pre_plus' _k_eq_m`=-`v''" } else { loc post_plus "`post_plus' _k_eq_p`=`v''" } } loc pre_plus = strtrim("`pre_plus'") if "`pre_plus'"=="" { di as err "No pre-event coefficients to calulate the difference in averages" exit 301 } loc post_plus = strtrim("`post_plus'") if "`post_plus'"=="" { di as err "No post-event coefficients to calulate the difference in averages" exit 301 } loc pre_plus : subinstr local pre_plus " " " + ", all loc post_plus : subinstr local post_plus " " " + ", all di as text _n "Difference in pre and post-period averages from lincom:" lincom ((`post_plus') / (`postden')) - ((`pre_plus') / (`preden')), cformat(%9.4g) } * Variables for overlay plot if trend if "`saveov'"!="" { _estimates hold mainols unab included2 : _k* loc toexc "_k_eq_m1" loc included2: list local included2 - toexc *estimate the contrafactual: no adjusting by trend. only exclude event-time dummy -1 *trend excludes from trend (e.g. -3) to -1 if "`sun_abraham'"!="" _estimates unhold `est_sun_abraham' //call the SA estimations that have not been corrected by trend else { if "`reghdfe'"== "" { qui _regress `varlist' `included2' `te' [`weight'`exp'] if `touse', `abs' `options' } else { qui reghdfe `varlist' `included2' [`weight'`exp'] if `touse', `abs' `noabsorb' `options' } } loc j=1 loc names2 "" foreach var in `included2' { if `j'==1 loc names2 `""`var'""' else loc names2 `"`names2'.."`var'""' * " loc ++ j } * Generate the trend to plot qui gen double `trendy'=. qui gen int `trendx'=. loc j=1 forv c=`trcoef'(1)-1 { loc absc = abs(`c') if "`methodt'"=="ols"{ if `c'!=-1 qui replace `trendy'=_b[_k_eq_m`absc'] in `j' else if `c'==-1 qui replace `trendy'=0 in `j' } else if "`methodt'"=="gmm"{ if `c'!=-1 qui replace `trendy'=`gmm_trcoefs'[1,"_k_eq_m`absc'"] in `j' else if `c'==-1 qui replace `trendy'=0 in `j' } qui replace `trendx'=`c' in `j' loc ++ j } tempname bbov VVov deltaov Vdeltaov mattrendy mattrendx mat `bbov'=e(b) mat `VVov'=e(V) mat `deltaov' = `bbov'[1,`names2'] mat `Vdeltaov' = `VVov'[`names2',`names2'] mkmat `trendy', matrix(`mattrendy') nomiss mkmat `trendx', matrix(`mattrendx') nomiss _estimates unhold mainols } if "`sun_abraham'"!="" { if "`control_cohorttype'"=="create" { if "`control_cohortsave'"=="save" { cap confirm variable `policyvar'_control_cohort if !_rc { if "`control_cohortreplace'"=="replace" { drop `policyvar'_control_cohort ren _control_cohort `policyvar'_control_cohort } else { di as err _n "variable `policyvar'_control_cohort already exists" drop _control_cohort exit 301 } } else { ren _control_cohort `policyvar'_control_cohort } } else drop _control_cohort } if "`cohorttype'"=="create" { if "`cohortsave'"=="save" { cap confirm variable `policyvar'_cohort if !_rc { if "`cohortreplace'"=="replace" { drop `policyvar'_cohort ren _cohort `policyvar'_cohort } else { di as err _n "variable `policyvar'_cohort already exists" drop _cohort exit 301 } } else { ren _cohort `policyvar'_cohort } } else drop _cohort } } *save a temporary copy of the event-time dummy corresponding to the normalized period before dropping that dummy variable tempvar temp_k if `norm' < 0 loc kvomit = "m`=abs(`norm')'" else loc kvomit "p`=abs(`norm')'" qui gen `temp_k'=_k_eq_`kvomit' *recover omitted k vars loc kvars_omit "" if "`komit'"!=""{ foreach k in `komit' { if `k'<0 loc kvars_omit "`kvars_omit' _k_eq_m`=abs(`k')'" else loc kvars_omit "`kvars_omit' _k_eq_p`=abs(`k')'" } } *full list of event-time dummies (included + omitted) loc eventtd = "`included' `kvars_omit'" * Drop variables if "`savek'" == "" & "`drop'"!="nodrop" { cap confirm var `eventtd', exact if !_rc drop `eventtd' cap confirm var __k, exact if !_rc qui drop __k if "`methodt'"=="ols" { cap confirm var _ttrend, exact if !_rc qui drop _ttrend } if "`saveint'"!="" { cap confirm var `interact_varlist', exact if !_rc qui drop `interact_varlist' } } else if "`savek'" != "" & "`drop'"!="nodrop" { *change prefix loc eventtd_savek : subinstr local eventtd "_k" "`savek'", all *If replace suboption, drop the existing variables before renaming the recently created ones if "`kreplace'"!="" { *event-time dummies foreach v in `eventtd_savek' { cap confirm variable `v', exact if !_rc drop `v' } *event-time variable cap confirm variable `savek'_evtime, exact if !_rc drop `savek'_evtime *trend cap confirm variable `savek'_trend, exact if !_rc drop `savek'_trend *interaction variables foreach v in `interact_varlist' { cap confirm variable `savek'`v', exact if !_rc drop `savek'`v' } } *Check that variables don't exist cap confirm variable `eventtd_savek', exact if !_rc { di as err _n "You specified to save the event-time dummy variables using the prefix {bf:`savek'}, but you already have event-time dummy variables saved with that prefix." di as err _n "Use the {bf:replace} suboption to replace the existing variables." exit 110 } cap confirm variable `savek'_evtime, exact if !_rc { di as err _n "You specified to save the event-time variable using the prefix {bf:`savek'}, but you already have an event-time variable saved with that prefix." di as err _n "Use the {bf:replace} suboption to replace the existing variable." exit 110 } if "`trend'"!=""{ cap confirm variable `savek'_trend, exact if !_rc { di as err _n "You specified to save the trend variable using the prefix {bf:`savek'}, but you already have a trend variable saved with that prefix." di as err _n "Use the {bf:replace} suboption to replace the existing variable." exit 110 } } if "`sun_abraham'"!="" { foreach v in `interact_varlist' { cap confirm variable `savek'`v', exact if !_rc { di as err _n "You have variable names with the {bf:`savek'_interact} prefix" di as err _n "{bf:`savek'_interact} is reserved for the interaction variables in Sun-and-Abraham estimation" di as err _n "Use the {bf:replace} suboption to replace the existing variables." exit 110 } } } ren __k `savek'_evtime ren (`eventtd') (`eventtd_savek') if "`methodt'"=="ols" ren _ttrend `savek'_trend loc interact_varlist_savek : subinstr local interact_varlist "_interact" "`savek'_interact", all if "`saveint'"!="" ren (`interact_varlist') (`interact_varlist_savek') } *skip the rest of the program if the user indicated not to estimate return local noestimate "`noestimate'" if "`noestimate'"!="" exit * Calculate mean before change in policy for 2nd axis in plot * This needs to be relative to normalization tokenize `varlist' loc depvar "`1'" qui su `1' if `temp_k'!=0 & `temp_k'!=. & `esample', meanonly loc y1 = r(mean) * Returns if "`w_type'"=="string" { return local lwindow = `lwindow' return local rwindow = `rwindow' } return matrix b = `bb' return matrix V = `VV' return matrix delta=`delta' return matrix Vdelta = `Vdelta' loc names: subinstr local names ".." " ", all loc names: subinstr local names `"""' "", all return local names = "`names'" return local cmd "`cmd'" return local df = `df' return local komit = "`komit'" return local kmiss = "`kmiss'" return local ambiguous = "`ambiguous'" return local y1 = `y1' return local depvar = "`depvar'" if "`sun_abraham'"!=""{ return matrix b_ir `b_ir' // coefficients of variables in the interacted regression return matrix V_ir `V_ir' // covariance matrix of variables in the interacted regression return matrix b_interact `evt_bb' //cohort-specific effect for the given relative time return matrix V_interact `evt_VV' // variance estimate of the cohort-specific effect estimator return matrix ff_w `ff_w' //cohort shares return matrix Sigma_ff `Sigma_ff' //variance estimate of the cohort share estimators return loc sun_abraham = "sun_abraham" } if "`saveov'"!="" { return matrix deltaov = `deltaov' //user:delta coefs from unadjusted regression. excludes only norm=-1 return matrix Vdeltaov = `Vdeltaov' return matrix mattrendy = `mattrendy' return matrix mattrendx = `mattrendx' return local trendsaveov = "trendsaveov" } if "`trend'"!="" { return local trend = "trend" return local trendmethod = "`methodt'" } return local method = "ols" end mata void adjdelta( real scalar trend, real scalar lwindow, real scalar rwindow, real scalar icovars, string scalar getDeltaL, string scalar getOmega, string scalar getOmegaL, string scalar getdelta, string scalar getOmegapsi, string scalar getOmegadeltapsi, string scalar gmm_trcoefs, string scalar deltaadj, string scalar Vadj, string scalar Valladj) { real matrix deltaL, Omega, OmegaL, delta, Omegapsi, Omegadeltapsi, HL, W, Vphi_hat, LambdaL, phi_hat, H, delta_star, Lambda, Vdelta_star, V_star11, V_star12, V_star21, V_star22, V_star, H_phi_hat deltaL = st_matrix(getDeltaL) Omega = st_matrix(getOmega) OmegaL = st_matrix(getOmegaL) delta = st_matrix(getdelta) delta = delta' if (icovars == 1) { Omegapsi=st_matrix(getOmegapsi) Omegadeltapsi=st_matrix(getOmegadeltapsi) } /* deltaL Omega OmegaL delta Omegapsi Omegadeltapsi */ /* Build H_L */ HL = range(trend+1,0,1) W= invsym(OmegaL) /* Solve for phi_hat */ Vphi_hat = invsym(HL'*W*HL) LambdaL = Vphi_hat*HL'*W phi_hat = LambdaL*deltaL /* Get adjusted delta */ H= (range(lwindow,-1,1)\range(1,rwindow+2,1)) st_matrix("H",H) st_matrix("phi_hat",phi_hat) delta_star = delta - H*phi_hat /* Get variance of the adjusted deltas */ Lambda = (J(rows(phi_hat),1,0),LambdaL,J(rows(phi_hat),rows(delta)-1-cols(LambdaL),0)) Vdelta_star = Omega - H*Lambda*Omega - Omega'*Lambda'*H' + H*Lambda*Omega*Lambda'*H' /* Get variance of entire adjusted vector. Other coefs do not change but their covariance with delta does */ V_star11 = (I(rows(delta)) - H*Lambda)* Omega * (I(rows(delta)) - Lambda'*H') if (icovars == 1) { V_star12 = (I(rows(delta)) - H*Lambda) * Omegadeltapsi V_star21 = Omegadeltapsi' * (I(rows(delta)) - Lambda'*H') V_star22 = Omegapsi V_star = (V_star11,V_star12\V_star21,V_star22) } else V_star = V_star11 /* Average to kill eps errors */ V_star = 0.5*(V_star + V_star') /* values of the trend for overlay plot */ H_phi_hat=H*phi_hat /* return trend coeffcients*/ st_matrix(gmm_trcoefs,H_phi_hat') /*return adjusted matrices*/ st_matrix(deltaadj,delta_star') st_matrix(Vadj,Vdelta_star) st_matrix(Valladj,V_star) } end program define repostdelta, eclass *https://www.statalist.org/forums/forum/general-stata-discussion/general/1496551-ereturn-repost-resize-displays-resize-option-not-allowed ereturn post `1' `2', noclear end * Program to parse trend program define parsetrend, rclass syntax [anything] , [method(string) SAVEOVerlay] return local trcoef "`anything'" if "`method'"=="" loc method "gmm" return local methodt "`method'" return local saveoverlay "`saveoverlay'" end *program to parse savek program define parsesavek, rclass syntax [anything] , [NOEstimate SAVEINTeract replace] return local savek "`anything'" return local noestimate "`noestimate'" return local saveint "`saveinteract'" return local kreplace "`replace'" end * program to parse cohort program define parsecohort, rclass syntax namelist(min=1 max=2), [force] [save] [replace] loc words=wordcount("`namelist'") if `words' == 1 { if "`namelist'"=="create" { loc cohortvar = "" loc cohorttype = "create" } else { confirm variable `namelist' di as text _n "Warning: Using old syntax for cohort. New syntax for using a cohort variable is cohort(variable varname)" di as text "The old syntax will be deprecated in the next version of {cmd:xtevent}" loc cohortvar "`namelist'" loc cohorttype "variable" } } else if `words'==2 { loc first: word 1 of `namelist' if ("`first'" != "variable") { di as err _n "Invalid syntax for cohort" exit 301 } loc second : word 2 of `namelist' loc cohortvar "`second'" loc cohorttype "variable" } if "`cohorttype'"=="create" & "`force'"!="" { di _n "cohort variable created, force option ignored" } if ("`save'"!="" | "`replace'"!="") & "`cohorttype'"=="variable" { di as err _n "cohort variable can only be saved/replaced if created" exit 301 } if "`replace'"!="" & "`save'"=="" { di as err _n "cohort variable replace only allowed with save" exit 301 } return local cohortvar = "`cohortvar'" return local cohorttype = "`cohorttype'" return local force = "`force'" return local save = "`save'" return local replace = "`replace'" end * program to parse control_cohort program define parsecontrol_cohort, rclass syntax namelist(min=1 max=2), [save] [replace] loc words=wordcount("`namelist'") if `words' == 1 { if "`namelist'"=="create" { loc control_cohortvar = "" loc control_cohorttype = "create" } else { confirm variable `namelist' di as text _n "Warning: Using old syntax for control_cohort. New syntax for using a control cohort variable is" di as text "control_cohort(variable varname)" di as text "The old syntax will be deprecated in the next version of {cmd:xtevent}" loc control_cohortvar "`namelist'" loc control_cohorttype "variable" } } else if `words'==2 { loc first: word 1 of `namelist' if ("`first'" != "variable") { di as err _n "Invalid syntax for control_cohort" exit 301 } loc second : word 2 of `namelist' loc control_cohortvar "`second'" loc control_cohorttype "variable" } if ("`save'"!="" | "`replace'"!="") & "`control_cohorttype'"=="variable" { di as err _n "control cohort variable can only be saved/replaced if created" exit 301 } if "`replace'"!="" & "`save'"=="" { di as err _n "control cohort variable replace only allowed with save" exit 301 } return local control_cohortvar = "`control_cohortvar'" return local control_cohorttype = "`control_cohorttype'" return local save = "`save'" return local replace = "`replace'" end