21.7 案例:最低工资对低薪就业的 DID 估计

面向经管学生、研究者与从业者的 AI 智能体设计教材

作者

李学恒、林建浩、严翊歆

发布于

2026-05-11

配图:案例系统架构

这一节把本章的机制串成一个端到端可跑的案例。读者跟随一份研究问题,完成项目初始化、数据清洗、主回归、稳健性矩阵、复现包组织的全过程,所有配置文件和交互指令都可以直接复用到自己的研究项目中。

研究问题与识别假设

研究问题:美国各州最低工资提高是否减少了低薪就业岗位的数量?

这是最低工资经济学中被讨论最多的问题之一。Cengiz, Dube, Lindner & Zipperer (2019, QJE) 提出了一个有影响力的分析框架:把各州工资分布按 0.25 美元一个箱切分,形成”州-年份-工资箱”三维面板,观察最低工资上调对不同工资箱内就业人数的影响。他们发现最低工资上调只是把就业从受约束的低箱推向刚超过最低工资的箱,整体低薪就业数量无显著下降。

本案例沿用原文的渐进 DID 设计,但有两处方法论升级。一是把原文采用的 TWFE 估计量替换为 Callaway-Sant’Anna (2021) 的 csdid,以避免渐进采纳设计下 TWFE 可能出现的负权重问题。二是把 Arkhangelsky et al. (2021) 的合成 DID (sdid) 加入稳健性矩阵,检验平行趋势假设较弱时结论是否依然成立。

识别假设:若没有最低工资政策变化,受影响工资箱与未受影响工资箱的就业趋势在处理前应平行。这一假设通过事件研究图检验,由研究者判断是否成立。

数据来源:BLS 的 QCEW (Quarterly Census of Employment and Wages),2000-2019 年,州-年份-工资箱面板;Vaghul-Zipperer 整理的各州最低工资历史数据。QCEW 为公开数据,可随复现包分发。工资箱维度(wage_bin,宽度 $0.25)是 Cengiz 等人设计的核心——原始 QCEW 按州-年聚合后,data-clean Skill 进一步按工资水平切分为 124 个箱,形成 50 州 × 20 年 × 124 箱 = 124000 条的三维面板。21.3 的清洗脚本为简化展示只演示州-年聚合步骤,工资箱切分由 data-clean Skill 在实际运行时补充。

案例启示

AI 跑遍稳健性矩阵,研究者只做方向判断。案例中的 36 个稳健性格子(3 种估计量 × 3 种控制变量 × 4 种样本),若由研究者手工配置,耗时可能在两周以上;交给子代理并行执行,约 40 分钟完成。研究者的时间从”跑代码”转向”判断哪些检验值得做、结论在哪些维度上稳健”。

项目目录结构

项目按 AEA DCAS v1.0 复现包规范组织,目录分四个顶层:配置(根目录和 .claude/)、数据(data/)、代码(code/)、产出(output/)。

min-wage-did/
├── CLAUDE.md                  # 项目规则基座
├── README.md                  # AEA 复现说明
├── .gitignore                 # 排除 data/raw/
├── .claude/
│   ├── skills/                # 本章 Skill 定义
│   │   ├── data-clean/SKILL.md
│   │   ├── did-estimate/SKILL.md
│   │   ├── robustness/SKILL.md
│   │   └── replication-pack/SKILL.md
│   ├── agents/                # 本章子代理定义
│   │   ├── dataprep-agent.md
│   │   ├── estimator-agent.md
│   │   └── reviewer-agent.md
│   └── hooks.json             # PreToolUse / PostToolUse / Stop
├── data/
│   ├── raw/                   # QCEW 原始数据(本地,不纳入 Git)
│   ├── processed/             # 清洗后面板(由脚本生成)
│   └── external/              # 最低工资历史数据
├── code/
│   ├── 01_download.do         # BLS API 拉取
│   ├── 02_clean.do            # 面板构建
│   ├── 03_descriptive.do      # 描述统计
│   ├── 04_main_reg.do         # csdid + reghdfe
│   ├── 05_robustness.do       # sdid + 替换样本矩阵
│   └── 06_tables_figs.do      # 最终表图输出
├── output/
│   ├── tables/                # tab_main.tex / tab_robustness.tex
│   ├── figures/               # fig_event_study.pdf
│   └── logs/                  # Stata 日志
└── references/
    ├── did-checklist.md       # DID 识别假设清单
    ├── iv-checklist.md        # IV 有效性清单
    └── rdd-bandwidth.md       # RDD 带宽选择

data/raw/ 目录在 .gitignore 中标记排除。原始数据留在研究者本地,AI 代理只处理路径引用,不把 QCEW 压缩包的二进制内容上传到任何云端。

完整 CLAUDE.md

项目根目录的 CLAUDE.md 是 Claude Code 每次启动都会读取的规则基座。下面是本项目的完整版本,可以作为读者自己做实证项目的起点模板。

# 项目规则:最低工资对低薪就业的 DID 估计

## 项目目标

复现并扩展 Cengiz et al. (2019, QJE) 的最低工资 × 低薪就业分析,使用
Callaway-Sant'Anna (2021) 估计量,QCEW 2000-2019 年数据。

## 路径规范

- `data/raw/`        只读目录。禁止任何脚本或 Skill 写入此目录。
- `data/processed/`  清洗产物目录。仅由 `code/02_clean.do` 生成。
- `data/external/`   外部数据(最低工资历史)。手动维护。
- `output/`          所有表图产出。每次重跑可安全清空。
- `.gitignore` 已排除 `data/raw/`,原始数据不提交到仓库。

## 可复现性约束

- 所有 `.do` 文件第一行必须是 `version 18.0`,锁定 Stata 版本。
- 所有涉及随机化(bootstrap / permutation / sample split)的命令,
  必须在调用前执行 `set seed 20260417`。
- `ssc install` 记录在 `code/00_setup.do`,不在分析脚本中即席安装。

## 变量命名约定

- 州 ID:`state_id`(FIPS 两位码)
- 时间:`year`(四位数年份,最低频率单位为年)
- 处理指示:`treat_indicator`(0/1);首次处理年份:`g_first_treated`
- 结果变量:`log_emp`(就业对数);水平值为 `emp_level`
- 变量一律小写 + 下划线;分类变量在回归中带 `i.` 前缀。

## 聚类与标准误

- 主回归默认在 `state_id` 层级聚类:`vce(cluster state_id)`。
- 若估计命令为 `csdid`,须显式设置重抽次数,如 `wboot reps(999)`。
- `estout` / `esttab` 使用星号标准:`* 0.1 ** 0.05 *** 0.01`。

## 数据许可

- QCEW 为 BLS 公开数据,可随复现包分发。
- 本项目不使用 WRDS、Wind 等受许可数据。
- 若后续扩展用到 CPS 微观数据,仅保留 IPUMS 抽样脚本,不上传原始文件。

## AI 使用披露

本项目代码由 Claude Code 辅助生成;所有分析结果已由研究者独立核验。
在论文致谢或方法说明脚注中声明:
"We used Claude Code (Anthropic) to assist with Stata code generation,
 data cleaning scripts, and table formatting. All estimates were
 independently verified by the authors."
复现本案例的常见卡点

第一个卡点是 csdidsdid 都依赖 ftoolsreghdfe,若未预先 ssc install,脚本会在第二次运行时报错。把所有 ssc install 集中在 code/00_setup.do。第二个卡点是 QCEW 原始文件按季度发布,需要在 02_clean.do 中用 collapse (sum) emp_level, by(state_id year wage_bin) 聚合到年度。第三个卡点是最低工资历史数据的州代码(两位字母)需要和 QCEW 的 FIPS 数字码对齐,merge 前必须 encodedestring 统一格式。

四个 SKILL.md

本案例的 Skill 分为四个,对应数据、估计、稳健性、复现包四个阶段。下面完整展示核心的 did-estimate,其余三个 Skill 的职责和触发方式见汇总表。

Skill 名称 职责 触发方式 核心步骤数
data-clean 读取 QCEW 季度文件,聚合为州-年份-工资箱面板 “清洗 QCEW”、“构建工资箱面板” 8
robustness 沿估计量×控制变量×样本三维展开稳健性矩阵 “稳健性”、“跑矩阵” 5
replication-pack 按 AEA DCAS v1.0 规范收拢代码与产出 “复现包”、“AEA 提交” 6

did-estimate Skill

运行主回归 csdid 并用 reghdfe 作对照,生成事件研究图。

▶ Skill
---
name: did-estimate
description: 运行最低工资 DID 主回归。使用 Callaway-Sant'Anna 估计量 csdid,并用双向固定效应 reghdfe 作对照。输出主回归表 tab_main.tex 和事件研究图 fig_event_study.pdf。触发关键词:"跑主回归"、"DID 估计"、"did-estimate"。
---

## 前置检查

- 确认 `data/processed/panel_balanced.dta` 存在且最后修改时间晚于
  最近一次 `02_clean.do` 运行。
- 确认 `set seed 20260417` 已设置。

## 执行步骤

1. 读取清洗面板:`use data/processed/panel_balanced.dta, clear`。
2. 运行 csdid 主规范:以 `log_emp` 为被解释变量,`ivar(state_id)`、`tvar(year)`、`gvar(g_first_treated)` 指定面板维度与首次处理年份,加 `notyet` 指定"尚未处理"为对照,加 `wboot reps(999)` 做 Wild Bootstrap。接着 `estat simple` 输出聚合 ATT,`estimates store cs_main`,`estat event` 输出动态路径,`csdid_plot` 绘图并 `graph export output/figures/fig_event_study.pdf, replace`。
3. 运行对照 TWFE:`reghdfe log_emp treat_indicator, absorb(state_id year) vce(cluster state_id)`,随后 `estimates store twfe_main`。
4. 调用 esttab 输出 `output/tables/tab_main.tex`,双列并列 csdid 与 twfe。

## HITL 节点

执行前将固定效应结构(`state_id + year`)和聚类层级(`state_id`)
以摘要形式输出给用户,等待确认后再跑回归。

三个 agent.md

三个子代理分工对应 Korinek (2025) 的规划-执行-审阅框架。下面完整展示核心的 reviewer-agent,其余两个子代理的职责见汇总表。

子代理 职责 调用的 Skill 权限
dataprep-agent 调用 /data-clean 构建面板,输出清洗日志,遇异常上报 HITL data-clean Read, Write, Edit, Bash
estimator-agent 依次调用 /did-estimate 和 /robustness,确保同一随机种子 did-estimate, robustness Read, Write, Edit, Bash

reviewer-agent

▶ Agent
---
name: reviewer-agent
description: 负责输出质量审阅的子代理。核查样本量一致性、星号标准、表格列对齐、聚类层级与 CLAUDE.md 约定是否吻合。输出结构化审阅报告。
tools: Read
model: sonnet
---

你是审阅子代理。只读权限,不修改任何文件。

核查清单:

1. 主回归表 csdid 和 twfe 两列的样本量差异是否在 1% 以内。
2. 稳健性矩阵 36 格样本量与主回归的偏离是否在 5% 以内。
3. 所有表的星号标准是否为 `* 0.1 ** 0.05 *** 0.01`。
4. 聚类层级是否一律为 `state_id`。
5. 事件研究图的 x 轴零点是否对齐处理年份,y 轴是否含 95% 置信带。

输出格式:一份 Markdown 报告,每条核查对应"通过/失败/待确认"三态
之一,失败或待确认条目要给出具体原因和行号引用。

三段 opencode 对话实录

下面是研究者在 Claude Code 中与三个子代理的实际交互。

实录 1:项目初始化

▶ Claude Code
按 AEA DCAS v1.0 规范创建一个最低工资 DID 研究项目,项目名 min-wage-did,语言为 Stata 18.0。帮我生成根目录的 CLAUDE.md 和 .gitignore;需要锁定版本、固定随机种子为 20260417、州 ID 统一为 state_id、时间变量统一为 year。披露条款按 AEA 2025 政策填入致谢段模板。

Claude Code 创建五个顶层目录,起草 CLAUDE.md(含路径规范、版本锁定、变量命名、聚类约定、许可声明、AI 披露),并请研究者确认披露条款后落盘。

实录 2:触发数据清洗

▶ Claude Code
data/raw/ 里已经放好 2000-2019 年 QCEW 年度压缩包。调度 dataprep-agent 调用 /data-clean Skill,构建州-年份-工资箱面板。工资箱宽度 $0.25,仅保留私人部门 (own_code=5)。最低工资历史用 data/external/min_wage_history.dta。完成后给我清洗日志的关键指标,先不要跑回归。

Claude Code 的回复摘要:dataprep-agent 调用 /data-clean,脚本用时约 6 分钟。写入 02_clean.do 时,PreToolUse(Write) Hook 自动扫描脚本内容,确认 set seed 20260417version 18.0 均已包含,放行写入。日志关键指标:年份 2000-2019 全覆盖,50 州全量,工资箱 124 个(覆盖 $5.00-$36.00 区间),面板单元共 124000 条,最低工资历史匹配率 98.7%(未匹配主要为阿拉斯加 2000-2002 三年,已列清单)。脚本执行完毕后 Stop Hook 触发提示:“数据清洗脚本已写入,请在继续前确认 data/processed/panel_balanced.dta 样本量。”dataprep-agent 提出 HITL 请求:是否用前值填充 Alaska 2000-2002 年的缺失,还是直接排除?研究者回复”排除这三个州-年份,在 README 中说明”。

实录 3:主回归 + 稳健性 + 审阅

▶ Claude Code
调度 estimator-agent,先跑 /did-estimate 主回归,固定效应 state_id + year,聚类 state_id。主回归通过后让它继续跑 /robustness 矩阵:估计量 TWFE/CS/SDID、控制变量 无/基准/扩展、样本 全样本/排除加州得克萨斯/排除 2008-2009 金融危机期间。全部完成后调 reviewer-agent 核查输出。

Claude Code 的回复摘要:estimator-agent 运行 /did-estimate 约 8 分钟,csdid 简单 ATT 系数 0.012 (se 0.018),twfe 对照 0.009 (se 0.016),两列样本量差异 0.3%,差异在 HITL 阈值内,请求放行稳健性矩阵。研究者放行后,36 格矩阵耗时 34 分钟。reviewer-agent 输出报告:4 项核查通过,1 项待确认——事件研究图 y 轴未包含 95% 置信带,建议在 csdid_plot 中追加 level(95) 选项重跑。研究者确认修改后重跑事件研究图。

实录 4:复现包打包

▶ Claude Code
审阅通过,请调用 /replication-pack 按 AEA DCAS v1.0 规范打包复现包。

Claude Code 的回复摘要:/replication-pack Skill 执行 6 步检查——校验 README.md 完整性、确认所有 do 文件首行含 version 18.0、核对 output/tables/output/figures/ 产出清单、验证 .gitignore 已排除 data/raw/、在 README 末尾追加 AI 使用披露段、将 code/output/references/CLAUDE.mdREADME.md 打包为 min-wage-did-replication.zip。研究者确认披露措辞后落盘。

最终产出预览

项目运行结束后,output/ 下产生三件交付物。

主回归表 tab_main.tex。csdid 与 reghdfe 两列系数同方向、同数量级,均不显著(ATT 分别为 0.012 和 0.009),与 Cengiz et al. (2019) 的核心发现一致:最低工资上调对低薪就业总量无显著负效应。

事件研究图 fig_event_study.pdf。横轴为相对处理年(-6 到 +6),纵轴为就业对数系数。图中显示处理前 6 期系数点估计均落在 0 附近、95% 置信带覆盖 0,说明平行趋势假设基本成立;处理后 0 到 +3 期系数略微上移但未显著偏离 0,处理后 +4 期以后置信带急剧变宽,提示长期效应估计不可靠。

稳健性矩阵表 tab_robustness.tex 的列标题清单:

列 1-3:TWFE × {无控制, 基准控制, 扩展控制}  · 全样本
列 4-6:CS   × {无控制, 基准控制, 扩展控制}  · 全样本
列 7-9:SDID × {无控制, 基准控制, 扩展控制}  · 全样本
列 10-12:三种估计量 × 基准控制 · 排除加州得克萨斯
列 13-15:三种估计量 × 基准控制 · 排除 2008-2009

36 格中 34 格系数符号与主回归一致,2 格(SDID × 扩展控制 × 排除加州得克萨斯)出现反号但不显著。

README.md 片段 记录运行约束:

# 最低工资 × 低薪就业 DID 复现包

## 数据来源

- QCEW 2000-2019 annual files(BLS,公开):
  https://www.bls.gov/cew/downloadable-data.htm
- 各州最低工资历史:Vaghul & Zipperer (2016) 整理版。

## 运行顺序

1. code/00_setup.do  (安装 ssc 依赖)
2. code/01_download.do
3. code/02_clean.do
4. code/03_descriptive.do
5. code/04_main_reg.do
6. code/05_robustness.do
7. code/06_tables_figs.do

## 软件版本

- Stata 18.0 (MP-4)
- csdid 1.7、reghdfe 6.12、sdid 1.0.2、estout 3.30
- 随机种子:20260417

以上交付物加上 CLAUDE.md、四个 Skills、三个子代理,共同构成最低工资 DID 案例的完整交付物清单。读者把这个项目结构 clone 到自己的研究问题上,替换数据源和估计量 Skill 的具体命令,就能得到一个迁移版本。