你有没有过这样的瞬间:reghdfe跑完双向固定效应,esttab出表,贴进 Word,导师看了一眼问“你这模型到底控没控制年份固定效应?”你低头一看,表格底部干干净净,固定效应那一行根本不存在。我以前处理这个问题的办法非常原始——在 Word 里手动敲 Yes/No。一次论文十几个模型,改一轮模型就要改一轮表,不仅浪费时间,还容易敲错:模型 2 明明加了年份固定效应,表格里却漏标。后来我把esttab的indicate()和reg2docx的固定效应自动化标注彻底摸了一遍,才发现这件事完全可以一分钟搞定。这篇文章就把完整链路写清楚:reghdfe怎么装对、esttab怎么手动标、reg2docx怎么自动标,以及出表过程中踩过的坑和排查思路。
1. 固定效应行空白之谜:reghdfe 的 absorb() 与 esttab 之间发生了什么
1.1 一个很典型的面板回归出表场景
我先给你搭一个标准场景。用 Stata 自带的雇主-雇员纵向数据nlswork,跑三个递进模型:不带固定效应、只带个体固定效应、个体+年份双向固定效应。
webuse nlswork, clear xtset idcode year * 模型 1:不加固定效应 reg ln_wage hours age ttl_exp est store m1 * 模型 2:个体固定效应 reghdfe ln_wage hours age ttl_exp, absorb(idcode) est store m2 * 模型 3:个体 + 年份双向固定效应 reghdfe ln_wage hours age ttl_exp, absorb(idcode year) est store m3跑完以后,如果你直接敲:
esttab m1 m2 m3, b(%9.3f) t(%9.3f) star(* 0.1 ** 0.05 *** 0.01) scalars(N r2_a)输出结果里就只有系数、t 值、样本量和 R²,表格底部干干净净,没有“固定效应:是/否”这一行。很多人的第一反应是去翻 esttab 的 help,找“固定效应”字样,结果越翻越糊涂。
1.2 为什么 reghdfe 吸收固定效应后,esttab 就“看不见”了
这里要解释一下机制。reghdfe的核心操作是“吸收”,也就是把absorb()里的变量对应的组内均值直接从因变量和解释变量中投影掉。这样做的好处是计算快、内存省,尤其absorb(idcode year)这种多维固定效应,普通reg i.idcode i.year要生成几千个虚拟变量,而reghdfe几乎不生成虚拟变量。
代价是:这些被吸收的变量不会出现在估计结果e(b)的系数向量里,也不在e(indepvars)里。esttab默认只根据e(b)里的内容生成表格主体,所以它根本不知道idcode和year曾经被吸收过。不是它不想显示,而是存储的结果信息里就缺了这一块。
不过reghdfe会在e(absorb)里记录吸收变量的名字。这就是后面两个方案能实现自动化标注的关键。
1.3 自动化标注要解决两层问题,缺一不可
明白了机制,你就知道“自动化标注”这件事拆开其实是两层:
第一层是“判断”:某个模型到底有没有控制某个固定效应。这个信息藏在e(absorb)里,需要工具去读取。
第二层是“输出”:判断完了,还要按表格的固定位置生成一行“Yes/No”,并且保证多个模型之间位置对齐。第一列模型有、第二列模型没有,各行也要严格对应,不能错位。
手动标注最容易出的问题就在第二层:十个模型,三行固定效应,手敲只要错一格,整个表格就废了。所以我们要的不是“能标出来”,而是“自动、准确、可复现地标出来”。
2. 装对 reghdfe:安装命令、验证方法和两个常见报错
2.1 标准安装路径:ssc 和 GitHub 双通道
先解决工具本身。reghdfe的安装说简单也简单,说麻烦也麻烦,主要卡在依赖包上。
最稳妥的路径是先装依赖再装主命令:
ssc install ftools, replace ssc install reghdfe, replaceftools是reghdfe的底层依赖包,提供高效的因子变量处理和分组操作。很多安装失败实际上都是因为ftools没装。
如果你希望装比较新的版本,更推荐走 GitHub 通道:
ssc install github, replace github install sergiocornejo/reghdfeGitHub 版本的更新比 SSC 快,修复了一些老版本在 Stata 14/15 下的兼容问题。对于日常写论文来说,两种通道都可以,我一般建议:能访问 GitHub 就装 GitHub 版,装完更省心。
2.2 报错一:reghdfe requires the -ftools- package
这个报错是最常见的。原因很简单:装了reghdfe但没装ftools,或者ftools版本太旧。
解决办法就是先执行:
ssc install ftools, replace然后重新安装reghdfe。装完以后可以用which ftools看一下路径,能看到.ado文件路径就说明装好了。
2.3 报错二:版本过旧或 mata 相关错误
有时候你会遇到reghdfe能装上,但一跑就报什么mata函数找不到,或者reghdfe not found。这种八成是 Stata 版本太老,或者装到了不同用户目录。
reghdfe新版对 Stata 版本有要求,如果你的 Stata 版本比较旧,建议先用 GitHub 通道安装,如果依然报错,那就只能退回旧版ftools和reghdfe组合。另外,检查一下自己是不是真的把命令装到了当前用户的 ado 目录:sysdir和adopath可以查看路径环境。遇到reghdfe明明装了却调用不到,把 ado 路径加进adopath +就能解决。
2.4 装好后一分钟验证
安装不是终点,验证才是。跑完安装命令后,我习惯做两步验证:
which reghdfe能返回路径,例如C:\ado\plus\r\reghdfe.ado,说明命令本身可用。然后直接跑一个最简单的吸收模型:
webuse nlswork, clear xtset idcode year reghdfe ln_wage hours age ttl_exp, absorb(idcode year)不报错、能正常输出系数,就说明环境没问题。
3. esttab 的 indicate() 手动标注:能救急,但离“自动化”还差一步
3.1 indicate() 的检查逻辑其实很简单
esttab虽然没有自动识别固定效应的能力,但它给我们留了一个口子:indicate()选项。
indicate("显示标签=变量名")的核心逻辑是:检查一个模型存储的信息里是否存在某个变量,存在就输出 “Yes”,不存在就输出 “No”。由于reghdfe会把absorb()中的变量记在e(absorb)里,所以indicate()能间接识别到固定效应。
换句话说,你手动告诉esttab:去帮我查一下idcode和year这两个变量在不在模型里,然后按固定位置汇报出来。
3.2 一个模型和多个模型的标注示例
还是之前三个模型,加一行indicate()就够了:
esttab m1 m2 m3, b(%9.3f) t(%9.3f) star(* 0.1 ** 0.05 *** 0.01) /// scalars(N r2_a) /// indicate("个体固定效应=idcode" "年份固定效应=year") /// mtitles("M1" "M2" "M3")输出效果大致是:
(1) (2) (3) hours 0.018*** 0.014*** 0.013*** (5.23) (4.87) (4.66) ... 个体固定效应 No Yes Yes 年份固定效应 No No Yes N 28099 28099 28099 r2_a 0.097 0.189 0.203第一次看到这个输出的时候,我的反应是:原来只要手写一行,固定效应就能自动判断。模型 2 只有个体固定效应,年份那一行自动变 No;模型 3 两个都控制了,就都是 Yes。确实比自己手动敲要强。
3.3 方案一的局限:当模型数量涨到 20 个
用了几次之后,你会发现esttab + indicate()仍然不是真正的“全自动”。问题在于:
indicate()里的变量名要你手动维护,变量多的时候很容易写错。我有一回把year写成了yr,结果所有模型的年份固定效应行全部显示 No,一开始还以为是模型没控制对,浪费了一个小时排查。其次,esttab默认输出的 RTF 文件在中文环境下很容易乱码,中文标签进入 Word 后格式也很难看,每次都要重新调整字体和列宽。再者,如果你需要按照不同期刊要求输出三线表,esttab默认的控制台样式和 Word 文档的排版习惯还是有不少差距。
所以结论是:esttab + indicate()适合快速验收、临时看结果,但如果你要正式写论文、交付 Word 表格,它还不够省心。
4. reg2docx 实现固定效应 Yes/NO 自动化标注:Word 表格一条龙
4.1 reg2docx 就是为解决这个场景而生的
reg2docx是爬虫俱乐部团队开发的一个 Stata 命令,专门把回归结果输出为.docx格式的学术表格。它从一开始就考虑到了中文论文场景:支持中文标题、中文变量标签、中文注释,而且输出的是真正的 Word 表格,不是那种贴进去还要重新调格式的 RTF。
和我用的最多的esttab相比,reg2docx的语法非常接近,但它对reghdfe固定效应的支持要完善得多。同样是indicate(),reg2docx会主动读取模型存储信息里的固定效应记录,输出固定效应 Yes/NO 行,并且天然和中文论文的三线表要求对齐。安装命令也很简单:
ssc install reg2docx, replace如果网络受限装不上,可以findit reg2docx或去作者公众号提供的离线包手动安装。
4.2 完整实战:三模型回归表直接出 Word
我们还是用前面的nlswork数据,跑完三个模型之后,只需要一条命令:
reg2docx m1 m2 m3 using "reg_table.docx", replace /// b(%9.3f) t(%9.3f) star(* 0.1 ** 0.05 *** 0.01) /// scalars("N 样本量" "r2_a 调整R方") /// indicate("个体固定效应=idcode" "年份固定效应=year") /// order(hours age ttl_exp) /// varlabels(hours "工作时长" age "年龄" ttl_exp "工作经验") /// title("表1 工资决定因素的回归结果") /// note("注:括号内为 t 值;* p<0.1, ** p<0.05, *** p<0.01")运行完,工作目录下会生成一个reg_table.docx。打开文件,你会看到一张已经排好版的三线表,表格长这样:
| (1) | (2) | (3) | |
|---|---|---|---|
| 工作时长 | 0.018*** | 0.014*** | 0.013*** |
| (5.23) | (4.87) | (4.66) | |
| 年龄 | ... | ... | ... |
| 个体固定效应 | No | Yes | Yes |
| 年份固定效应 | No | No | Yes |
| 样本量 | 28099 | 28099 | 28099 |
| 调整R方 | 0.097 | 0.189 | 0.203 |
固定效应行自动出现在表格底部,而且每个模型都自动判断了 Yes/No。模型 1 什么都没控制,两行都是 No;模型 2 只吸收了个体,个体固定效应显示 Yes,年份显示 No;模型 3 双向固定效应,两行都是 Yes。这才是真正的“自动化标注”。
4.3 reg2docx 的 indicate() 为什么比 esttab 更省心
核心原因是reg2docx把“读取固定效应状态”这个环节做成了内建逻辑。你在indicate()里写“显示标签=变量名”,它就会去检查模型存储信息中该变量是否被吸收、是否存在于回归中,然后自动填 YES 或 NO。
它和esttab的本质区别不是语法,而是它默认考虑到reghdfe、areg、xtreg这类命令的固定效应存储特点。对使用者来说,你只需要保证indicate()里的变量名和模型估计时absorb()里的变量名完全一致就够了。
我个人的习惯是:把reghdfe命令里absorb()的内容直接复制到indicate()里,改一下引号格式,从根上避免拼写不一致。
4.4 常用选项组合:星号、统计量、题注和变量标签
reg2docx很多选项和esttab是同源的,但有些细节值得注意。
星号标注用star()控制,比如经济学论文常用的三档:star(* 0.1 ** 0.05 *** 0.01),这会自动给系数加上星号。
统计量用scalars()添加,scalars("N 样本量" "r2_a 调整R方")这种带中文标签的写法比纯scalars(N r2_a)更友好,出来的表格里显示的是“样本量”而不是 N,“调整R方”而不是 r2_a。
变量顺序用order(),变量显示名用varlabels()。这几个选项组合起来,基本可以做到 “跑完模型,表格直接能交”。
我自己最常用的固定搭配是:
reg2docx 模型列表 using "表.docx", replace /// b(%9.3f) t(%9.3f) star(* 0.1 ** 0.05 *** 0.01) /// scalars("N 样本量" "r2_a 调整R方") /// indicate("个体固定效应=idcode" "年份固定效应=year") /// order(核心解释变量) /// title("表:xxx的回归结果") /// note("注:括号内为 t 值。")跑完打开 Word,几乎不需要再手工排格式。
4.5 想把 Yes/No 换成“控制/未控制”怎么办
有些期刊或导师偏好中文的“控制/未控制”,而不是英文的 Yes/No。reg2docx不同版本的indicate()对自定义显示文本的支持不太一样,稳妥的做法是先生成默认的 Yes/No 表格,然后在 Word 里直接批量替换:查找“Yes”替换为“控制”,查找“No”替换为“未控制”。十秒钟搞定,不会影响表格结构。
如果你用的是较新版本且help reg2docx里显示indicate()支持labels()子选项,那就可以直接在命令里写清楚,生成时就是你要的文字。建议以你本机版本的帮助文档为准。
5. 论文成品阶段的微调、踩坑与排查链路
5.1 Word 里的三线表微调:字体、列宽和居中
reg2docx输出的是标准三线表框架,但中文论文往往还有具体要求。我拿到 docx 后的常规操作是三步:
- 全选表格,把中文字体设成宋体,西文字体设成 Times New Roman,字号一般小五或五号。
- 根据变量名长度调整列宽,核心解释变量列适当加宽,让表格不要挤成一团。
- 表头文字居中,系数列的小数点尽量对齐。Word 里手动对齐确实麻烦,但表格行数不多时,花两分钟调一次也还行。
5.2 变量顺序和变量标签:让表格服务于论证
表格不是把所有跑过的变量都堆上去就好。如果控制变量太多,我会用keep()只保留核心解释变量,把控制变量的估计结果放到附注里。或者用order()把核心解释变量提到最前面,让读者第一眼看到的就是最想论证的系数。
变量标签建议都设置成中文字面意思,避免表格里出现ttl_exp这种变量名。varlabels()这个选项就是干这个的。
5.3 坑一:indicate() 变量名和 absorb() 不一致,导致固定效应行全是 No
这个坑我踩过一次,排查过程非常典型。某次出表,模型里明明absorb(idcode year)控制得很好,reg2docx输出后“年份固定效应”那一行却是 No。第一反应是reg2docx出了问题,反复检查选项之后才发现,indicate()里写的是year_dum,而模型里absorb()用的是year。变量名对不上,工具自然判断“模型里没有这个变量”。
排查链路其实很简单:先看reghdfe命令里absorb()里的准确写法,再逐字对照indicate()里的右侧变量名。不要凭记忆写,直接复制粘贴。
5.4 坑二:est store 命名被覆盖,两张表长得一模一样
有一次我连续输出两张表,用的模型列表分别是m1 m2和m2 m3。结果两张表的第二列看起来一模一样,开始以为是reg2docx缓存了什么,后来才发现自己写循环时不小心把m3重新赋值覆盖了m2。Stata 的est store是按名字存储,你后续一旦再次est store m2,之前那个 m2 就被覆盖了。
建议给模型起不容易冲突的名字,比如m_base、m_fe、m_twoway,不要用m1 m2 m3这种极其容易被覆盖的短名字。每次出表前敲一句est dir,看一眼当前存储的模型列表,能避免一大半低级错误。
5.5 坑三:加入了聚类标准误后,额外统计量怎么加
reghdfe做面板回归一般会加vce(cluster idcode),这时很多人希望表格里同时报告聚类数量。reg2docx的scalars()可以自由添加统计量,常见写法是:
reghdfe ln_wage hours age ttl_exp, absorb(idcode year) vce(cluster idcode) est store m3 reg2docx m3 using "表.docx", replace /// b(%9.3f) t(%9.3f) star(* 0.1 ** 0.05 *** 0.01) /// scalars("N 样本量" "r2_a 调整R方" "N_clust 聚类数") /// indicate("个体固定效应=idcode" "年份固定效应=year")N_clust是reghdfe存储的聚类数量标量名,不同版本可能略有差异,如果输出为空,可以用ereturn list查看实际标量名再调整。
5.6 离线环境安装 reg2docx 的笨办法
有些单位网络环境受限,ssc install经常超时。我的应对方法是:在有网的电脑上把.ado文件包含 help 文件打包下载下来,拷到个人 ado 目录。具体目录可以通过sysdir查询PERSONAL或PLUS路径,然后把文件放进去,重启 Stata 就能调用。这个方法对reghdfe、ftools、reg2docx都适用,属于通用解法。
6. 我目前在用的最终工作流:esttab 与 reg2docx 各管一段
6.1 我的主力流程
经过反复折腾,我现在的主力流程非常固定:
数据清洗完成后,核心模型固定用reghdfe,并加上vce(cluster ...)。每个模型用带语义的名字est store。需要出正式表格时,直接用reg2docx生成中文 Word 三线表,固定效应行用indicate()自动化标注。出表后再用 Word 批量替换把 Yes/No 改成中文“控制/未控制”,微调字体和列宽,任务就结束了。
这套流程对中文论文特别友好,因为reg2docx生成的 docx 天然不裂表、不乱码,标题和注释都能用中文。
6.2 什么情况下我还会用 esttab
esttab并没有被完全替代。需要输出 LaTeX 表格时,我仍然会回到esttab。学术会议投稿或模板要求 LaTeX 时,esttab m1 m2 m3 using table.tex, replace ...再配上一段booktabs设置,效率依然很高。另外,在 Stata 窗口里快速看回归对比,esttab的文本输出也更方便。
换句话说,esttab管快速预览和 LaTeX 输出,reg2docx管最终 Word 成品,两者在这个工作流里各管一段。
6.3 给新人的三点实际建议
最后说三个我踩过坑之后形成的习惯。
第一,模型名一定要起得有意义。m_base、m_fe、m_twoway比m1 m2 m3安全得多,尤其在模型数量多的时候。
第二,indicate()里的变量名永远是问题高发区。每次写完reg2docx命令,我都会对照reghdfe的absorb()把变量名对一遍,宁可慢三十秒,也不愿意事后排查半小时。
第三,自动化标注不是免检标志。工具自动生成的 Yes/No 基本不会错,但你仍然要在最终表格里肉眼核对一遍:模型 3 的两行固定效应到底是不是 Yes。多模型表格最怕的不是工具出错,而是人在复制粘贴时看走了眼。
固定效应自动化标注这件事,解决的不只是省几分钟时间。它让每个模型的固定效应状态都必须经过模型估计本身来确认,而不是依赖你手动敲进 Word 里的那几个字母。对我这种动辄十几个回归的日常工作来说,这一个改变,省下的精力和避免的低级错误,远比我最初预期的多。