前置条件与学习依赖 / PREREQUISITES
① 数学 / 统计基础
关系型数据库思想(主键 / 外键、1:1 / 1:m / m:1 连接)、面板数据结构(公司-年份的长格式 long 与宽格式 wide)。
② 经济学理论前置
无专门理论要求;理解面板数据的组织逻辑即可。
③ 软件 / 计算前置
Stata 17+(mergeappendreshapextsetxtdescribe;本页不依赖额外外部命令)。
④ 站内前置页面
先学 01 数据清洗,再做合并与面板整理。
⑤ 难度分级
入门

01 为什么"合并"是面板数据的核心工程问题

在中文实证研究里,你几乎永远不会从一个 .dta 文件开始。你会有一堆来源不同、粒度不同、时间覆盖不同的表:CSMAR 的财务报表表、Wind 的股价表、国家统计局的行业层面表、手工收集的政策冲击表。把它们整合成"公司 i × 年份 t"的矩形面板,是描述性统计和回归之前必须完成的工程。

合并错了,后面所有结论都会错。最常见的两类错误:(1) merge 模式选错,把一对一的表按多对一合并,结果同一行被复制成多行,样本量莫名其妙翻倍;(2) 关键字段没对齐,股票代码在不同年份编码规则变了,导致匹配率(match rate)看起来正常但其实是张冠李戴。本节把这两件事拆透。

02 merge 三种连接:1:1 / 1:m / m:1

Stata 的 merge 命令按"两边关键字段的唯一性"分三种模式。理解这三种模式的关键,是先问自己一个问题:主数据集(master)和被合并数据集(using)里,每个关键键值是唯一的还是会重复?

  • 1:1:两边每个键值都唯一。典型场景:公司基本信息表(每家公司一行)与公司治理结构表(每家公司一行)合并。
  • m:1:master 里一个键值多行,using 里每个键值一行。典型场景:你已经有公司-年份面板,要给每一行"贴上"行业代码(行业表是公司层面的,每家公司一行)。
  • 1:m:master 里每个键值一行,using 里多行。很少用,方向与 m:1 相反——通常把 using 放前面反过来写。
Stata 不支持 m:m(多对多)

很多新手上手会写 merge m:m id using ...。Stata 明确拒绝这种写法——它的官方文档把 m:m 称为"deprecated and dangerous"。正确做法是先想清楚两边的真正粒度,然后降级成 1:1 / 1:m / m:1。

Stata · 三种 merge 模式演示
*--------------------------------------------------------------*
*  02-merge-modes.do
*--------------------------------------------------------------*
clear all
set more off

* ---------- 构造演示数据 ----------
* master: 公司-年份面板(已清洗好的财务数据)
clear
input stkcd year roe
1 2010 0.05
1 2011 0.08
2 2010 0.03
2 2011 0.04
3 2010 0.09
end
save "master_fin.dta", replace

* using-A: 公司基本信息表(每家公司一行)—— 1:1 / m:1 都可以用
clear
input stkcd industry province
1 "C3" "Guangdong"
2 "C4" "Beijing"
end
save "using_info.dta", replace

* using-B: 行业层面宏观变量(每个行业-年份一行)
clear
input industry year ind_growth
"C3" 2010 0.12
"C3" 2011 0.15
"C4" 2010 0.08
"C4" 2011 0.09
end
save "using_indmacro.dta", replace

* ---------- 1:1 合并:公司基本信息 ----------
* 因为 master 里 stkcd-year 唯一、using 里 stkcd 唯一,
* 这里用 m:1 stkcd(master 多行、using 一行)最自然
use "master_fin.dta", clear
merge m:1 stkcd using "using_info.dta"
* _merge 取值:
*   1 = 主数据集独有(using 没匹配到)
*   2 = 被合并数据集独有(master 没这行)
*   3 = 两边都有(完美匹配)

* 查看匹配情况
tab _merge

* 删掉"using 独有"的行(对我们没意义)
keep if _merge == 1 | _merge == 3
drop _merge

* ---------- 多关键字 m:1:行业 × 年份 ----------
* 这次要合并的是"行业-年份"层面的宏观变量,
* 主数据集中没有 industry,需要先在 master 里加 industry 列。
* 为演示,我们直接重建 master:
use "master_fin.dta", clear
gen industry = ""
replace industry = "C3" if stkcd == 1
replace industry = "C4" if stkcd == 2
replace industry = "C4" if stkcd == 3

* 按 (industry, year) 两个关键字合并
merge m:1 industry year using "using_indmacro.dta"
tab _merge
drop _merge
list

03 理解 _merge:1 / 2 / 3 三种结果

每次 merge 之后,Stata 会自动生成一个叫 _merge 的变量,告诉你每一行的来源。读懂 _merge 的分布,是判断合并是否成功的关键。

_merge 取值含义实务含义
_merge == 1主数据集 (master) 有、using 没有这一行没找到对方的数据,新变量会是缺失
_merge == 2using 有、master 没有被合并表里多出的行,对主样本无意义,通常直接删
_merge == 3两边都有(完美匹配)理想情况,新变量成功贴到对应行上

一个经验法则:合并完先 tab _merge,把 _merge==3 的占比作为"匹配率"。如果你的匹配率不到 80%,要回头检查关键字段是否对齐(股票代码是否带前导零、行业代码是否跨年份换了标准)。

匹配率(match rate)的写法
$$\text{Match rate} = \frac{\#\{\_merge = 3\}}{\#\{\text{所有主数据集观测}\}}$$

04 多关键字匹配与行业代码对齐

真实研究里最常见的匹配痛点,是行业代码跨年份不一致。中国证监会 2001 年、2012 年两次修订《上市公司行业分类指引》,旧代码(如"C8"医药)和新代码(如"C27"医药)的映射并不一一对应。直接按 industry 合并,会把 2011 年的"C8"贴到 2012 年的"C8"上,而后者在新口径下可能已经变成了别的行业。

Step 1 · 建立行业代码映射表
手工或从证监会官网下载一张 industry_map.dta:每行一对 (old_code, new_code, broad_category)。这一步是一次性体力活,但收益巨大。
Step 2 · 按年份分别 merge
在 2011 及以前用老代码表,2012 年及以后用新代码表,分别合并,再纵向 append 回来。
Step 3 · 用 broad_category 做稳健性
主回归用三位行业代码,稳健性检验里用一位大类(制造业 C 全部归一类)。
Stata · 行业代码对齐的常见套路
*--------------------------------------------------------------*
*  04-industry-match.do
*--------------------------------------------------------------*
* 假设 master 里有 stkcd year industry
* 2011 年及以前用老版(CSRC 2001),2012 起用新版(CSRC 2012)

* 1) 把年份分段
gen old_code_flag = (year <= 2011)

* 2) 准备两张映射表(省略手工构造代码,假设已经在 industry_map_2001.dta 和 industry_map_2012.dta 里)
* use industry_map_2001.dta, clear
* keep if old_code_flag == 1
* merge m:1 industry using ...

* 3) 一个更实用的写法:把行业代码截断成"证监会一位大类"
*    旧代码形如 "C8",新代码形如 "C27"
gen ind1 = substr(industry, 1, 1)        // 把 C8 / C27 都变成 "C"
* 三位行业大类:旧版取前两位,新版取前一位加两位数字
gen ind3 = ""
replace ind3 = substr(industry, 1, 2) if year <= 2011
replace ind3 = substr(industry, 1, 3) if year >= 2012

* 4) 行业 × 年份固定效应:用 ind3 × year
egen ind_year = group(ind3 year)
* 之后 reghdfe y x, absorb(ind_year firm) 即可

05 append:纵向堆叠不同年份

appendmerge 完全不同:merge 是横向拼接(增加列),append 是纵向堆叠(增加行)。当你按年份分别下载了 2007–2020 年的财务表,每个文件结构相同,就用 append 把它们摞起来。

Stata · append 纵向堆叠
*--------------------------------------------------------------*
*  05-append.do
*--------------------------------------------------------------*
clear all
set more off

* 假设 data/raw/ 下有 fin_2007.dta ... fin_2020.dta
* 标准做法:先清空,再 append using
use "data/raw/fin_2007.dta", clear
forvalues y = 2008/2020 {
    append using "data/raw/fin_`y'.dta"
}
describe
count

* 关键检查:append 后变量名应当完全一致
* 如果有"2015 年新增了一个变量 R&D",旧年份该变量会自动变成缺失
* 用 misstable summarize 检查一下

06 reshape long/wide:宽表与长表互转

很多下载下来的数据是 wide 形式:每家公司一行,每年一个变量列(如 roe_2007 roe_2008 ... roe_2020)。但 Stata 的面板命令(xtset、xtreg、reghdfe)要求 long 形式:每行是一个 (公司, 年份) 观测。reshape 命令就是在这两种形态之间切换的瑞士军刀。

long vs wide 的语法核心
$$\text{wide: } x_{i,2007}, x_{i,2008}, \dots \quad \Longleftrightarrow \quad \text{long: } (i, t, x_{i,t})$$ id = 公司标识,j = 年份(reshape 里的"时变变量名")。
Stata · reshape 长表 ⇄ 宽表
*--------------------------------------------------------------*
*  06-reshape.do
*--------------------------------------------------------------*
clear all
set more off

* ---------- 第一步:构造一个 wide 表 ----------
* 每家公司一行,roe_2010 ... roe_2012 三列
clear
input stkcd roe_2010 roe_2011 roe_2012
1 0.05 0.08 0.06
2 0.03 0.04 0.05
3 0.09 0.10 0.11
end
list

* ---------- wide -> long(最常用!) ----------
* reshape long 告诉 Stata:
*   "把 roe_<j> 拆成 (j, roe)"
* 语法:reshape long 变量前缀, i(id) j(timevar)
reshape long roe, i(stkcd) j(year)
list, sepby(stkcd)
* 现在是 (stkcd, year, roe) 的长表

* ---------- long -> wide(反向操作) ----------
reshape wide roe, i(stkcd) j(year)
list
reshape 的两个高频报错

报错 1:"data have multiple observations within (stkcd) that are not unique" —— 说明你的 (i, j) 组合已经重复了,先 duplicates drop。
报错 2:"j variable (year) contains negative values" —— reshape 要求 j 是正整数年份。如果原始列名是 roe_2010,Stata 会自动把 2010 解析成 year;如果是 roe_a roe_b,需要先把字母 j 编码成数字。

07 xtset 面板设定与 xtdescribe

一旦你有了 (stkcd, year) 长表,下一步就是告诉 Stata"这是一个面板数据":xtset panelvar timevar。这一步做完,所有 xt 开头的命令(xtreg、xtsum、xtdescribe、xtsum 等)才能识别面板结构。

Stata · xtset 与 xtdescribe
*--------------------------------------------------------------*
*  07-xtset.do
*--------------------------------------------------------------*
webuse nlswork, clear      // Stata 自带的面板:员工-年份

* 1) xtset:声明 panelvar = idcode, timevar = year
xtset idcode year

* 2) xtdescribe:检查面板的"形状"
*    输出会告诉你:
*    - 总共多少个组(公司/个人)
*    - 时间跨度
*    - 是否平衡(balanced)
*    - 每个组有多少期观测
xtdescribe

* 3) xtsum:面板版的 summarize(组内/组间/总体方差分解)
xtsum ln_wage age tenure

* 4) 看一下"时间有没有跳跃"
*    比如某个员工 1980 年之后跳到 1985 年,xtset 会警告
list idcode year in 1/30
xtdescribe 输出怎么读

重点看三块:① "n = 4711" 是组(公司)个数;② "T = 12" 是时间维度;③ Distribution of T_i 表格——如果大多数组都有 12 期、少数只有几期,说明是非平衡面板(unbalanced panel)。现代实证方法(reghdfe、xtreg fe)都默认支持非平衡,你不需要强行把它变成平衡。

08 平衡 vs 非平衡面板的转换

平衡面板(balanced panel)指每个公司都恰好有 T 期观测;非平衡面板(unbalanced panel)指有的公司只观察到几年、有的公司观察了全部年份。中国上市公司面板几乎从来不是平衡的——IPO、退市、合并报表口径变化都会让某个公司"消失"几年。

什么时候需要转成平衡面板?通常只有两种情况:(1) 跑差分 GMM / 系统 GMM(xtabond2),这些方法对非平衡面板敏感;(2) 做一些特殊的稳健性检验。主回归一般用非平衡面板即可,无需强求平衡。

平衡面板的定义
$$\text{Balanced} \iff \text{每个 } i \text{ 都恰好观测到 } t = 1, 2, \dots, T \text{ 全部期}$$
Stata · 转成平衡面板
*--------------------------------------------------------------*
*  08-balanced.do
*--------------------------------------------------------------*
webuse nlswork, clear
xtset idcode year
xtdescribe    // 此时是非平衡

* 方法:只保留"恰好观察满所有年份"的公司
* 1) 每个公司有多少期?
bysort idcode: gen n_years = _N
* 2) 总年数是多少?
quietly levelsof year, local(allyears)
scalar T_total = `: word count `allyears''
* 3) 保留恰好等于 T_total 的公司
keep if n_years == T_total
xtset idcode year
xtdescribe    // 现在应当是 "balanced"

09 论文案例:合并策略如何影响识别

English · 经典
Does Foreign Direct Investment Increase the Productivity of Domestic Firms? In Search of Spillovers Through Backward Linkages
Beata Smarzynska Javorcik · American Economic Review · 2004
用立陶宛企业面板 + 投入产出表,构造上下游 FDI 暴露变量。其数据工程的核心就是本节讲的 merge:把企业层面(公司-年份)数据与行业层面(行业-年份)的投入产出系数按 m:1 industry year 合并。匹配率与行业代码对齐直接决定了下游溢出估计的可信度。
English · 经典
The Collapse of the International Trade
Mary Amiti & David Weinstein · Quarterly Journal of Economics · 2011
用日本企业-银行-贷款三层网络数据研究信贷冲击的传导。要把企业财务面板、银行-贷款关系面板、银行资产负债表面板三张表,按"企业 × 主银行 × 年份"三层关键字 merge 在一起。是 m:m 场景"如何降级成多次 m:1"的教科书案例。
中文顶刊 · 标杆
高铁开通与企业生产率
黄群慧、余泳泽、张松林 · 《中国工业经济》 · 2018 年(及后续龙玉等 2017 《经济研究》系列)
把中国工业企业数据库(或上市公司数据库)的公司-年份面板,与手工收集的"城市 × 年份 × 是否通高铁"政策表,按"城市 × 年份"多关键字 merge。其匹配策略、平衡/非平衡面板选择、行业代码对齐,几乎就是本节代码的论文版本。
中文顶刊 · 标杆
《最低工资与企业加成率》
赵瑞丽、孙楚仁、陈勇兵 · 《世界经济》 · 2018 年第 6 期(相关:孙楚仁等 2019 《经济研究》最低工资系列)
把中国工业企业数据库与全国 2000+ 个县级最低工资标准表,按"县 × 年份"多关键字 merge。演示了多关键字 merge 的真实复杂度:同一公司可能跨县迁址,需要用"公司注册地址 × 生效年份"做动态匹配。

10 常见错误与调试

错误 1:merge 后忘记 drop _merge

第二次再 merge 同名关键字时,Stata 会抱怨"_merge already defined"。规范做法:每次 merge 后 drop _merge,或者用 merge ... , keep(match master) noreport 一次性处理。

错误 2:股票代码前导零丢失

Stata 读 Excel 时会把 "000001" 读成数字 1,下次与字符串型股票代码 merge 永远匹配不上。预防:(1) 导入时用 import excel ..., cellranges(...) clear 后立刻 tostring stkcd, format(%06.0f) replace;(2) merge 前两边都用 gen stkcd_str = string(stkcd, "%06.0f") 标准化。

错误 3:append 后年份变量被错误合并

append 要求两边变量类型一致。如果某一年的 year 是字符串、另一年是数值,append 后整个 year 变成字符串,xtset 报错。预防:append 前 describe 一遍,或在每个文件里就强制 destring year, replace

错误 4:reshape 后忘记 xtset

reshape 只是改变数据形状,不会自动告诉 Stata 面板结构。做完 reshape 必须 xtset stkcd year,否则后面 xtreg / reghdfe 报错 "not sorted" 或直接把数据当截面跑。

11 进阶学习资料

  • Stata 官方help mergehelp appendhelp reshapehelp xtsethelp xtdescribe。其中 merge 的 PDF 手册附录里有非常详细的"为什么 m:m 是错的"。
  • 教材:Cameron & Trivedi, Microeconometrics Using Stata 第 2 章(data management);陈强《高级计量经济学及 Stata 应用》第 11 章"面板数据"。
  • 网络资源:Stata 官方 Data Management FAQs,覆盖 merge / reshape 几乎所有常见坑。
  • 中文实战:中国工业企业数据库匹配指南(杨汝岱 2015);CSMAR 数据中心"上市公司财务指标"代码手册。