******************************************************************************** * PROGRAM "tfdiff" ******************************************************************************** *! tfdiff, v11, G.Cerulli, 03june2022 program tfdiff, eclass sortpreserve version 14 #delimit; syntax varlist(numeric ts fv) [if] [in] [aweight fweight pweight] , t(numlist max=1) tvar(varlist numeric max=1) datatype(string) [ pvar(varlist numeric max=1) ci(numlist max=1) test_pt vce(string) save_graph(string) save_results(string) graph ]; #delimit cr ******************************************************************************** marksample touse tokenize `varlist' local y `1' // outcome local w `2' // treatment macro shift macro shift local xvars `*' ******************************************************************************** local model "ols" ******************************************************************************** * Labels ******************************************************************************** la var `w' "Binary treatment variable" la var `y' "Outcome variable" ******************************************************************************** * Warning 1 ******************************************************************************** preserve // preserve the original dataset qui keep if `touse' // consider just the subsample identified by the "if" qui count if `w'==1 & `touse' local N1=r(N) qui count if `touse' local N=r(N) qui sum `w' if `touse' if r(mean)!=(`N1'/`N'){ di as text in red "" di as text in red "" di as text in red "{hline}" di as text in red "{bf:******************************************************************************}" di as text in red "{bf:********* WARNING: The treatment variable must be binary 0/1 *****************}" di as text in red "{bf:******************************************************************************}" exit } restore ******************************************************************************** * Warning 2 ******************************************************************************** if ("`datatype'"!="panel" & "`datatype'"!="cross-section") { di _newline(2) di as result in red "***************************************************************" di as result in red "Warning: only one of the following datatype must be " di as result in red "declared into the option 'datatype()': 'panel', 'cross-section'" di as result in red "***************************************************************" exit } ******************************************************************************** * Warning 3 ******************************************************************************** if ("`datatype'"=="panel" & "`model'"!="ols" & "`model'"!="fe") { di _newline(2) di as result in red "********************************************************" di as result in red "Warning: only one of the following models must be " di as result in red "declared into the option 'model()': 'ols', 'fe'" di as result in red "********************************************************" exit } ******************************************************************************** * Warning 4 ******************************************************************************** if ("`datatype'"=="cross-section" & "`model'"!="ols") { di _newline(2) di as result in red "*********************************************************" di as result in red "Warning: with cross-section data, only one models must be" di as result in red "declared into the option 'model()': 'ols'" di as result in red "*********************************************************" exit } ******************************************************************************** * GENERATE THE TIME DUMMIES ******************************************************************************** qui sum `tvar' ******************************************************************************** local T = r(max) - r(min) +1 local M = `t'-r(min)-1 // number of pre-treatment times minus one forvalues i=1/`T'{ cap drop _D`i' } qui tab `tvar' , gen(_D) ******************************************************************************** drop _D1 // Drop the first time dummy ******************************************************************************** * PANEL DATA ESTIMATION ******************************************************************************** if "`datatype'"=="panel"{ di _newline(1) di as result in red "***********************************************************" di as result in red "Data type: PANEL" di as result in red "***********************************************************" di _newline(1) ******************************************************************************** if "`model'"=="ols" & "`pvar'"==""{ di _newline(1) di as result in red "*************************************************************" di as result in red "Warning: the option 'pvar()' must be declared with panel data" di as result in red "*************************************************************" exit } else if "`model'"=="ols" & "`pvar'"!=""{ di as result in red "***********************************************************" di as result in red "Model type: OLS" di as result in red "***********************************************************" tsset `pvar' `tvar' reg `y' i.`w'##i._D* `xvars' [`weight' `exp'] if `touse' , vce(`vce') noomitted // ols } ******************************************************************************** } ******************************************************************************** * CROSS-SECTION ESTIMATION ******************************************************************************** if "`datatype'"=="cross-section"{ di _newline(1) di as result in red "***********************************************************" di as result in red "Data type: CROSS-SECTION" di as result in red "***********************************************************" di _newline(1) if "`model'"=="ols"{ di as result in red "***********************************************************" di as result in red "Model type: OLS" di as result in red "***********************************************************" reg `y' i.`w'##i._D* `xvars' [`weight' `exp'] if `touse' , vce(`vce') noomitted // ols } } ******************************************************************************** * REST OF THE CODE ******************************************************************************** ereturn scalar phi=_b[1.`w'] qui count if `touse' ereturn scalar N=r(N) qui count if `w'==1 & `touse' ereturn scalar N1=r(N) qui count if `w'==0 & `touse' ereturn scalar N0=r(N) ******************************************************************************** * TESTING THE "PARALLEL TREND" ******************************************************************************** *qui tsset `pvar' `tvar' qui sum `tvar' local M = `t'-r(min)-1 // number of pre-treatment times minus one ******************************************************************************** if "`test_pt'"!=""{ di as text "" di as text "" di as text "{hline}" di as text "{bf:******************************************************************************}" di as text "{bf:**************** TEST FOR 'PARALLEL TREND' ***********************************}" di as text "{bf:******************************************************************************}" local sum "" forvalues i=2/`M'{ local sum `sum' _b[1.`w'#1._D`i']= } local sum _b[1.`w']=`sum'0 di as text "{bf:Null to be tested:}" di "`sum'" test `sum' if r(p)>=0.05{ di as text "" di as result "RESULT: 'Parallel-trend' passed" } else{ di as result "RESULT: 'Parallel-trend' not passed" } di as text "" di as text "{bf:******************************************************************************}" di as text "" } ******************************************************************************** * GENERATE CONFIDENCE INTERVALS FOR ATE(t) ******************************************************************************** tempname V mat `V'=e(V) ******************************************************************************** scalar _b1=_b[1.`w'] mat _var_`w'=`V'["1.`w'","1.`w'"] scalar _var_`w'=_var_`w'[1,1] scalar _se_b1=sqrt(_var_`w') mat _var_`w'_D2=`V'["1.`w'#1._D2","1.`w'#1._D2"] mat _var_`w'_D3=`V'["1.`w'#1._D3","1.`w'#1._D3"] ******************************************************************************** * SETTING THE GRAPH CONFIDENCE INTERVAL SIGNIFICANCE ******************************************************************************** if "`ci'"=="" { local cis=1.96 // default 5% significance } else if "`ci'"!="" & `ci'==1{ local cis=2.576 } else if "`ci'"!="" & `ci'==5{ local cis=1.96 } else if "`ci'"!="" & `ci'==10{ local cis=1.645 } else if "`ci'"!="" & (`ci'!=1 |`ci'!=5 |`ci'!=10){ di in red "Warning: the option 'ci(#)' accepts only these values: 1, 5, or 10." error 121 } ******************************************************************************** tempname M mat `M'=J(`T',3,.) mat `M'[1,1]=_b1 mat `M'[1,2]=_b1+`cis'*_se_b1 mat `M'[1,3]=_b1-`cis'*_se_b1 ******************************************************************************** forvalues i= 2/`T'{ mat _var_`w'_D`i'=`V'["1.`w'#1._D`i'","1.`w'#1._D`i'"] scalar _var_`w'_D`i'=_var_`w'_D`i'[1,1] mat _cov_`w'_D`i'=`V'["1.`w'#1._D`i'","1.`w'"] scalar _cov_`w'_D`i'=_cov_`w'_D`i'[1,1] mat _b`i' = (_b[1.`w'] + _b[1.`w'#1._D`i']) scalar _b`i' =_b`i'[1,1] mat _var_b`i' = _var_`w' + _var_`w'_D`i' + 2*_cov_`w'_D`i' scalar _var_b`i'=_var_b`i'[1,1] scalar _se_b`i' = sqrt(_var_b`i') mat `M'[`i',1]=_b`i' mat `M'[`i',2]=_b`i'+`cis'*_se_b`i' mat `M'[`i',3]=_b`i'-`cis'*_se_b`i' } ******************************************************************************** preserve ******************************************************************************** if "`graph'"=="" & "`save_graph'"!=""{ di as error "Warning: the option 'save_graph' requires to jointly specify the option 'graph'." error } ******************************************************************************** svmat `M' gen _id=`tvar' qui keep in 1/`T' ******************************************************************************** cap gen __ATE_t=`M'1 la var __ATE_t "Average Treatment Effect at each time t" cap gen __ATE_t_ub=`M'2 la var __ATE_t_ub "Upper bound confidence interval of ATE(t) - significance `ci'%" cap gen __ATE_t_lb=`M'3 la var __ATE_t_lb "Lower bound confidence interval of ATE(t) - significance `ci'%" ******************************************************************************** if "`graph'"!=""{ twoway (rcap `M'2 `M'3 _id) /// (scatter `M'1 _id , yline(0 , lpattern(dash)) xline(`t' ,lw(thick) lp(dash))) /// (connected `M'1 _id) /// , legend(off) xtitle(Time) ytitle(ATE(t)) note(Confidence interval significance level: `ci'%) /// graphregion(fcolor(white)) scheme(s1mono) } ******************************************************************************** if "`graph'"!="" & "`save_graph'"!=""{ twoway (rcap `M'2 `M'3 _id) /// (scatter `M'1 _id , yline(0 , lpattern(dash)) xline(`t' ,lw(thick) lp(dash))) /// (connected `M'1 _id) /// , legend(off) xtitle(Time) ytitle(ATE(t)) note(Confidence interval significance level: `ci'%) /// graphregion(fcolor(white)) scheme(s1mono) saving(`save_graph', replace) } ******************************************************************************** qui{ if "`save_results'" != ""{ keep `tvar' __ATE_t __ATE_t_lb __ATE_t_ub save `save_results' , replace } } ******************************************************************************** restore ******************************************************************************** end