用好esttab,导遍表格都不怕[进阶操作]
DreiStein
2022年04月11日 08:30
收录于文集
共8篇

上次给大家带来了使用 esttab 命令从 Stata 导出 描述性统计表、相关系数矩阵、分组 t 检验表、多元多模型回归结果。并且详细教大家怎么定制化自己的表格,使其更符合学术论文三线表要求。详情可见

对于一般的朋友,这已经足以应对他们的要求了,但是,总有一些朋(chu)友(nv)们(zuo)会有更(bian)高(tai)的要求。本期将为大家带来更加详细的介绍。本期为大家带来多方程模型结果导出(如似不相关回归分组系数检验),变量分组展示(如控制了个人、企业、行业层面的变量,希望分组展示),以及矩阵导出bootstrap法检验中介效应结果导出)


先补一个坑

上一篇中,细心的小伙伴应该也发现了,尽管我们使用中文的变量 label 可以让导出的变量以中文形式出现,但是常数项 Constant 依然保持英文,我们该怎么让它也输出 常数项 这个中文呢?

其实,esttab 本身也已经提供了相关的方法,也就是 coeflabels() 选项,可以简写为 coef(),代码如下(coef()被我放在esttab的 note选项后面)

代码块
clike
自动换行
复制代码
* --------------------------------------------------
* 与上一次相同的设定
* --------------------------------------------------
use "http://www.stata-press.com/data/r16/nlswork.dta",clear
xtset idcode year // 指定面板数据
glo y ln_wage // 因变量
glo x hours   // 自变量
glo z c_city  // 工具变量
glo m race    // 调节变量
glo c grade age // 控制变量
glo f i.year i.ind_code // 年份和行业固定效应
// 变量标签标记为中文
label var $y "工资"
label var $x "工时"
label var $z "中心城市"
label var $m "种族"
label var grade "学历"
label var age "年龄"
// 值标签标记为中文
label define racelbl 1 "白人" 2 "黑人" 3 "其他", replace
* --------------------------------------------------
*     储存回归结果
* --------------------------------------------------
eststo olsbase:       reg      $y                               $c $f , vce(cluster idcode)
eststo olsmain:       reg      $y   $x                          $c $f , vce(cluster idcode)
eststo olsint :       reg      $y c.${x}##ib3.${m}              $c $f , vce(cluster idcode)
eststo regivm : ivregress 2sls $y (${x} = ${z})                 $c $f , vce(cluster idcode)
eststo regivi : ivregress 2sls $y (${x} c.${x}#ib3.${m} = ///
                                   ${z} c.${z}#ib3.${m}) ib3.$m $c $f , vce(cluster idcode) 
* --------------------------------------------------
*     导出回归结果
* --------------------------------------------------
#delimit;
esttab olsbase olsmain olsint regivm regivi using mytable.rtf, append label
       title(回归结果) b(3) se(3) brac nogap compress order($x *.${m}*)
       note(注:括号中为聚类稳健标准误) coef(_cons "常数项")
       indicate("年份=*.year" "行业=*.ind_code",label("控制" "")) nobase interact(" × ")
       mgroup("基本模型" "OLS回归" "工具变量回归",pattern(1 1 0 1 0))
       mtitle("基本" "主效应" "调节效应" "主效应" "调节效应")
       stat(N r2 r2_a F chi2, fmt(0 3 3 3 3) star(F chi2)
            label("样本量" "{\i R}²" "调整{\i R}²" "{\i F} 值" "{\i χ}²"));
#delimit cr 
复制成功

coef() 选项中成对指定变量的标签,如果不含空格,可以不用双引号,但我个人建议使用双引号来区分变量名和标签。常用用法为 coef(varName1 "varLabel1&#​34; varName2 "varLabel2&#​34;)

结果如下:

带有中文“常数项”的回归结果


这么长一串才进入正文也是独我一份了吧,那么,废话不多说,进入正题,先来看多方程模型的结果导出

首先多方程模型一般有两种,一种是分步回归(如heckman,2SLS,bdiff),另一种是结构方程模型SEM(如sureg,sem,gsem)。Stata展示结果时涉及两种情况:(1)分步回归一般都是多表格展示结果(或者通常略去第一步的结果);(2)SEM通常是把多个方程在一张表里展示。对于情况(1),esttab直接导出一般只能导出最后一步结果,因此需要自己跑一遍前面的步骤;对于情况(2),esttab可以直接导出结果。

代码块
clike
自动换行
复制代码
eststo olsbase: reg $y    $c $f , vce(cluster idcode)
eststo olsmain: reg $y $x $c $f , vce(cluster idcode)
/* 针对情况(1) 分步回归 Stata通常不汇报第一阶段 */
eststo regivm1:   reg $x $z $c $f , vce(cluster idcode) // 自己手动跑第一阶段回归并保存
eststo regivm2: ivregress 2sls $y (${x} = ${z}) $c $f , vce(cluster idcode) first // 指定 first 可以让 Stata 汇报第一阶段结果,但是 esttab 无法直接导出,但可以用来验证是否正确
#delimit ;
esttab olsbase olsmain regivm1 regivm2 using mytable.rtf, replace label
       title(回归结果) b(3) se(3) brac nogap compress order($x)
       note(注:括号中为聚类稳健标准误) coef(_cons "常数项")
       indicate("年份=*.year" "行业=*.ind_code",label("控制" "")) nobase
       mgroup("基本模型" "OLS回归" "工具变量回归",pattern(1 1 1 0))
       mtitle("基本" "主效应" "第一阶段" "第二阶段")
       stat(N r2 r2_a F chi2, fmt(0 3 3 3 3) star(F chi2)
            label("样本量" "{\i R}²" "调整{\i R}²" "{\i F} 值" "{\i χ}²"));
#delimit cr
复制成功

结果如下

包含第一阶段的两阶段工具变量回归

下面讲第二种情况:多方程回归,Stata在一张表内展示结果,我们用 heckman 命令来演示(注意heckman的汇报Stata是优先汇报第二阶段,而且其中非常重要的mills比率lambda是在单独的方程中,非常有代表性)

代码块
clike
自动换行
复制代码
/* 假定我们的 $y 只有 $s == 1 的时候才有取值 */
glo s "south"
// 尽管 heckman 有两种估计,但两种都是在一张表内汇报结果的,在这里一起演示
eststo hecktwo: heckman $y $x $c $f , select($s = $z $c $f ) two
eststo heckmle: heckman $y $x $c $f , select($s = $z $c $f ) mle

/* 1种成熟方案 + 1种折腾但是依旧不好的方案 */
* 成熟方案 手动分步回归存储
* 优点是信息完备 缺点是步骤有些繁琐
est restore hecktwo
eststo heckman1: probit $s $z $c $f if e(sample) // mle 和 two 的第一阶段完全一致不用做两遍了
#delimit;
esttab heckman1 hecktwo heckmle using mytable.rtf, replace label 
       equation(main = 1, other = .:2:2, IMR = .:3:3) 
       order(${x}) drop(other:) nonumber noeqline eqlabel("",none)
       title(多方程回归结果) b(3) se(3) brac nogap compress 
       note(注:括号中为聚类稳健标准误) coef(_cons "常数项")
       indicate("年份=*.year" "行业=*.ind_code",label("控制" "")) nobase
       mtitle("Step 1" "两阶段Heckman" "MLE Heckman")
       stat(N chi2, fmt(0 3) star(chi2)
            label("样本量" "{\i χ}²"));
#delimit cr


* 穷折腾方案 最后还是不讨好 有大神可以优化
* 优点是不需要跑更多回归了
* 缺点是缺第一阶段统计量;
*      除非愿意先放第二阶段,否则统计量位置不对;
*      IMR需要手动标星
#delimit;
esttab hecktwo heckmle using mytable.rtf, replace label 
       equation(Step2 = 1, Step1 = 2, other = 3) unstack
       order(Step1: Step2:${x}) drop(other:) nonumber eqlabel("Step 1" "Step 2")
       title(多方程回归结果) b(3) se(3) brac nogap compress 
       note(注:括号中为聚类稳健标准误) coef(_cons "常数项")
       indicate("年份=*.year" "行业=*.ind_code",label("控制" "")) nobase
       mtitle("两阶段Heckman" "MLE Heckman")
       stat(lambda selambda N chi2, fmt(3 3 0 3) star(chi2)
            label("IMR" "S.E. IMR" "样本量" "{\i χ}²"));
#delimit cr
复制成功

成熟方案结果

主要关注第二和第三行的导出命令:

equation() 用于对每一个多方程模型的方程分类,这里分了三类,main代表主要的,有用的方程,分别是三个模型的第一个方程(main = 1),other代表要删去的 heckman报告的、缺信息的第一阶段方程,是第二、第三个模型的第二个方程(other = .:2:2)不同模型用英文冒号隔开,如果该模型没有归属这一类的方程则用英文句点表示,IMR是用来衡量Heckman效果的参数(lambda,rho,sigma)在第二、第三个模型的第三个方程(IMR= .:3:3)

drop(other:) 代表扔掉归属 other 的方程内容,也可以用来扔掉变量drop(varName)或者某个模型的变量drop(eqName:varName)

nonumber 由于一个模型只有一个编号,因此不如不要编号

noeqline 将不同方程的分割线去掉

eqlabel("&#​34;,none) 加上 none 代表不显示方程的标签,若要显示,可参考下文说明

穷折腾方案结果

依然关注第2和第3行命令以及stats()内容

unstack 代表将不同的方程分列显示,因此我们可以在不同列分别显示第一、第二阶段结果

order(Step1: Step2:${x}) 先显示归属 Step1 的方程,在 Step2 方程中优先显示 $x 变量

drop(other:) 扔掉第三个方程内容,用其他方式汇报 IMR

eqlabel("Step 1&#​34; "Step 2&#​34;) 按照方程显示的顺序(可用order指定)分别给方程一个标签

stat(lambda selambda) 用于显示 IMR 的系数和标准误,需要手动据此判断是否需要标星


第二个问题是给变量分组。特别是当我们需要控制很多控制变量时,通过分类能使我们的结果更具有条理。

核心功能就是 refcat() 选项,该选项本身是用于帮助指定离散变量的参考值(比如ib0.foreign 意味着 foreign == 0 为baseline,不参与系数估计)但是活用这个功能可以给我们带来非常漂亮的结果

代码块
clike
自动换行
复制代码
/* 依然选择之前保存的 olsbase olsmain olsint regivm regivi 回归结果予以说明 */
#delimit;
esttab olsbase olsmain olsint regivm regivi , append label
       refcat(grade "{\i\b Controls}" 2.ind_code "{\i\b Fixed Effect}",nolabel)
       title(回归结果) b(3) se(3) brac nogap compress order($x *.${m}*)
       note(注:括号中为聚类稳健标准误) coef(_cons "常数项")
       indicate("年份=*.year" ,label("控制" "")) nobase interact(" × ")
       mgroup("基本模型" "OLS回归" "工具变量回归",pattern(1 1 0 1 0))
       mtitle("基本" "主效应" "调节效应" "主效应" "调节效应")
       stat(N r2 r2_a F chi2, fmt(0 3 3 3 3) star(F chi2)
            label("样本量" "{\i R}²" "调整{\i R}²" "{\i F} 值" "{\i χ}²"));
#delimit cr
复制成功

带上分组的控制变量 表格过长没有放全

关键看esttab第二行的 refcat() 用法

refcat(grade "{\i\b Controls}&#​34; 2.ind_code "{\i\b Fixed Effect}&#​34;,nolabel) 表示在变量 grade 和 2.ind_code 上方加一行 加粗斜体 的  和 。2.ind_code 是因为默认把 1.ind_code作为行业的参考值。注意一定要加上 ,nolabel 这个 suboption


终于到了最后的 矩阵导出 了

参考连玉君老师 [一文读懂中介效应分析](https://zhuanlan.zhihu.com/p/99435552)的内容,使用bootstrap和sgmediation来进行检验时,最终的结果是通过 estat boot 导出的,并不能直接输出到word文档中。

代码块
clike
自动换行
复制代码
bootstrap r(ind_eff) r(dir_eff) r(tot_eff), reps(500) :  ///
          sgmediation ${y}, mv(${m}) iv(${x}) cv(${c} ${f})    //计算中介效应和间接效应
estat bootstrap, bc      //计算置信区间 由于 percentile 参考价值不高故只报告 bc 的置信区间

/* estat boot, bc 结果如下 */
Bootstrap results                               Number of obs     =     28,104
                                                Replications      =        500

      Command: sgmediation2 ln_wage, mv(race) iv(hours) cv(grade age i.year i.ind_code)
        _bs_1: r(ind_eff)
        _bs_2: r(dir_eff)
        _bs_3: r(tot_eff)

------------------------------------------------------------------------------
             |    Observed               Bootstrap
             | coefficient       Bias    std. err.  [95% conf. interval]
-------------+----------------------------------------------------------------
       _bs_1 |  -.00019584  -1.40e-07   .00002278   -.0002436  -.0001561   (P)
             |                                      -.0002438  -.0001564  (BC)
       _bs_2 |   .00103865   .0000167    .0003553    .0004068   .0017334   (P)
             |                                       .0004121    .001745  (BC)
       _bs_3 |   .00084281   .0000166   .00035402    .0002039   .0015258   (P)
             |                                       .0001944    .001519  (BC)
------------------------------------------------------------------------------
Key:  P: Percentile
     BC: Bias-corrected
复制成功

我尝试对结果进行调整,最终结果如下

间接效应路径系数与置信区间

代码如下

代码块
clike
自动换行
复制代码
bootstrap r(ind_eff) , reps(500) :  /// 由于中介效应是否显著仅关注简介效应就行,为此我们只汇报 r(ind_eff)
          sgmediation2 ${y}, mv(${m}) iv(${x}) cv(${c} ${f})

estat boot, bc percentile // 查看结果方便对照
cap mat drop Boot
mat Boot = [e(b)',e(ci_bc)']

local lx:   var label $x
local lmed: var label $m
local ly:   var label $y
mat rownames Boot = "`lx' -> `lmed' -> `ly'" // 多中介变量以空格分隔,依次命名
mat colnames Boot = "路径系数" "95% 置信区间" // 最后两列在 word 中合并单元格即可
esttab mat(Boot, fmt(a3 a3 a3)) using mytable2.rtf, replace nomtitle
复制成功

核心在于 用 esttab 导出矩阵

系数和置信区间分别存储在 e(b) 和 e(ci_bc) 中,加英文单引号'是为了赚置矩阵

mat Boot = [e(b)',e(ci_bc)&#​39;] 将路径系数与置信区间放在一个矩阵中

mat rownames 和 mat colnames 分别指定了矩阵的名称


好啦,真的被榨干了,一滴都不剩了。esttab其实还有很多可以玩的花活,不过不遇到还真想不起来还有啥冷门的导出。

希望写的足够清晰能让大家看明白