第一次接触PSM-DID方法时,我被一个基础问题困扰了很久:为什么匹配要用截面数据,而差分却需要面板数据?这就像试图用螺丝刀拧螺母——工具和任务根本不匹配。后来在实际项目中踩过几次坑才明白,这个矛盾恰恰是影响模型效果的关键。
PSM(倾向得分匹配)的本质是通过概率模型寻找"双胞胎"。比如研究某项政策对企业利润的影响,我们需要为每个受政策影响的企业(处理组)找到未受政策影响但其他特征相似的对照企业。这里有个重要前提:所有企业的特征数据必须来自同一时间截面。就像相亲时比较双方条件,肯定要用当前时点的数据,总不能拿男方现在的收入和女方五年前的学历做匹配。
而DID(双重差分法)的核心是观察处理组和对照组在政策前后的变化差异。这就必须使用面板数据,至少要包含政策实施前后两个时期的数据。好比想验证健身效果,光看现在的体重没意义,必须对比健身前后的体重变化。
常见错误操作:
把面板数据压平成截面数据是最偷懒的做法,但实测下来问题很严重。举个例子:用2010-2020年企业数据研究环保政策效应,如果把10年数据堆叠,2015年的处理组企业可能匹配到2012年的对照组企业。这就像用2023年的iPhone和2018年的三星手机比性能,时间差异会污染政策效应估计。
我在上市公司数据测试中发现,这种自匹配会导致:
stata复制// 错误示例:面板数据压平匹配
webuse nlswork, clear
stack year idcode ln_w grade age, into(year2 idcode2 ln_w2 grade2 age2) wide clear
psmatch2 treated $xlist if year==year2, logit neighbor(1)
另一种思路是每年单独匹配。比如为2015年处理组匹配2015年对照组,2016年处理组匹配2016年对照组...但这个方法在实操中有两个致命伤:
第一是特殊类别变量可能错配。假设研究制造业补贴政策,理想情况是处理组和对照组都来自同一细分行业(比如都生产汽车零部件)。但PSM只保证倾向得分接近,可能把汽车厂商和食品厂商匹配在一起。我在省级数据中就遇到过这种问题,导致农业大省和工业大省被错误配对。
第二是匹配对象不稳定。某企业2015年的对照可能是A公司,2016年变成B公司。这种"换伴侣"现象会引入额外噪音,我用蒙特卡洛模拟显示会使得标准误膨胀30%以上。
现实数据常常存在缺失值,比如某些企业某些年份的数据不全。逐期匹配时,如果某年最佳匹配对象恰好数据缺失,就只能退而求其次选择次优匹配。我在民营企业数据集中发现:
在跑任何匹配模型前,建议先用交叉表分析处理组和对照组的类别变量分布。我常用的筛查流程:
stata复制// 特殊变量筛查示例
tab industry treated, col chi2
tab region treated, col chi2
// 发现制造业占比差异显著,则匹配时限制:
psmatch2 treated $xlist, exact(industry) neighbor(1)
正确的做法是为每个个体-时期组合计算独立的倾向得分。这里有个细节优化:可以加入年份虚拟变量或时间趋势项,控制时间固定效应。我在实际操作中发现:
stata复制// 时变PS值计算
forvalues y=70/80 {
logit treated $xlist i.industry if year==`y'
predict ps`y' if e(sample)
}
egen ps_seq = rowtotal(ps*)
核心思路是在时间维度上寻找稳定的匹配对。我的改进版操作步骤:
stata复制// 序列匹配实现
xtset id year
bysort treated: asrol ps, gen(ps_mean) stat(mean) window(year 5)
psmatch2 treated ps_mean*, mahalanobis($xlist) caliper(0.2)
匹配后必须做两个检验:
我习惯用这个检验组合:
stata复制// 平衡性检验
pstest $xlist, both graph
// 稳定性检验
xtreg ps i.year##treated, fe
testparm i.year#treated // 希望不显著
用2010-2020年A股制造业数据演示完整流程。假设研究对象是环保税改革对ROA的影响,处理组是重点监控行业企业。
stata复制use panel_data.dta, clear
xtset stkcd year
gen post = (year>=2016) // 政策时点
gen treated = (industry=="钢铁" | industry=="水泥") // 处理组定义
// 控制变量处理
gen size = log(assets)
gen lev = debt/assets
gen age = year - ipo_year + 1
gen age2 = age^2
global xlist size lev age age2 ownership
stata复制// 步骤1:分年度计算PS值
forvalues y=2010/2020 {
logit treated $xlist if year==`y'
predict ps`y' if e(sample)
}
// 步骤2:构建PS序列
egen ps_seq = rowtotal(ps2010-ps2020)
// 步骤3:序列匹配
psmatch2 treated ps_seq $xlist, mahalanobis($xlist) caliper(0.3) common
// 步骤4:检验
pstest $xlist, both graph
xtreg ps_seq i.year##treated if _weight!=0, fe
stata复制// 基础DID
reg roa i.post##treated $xlist if _weight!=0, vce(cluster stkcd)
// 动态效应检验
forvalues y=2010/2020 {
gen treated`y' = (year==`y')*treated
}
reg roa treated2010-treated2020 $xlist if _weight!=0, vce(cluster stkcd)
stata复制// 安慰剂检验
gen fake_year = 2013 // 虚构政策时点
gen fake_post = (year>=fake_year)
reg roa i.fake_post##treated $xlist if _weight!=0
这个案例中,采用稳健匹配方法后,政策效应估计值比传统方法低约18%,但标准误更小,统计显著性反而提高。这说明控制住匹配质量确实能提升估计效率。