你了解渐进式DID平行趋势图的几种画法吗?
正文出现红色字体,对应Stata命令;
正文中出现蓝色字体,对应往期链接;
do文件中:'//'符号代表作者注释内容,帮助理解;'**'代表分节,便于阅读
关键词:coefplot tvdiff
双重差分模型(Difference in Difference,DID)是评估政策经常会使用的计量方法,之前挑选的10篇精读文章里有3篇涉及到该方法,其基本原理是利用处理组和控制组在政策实施前后差异的差分来估计因果效应。如下图所示,在满足系列假设的前提下某项政策的因果效应为
。这些假设包括共同趋势假设(平行趋势)、共同区间假设、干预外生性假设以及干预不存在溢出效应等。后面三个假设是否满足一般可以直接判断或者通过文字阐述,而第一个假设主要通过图表的形式展现。政策干预的形式不同,一般可分为'一刀切'政策(2015年的固定资产加速折旧)和逐步试点推开政策(金税三期工程实施等),不同的政策对应的DID模型设定存在差异。本文重点分析后者,即渐进式DID的平行趋势图表检验方法,并结合模拟数据进行解释。
tvdiff命令画平行趋势检验图
**tvdiff命令提供了模拟数据,数据创造过程如下:
clear
set obs 5 //设置5个个体
set seed 10101
gen id=_n //生成个体id
expand 50 //观察值扩展50倍
drop in 1/5 //删除前五个观察值
bysort id: gen time=_n+1999 //时间从2000-2048年,共49期
gen D=rbinomial(1,0.4)
gen x1=rnormal(1,7)
tsset id time
forvalues i=1/6{
gen L`i'_x=L`i'.x
}
bys id: gen y0=5+1*x+ rnormal()
bys id: gen y1=100+5*x+90*L1_x+90*L2_x+120*L3_x+100*L4_x+90*L5_x +90*L6_x + rnormal()
gen A=6*x+rnormal()
replace D=1 if A>=15
replace D=0 if A<15
gen y=y0+D*(y1-y0)
tsset id time
**基准回归模型中Dit的设定,此时回归得到的Dit系数代表的是政策实施后所有期间的平均效应
input id time Dit
1 2000 0
1 2001 0
1 2002 1
1 2003 1
1 2004 1
1 2005 1
2 2000 0
2 2001 0
2 2002 0
2 2003 1
2 2004 1
2 2005 1
3 2000 0
3 2001 0
3 2002 0
3 2003 0
3 2004 0
3 2005 0
end
**系统自带数据中Dit的设定方式,此时回归代表的是政策实施当年的平均效应
input id time Dit
1 2000 0
1 2001 0
1 2002 1
1 2003 0
1 2004 0
1 2005 0
2 2000 0
2 2001 0
2 2002 0
2 2003 1
2 2004 0
2 2005 0
3 2000 0
3 2001 0
3 2002 0
3 2003 0
3 2004 0
3 2005 0
end
xi: tvdiff y D x, model(fe) pre(6) post(6) vce(robust) graph save_graph(mygraph)
//D代表渐进式DID的核心变量,x是控制变量,y是被解释变量
//model代表回归方式,可选择fe或ols
//pre(6)代表将政策向前推移6期(lead,可使用F.命令实现),而post代表将政策向后推移6期(lag,可使用L.命令实现)
//pre和post括号中的选择会使样本出现缺失,期数越多损失越大
图1 tvdiff命令得到的平行趋势图
coefplot命令画平行趋势检验图
**查看tvdiff与coefplot命令的区别
viewsource tvdiff.ado
**还原tvdiff命令运行的详细过程
marksample touse
//设定回归样本
*LAGS:
local lags
forvalue i=1/6{
cap drop _D_L`i'
gen _D_L`i'=L`i'.D if `touse'
local lags '`lags' _D_L`i''
}
//构造滞后变量,对应post(6),即将政策后移,每后移一期生成新的变量会有缺失值
*LEADS:
local leads
forvalue i=6(-1)1{
cap drop _D_F`i'
gen _D_F`i'=F`i'.D if `touse'
local leads '`leads' _D_F`i''
}
//构造滞后变量,对应pre(6),即将政策前移,每前移一期生成新的变量会有缺失值
xtreg y `leads' D `lags' x if `touse',vce(robust) fe noomitted
//进行回归,对应model(fe)
//noomitted等价于:drop if missing(_D_F6,_D_F5,_D_F4,_D_F3,_D_F2,_D_F1,_D_L1,_D_L2,_D_L3,_D_L4,_D_L5,_D_L6)
//该选择项意味着构造的所有新变量含有缺失值的样本一律被剔除,这是
tempname B C
mat `B'=e(b)
//提取回归结果中储存的系数
mat list `B'
mat `C'=`B''
//'表示转置
mat list `C'
local M=6+6+1
mat `C'=`C'[1..`M',1...]
//取C矩阵的前M行,第一列
//提取各个政策变量的系数
mat list `C'
cap drop `C'1
svmat `C'
//将矩阵生成变量
tempvar id2
gen `id2'=_n
//每个系数的编号对应id
local sum_lags
forval i=1/6{
local sum_lags `sum_lags' _D_L`i'=t+`i'
//宏的嵌套
}
local sum_leads
forval i=6(-1)1{
local sum_leads `sum_leads' _D_F`i'=t-`i'
}
local myD 'D=t'
test `leads'
if r(p)>=0.05{
di as text ''
di as result 'RESULT: 'Parallel-trend' passed'
}
//进行平行趋势检验,从这里我们可以看出平行趋势检验关注的前移(lead)政策变量的显著性
coefplot .,vertical drop(_cons) yline(0) msymbol(d) mcolor(white) ///
title('',size(medium)) ///
levels(99 95 90 80 70) ciopts(lwidth(3 ..) lcolor(*0.2 *0.4 *0.6 *0.8 *1)) addplot(line `C'1 `id2') keep(`leads' D `lags') ///
legend(order(1 '99' 2 '95' 3 '90' 4 '80' 5 '70') row(1)) ///
coeflabels(`sum_leads' `myD' `sum_lags')
graph export 'Parallel0.png',replace
//画图,得到和图1一样的结果
图1并不是我们在论文中看到的通常意义上的平行趋势检验图,但是通过对tvdiff命令的溯源分析,我们就能在掌握平行趋势检验基本原理的基础上,进一步修改元素属性的设置,如下:
*调整图表属性设置
coefplot,vertical drop(_cons) msymbol(d) ///
title('',size(medium)) ///
ylabel(,labsize(small) nogrid tl(0.5)) xlabel(,labsize(small) tl(0.5)) ///
levels(95) ciopts(lwidth(*0.5) lcolor(*1) recast(rcap) lp(dash)) addplot(scatter `C'1 `id2',connect(l) msymbol(O) mcolor(cranberry*0.5) lcolor(cranberry*0.5)) keep(`leads' D `lags') ///
legend(order(1 '95') row(1) region(lcolor(gs15) fcolor(gs15) margin(small)) symxsize(*2) symysize(*0.1) size(vsmall)) ///
yline(0,lcolor(gs10) lwidth(0.1)) ///
graphregion(color(white)) ///
coeflabels(`sum_leads' `myD' `sum_lags')
graph export 'parallel2.png',replace
通过运行上述命令,我们可以得到图2。但图2有一个问题就是我们并没有选择基准组,tvdiff命令运行的结果告诉我们这并没有错误。但是在现实情况中,我们经常会看到一些论文展示的平行趋势图表通常会有某一期系数为零,并将其作为比较的基准组,这是如何实现的呢?
图2 coefplot画平行趋势检验图
提取回归系数画平行趋势检验图
use 'data.dta',clear
gen D=(year-time==0)
//year表示年份,time表示政策实施时间
**tvdiff命令
tvdiff y D $X,model(fe) pre(4) post(2) vce(robust) graph save_graph(mygraph)
运行上述命令后,我们可以得到图3的结果。与之前不同的是,我们在tvdiff的语法中设定了post(2),但在最终的图表呈现中我们只看到了[t-4,t-1]期的回归系数,很重要的一个原因就是我们的样本是非平衡面板,tvdiff命令在进行回归的时候会加noomitted选择项,这会导致回归样本大幅减少,导致政策当期与滞后两期的变量出现完全共线性问题,最终回归结果也就被ommited掉了,那有没有好的方法能够缓解这一问题呢?
我们可以采取类似虚拟变量的设置方法检验平行趋势,该方法的好处在于避免了F.和L.命令的使用,减少回归样本的损失,实现过程如下:
**对政策实施年份进行标准化
gen tt=year-time
//tt代表政策实施时间
replace tt=-5 if tt<=-5
//将早于政策实施时间5年的样本全部设定为5年
replace tt=2 if tt>=2
//将晚于政策实施时间2年的样本全部设定为2年
gen tt_d=tt+5
//将政策时间加5,保证tt大于0
**coefplot命令
xtreg Y ib4.tt_d $X,fe r
//使用4作为基准组
forvalues i = 0/4{
cap drop b_`i'
gen b_`i' = _b[`i'.tt_d]
}
cap drop avg_coef
gen avg_coef = (b_4+b_3+b_2+b_1+b_0)/5
su avg_coef
//生成前五期系数均值
coefplot, baselevels ///
keep(0.tt_d 1.tt_d 2.tt_d 3.tt_d 4.tt_d 5.tt_d 6.tt_d 7.tt_d) ///
coeflabels(0.tt_d = 't-5' ///
1.tt_d = 't-4' ///
2.tt_d = 't-3' ///
3.tt_d = 't-2' ///
4.tt_d = 't-1' ///
5.tt_d = 't' ///
6.tt_d = 't+1' ///
7.tt_d = 't+2') ///更改系数的label
vertical ///转置图形
yline(0, lwidth(vthin) lpattern(dash) lcolor(gs10)) ///加入y=0这条虚线
xline(5, lwidth(vthin) lpattern(dash) lcolor(cranberry)) ///
transform(*=@-r(mean)) ///去除前五期的系数均值
addplot(line @b @at,lp(dash) lwidth(*0.5)) ///增加点之间的连线
ciopts(lpattern(solid) lcolor(navy) recast(rcap) msize(medium)) ///CI为虚线上下封口
msymbol(O) mcolor(navy) ///plot空心格式
xlabel(,labsize(small) tl(0.8)) ylabel(,labsize(small) tl(0.8)) ///
ytitle('{bf:置信区间}',box size(small) lc(white) margin(medium) fc(white) color(navy)) xtitle('{bf:政策实施时间}',box size(small) lc(white) margin(medium) fc(white) color(navy)) ///
scheme(s1mono) plotregion(style(none))
graph export 'parallel4.png',replace
**提取回归系数
xtreg Y ib0.tt_d $X,fe r
gen b=.
gen se=.
forvalue i=1/8{
local a=`i'-1
replace b=_b[`a'.tt_d] if _n==`i'
}
forvalue i=1/8{
local a=`i'-1
replace se=_se[`a'.tt_d] if _n==`i'
}
gen b_up=b+1.96*se
gen b_low=b-1.96*se
gen id=_n if b~=.
egen a=mean(b) if _n<=5
egen avecoef=max(a) if _n<=8
gen b_sd=b-avecoef
replace b_sd=0 if _n==1
scatter b_sd id if id<=8,connect(l) lcolor(gs10) lwidth(*0.5) lp(dash) msymbol(O) mcolor(navy) || (rcap b_low b_up id if id<=8,lcolor(navy) lp(solid) lwidth(*0.5)) ///
,yline(0,lcolor(gs10) lwidth(0.1)) xline(1,lwidth(0.15) lcolor(cranberry)) ylabel(,labsize(small) tl(0.8) nogrid) ysca(r(0 0.045)) ///
xlabel(1 't-5' 2 't-4' 3 't-3' 4 't-2' 5 't-1' 6 't' 7 't+1' 8 't+2',labsize(small) tl(0.8)) xsca(r(0.75 8.25)) ///
xtitle('政策实施时间',box margin(medium) size(small) fcolor(white) lc(white)) ///
graphregion(color(white)) ///
legend(off)
graph export 'parallel5.png',replace
图5 提取回归系数得到的平行趋势检验图
gen pre4and_more=0
replace pre4and_more=1 if tt<=-5
tab tt,g(dum)
gen post2more=0
replace post2more=1 if tt>=2
rename dum6 pre4
rename dum7 pre3
rename dum8 pre2
rename dum9 pre1
rename dum10 current
rename dum11 post1
xtreg Y pre4 pre3 pre2 pre1 current post* $X,fe r
forvalue i=1/4{
gen pre_`i'=_b[pre`i']
}
gen avg_pre = (pre_1+pre_2+pre_3+pre_4)/5
sum avg_pre
coefplot, baselevels ///
keep(pre* current post*) ///
coeflabels( pre4='t-4' pre3='t-3' pre2='t-2' pre1='t-1' current='t' post1='t+1' post2more='t+2') ///更改系数的label
vertical ///转置图形
yline(0, lwidth(vthin) lpattern(dash) lcolor(teal)) ///加入y=0这条虚线
xline(6, lwidth(vthin) lpattern(dash) lcolor(teal)) ///
ytitle('Percentage Changes', size(small)) ///加入Y轴标题,大小small
xtitle('Years relative to branch deregulation', size(small)) ///加入X轴标题,大小small
transform(*=@-r(mean)) ///去除前五期的系数均值
addplot(line @b @at) ///增加点之间的连线
ciopts(lpattern(dash) recast(rcap) msize(medium)) ///CI为虚线上下封口
msymbol(circle_hollow) ///plot空心格式
scheme(s1mono)
图6 提取回归系数得到的平行趋势检验图
新手上路一枚,有解读错误的地方请大家多多指正!
点个赞、打个赏、转个发,都是对作者莫大的鼓励