Chow 检验是什么?
Chow 检验问的是两个组或断点前后的回归系数是否相同。它不是检验某一组内部是否显著,而是检验模型关系是否在已知分组或已知时间断点上发生结构变化。
先用一句话理解
把 Chow 检验想成问两条线是不是同一条线。A 组有一条斜率,B 组也有一条斜率;Chow 检验关心的是这两条线的截距和斜率是否可以看作一样。
什么时候用
| 判断项 | 教科书式判断 |
|---|---|
| 适合用在 | 有事前明确的分组、制度断点或时间断点,需要检验系数稳定性。 |
| 先不要用在 | 断点是从结果里搜索出来的,或两个子样本模型变量口径不一致。 |
| 最关键的前提 | 被比较的两个模型应有相同变量定义和可比样本结构。 |
| 读表顺序 | 先看断点/分组依据,再看系数差或联合检验 p 值。 |
| 论文表述边界 | Chow 回答结构是否变化,不回答为什么变化。 |
把它和相近方法分清楚
| 容易混淆的对象 | 怎么区分 | 初学者记法 |
|---|---|---|
| 分组回归 | 分别估计两组系数。 | 只能展示差异,不能自动检验差异。 |
| Chow 检验 | 联合检验两组系数是否相同。 | 适合已知断点或分组。 |
| 异质性交互项 | 用交互项在同一模型中检验差异。 | 经验论文中更常用也更灵活。 |
核心直觉和模型公式
系数稳定性假设
Chow 检验的核心原假设是两组系数相同:
\[H_0:\beta_A=\beta_B\]
\[F=\frac{(SSR_p-(SSR_A+SSR_B))/k}{(SSR_A+SSR_B)/(n_A+n_B-2k)}\]
SSR_p 是合并样本残差平方和,SSR_A 和 SSR_B 是两组分别回归的残差平方和。
公式里的符号怎么读
| 符号 | 含义 | 在本页怎么理解 |
|---|---|---|
| β_A / β_B | 两组系数 | 不同组或断点前后的模型参数。 |
| SSR_p | 合并模型残差平方和 | 假设两组系数相同。 |
| SSR_A / SSR_B | 分组模型残差平方和 | 允许两组系数不同。 |
| k | 参数个数 | 被联合检验的系数数量。 |
| F | Chow 统计量 | 衡量分组后拟合改善是否足够大。 |
专业使用口径
专业 Chow 写法要说明断点是事前给定的,不是为了显著性搜索出来的。
| 口径 | 专业写法 |
|---|---|
| 断点来源 | 来自制度、政策、年份或理论分组。 |
| 模型一致 | 两组模型变量、函数形式和样本定义要可比。 |
| 检验对象 | 说明是全体系数稳定性,还是核心系数差异。 |
| 替代写法 | 可用交互项和 Wald 检验报告更直观的组间差。 |
一步一步做:从问题到结果的五步
| 步骤 | 这一页具体做什么 | 做到什么程度才算读懂 |
|---|---|---|
| 1. 先定义断点或分组 | 断点应来自理论或制度。 | 不能先看结果再定断点。 |
| 2. 跑合并模型 | 假设两组共享同一套系数。 | 这是比较基准。 |
| 3. 跑分组模型 | 允许两组系数不同。 | 变量口径必须一致。 |
| 4. 做联合检验 | 检验系数是否整体相等。 | 重点是差异而非组内星号。 |
| 5. 写出结构变化 | 说明关系在断点前后或组间是否改变。 | 不要直接写原因。 |
结果怎么读:先读口径,再读数字
本页案例读表时,先记住这个口径:本案例二分组检验的组间系数差是 -0.000924,标准误是 0.080567,p 值是 0.9909。
| 读表顺序 | 本页怎么读 | 不要这样读 |
|---|---|---|
| 先看分组依据 | 分组是否事前合理。 | 事后分组会削弱可信度。 |
| 再看检验 p 值 | p 小说明系数稳定性被拒绝。 | 不等于某组更重要。 |
| 然后看差异方向 | 具体哪些系数变化。 | 联合显著需要进一步解释来源。 |
| 最后看替代口径 | 交互项或 Wald 检验是否一致。 | 单一 Chow 表不够直观。 |
初学者最容易错在哪里
| 误读 | 为什么错 |
|---|---|
| 把组内显著当组间差异 | 错。Chow 检验的是系数是否相同。 |
| 断点事后搜索 | 错。会产生过拟合和多重检验问题。 |
| 两组变量口径不同 | 错。系数不可比。 |
| 拒绝稳定性就解释机制 | 过快。Chow 只说明结构变了,不说明原因。 |
先看这个案例的结论
- 本案例二分组检验的组间系数差是 -0.000924,标准误是 0.080567,p 值是 0.9909。
- 输出结论为:组间差异不显著。
- Chow/交互项检验回答的是“组间系数是否不同”,不是“每组内部是否显著”。这两个问题不能混写。
变量要求和案例口径
| 字段 | 口径 |
|---|---|
| 数据 | CSMAR 风格 A 股企业创新面板 |
| 原始样本 | 196 家上市公司,2015-2020 年,约 1200 个公司-年观测;各方法有效样本以本页输出表 N 为准 |
| 因变量 | patent_count;回归页通常使用 ln(1 + patent_count) |
| 核心解释变量 | dfi_index,数字普惠金融指数;部分案例输出展示的是标准化后的 dfi_index |
| 控制变量 | roa、lev、size、growth、cashflow、tobinq、top1、dual、board、indep、soe、age |
| 输出文件 | chow_test_results.csv |
| 角色要求 | 因变量、核心解释变量、时间变量 |
| 依赖包 | 无额外 Stata 社区包要求 |
实际代码
下面是本页案例里的最小 Stata 代码。它的作用是帮助你看清变量怎么进入模型、结果文件怎么生成,而不是要求你在网页里手动运行。
log using "/root/workspace/empirical-wizard/workspace/f3e008aa/analysis.log", replace text
global JOB_DIR "/root/workspace/empirical-wizard/workspace/f3e008aa"
set more off
adopath + "/root/ado/plus"
global DATA_PATH "/root/workspace/empirical-wizard/workspace/test_e2e/csmar_innovation.csv"
import delimited "/root/workspace/empirical-wizard/workspace/test_e2e/csmar_innovation.csv", clear case(preserve)
capture confirm global JOB_DIR
if _rc global JOB_DIR "."
* 自动去除完全重复行(同列同值),避免 N 虚增与 xtset 失败
quietly duplicates drop
local idvar ""
local timevar ""
capture confirm variable stkcd
if !_rc {
capture confirm numeric variable stkcd
if _rc {
tempvar __ewiz_id
capture encode stkcd, gen(`__ewiz_id')
if !_rc local idvar "`__ewiz_id'"
}
else {
local idvar "stkcd"
}
}
else {
di as text "面板ID变量不存在,跳过 xtset ID:stkcd"
}
capture confirm variable year
if !_rc {
capture confirm numeric variable year
if _rc {
tempvar __ewiz_time
capture encode year, gen(`__ewiz_time')
if !_rc local timevar "`__ewiz_time'"
}
else {
local timevar "year"
}
}
else {
di as text "时间变量不存在,跳过 xtset time:year"
}
if "`idvar'" != "" & "`timevar'" != "" {
capture xtset `idvar' `timevar'
}
capture confirm numeric variable size
tempvar __grp_src
if _rc {
encode size, gen(`__grp_src')
}
else {
gen double `__grp_src' = size
}
quietly summarize `__grp_src', detail
local __med = r(p50)
tempvar __grp
gen byte `__grp' = cond(`__grp_src' >= `__med', 1, 0) if !missing(`__grp_src')
levelsof `__grp', local(groups_all)
// 样本充分性护栏:每组至少需要 params+10 观测才跑回归,否则跳过
local _min_n = 27 // params (iv+controls) + safety buffer
local groups ""
foreach g of local groups_all {
quietly count if `__grp'==`g'
if r(N) >= `_min_n' {
local groups "`groups' `g'"
}
else {
di as text "[异质性] 跳过 g=`g',N=" r(N) " < " `_min_n' "(样本不足以承载 FE + 控制变量,会导致 R²=1 过拟合)"
}
}
if "`groups'" == "" {
di as error "[异质性] 所有分组样本均不足,无法执行异质性分析。建议改为 by_median 或 by_terciles 粗分组。"
exit 0
}
tempname fh
capture file close `fh'
file open `fh' using "$JOB_DIR/regression_table_异质性检验.csv", write replace
local __hdr = "变量"
foreach g of local groups {
local __grp_label "(size<中位数)"
if "`g'" == "1" local __grp_label "(size≥中位数)"
local __hdr = "`__hdr',`__grp_label'"
}
file write `fh' "`__hdr'" _n
local __row = "dfi_index"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[dfi_index]
capture local __se = _se[dfi_index]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[dfi_index]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "roa"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[roa]
capture local __se = _se[roa]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[roa]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "lev"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[lev]
capture local __se = _se[lev]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[lev]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "size"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[size]
capture local __se = _se[size]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[size]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "growth"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[growth]
capture local __se = _se[growth]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[growth]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "cashflow"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[cashflow]
capture local __se = _se[cashflow]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[cashflow]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "tobinq"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[tobinq]
capture local __se = _se[tobinq]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[tobinq]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "top1"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[top1]
capture local __se = _se[top1]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[top1]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "dual"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[dual]
capture local __se = _se[dual]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[dual]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "board"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[board]
capture local __se = _se[board]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[board]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "indep"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[indep]
capture local __se = _se[indep]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[indep]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "soe"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[soe]
capture local __se = _se[soe]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[soe]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "age"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __c = _b[age]
capture local __se = _se[age]
capture local __p = 2*ttail(e(df_r), abs(`__c'/`__se'))
local __stars = cond(`__p'<0.01,"***",cond(`__p'<0.05,"**",cond(`__p'<0.1,"*","")))
local __coef_s : display %9.4f `__c'
capture if abs(`__c') < 0.00005 & `__c' != 0 local __coef_s : display %9.2e `__c'
local __row = "`__row',`__coef_s'`__stars'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = ""
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
capture local __se = _se[age]
local __se_s : display %9.4f `__se'
capture if abs(`__se') < 0.00005 & `__se' != 0 local __se_s : display %9.2e `__se'
local __row = "`__row',(`__se_s')"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "N"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
local __v = e(N)
local __v_s : display %9.0f `__v'
local __row = "`__row',`__v_s'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
local __row = "R²"
foreach g of local groups {
capture quietly reghdfe patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp'==`g', absorb(`timevar' ind) vce(robust) keepsingletons
if _rc==0 {
local __v = e(r2)
local __v_s : display %9.4f `__v'
local __row = "`__row',`__v_s'"
}
else {
local __row = "`__row',"
}
}
file write `fh' "`__row'" _n
file close `fh'
* ── 组间系数差异检验(two-group case)──
* 构造 group × 核心解释变量 交互项,若交互显著则组间差异显著
tempname diff_fh
capture file close `diff_fh'
file open `diff_fh' using "$JOB_DIR/heterogeneity_diff_test.csv", write replace
file write `diff_fh' "项目,值,说明" _n
quietly count if !missing(`__grp')
local _n_total = r(N)
quietly levelsof `__grp', local(_all_g)
local _n_groups : word count `_all_g'
file write `diff_fh' "分组数,`_n_groups',仅二分组情形下 Chow/交互项检验成立" _n
if `_n_groups' == 2 {
tempvar __g_bin __iv_x_g
quietly gen double `__g_bin' = `__grp' - `: word 1 of `_all_g''
quietly replace `__g_bin' = `__g_bin' / (`: word 2 of `_all_g'' - `: word 1 of `_all_g'')
quietly gen double `__iv_x_g' = dfi_index * `__g_bin'
capture reg patent_count dfi_index `__g_bin' `__iv_x_g' roa lev size growth cashflow tobinq top1 dual board indep soe age if !missing(`__grp'), vce(robust)
if _rc == 0 {
local diff_coef = _b[`__iv_x_g']
local diff_se = _se[`__iv_x_g']
local diff_t = `diff_coef' / `diff_se'
* Use t-distribution with regression df_r when available; fall
* back to normal only when df_r is missing. Normal approximation
* over-rejects in small/medium samples.
capture local _diff_df = e(df_r)
if "`_diff_df'" == "" | "`_diff_df'" == "." {
local diff_p = 2*(1 - normal(abs(`diff_t')))
}
else {
local diff_p = 2*ttail(`_diff_df', abs(`diff_t'))
}
local diff_coef_s : display %12.6f `diff_coef'
local diff_se_s : display %12.6f `diff_se'
local diff_p_s : display %9.4f `diff_p'
file write `diff_fh' "组间系数差(组2-组1),`diff_coef_s'," _n
file write `diff_fh' "标准误,`diff_se_s'," _n
file write `diff_fh' "p 值,`diff_p_s',p<0.1 * / p<0.05 ** / p<0.01 ***" _n
if `diff_p' < 0.01 {
file write `diff_fh' "结论,组间差异显著,在 1%% 水平显著" _n
}
else if `diff_p' < 0.05 {
file write `diff_fh' "结论,组间差异显著,在 5%% 水平显著" _n
}
else if `diff_p' < 0.1 {
file write `diff_fh' "结论,组间差异边际显著,在 10%% 水平显著" _n
}
else {
file write `diff_fh' "结论,组间差异不显著,p 值 = `diff_p_s'不能拒绝组间系数相等" _n
}
}
else {
file write `diff_fh' "结论,交互项回归失败," _n
}
}
else if `_n_groups' >= 3 & `_n_groups' <= 6 {
* ── >2 组:suest 多组系数联合相等检验 ──
* 对每个 group 单独估计同一规格,suest 合并方差矩阵后做
* H0: [g1] iv = [g2] iv = ... = [gk] iv 的 Wald 检验。
local __suest_ok = 1
local __est_names ""
local __test_eq ""
local __i = 0
foreach _gv of local _all_g {
local __i = `__i' + 1
capture noisily {
quietly reg patent_count dfi_index roa lev size growth cashflow tobinq top1 dual board indep soe age if `__grp' == `_gv', vce(robust)
}
if _rc != 0 {
local __suest_ok = 0
}
else {
estimates store __het`__i'
local __est_names "`__est_names' __het`__i'"
if `__i' == 1 {
local __test_eq "[__het`__i'_mean]dfi_index"
}
else {
local __test_eq "`__test_eq' = [__het`__i'_mean]dfi_index"
}
}
}
if `__suest_ok' == 1 {
capture noisily suest `__est_names', vce(robust)
if _rc == 0 {
capture noisily test `__test_eq'
if _rc == 0 {
local joint_chi2 = r(chi2)
local joint_df = r(df)
local joint_p = r(p)
local joint_chi2_s : display %9.4f `joint_chi2'
local joint_df_s : display %2.0f `joint_df'
local joint_p_s : display %9.4f `joint_p'
file write `diff_fh' "Wald chi2,`joint_chi2_s',suest 联合检验" _n
file write `diff_fh' "df,`joint_df_s'," _n
file write `diff_fh' "p 值,`joint_p_s'," _n
if `joint_p' < 0.01 file write `diff_fh' "结论,各组系数显著不等,1% 水平拒绝同质" _n
else if `joint_p' < 0.05 file write `diff_fh' "结论,各组系数显著不等,5% 水平拒绝同质" _n
else if `joint_p' < 0.1 file write `diff_fh' "结论,各组系数边际不等,10% 水平拒绝同质" _n
else file write `diff_fh' "结论,各组系数同质性未被拒绝,p=`joint_p_s'" _n
}
else {
file write `diff_fh' "结论,suest test 失败,可能是估计向量不可比" _n
}
}
else {
file write `diff_fh' "结论,suest 合并方差矩阵失败," _n
}
}
else {
file write `diff_fh' "结论,部分子组回归失败 — 无法做 suest 联合检验," _n
}
foreach _en of local __est_names {
capture estimates drop `_en'
}
}
else {
file write `diff_fh' "结论,分组数过多 (>6) — 跳过 suest,建议改为子样本回归," _n
}
file close `diff_fh'
* ── 扩展异质性候选矩阵(exploratory disclosure matrix)──
* 目的:记录候选分组变量的组间差异检验结果;不把显著项自动包装为预设主结论。
tempname cm_fh
capture file close `cm_fh'
file open `cm_fh' using "$JOB_DIR/heterogeneity_candidate_matrix.csv", write replace
file write `cm_fh' "candidate,method,source_groups,n_group0,n_group1,diff_coef,diff_se,diff_p,status,note" _n
* candidate grouping: size
capture confirm variable size
if _rc {
file write `cm_fh' "size,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable size
if _rc {
capture encode size, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = size
}
if _rc {
file write `cm_fh' "size,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "size,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "size,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev growth cashflow tobinq top1 dual board indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "size,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "size,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: age
capture confirm variable age
if _rc {
file write `cm_fh' "age,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable age
if _rc {
capture encode age, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = age
}
if _rc {
file write `cm_fh' "age,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "age,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "age,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth cashflow tobinq top1 dual board indep soe if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "age,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "age,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: indep
capture confirm variable indep
if _rc {
file write `cm_fh' "indep,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable indep
if _rc {
capture encode indep, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = indep
}
if _rc {
file write `cm_fh' "indep,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "indep,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "indep,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth cashflow tobinq top1 dual board soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "indep,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "indep,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: ind
capture confirm variable ind
if _rc {
file write `cm_fh' "ind,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable ind
if _rc {
capture encode ind, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = ind
}
if _rc {
file write `cm_fh' "ind,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "ind,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "ind,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth cashflow tobinq top1 dual board indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "ind,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "ind,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: board
capture confirm variable board
if _rc {
file write `cm_fh' "board,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable board
if _rc {
capture encode board, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = board
}
if _rc {
file write `cm_fh' "board,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "board,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "board,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth cashflow tobinq top1 dual indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "board,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "board,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: cashflow
capture confirm variable cashflow
if _rc {
file write `cm_fh' "cashflow,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable cashflow
if _rc {
capture encode cashflow, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = cashflow
}
if _rc {
file write `cm_fh' "cashflow,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "cashflow,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "cashflow,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth tobinq top1 dual board indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "cashflow,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "cashflow,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: dual
capture confirm variable dual
if _rc {
file write `cm_fh' "dual,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable dual
if _rc {
capture encode dual, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = dual
}
if _rc {
file write `cm_fh' "dual,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "dual,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "dual,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size growth cashflow tobinq top1 board indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "dual,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "dual,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
* candidate grouping: growth
capture confirm variable growth
if _rc {
file write `cm_fh' "growth,auto_binary_or_median,.,.,.,.,.,.,missing_variable,候选变量不存在" _n
}
else {
tempvar __cm_src __cm_grp __cm_gbin __cm_xg __cm_tag
capture confirm numeric variable growth
if _rc {
capture encode growth, gen(`__cm_src')
}
else {
capture gen double `__cm_src' = growth
}
if _rc {
file write `cm_fh' "growth,auto_binary_or_median,.,.,.,.,.,.,encoding_failed,变量无法数值化" _n
}
else {
quietly egen byte `__cm_tag' = tag(`__cm_src') if !missing(`__cm_src')
quietly count if `__cm_tag' == 1
local __cm_ng = r(N)
if `__cm_ng' < 2 {
file write `cm_fh' "growth,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,有效分组少于2组" _n
}
else {
if `__cm_ng' == 2 {
quietly levelsof `__cm_src' if !missing(`__cm_src'), local(__cm_lvls)
quietly gen double `__cm_grp' = `__cm_src'
local __cm_method "binary_or_two_value"
}
else {
quietly summarize `__cm_src' if !missing(`__cm_src'), detail
local __cm_med = r(p50)
quietly gen byte `__cm_grp' = (`__cm_src' >= `__cm_med') if !missing(`__cm_src')
local __cm_method "median_split"
}
quietly levelsof `__cm_grp' if !missing(`__cm_grp'), local(__cm_glvls)
local __cm_gg : word count `__cm_glvls'
if `__cm_gg' != 2 {
file write `cm_fh' "growth,auto_binary_or_median,`__cm_ng',.,.,.,.,.,degenerate,二分后仍不是2组" _n
}
else {
local __cm_g0 : word 1 of `__cm_glvls'
local __cm_g1 : word 2 of `__cm_glvls'
quietly count if `__cm_grp'==`__cm_g0'
local __cm_n0 = r(N)
quietly count if `__cm_grp'==`__cm_g1'
local __cm_n1 = r(N)
quietly gen double `__cm_gbin' = (`__cm_grp' == `__cm_g1') if !missing(`__cm_grp')
quietly gen double `__cm_xg' = dfi_index * `__cm_gbin'
capture reg patent_count dfi_index `__cm_gbin' `__cm_xg' roa lev size cashflow tobinq top1 dual board indep soe age if !missing(`__cm_grp'), vce(robust)
if _rc == 0 {
local __cm_diff = _b[`__cm_xg']
local __cm_se = _se[`__cm_xg']
local __cm_t = `__cm_diff' / `__cm_se'
capture local __cm_df = e(df_r)
local __cm_p = cond("`__cm_df'"=="" | "`__cm_df'"==".", 2*(1-normal(abs(`__cm_t'))), 2*ttail(`__cm_df', abs(`__cm_t')))
local __cm_diff_s : display %12.6f `__cm_diff'
local __cm_se_s : display %12.6f `__cm_se'
local __cm_p_s : display %9.4f `__cm_p'
local __cm_status "not_supported"
if `__cm_p' < 0.1 local __cm_status "supported"
file write `cm_fh' "growth,`__cm_method',`__cm_ng',`__cm_n0',`__cm_n1',`__cm_diff_s',`__cm_se_s',`__cm_p_s',`__cm_status',exploratory disclosed candidate" _n
}
else {
file write `cm_fh' "growth,auto_binary_or_median,`__cm_ng',`__cm_n0',`__cm_n1',.,.,.,regression_failed,交互项回归失败" _n
}
}
}
}
}
file close `cm_fh'
di "异质性分析完成:分组变量=size,方法=by_median"
log close
实际输出表
这张表是本方法页配套案例的输出结果,用来把前面的公式、变量口径和代码对应到实际数字。
| 项目 | 值 | 说明 |
|---|---|---|
| 分组数 | 2 | 仅二分组情形下 Chow/交互项检验成立 |
| 组间系数差(组2-组1) | -0.000924 | |
| 标准误 | 0.080567 | |
| p 值 | 0.9909 | p<0.1 * / p<0.05 ** / p<0.01 *** |
| 结论 | 组间差异不显著 | p 值 = 0.9909不能拒绝组间系数相等 |
补充输出
下面这些文件来自同一次案例生成流程,用来补齐主表之外的诊断信息。
heterogeneity_candidate_matrix.csv
| candidate | method | source_groups | n_group0 | n_group1 | diff_coef | diff_se | diff_p | status | note |
|---|---|---|---|---|---|---|---|---|---|
| size | median_split | 713 | 360 | 360 | -0.014187 | 0.081464 | 0.8618 | not_supported | exploratory disclosed candidate |
| age | median_split | 711 | 360 | 360 | 0.080481 | 0.082598 | 0.3302 | not_supported | exploratory disclosed candidate |
| indep | median_split | 711 | 360 | 360 | -0.041161 | 0.083297 | 0.6214 | not_supported | exploratory disclosed candidate |
| ind | median_split | 8 | 360 | 360 | -0.042618 | 0.082585 | 0.6060 | not_supported | exploratory disclosed candidate |
| board | median_split | 716 | 360 | 360 | -0.083428 | 0.084312 | 0.3228 | not_supported | exploratory disclosed candidate |
| cashflow | median_split | 712 | 360 | 360 | 0.027120 | 0.081387 | 0.7391 | not_supported | exploratory disclosed candidate |
| dual | median_split | 715 | 360 | 360 | 0.177181 | 0.081392 | 0.0298 | supported | exploratory disclosed candidate |
| growth | median_split | 715 | 360 | 360 | -0.009234 | 0.081983 | 0.9104 | not_supported | exploratory disclosed candidate |
regression_table_异质性检验.csv
| 变量 | (size<中位数) | (size≥中位数) |
|---|---|---|
| dfi_index | 0.5890*** | 0.5784*** |
| ( 0.0596) | ( 0.0542) | |
| roa | 0.2790*** | 0.3030*** |
| ( 0.0636) | ( 0.0592) | |
| lev | -0.0311 | -0.0202 |
| ( 0.0613) | ( 0.0655) | |
| size | 0.1006 | 0.3969*** |
| ( 0.0855) | ( 0.1092) | |
| growth | 0.0389 | -0.1402** |
| ( 0.0611) | ( 0.0640) | |
| cashflow | 0.0532 | -0.0009 |
| ( 0.0649) | ( 0.0546) | |
| tobinq | 0.0692 | -0.0029 |
| ( 0.0695) | ( 0.0608) | |
| top1 | -0.0406 | 0.0147 |
| ( 0.0633) | ( 0.0638) | |
| dual | -0.0110 | 0.0127 |
| ( 0.0603) | ( 0.0657) | |
| board | -0.0549 | 0.0066 |
| ( 0.0552) | ( 0.0593) | |
| indep | 0.0553 | -0.0808 |
| ( 0.0667) | ( 0.0574) | |
| soe | 0.0379 | -0.0521 |
| ( 0.0602) | ( 0.0681) | |
| age | 0.0449 | -0.0628 |
| ( 0.0606) | ( 0.0569) | |
| N | 360 | 360 |
| R² | 0.2840 | 0.3323 |
案例图
这是一张由同一份案例数据生成的页面内诊断图。

论文里怎么写
本文在共用企业面板样本上报告Chow 已知断点检验,核心输出见 chow_test_results.csv。结果解释时同时关注样本口径、变量构造、系数方向、标准误和适用前提,避免只凭单个 p 值完成方法选择。
检查清单
- 先说明断点或分组是事前确定的,不是看结果后挑出来的。
- Chow 检验回答的是两组系数是否相同,不是每组内部是否显著。
- 如果主表使用稳健标准误,最好用交互项或 Wald 检验作稳健口径。