刚接触Stata实证分析的朋友,十有八九都见过那行刺眼的红字:reghdfe跑得正欢,突然来一句"convergence not achieved"或者"variable omitted because of collinearity",瞬间心态就崩了。尤其是当你辛辛苦苦把行业、地区、年份的虚拟变量一股脑塞进回归方程,想着把固定效应做全做细,结果Stata直接给你扔出一堆omitted,系数没出来,标准误没出来,整个回归结果跟报废没什么两样。这篇文章就围绕这个经典痛点展开,讲讲为什么用传统方式做高维固定效应容易翻车,以及reghdfe这个命令凭什么能帮你把行业、地区、年份多重固定效应稳稳跑出来。
先给结论:reghdfe是处理高维固定效应的利器,它的核心卖点不是"多了一个回归命令",而是把固定效应从"放进回归列表里逐个估计"变成"先行剔除再估计",这样既避免了虚拟变量爆炸带来的计算灾难,也从根源上绕开了一大批共线性问题。适合正在做面板数据、公司金融、区域经济这类需要控制行业和地区特征的实证研究者,尤其是那些被"共线性"三个字折磨过的新手。
1. 为什么"行业+地区+年份"三个固定效应一起加,经常跑出来一团乱
很多人一开始用的都是最直觉的思路:既然要控制行业、地区、年份,那就用reg y x i.industry i.province i.year把虚拟变量全部放进去。小样本、少类别的情况下,这个做法还能勉强出结果,但一旦行业分类到两位数编码、省份有31个、年份跨度十几年,问题就接踵而至。
第一层问题是虚拟变量爆炸。假设你有48个行业、31个省份、12个年份,光这三维虚拟变量就是91个,再算上常数项和核心解释变量,回归矩阵的规模一下就上去了。Stata不是跑不动,但计算效率会明显下降,内存占用飙升,跑一次回归够你喝两杯咖啡。而且行业和省份之间存在大量交叉单元格,比如某个行业只在少数几个省份出现,某些省份根本没有某些行业,这些空单元格会让矩阵"秩亏",系数矩阵没法正常求逆,结果就是整列虚拟变量被omitted。
第二层问题是真正的共线性来源。行业虚拟变量、省份虚拟变量、年份虚拟变量三者之间,本身就存在完全线性相关的可能。比如你的样本里某个行业只在某一年出现,那么这个"行业×年份"的组合效应就和单独的行业虚拟变量、年份虚拟变量混在一起分不开。更常见的是,所有行业虚拟变量加起来等于常数项,所有省份虚拟变量加起来也等于常数项,如果你在回归里同时放了常数项和全部虚拟变量,必然撞车。Stata为了识别模型,会自动剔除一组基准组,但剔除的逻辑不受你控制,经常剔除的不是你想要的那个,于是关键变量的系数估计就变得怪异,甚至彻底被吃掉。
第三层问题是LSDV方式带来的标准误偏差。哪怕你的回归勉强跑出来了,逐个估计一两百个虚拟变量的系数,自由度消耗非常严重。每个虚拟变量都在"吃掉"样本信息,核心解释变量的标准误会因此被放大,t值不显著,但这不是真实关系不显著,而是你的估计方法把精度浪费在了那些你根本不关心的行业系数和省份系数上。
所以问题的本质不是"固定效应不该加",而是"加的方式不对"。你真正想要的只是把行业差异、地区差异、时间差异从扰动项里清理掉,并不Care行业48个系数各自是多少、省份31个系数各自是多少,但传统做法把这堆无关系数全部估计了一遍,既慢又容易出共线性。
2. reghdfe的底层逻辑:先把固定效应"吸收"掉,再回归
reghdfe命令的全称是"regress with high-dimensional fixed effects",由Sergio Correia开发。它解决上述问题的思路非常朴素:既然不关心固定效应本身的系数,那就把它们从方程里彻底消掉,只估计剩余变量的系数。
这个"消掉"的过程在计量经济学里叫"组内去均值"或者"within transformation",但在高维固定效应场景下,不能简单地对一个维度去均值,而是要同时对多个维度做"交替投影"(alternating projections)。通俗理解就是:先把行业固定效应的影响从变量里剥掉,再把地区固定效应的影响剥掉,再剥年份,然后重新回到行业维度继续剥,循环往复,直到收敛。这个过程类似把一块沾了多层颜料的白布反复漂洗,每轮漂掉一层颜色,最后剩下的就是"纯净的"扰动和解释变量信息,然后用干净的数据跑一个普通最小二乘回归。
reghdfe内部用的具体算法是Kloek(1981)提出的迭代去均值方法,配合Convergence Criteria来控制迭代次数。对于绝大多数面板数据集,几十次迭代内就能收敛,速度远快于生成上百个虚拟变量后做矩阵求逆。这也是为什么你的数据里有几万个观测值、三位固定效应时,reghdfe依然能秒出结果,而reg i.year i.province i.industry可能要跑上几分钟甚至内存溢出。
由此引出几个关键概念:
- absorb():括号里写你要吸收掉的固定效应维度。这就是
reghdfe最核心的选项。 - cluster():聚类标准误选项,吸收完固定效应之后,它会在选定的聚类维度上做稳健调整。
- vce(robust):异方差稳健标准误。
- keep():在吸收高维固定效应的同时,保留某些虚拟变量不吸收,比如某个年份虚拟变量的交互项。
这个设计带来了一个直接好处:因为固定效应不再显式进入回归矩阵,所以行业、省份、年份三者之间的虚拟变量线性相关问题被绕开了。你根本不需要去关心Stata自动剔除了哪一组基准组,因为reghdfe在吸收阶段就把这些维度上的共同信息全部剥离,剩下的变异都是"行业内随时间的变化"、"省内随时间的变化"这类纯粹的组内变异。
3. 实操全流程:从数据准备到命令输出,直接照抄
纸上谈兵没意思,直接上一份可以本地复现的实操流程。假设你在做一个制造业上市公司的面板数据,核心解释变量是研发投入强度rd_intensity,被解释变量是企业全要素生产率tfp,需要控制行业(industry_code)、省份(province_code)、年份(year)。
先安装命令。reghdfe不是Stata官方命令,需要从scc仓库安装:
ssc install reghdfe, replace同时建议安装两个配套命令,一个用于输出表格,一个用于处理多维聚类:
ssc install estout, replace ssc install ftools, replaceftools是reghdfe早期版本依赖的工具包,新版可能不再强制需要,但装上没坏处。
然后做数据预处理。建议把行业和省份的变量先转成数值型编码,避免字符串变量带来麻烦:
encode industry_name, gen(industry_code) encode province_name, gen(province_code) xtset company_id yearxtset这行不是reghdfe必需的,但如果你后面还要跑xtreg做对比,或者用xttest0检验随机效应,那就提前设好。
核心回归只有一行:
reghdfe tfp rd_intensity size leverage age, absorb(industry_code province_code year) vce(cluster company_id)解释一下这行代码的意思:tfp是被解释变量,rd_intensity是核心解释变量,size leverage age是控制变量,absorb()里放了三个固定效应维度,vce(cluster company_id)表示按公司聚类调整标准误,允许同一家公司不同年份之间的扰动项相关。
跑完以后,Stata会输出一大张表。常见的结果展示包括:
| 项目 | 含义 |
|---|---|
tfp | 被解释变量,企业全要素生产率 |
rd_intensity | 核心解释变量,研发投入强度 |
size | 企业规模控制变量 |
leverage | 杠杆率控制变量 |
age | 企业年龄控制变量 |
Absorbed | 显示被吸收的三个固定效应及对应的类别数量 |
F test | 联合显著性检验,检验所有解释变量是否整体显著 |
R-squared | 模型解释力 |
Number of obs | 观测值数量 |
需要特别注意输出表底部的Absorbed行,它会写"industry_code absorbed(48 categories)"这样的信息,告诉你行业固定效应吸收了48个类别。如果这里显示的类别数量和你的数据编码一致,说明吸收过程正常。
跑完reghdfe后,有人习惯用estat vif检查多重共线性,这里要提醒一句:reghdfe执行完之后,estat vif很多时候是不能用的,因为它基于的是吸收后残差数据的方差膨胀因子,本质上只能反映解释变量之间在"去除固定效应后"的共线性,跟你担心的行业地区虚拟变量共线性已经不是一个概念了。想检查原始解释变量之间的共线性,应该在普通reg之后跑,或者直接看相关系数矩阵。
结果解释上,rd_intensity的系数就是你要的核心结论。比如系数是0.032,p值0.01,意味着研发投入强度每提高1个百分点,企业全要素生产率平均提高0.032个单位。注意这个解释必须放在"控制了行业差异、地区差异、年份差异之后"的语境下,不能省略固定效应控制的限定语。
4. 输出三线表与标准化处理:让结果能直接进论文
跑出结果只是第一步,写论文的人都知道,最后要的是规范的三线表。我用的是esttab配合reghdfe的组合,这个流程自己走顺了之后效率很高。
先给基础回归编号并存储结果:
* 模型1:只有核心解释变量 reghdfe tfp rd_intensity, absorb(industry_code province_code year) vce(cluster company_id) est store m1 * 模型2:加入控制变量 reghdfe tfp rd_intensity size leverage age, absorb(industry_code province_code year) vce(cluster company_id) est store m2 * 模型3:换成二维聚类标准误 reghdfe tfp rd_intensity size leverage age, absorb(industry_code province_code year) vce(cluster company_id year) est store m3三个模型分别存储后,一行代码输出表格:
esttab m1 m2 m3, b(3) se(3) star(* 0.10 ** 0.05 *** 0.01) /// title("Table 1: 固定效应回归结果") /// mtitles("基准模型" "加入控制变量" "二维聚类") /// keep(tfp rd_intensity size leverage age) /// stats(N r2, labels("样本量" "R-squared"))参数含义:b(3)表示系数保留三位小数,se(3)是标准误保留三位小数,star指定显著性符号规则,keep限制表格里只显示核心变量,避免固定效应信息刷屏。stats控制表格底部报告什么统计量。
如果你用的是中文环境,标题和标签都可以写中文,Stata对中文标签的支持在输出docx或tex时完全没问题,但控制台显示可能乱码,不影响最终导出。
导出到外部文档:
esttab m1 m2 m3 using "固定效应结果.csv", replace b(3) se(3) star(* 0.10 ** 0.05 *** 0.01) /// mtitles("基准" "加控制" "二维聚类") keep(tfp rd_intensity size leverage age)推荐先导出csv,在Excel里微调格式后再转成Word表格,比直接输出rtf好用,因为rtf表格的列宽控制比较费劲。
关于标准误的问题多说一句。行业、地区双重固定效应模型里,同一省份内的企业扰动项往往存在相关性,同一行业内的企业也存在相关性,所以单独按公司聚类可能不够稳健。如果你担心的是行业层面的相关性,可以考虑vce(cluster industry_code);如果担心行业和省份两个维度同时存在聚类效应,可以用双向聚类:
reghdfe tfp rd_intensity size leverage age, absorb(industry_code province_code year) vce(cluster industry_code province_code)不过双向聚类对数据量要求较高,样本太少时协方差矩阵可能不是正定的,跑出负方差不要慌,先看看每个聚类维度的类别数够不够。
5. reghdfe的边界:什么情况它救不了你
reghdfe不是万能药,有几个场景它确实搞不定,新手最容易被表面上的"跑通"迷惑,实际模型已经出了问题。
第一个是完全共线性问题并没有消失,只是被隐藏了。如果你的核心解释变量是industry_code这个维度上的常量,比如你想研究"行业平均工资对企业生产率的影响",但这个变量本来就是行业代码的直接映射,那么它在吸收了行业固定效应之后,就完全没有任何变异了,reghdfe会直接把它drop掉,输出一个系数为空的结果。这其实是一个重要信号:你的变量和固定效应完全重合,需要换个研究设计。同理,某省的虚拟变量如果吸收了省份固定效应,系数自然也是空的。很多人看到空结果以为reghdfe出bug了,其实不是,这是模型识别不了,本质上是"你的数据里,这个变量的变异全部来自你已经吸收掉的那个维度"。
第二个是样本量太小或单元格太稀疏。假设你的样本里,某行业在大多数省份都没有企业,只有个别省份有少数企业,那这个行业和省份的交叉效应就完全依赖几个孤点支撑。reghdfe虽然能跑,但估计结果极不稳定,标准误巨大。遇到这种情况,建议合并行业分类,或者把省份聚合成大区,减少吸收维度里的类别数量。
第三个是多重高维固定效应下的聚类标准误偏误。reghdfe的vce(cluster company_id)在固定效应完全吸收后,其实是在做"有限样本修正",但当每个聚类组里只有一两个观测值时,修正效果不可靠。我自己踩过的坑是:把年份聚类和公司聚类同时放进去,结果协方差矩阵不是正定的,Stata直接报错"convergence not achieved"。解决思路是减少聚类维度,或者检查是不是某个聚类组里样本量太少。
另外,很多人在跑reghdfe时看到"singleton observations"的提示,这是说某些固定效应组合里只有一个观测值。Stata默认会报告这些singleton的数量,但不会自动删除。有研究建议删除singleton,因为它们实际上不提供识别信息,还容易让标准误变小。想删除的话可以用reghdfe的keep(singletons)选项来控制是否保留,默认是剔除。早期版本里如果保留singleton可能导致标准误下偏,新版本已经做了处理,但我个人习惯是保留默认剔除,让估计更保守。
6. 从reghdfe到更复杂的固定效应结构
掌握了基础用法之后,有几个进阶场景值得提一下,实际研究中经常遇到。
第一种是交互固定效应。有时候你要控制"行业×年份"的联合效应,比如某年国家对特定行业出台了扶持政策,这个政策同时随行业和时间变化,如果你只放行业固定效应和年份固定效应,政策效应会被混进扰动项。这时可以在absorb()里写交互项:
reghdfe tfp rd_intensity size leverage age, absorb(industry_code##year province_code) vce(cluster company_id)industry_code##year表示吸收"行业—年份"交互固定效应。这个操作在普通areg或xtreg里很难实现,因为类别数会爆炸性增长,reghdfe的优势就非常明显。需要注意的是,当你把交互固定效应吸收掉之后,所有随行业和年份同时变化的解释变量都没法识别了,比如某行业平均补贴强度这种变量,会跟交互固定效应完全共线,直接被删掉。
第二种是多维固定效应下的IV估计。如果你的核心解释变量有内生性,需要工具变量,可以结合ivreghdfe命令(也是Correia写的):
ssc install ivreghdfe, replace ivreghdfe tfp (rd_intensity = iv_rd), absorb(industry_code province_code year) cluster(company_id)ivreghdfe的用法跟reghdfe高度一致,只是多了工具变量设定,固定效应部分同样可以吸收多维度。做了IV之后,输出表会报告第一阶段的F统计量,判断工具变量是否弱识别,这个值最好大于10。
第三种是与ppmlhdfe的关系。如果你的被解释变量是贸易额、专利计数这种含有大量零值的数据,应该考虑ppmlhdfe(ppml with high-dimensional fixed effects),它处理的是泊松伪最大似然估计,比reghdfe更适用于计数数据。但两者在固定效应吸收逻辑上完全一致,学会reghdfe再转ppmlhdfe只用改估计方法,absorb()语法不变。
7. 几个新手最容易忽略的细节和实操心得
最后写一点个人经验。这些细节看起来不起眼,但都直接影响结果可信度。
第一,reghdfe的vce(cluster company_id)和xtreg, fe的聚类标准误不是一回事。xtreg, fe默认报告的是普通标准误,如果没加vce(robust)或vce(cluster),标准误可能偏小。reghdfe默认不加聚类时报告的是异方差稳健标准误,加了cluster才是聚类稳健。读论文时看到"标准误在公司层面聚类",你要确认对方用的是reghdfe还是xtreg,两者在有限样本下的修正方式不同,数字会有差别,但不影响系数本身。
第二,跑reghdfe之前先确认变量没有缺失值。reghdfe对缺失值的处理是直接删掉任意一个变量缺失的观测,如果你控制变量里有大量缺失,它会在你毫无感知的情况下把样本量缩水。跑完后看一眼Number of obs和你原始数据量差多少,如果缩水严重,回头清洗缺失值,别闷头解读结果。
第三,absorb()里每个维度的类别数不要太多,否则内存占用会高得吓人。遇到过有人把几十万量级的个人编码放进去吸收,结果Stata直接内存溢出。reghdfe对高维固定效应的处理极限取决于你的机器内存和Stata位数(64位版能处理更大的矩阵),如果你的个人编码有几十万类别,建议用esttab导出结果之前先考虑一下类别维度是否合理。
第四,关于共线性报错,如果是reghdfe (convergence not achieved),不要第一时间怀疑数据有问题,先检查是不是迭代次数不够。可以用iterate(1000)显式指定最大迭代次数,或者tolerance(1e-10)放宽收敛精度。更多时候,这个报错是由两个固定效应维度在某个子样本上完全共线引起的,比如某一年份只有某个行业有数据,那年份和行业在这个子样本上就会撞车。解决思路是删掉那些在特定维度上没有变异的观测,或者重新定义固定效应的粒度。
第五,新手容易混淆"显著"和"有意义"。reghdfe输出一行星号,代表的是统计显著性,如果你在几十个回归组合里反复搜索显著性组合,那已经涉及多重假设检验问题了。固定效应模型的本质是"更干净的对照组比较",跑出不显著的结果不代表你的研究没有价值,反而说明在控制了大量异质性之后,核心关系依然稳健,这本身就是有意义的结论。
从实际使用体验来看,reghdfe这套工具链已经成为我处理面板数据的标配。无论是两维固定效应、三维固定效应还是交互固定效应,一行命令就能搞定,速度和稳健性都比手动生成虚拟变量可靠得多。项目开始时多花十分钟把固定效应结构想清楚,远比反复试错省时间。