一条命令跑完 8 种主流交叠 DID 估计量 + 全套诊断。 不做选择,不用记语法,不写 300 行 do-file。
One command: 8 estimators, full diagnostics, publication-ready figures.
目录 / Table of Contents
中文
- 为什么使用它?
- 一句话之后发生什么
- 具体来说,省了什么?
- 30 秒跑起来
- 8 种估计量,一张表讲清楚
- 安装与前置依赖
- 命令语法
- 它会替你检查什么?
- 输出:所有产物一览
- 两张独立图
- 它会报什么?
- 更多示例
- FAQ
- 限制 & 边界
English
- Why Use It?
- What Happens After That Request
- Specifically, What Does It Save?
- 30-Second Quick Start
- The 8 Estimators at a Glance
- Installation & Prerequisites
- Command Syntax
- Pre-estimation Checks
- Output Files
- Two Independent Figures
- What You'll See
- More Examples
- FAQ
- Limitations & Boundaries
- Contributing & Citation
staggered-did 同时提供两条路径进入交叠 DID 分析:
- 自然语言 Skill(Codex / Claude Code / OpenCode 等常见 AI coding agent):最简单的入口只有一句话——"帮我对这个数据进行交叠DID分析。" 当项目代码和数据提供了足够证据时,它会自动识别结果变量、面板键、首次处理期、处理状态、控制变量和聚类层级;存在会实质改变研究设计的歧义时停下来询问,而不是猜测。
- 可独立安装的标准 Stata 命令:不依赖任何 AI agent、PowerShell 或 Python,纯 Stata 用户从
stata/目录直接使用。
无论从哪条路径进入,执行的始终是同一个 .ado,享有同样的预检、路由、诊断和输出契约。
自然语言使用,无需多模态。 这个 Skill 靠
SKILL.md前置声明里的触发短语工作。纯文本模型(包括不具备视觉能力的弱模型)只要能读SKILL.md并执行 PowerShell,就能完成自动分析;不需要看图,也不需要截图识别。
通用 LLM 可以临时帮你写一段交叠 DID 代码,但每次都从头开始--重新检查语法、重读长日志、重新对齐不同估计量的输出格式。本项目的价值不在于垄断方法知识,而是把这项能力固化为可重复、可审计的工作流:
- 统一契约:同一研究设计享有统一的预检、估计量路由和输出契约,不再每次重写。
- 机器可读证据:变量推断、方法跳过与回退全部留下可审计记录(
project_inference.json、status.csv、graph_coverage.csv)。 - 确定性执行:
.ado直接跑 Stata,大幅减少重复写代码和排错的时间。 - 边界自知:对于变量完全给定且只需单一估计量的一次性任务,简短的定制
.do文件可能更轻。
flowchart TD
A["用户:帮我对这个数据进行交叠DID分析"] --> B["遍历有效 .do 文件与数据来源"]
B --> C["识别结果变量、面板键、处理时点、控制变量与聚类"]
C --> D{"设计证据是否充分且一致?"}
D -- "否" --> E["说明冲突并向用户确认"]
D -- "是" --> F["先检查 Stata 命令和可选诊断依赖"]
F --> G["检查缺失值、重复行、面板结构、吸收处理和事件期支持"]
G --> H{"设计是否可用于共同 DID 比较?"}
H -- "否" --> I["停止或路由到适用设计,并给出原因"]
H -- "是" --> J["隔离运行适用估计量"]
J --> K["运行 Bacon、前趋势功效、同步区间和 HonestDiD 诊断"]
K --> L["分别生成动态效应图与总体效应图"]
L --> M["返回主要结论、稳健性差异、警告和结果文件"]
程序不会自动缩尾、插补、聚合或改写源数据。不能运行的方法会被明确标记为 skipped 或 failed,不会悄悄从图里消失。
完整系统架构与每一步工作流见 系统架构与工作流。
你当然可以手动跑 csdid + did_imputation + eventstudyinteract + ... 但需要自己处理这些:
| 手动方式 | staggered-did |
|---|---|
| 记住 6–8 种不同的语法和依赖要求 | 一条语法,统一调用 |
| 手动检查数据兼容性(平衡面板?二元处理?cohort 一致?) | 自动 10+ 项 preflight 审计 → preflight.csv |
| 每种方法的输出格式不同,需手动规范化到同一张表 | 统一 estimates_long.csv,所有方法同一 schema |
| 手动跑 Bacon 分解 + 前趋势检验 + HonestDiD 敏感性 | 一键全套诊断,自动记录每个诊断的状态 |
| 手动拼比较图:对齐 x 轴、标签、颜色、事件期约定 | 自动两张 publication-ready 对比图(作者-年份标注) |
| 一个方法挂了 → 整个 do-file 卡住 | 失败隔离:一个挂了继续跑其他,原因写入 status.csv |
| 完成后需要手动对账"哪些方法出现在哪张图里" | graph_coverage.csv 逐方法证明覆盖 |
你只需要告诉它 y、id、时间、谁被处理了。 剩下的预检、路由、估计、诊断、制图、写表——全都自动完成。
需要 Stata ≥ 17。
.ado声明version 17。经审计:wrapper 未使用 Stata 18 专有特性;上游包中自报版本最高的是pretrends(15.1),无包使用 17+ 专有特性。仅 Stata 18 MP 经过完整测试;17 为声明最低版本,遇上游报错请反馈。
* 1. 安装(见下方安装章节)
* AI 用户:说"帮我安装这个 skill"
* Stata 用户:net install 从本地 stata/ 目录
* 2. 跑
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) outdir("results")跑完后终端会打印:
[staggered_did] Design: panel, 7 cohorts, never-treated present
[staggered_did] Running 6 estimators...
[staggered_did] csdid: ok ......................... 7.2s
[staggered_did] did_imputation: ok ................ 3.1s
[staggered_did] eventstudyinteract: ok ............ 1.8s
[staggered_did] did2s: ok ......................... 0.9s
[staggered_did] stackedev: ok ..................... 2.4s
[staggered_did] reghdfe (diagnostic): ok .......... 0.6s
[staggered_did] completed: successes=6 failures=0 skipped=0
[staggered_did] status: results/status.csv
[staggered_did] estimates: results/estimates_long.csv
[staggered_did] preflight audit: results/preflight.csv
3 步,不到 2 分钟。 在 R 中用 did 包做同样的 6 方法对比需要 ~20 行代码分别调用、聚合、画图。
| 方法 | 命令 | 论文 | 默认? | 什么时候用? |
|---|---|---|---|---|
| C&S | csdid |
Callaway & Sant'Anna (2021) | ✅ 主要 | 你的首选。交叠处理时的标准做法。 |
| BJS | did_imputation |
Borusyak, Jaravel & Spiess (2024) | ✅ | 稳健性检验。用插补法估计反事实。 |
| Sun-Abraham | eventstudyinteract |
Sun & Abraham (2021) | ✅ | 稳健性检验。交互加权事件研究。 |
| Gardner | did2s |
Gardner (2022) | ✅ | 稳健性检验。两阶段 DID,自动放 unit FE。 |
| Stacked | stackedev |
Cengiz et al. (2019) | ✅ | 如果没有 never-treated 组会主动跳过。 |
| TWFE | reghdfe |
— | ✅ 诊断 | 传统双向固定效应。仅做诊断基准——异质效应下不可靠。 |
| dCDH | did_multiplegt_dyn |
de Chaisemartin & D'Haultfoeuille (2024) | 需 methods(full) |
处理非二元或非吸收型处理。大样本可能慢。 |
| Wooldridge | jwdid |
Wooldridge (2021) | 需 methods(full) |
ETWFE,处理组时间效应。资源消耗较高。 |
还不知道选哪个? 用默认的六种就行。C&S 作为主要结果,BJS/Sun-Abraham/Gardner/Stacked 作为稳健性,TWFE 让你看到不更新方法会得出什么结论。六种方法方向一致 → 你的结论扎实。
每个估计量的 estimand、对比组与诊断细节见 估计量路由与诊断;方法选择原理见 估计量原理。
SSC 投稿包已经整理完成;待 SSC 档案收录后即可使用
ssc install staggered_did。 收录前仍可使用下方 GitHub/本地安装方式。
| 需求 | 说明 |
|---|---|
| Stata | 最低 ≥ 17。 .ado 声明 version 17(依赖审计:上游自报最高 pretrends 15.1,无包用 17+ 特性)。MP / SE / IC 均可;仅 Stata 18 MP 经过 17 项完整测试。 |
| 操作系统 | Windows(经过完整验证);macOS/Linux 理论上可用但未正式测试。 |
ssc install staggered_did, replace
help staggered_didSSC 只分发本项目自己的 .ado/.sthlp;各估计量仍由原作者的软件包提供。首次使用前请按下方依赖清单安装所需的上游包。
有 Release 时:点击 GitHub 仓库页面右侧的 Releases -> 下载最新版 staggered-did-x.x.x.zip -> 解压到任意目录。
无 Release 或想用最新源码时:点击仓库页面绿色的 Code 按钮 -> Download ZIP -> 解压。
解压后,对 AI coding agent(Claude Code / Codex / OpenCode)说:
帮我安装这个 skill。
Agent 检测到 SKILL.md + scripts/setup.ps1,会自动:
- 把
.ado和.sthlp安装到 Stata PERSONAL 目录 - 为当前 agent 创建 skill junction
- 安装缺失的上游依赖(
csdid、did_imputation等) - 运行六方法自检,验证一切就绪
或手动运行:powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
安装完成后,同一句话就能跑分析:
帮我对这个数据进行交叠DID分析。
Skill 自动发现变量、推断设计、运行估计、返回报告。你不需要手动指定任何参数。
从解压后的目录安装:
* 将 <你的仓库路径> 替换为实际路径
net install staggered_did, from("<你的仓库路径>/stata") replace或托管 stata/ 目录后从 URL 安装(例如 GitHub raw):
net install staggered_did, from("https://raw.githubusercontent.com/pwya/staggered-did/main/stata/") replace安装后验证:
which staggered_did
help staggered_did手动安装还需要自己装上游依赖——按你实际需要的方法选择性安装即可(核心六方法需前七个):
ssc install ftools
ssc install reghdfe
ssc install avar
ssc install csdid
ssc install drdid
ssc install did_imputation
ssc install eventstudyinteract
ssc install did2s
ssc install stackedev
* 以下仅在 methods(full) 时需要:
ssc install did_multiplegt_dyn
ssc install jwdid
* 以下为可选诊断:
ssc install bacondecomp
net install pretrends, from("https://raw.githubusercontent.com/mcaceresb/stata-pretrends/main/") replace
net install honestdid, from("https://raw.githubusercontent.com/mcaceresb/stata-honestdid/main/") replace完整依赖清单、安装排错与运行环境见 依赖与运行环境。
当然,你也可以在 Claude Code 或 Codex 里直接说「帮我对这个数据跑交叠 DID」--AI 会读
SKILL.md自动推断这套语法里的大部分参数并生成命令,你通常只需核对。下面的语法清单供纯 Stata 用户和需要手动控制时参考。
staggered_did outcome, time(timevar) // 必选
[id(idvar) // 面板需要;重复截面省略
cohort(cohortvar) // 0 或 missing = never treated
treat(treatvar) // 二元且吸收型
controls(x1 x2) // 控制变量
cluster(clustervar) // 聚类;默认 = id
pre(4) post(4) // 事件窗口;超出的部分自动缩到数据支持范围
methods(core|full|csdid bjs sa ...) // 默认 core(6 种)
plotmethods(all|auto|csdid bjs sa) // 图中显示哪些方法
plotmax(.) // plotmethods(auto) 时限制数量
smallcohort(10) // 低于此数的小 cohort 会标 warning
outdir("staggered_did_output") // 输出目录
level(95) // 置信水平
control_group(auto|never|notyet|both) // 对照组契约;默认 auto
nodiagnostics] // 开发/测速用:跳过诊断cohort() 和 treat() 至少提供一个(面板),程序会推导缺失的那个并记录推导过程。两者同时提供时必须完全一致。
level() 默认 95,支持 [50,100) 内的实数,因此 level(90)、level(99) 和 level(97.5) 都可用。它统一控制八种估计量规范化结果中的逐点区间、C&S Bonferroni 同时置信带、Roth 前趋势功效计算的 alpha 和 HonestDiD 稳健区间。自然语言自动入口使用 -Level 90 等参数传递同一设置;未指定时仍为 95%。本次水平和 alpha 会写入 design_manifest.csv,所有区间图也会明确标注水平。联合前趋势 Wald 统计量和 p 值本身不随报告阈值改变。
control_group() 默认 auto,接受 auto|never|notyet|both。auto 保持历史默认行为:有从未处理组就用从未处理组,否则用尚未处理组。显式 never 只用从未处理组(样本没有时响亮报错);notyet 只用尚未处理组(不含从未处理,需要至少两个处理 cohort);both 两者都用(没有从未处理组时降级为 notyet 并在 manifest 披露)。显式请求下,不支持该对照组的估计量会写明原因后跳过(twfe 是诊断基准、不受此约束);若请求的方法里没有一个能办到,命令整体报错。
对于重复截面(反复抽样的横截面,没有个体 ID):
staggered_did y, time(year) cohort(first_treat) treat(D) ///
design(rcs) cluster(school_id) outdir("results")完整语法、选项与存储结果见 标准 Stata 软件包 与 help staggered_did。
程序不修改你的原始数据——但在运行前告诉你数据有没有问题:
| 检查项 | 如果发现问题… |
|---|---|
| 重复的个体-时点行 | 拒绝,让你手动去重 |
| 处理不是二元或出现逆转 | 拒绝,让你检查 cohort/treat 构造 |
| cohort 和 treat 不一致 | 拒绝,告诉你矛盾的观测数 |
| 某个 cohort 的时间不在观测范围内 | 拒绝 |
| 缺失值严重 | 在 preflight.csv 记录 warning |
| 不是严格平衡面板(Bacon 需要) | Bacon 跳过,记录原因(其他方法不受影响) |
| 聚类太少(< 30) | 记录 warning,不做自动拒绝 |
| 结果变量有离群值 | 记录,不做自动缩尾 |
| 控制变量是 time-invariant | 记录——会被 FE 吸收 |
| 请求的事件窗口超出数据支持 | 自动缩到数据实际支持的范围 |
所有这些记录在 preflight.csv 里。不需要截图 Log 或手动查找——打开 CSV 一目了然。
每项检查的判定阈值与状态语义见 数据验证与状态语义。
程序在 outdir() 写 20+ 个文件,全部 CSV(可读)+ DTA(可编程)+ PNG/PDF:
重复使用同一个 outdir() 时,程序只刷新自己拥有的文件,清除上一轮遗留的 .ster 和可选图,但不会删除你放入目录的其他文件。没有可用的 80% 功效序列时,pretrends_plot_data.csv/.dta 仍保留字段结构,只是行数为 0。
| 文件 | 内容 |
|---|---|
status.csv |
每个方法:成功了还是挂了,挂了为什么 |
method_selection.csv |
每个方法:角色/适用性在估计前固定;执行状态和实际主图覆盖在运行后对账 |
estimates_long.csv |
所有方法的规范估计值:系数、标准误、CI、对比组、样本量 |
preflight.csv |
运行前数据审计:缺失率、面板平衡性、cohort 大小、聚类数、离群值 |
graph_coverage.csv |
每种方法是否进入动态图和总体图——不能假装没跑 |
design_manifest.csv |
完整设计记录:推导了什么、请求了什么窗口、实际用了什么窗口 |
diagnostic_status.csv |
每个诊断的状态 |
twfe_diagnostics.csv |
Bacon 分解(TWFE 系数是哪些 2×2 的加权平均) |
pretrend_diagnostics.csv |
联合 Wald 检验 + 50%/80% 功效 |
pretrends_plot_data.csv |
80% 功效图的精确数据——目测斜率不如直接看数字 |
csdid_event_contract.csv |
C&S long2 规范系数位置 |
csdid_event_covariance.csv |
C&S 动态系数完整协方差矩阵(用于 HonestDiD 重现) |
csdid_simultaneous_bands.csv |
逐点 + Bonferroni family-wise 置信带 |
honestdid_sensitivity.csv |
平行趋势违反到多大程度结论会翻转 |
staggered_did_dynamic_comparison.png/pdf/gph |
动态效应对比图(.gph 可用 graph use 重载编辑) |
staggered_did_overall_comparison.png/pdf/gph |
总体效应对比图(.gph 可用 graph use 重载编辑) |
.ster 文件 |
每个方法保存的估计结果,可后续 estimates use |
每个产物的字段定义、来源与测试覆盖见 输出来源与测试体系。
动态效应和总体效应是不同的统计对象,永远分两幅图输出:
每个方法一条线,作者-年份标注。BJS 的处理后期包含在内(但它的 pre# 系数留在前趋势诊断里,因为参照期不同于常规 event -1 基准)。
不同 marker 区分不同 estimand 类别:◆ 主要 C&S ATT、○ 估计量原生替代量、△ 构造窗口平均、× TWFE 诊断。这些类别不代表共同 estimand——图用于透明比较。
[staggered_did] completed: successes=6 failures=0 skipped=0
[staggered_did] status: results/status.csv
[staggered_did] estimates: results/estimates_long.csv
[staggered_did] stacked: skipped (requires a never-treated comparison group)
[staggered_did] bjs: ok_fallback (controls spec failed; no-controls retry with disclosure)
每个失败都有原因,每种跳过都有解释。不让你猜。
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) controls(lpop gsp) pre(6) post(6) outdir("results_ctrl")当然,你也可以在 Claude Code 或 Codex 里说「帮我跑交叠 DID,控制 lpop gsp,看前 6 期和后 6 期」--AI 会自动从你的 .do 文件里推断变量映射,再生成上面这条命令。
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) methods(full) outdir("results_full")
* 跑完后查看 results_full/pretrend_diagnostics.csv:
* 如果 50% 功效下仍未检出前趋势偏离 → 你的前趋势检验才有信息量
* 查看 results_full/honestdid_sensitivity.csv:
* 看平行趋势需要违反到多大程度结论才会翻转当然,你也可以在 Claude Code 或 Codex 里说「帮我把 8 种方法都跑一遍,并检查前趋势和平行趋势敏感性」--AI 会自动加 methods(full),跑完后替你解读 pretrend_diagnostics.csv 和 honestdid_sensitivity.csv。
Q: 为什么默认不包含 dCDH 和 Wooldridge?
在大面板上可能很慢(dCDH)或很耗资源(jwdid)。加 methods(full) 即可。
Q: 我需要把 Stata 升到 18 吗?
不需要 18。命令声明 version 17,可运行于 Stata 17/18/19。审计显示 wrapper 与已装上游包均不依赖 17+ 专有特性(上游自报最高 pretrends 15.1)。仅 18 MP 完整测试;17 为声明最低版本,遇上游报错请反馈。
Q: 结果怎么引用?
引用这个 wrapper:见 CITATION.cff。更重要的是: 报告中使用的每个估计量也必须引用原论文和对应的 Stata 包。原论文 DOI 见 估计量路由与诊断。
Q: 不支持什么?
多期多值处理、连续剂量、合成 DID、空间溢出、三重差分、动态处理效应反转。这些要么路由到适用的设计,要么需要你自己写专用代码。
Q: 自动推断变量靠谱吗?
自然语言 Skill 只在证据充分且一致时才自动运行——如果存在两个同样可能的 outcome 或处理定义,它会停下来问你。推断过程和证据全记录在 project_inference.json 里,你可以审计。
- 软件验证 ≠ 识别假设验证:通过所有测试不代表你的平行趋势或无预期假设成立。
- 已验证环境:Windows + Stata 18 MP。声明最低 Stata 17(依赖审计支持);17 未单独实测,遇上游报错请反馈。
- CI 覆盖:GitHub Actions 验证仓库结构;17 项 Stata 回归测试在本地 Windows + Stata 18 MP 上运行并通过。
- 总体效应互不相等:不同方法的总体 estimand 权重和定义不同,不要声称它们应该相等。
- 本包不实现估计量:所有估计量由上游 Stata 命令提供。本包是路由器 + 审计器 + 报告器。
- 非 Stata Journal 已发表包:目前是 MIT 许可的 SSC 投稿候选版。
完整验证证据、测试矩阵与已验证边界见 验证证据 与 输出来源与测试体系。
潘王雨昂(Yuang Panwang)· panwangyuang.com · pwya1998@126.com
引用与贡献:CITATION.cff · CONTRIBUTING.md · CHANGELOG.md · MIT 许可证
欢迎关注微信公众号:计算公共治理
staggered-did provides two paths into staggered DID analysis:
- Natural-language Skill (Codex / Claude Code / OpenCode and other AI coding agents): the simplest entry is one sentence — "Help me run a staggered DID analysis on this data." When the project provides enough evidence, it automatically infers the outcome, panel keys, first-treatment timing, treatment status, controls, and clustering level. It asks when ambiguity would materially change the design, and never guesses.
- Standalone, independently installable Stata command: no AI agent, PowerShell, or Python required. Pure Stata users install directly from the
stata/directory.
Both paths execute the same .ado, sharing identical preflight checks, estimator routing, diagnostics, and output contracts.
Natural-language, no multimodal required. The Skill works via trigger phrases in the
SKILL.mdfrontmatter. Any text-based model (including weak non-vision models) can run the automatic analysis as long as it can readSKILL.mdand execute PowerShell; no image understanding or screenshot parsing is needed.
A general-purpose LLM can write you a one-off staggered DID script - but every session starts from scratch: re-checking syntax, re-reading long logs, re-aligning different estimators' output formats. This project's value is not in monopolizing domain knowledge, but in turning that capability into a repeatable, auditable workflow:
- Unified contract: the same design receives consistent preflight, routing, and output contracts - no rewriting every time.
- Machine-readable evidence: variable inference, method skips, and fallbacks all leave auditable records (
project_inference.json,status.csv,graph_coverage.csv). - Deterministic execution: the
.adoruns Stata directly, dramatically reducing time spent on repeated code generation and debugging. - Knows its bounds: for a one-off task with fully specified variables and a single estimator, a short custom
.dofile may be lighter.
flowchart TD
A["User asks: Help me run a staggered DID analysis"] --> B["Traverse active .do files and data sources"]
B --> C["Infer outcome, panel keys, treatment timing, controls, and clustering"]
C --> D{"Is the design evidence sufficient and consistent?"}
D -- "No" --> E["Explain the conflict and ask the user"]
D -- "Yes" --> F["Check required Stata commands and optional diagnostics first"]
F --> G["Audit missingness, duplicates, panel structure, absorbing treatment, and support"]
G --> H{"Is the design valid for the common DID comparison?"}
H -- "No" --> I["Stop or route to an applicable design with a reason"]
H -- "Yes" --> J["Run applicable estimators in isolation"]
J --> K["Run Bacon, pretrend-power, simultaneous-band, and HonestDiD diagnostics"]
K --> L["Create separate dynamic and overall figures"]
L --> M["Return findings, robustness differences, warnings, and artifacts"]
The workflow never silently winsorizes, imputes, aggregates, or rewrites source data. Inapplicable or failed methods remain visible in status and coverage outputs.
Full system architecture and the complete step-by-step workflow are in Architecture and workflow.
You can manually run csdid + did_imputation + eventstudyinteract + ... but here's what you'd have to handle yourself:
| Manual Approach | staggered-did |
|---|---|
| Remember 6–8 different syntaxes and dependency chains | One unified syntax for all |
| Manually check data compatibility (balanced panel? binary treatment? cohort consistent?) | Automated 10+ preflight audits → preflight.csv |
| Normalize different output formats into one table by hand | Single estimates_long.csv — one schema for all methods |
| Manually run Bacon decomp + pretrend tests + HonestDiD sensitivity | One-click full diagnostic suite with status tracking |
| Manually build comparison figures: align axes, labels, colors, event-time conventions | Two automatic publication-ready figures with author-year labels |
| One method crashes → entire do-file stops | Failure isolation: others keep running, reasons logged in status.csv |
| Manually verify "which methods actually made it into each figure" | graph_coverage.csv — per-method inclusion proof |
You tell it the outcome, id, time, and who's treated. The rest — preflight, routing, estimation, diagnostics, figures, tables — is automatic.
Requires Stata ≥ 17. The
.adodeclaresversion 17. Audit: the wrapper uses no Stata 18-specific features; the highest self-declared upstream floor ispretrends(15.1), and no upstream package uses 17+ features. Only Stata 18 MP is fully tested; 17 is the declared minimum.
* 1. Install (see Installation section below)
* AI users: say "Help me install this skill"
* Stata users: net install from local stata/ directory
* 2. Run
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) outdir("results")You'll see in the console:
[staggered_did] Design: panel, 7 cohorts, never-treated present
[staggered_did] Running 6 estimators...
[staggered_did] csdid: ok ......................... 7.2s
[staggered_did] did_imputation: ok ................ 3.1s
[staggered_did] eventstudyinteract: ok ............ 1.8s
[staggered_did] did2s: ok ......................... 0.9s
[staggered_did] stackedev: ok ..................... 2.4s
[staggered_did] reghdfe (diagnostic): ok .......... 0.6s
[staggered_did] completed: successes=6 failures=0 skipped=0
[staggered_did] status: results/status.csv
[staggered_did] estimates: results/estimates_long.csv
[staggered_did] preflight audit: results/preflight.csv
3 steps, under 2 minutes. The equivalent R workflow with the did package takes ~20 lines to separately call, aggregate, and plot six methods.
| Method | Command | Paper | Default? | When to Use |
|---|---|---|---|---|
| C&S | csdid |
Callaway & Sant'Anna (2021) | ✅ Primary | Your go-to. Standard for staggered timing. |
| BJS | did_imputation |
Borusyak, Jaravel & Spiess (2024) | ✅ | Robustness. Imputes counterfactual untreated outcomes. |
| Sun-Abraham | eventstudyinteract |
Sun & Abraham (2021) | ✅ | Robustness. Interaction-weighted event study. |
| Gardner | did2s |
Gardner (2022) | ✅ | Robustness. Two-stage DID with automatic unit FE. |
| Stacked | stackedev |
Cengiz et al. (2019) | ✅ | Gracefully skipped if no never-treated group. |
| TWFE | reghdfe |
— | ✅ Diagnostic | Conventional TWFE. Diagnostic benchmark only — unreliable under heterogeneity. |
| dCDH | did_multiplegt_dyn |
de Chaisemartin & D'Haultfoeuille (2024) | methods(full) |
Non-binary or non-absorbing treatment. Can be slow on large panels. |
| Wooldridge | jwdid |
Wooldridge (2021) | methods(full) |
ETWFE. Resource-intensive. |
Not sure which to pick? Use the six default methods. C&S as primary, BJS/Sun-Abraham/Gardner/Stacked as robustness, TWFE to show what you'd get without updating your methods. Agreement across six methods → solid conclusions.
Estimand, comparison group, and diagnostic details per estimator are in Estimator routing and diagnostics; selection rationale in methods.md.
The SSC submission bundle is ready;
ssc install staggered_didwill work after the archive accepts it. Until then, use the GitHub/local installation paths below.
| Requirement | Notes |
|---|---|
| Stata | Minimum ≥ 17. The .ado declares version 17 (dependency audit: highest self-declared upstream is pretrends 15.1; no package uses 17+ features). MP / SE / IC all work; only Stata 18 MP has passed the full 17-test suite. |
| OS | Windows (fully validated); macOS/Linux should work but are not formally tested. |
ssc install staggered_did, replace
help staggered_didSSC distributes this project's own .ado/.sthlp files. The estimators remain separate upstream packages maintained by their original authors; install the required dependencies listed below before first use.
If a Release exists: click Releases on the right side of the GitHub repo page -> download the latest staggered-did-x.x.x.zip -> unzip to any folder.
If no Release or you want the latest source: click the green Code button on the repo page -> Download ZIP -> unzip.
After unzipping, tell your AI coding agent (Claude Code / Codex / OpenCode):
Help me install this skill.
The agent detects SKILL.md + scripts/setup.ps1 and automatically:
- Installs
.adoand.sthlpto your Stata PERSONAL directory - Creates a skill junction for the current agent
- Installs missing upstream dependencies (
csdid,did_imputation, etc.) - Runs a six-method self-test to verify everything works
Or run manually: powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
After installation, the same natural-language interface handles analysis:
Help me run a staggered DID analysis on this data.
The Skill discovers variables, infers the design, runs estimation, and returns a report — you never need to specify parameters manually.
From your unzipped directory:
* Replace <your-repo-path> with the actual path
net install staggered_did, from("<your-repo-path>/stata") replaceOr host the stata/ directory and install from URL (e.g., GitHub raw):
net install staggered_did, from("https://raw.githubusercontent.com/pwya/staggered-did/main/stata/") replaceVerify:
which staggered_did
help staggered_didManual install also requires upstream dependencies — install what you need (first seven for the six-method core):
ssc install ftools
ssc install reghdfe
ssc install avar
ssc install csdid
ssc install drdid
ssc install did_imputation
ssc install eventstudyinteract
ssc install did2s
ssc install stackedev
* methods(full) only:
ssc install did_multiplegt_dyn
ssc install jwdid
* optional diagnostics:
ssc install bacondecomp
net install pretrends, from("https://raw.githubusercontent.com/mcaceresb/stata-pretrends/main/") replace
net install honestdid, from("https://raw.githubusercontent.com/mcaceresb/stata-honestdid/main/") replaceFull dependency list, install troubleshooting, and runtime in Dependencies and runtime.
Of course, you can also say in Claude Code or Codex: "Help me run a staggered DID on this data" - the AI reads
SKILL.md, infers most of the parameters in this syntax, and generates the command; you usually just verify it. The syntax below is for pure-Stata users and when you need manual control.
staggered_did outcome, time(timevar) // required
[id(idvar) // panel; omit for repeated cross-sections
cohort(cohortvar) // 0 or missing = never treated
treat(treatvar) // binary and absorbing
controls(x1 x2) // control variables
cluster(clustervar) // default = id
pre(4) post(4) // event window; shrinks to observed support
methods(core|full|csdid bjs sa ...) // default: core (6 methods)
plotmethods(all|auto|csdid bjs sa) // which methods appear in figures
plotmax(.) // cap for plotmethods(auto)
smallcohort(10) // flag cohorts with fewer units
outdir("staggered_did_output") // output directory
level(95) // confidence level
control_group(auto|never|notyet|both) // comparison-group contract; default auto
nodiagnostics] // dev/benchmark: skip diagnosticsFor panels, supply at least one of cohort() and treat(); the command derives the other and records the derivation. If both are supplied they must agree exactly.
level() defaults to 95 and accepts real values in [50,100), so level(90), level(99), and level(97.5) are all valid. It controls pointwise intervals in the normalized results for all eight estimators, C&S Bonferroni simultaneous bands, the alpha used by Roth pretrend-power calculations, and HonestDiD robust intervals. The natural-language entry point passes the same setting with -Level 90; omitting it retains 95%. The selected level and alpha are recorded in design_manifest.csv, and every interval figure labels its level. The joint pretrend Wald statistic and p-value do not themselves change with the reporting threshold.
control_group() defaults to auto and accepts auto|never|notyet|both. auto preserves the historical behavior: never-treated controls when any exist, otherwise not-yet-treated controls. Explicit never restricts to never-treated units (fails loudly when the sample has none); notyet restricts to not-yet-treated units excluding never-treated (requires at least two treated cohorts); both uses both (degrades to notyet with a manifest flag when no never-treated units exist). Under an explicit request, methods that cannot honor the group are skipped with a recorded reason (twfe is the exempt diagnostic benchmark); if no requested method can honor it, the whole run fails loudly.
For repeated cross-sections:
staggered_did y, time(year) cohort(first_treat) treat(D) ///
design(rcs) cluster(school_id) outdir("results")Full syntax, options, and stored results are in Conventional Stata package and help staggered_did.
The command never modifies your source data — but tells you everything that might be wrong:
| Check | If There's a Problem… |
|---|---|
| Duplicate unit-time rows | Hard stop — fix your data |
| Non-binary or reversing treatment | Hard stop — check cohort/treat construction |
| Cohort/treatment disagreement | Hard stop with mismatch count |
| Cohort timing outside observation range | Hard stop |
| Heavy missingness | Warning recorded in preflight.csv |
| Unbalanced panel (Bacon needs strong balance) | Bacon skips with reason; other methods unaffected |
| Few clusters (< 30) | Warning — not an automatic rejection |
| Outcome outliers | Recorded — never auto-winsorized |
| Time-invariant controls | Recorded — absorbed by FE |
| Requested window exceeds observed support | Shrinks to observed support, recorded in manifest |
All recorded in preflight.csv. No need to screenshot the log — open the CSV.
Decision thresholds and status semantics for each check are in Data validation and status.
The outdir() receives 20+ files — CSV (human-readable) + DTA (programmable) + PNG/PDF:
When the same outdir() is reused, the command refreshes only its own exact artifact allowlist: stale .ster and optional figures are removed, while unrelated user files are preserved. If no 80% power series is available, pretrends_plot_data.csv/.dta still retain their schema with zero rows.
| File | Contents |
|---|---|
status.csv |
Per-method success/failure/skip with reasons |
method_selection.csv |
Roles/eligibility fixed pre-estimation; execution and actual main-graph coverage reconciled post-run |
estimates_long.csv |
Normalized estimates: coefficients, SEs, CIs, comparison groups, sample sizes |
preflight.csv |
Pre-run data audit: missingness, balance, cohort sizes, cluster counts, outliers |
graph_coverage.csv |
Per-method proof of inclusion in both figures |
design_manifest.csv |
Complete design record: derivations, requested vs. effective windows |
diagnostic_status.csv |
Status of every diagnostic |
twfe_diagnostics.csv |
Bacon decomposition |
pretrend_diagnostics.csv |
Joint Wald test + 50%/80% detectable violations |
pretrends_plot_data.csv |
Exact 80% power figure data — don't eyeball the slope |
csdid_event_contract.csv |
Exact C&S long2 coefficient positions |
csdid_event_covariance.csv |
Full C&S dynamic covariance matrix |
csdid_simultaneous_bands.csv |
Pointwise + Bonferroni family-wise bands |
honestdid_sensitivity.csv |
How much violation flips your conclusion |
staggered_did_dynamic_comparison.png/pdf/gph |
Dynamic effect comparison figure (.gph reloadable via graph use) |
staggered_did_overall_comparison.png/pdf/gph |
Overall effect comparison figure (.gph reloadable via graph use) |
.ster files |
Per-method saved estimates for later estimates use |
Field definitions, provenance, and test coverage for every artifact are in Output provenance and testing.
Dynamic paths and overall effects are different statistical objects — always two separate figures:
Each method gets its own styled line with author-year labels. BJS post-treatment horizons are included; pre# coefficients remain separate diagnostics due to differing reference conventions.
Marker shapes distinguish estimand classes: ◆ primary C&S ATT, ○ estimator-native alternatives, △ constructed window averages, × TWFE diagnostic. Classes are not a common estimand — the figure enables transparent comparison.
[staggered_did] completed: successes=6 failures=0 skipped=0
[staggered_did] status: results/status.csv
[staggered_did] estimates: results/estimates_long.csv
[staggered_did] stacked: skipped (requires a never-treated comparison group)
[staggered_did] bjs: ok_fallback (controls spec failed; no-controls retry with disclosure)
Every failure has a reason. Every skip has an explanation.
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) controls(lpop gsp) pre(6) post(6) outdir("results_ctrl")Of course, you can also say in Claude Code or Codex: "Run a staggered DID for me, control for lpop gsp, look at 6 pre and 6 post periods" - the AI will infer the variable mapping from your .do files and generate the command above.
staggered_did lemp, id(countyreal) time(year) cohort(first_treat) ///
treat(treated_now) methods(full) outdir("results_full")
* Check results_full/pretrend_diagnostics.csv:
* If 50% power still doesn't detect a pretrend violation → your test is informative
* Check results_full/honestdid_sensitivity.csv:
* See how much parallel-trend violation it takes to flip your conclusionOf course, you can also say in Claude Code or Codex: "Run all eight methods and check the pretrends and parallel-trend sensitivity" - the AI adds methods(full) automatically and interprets pretrend_diagnostics.csv and honestdid_sensitivity.csv for you afterward.
Q: Why aren't dCDH and Wooldridge in the default?
They can be very slow (dCDH) or resource-intensive (jwdid) on large panels. Add methods(full) when you want them.
Q: Do I need Stata 18?
No. The command declares version 17 and runs on Stata 17/18/19. The audit found neither the wrapper nor installed upstream packages use 17+ features (highest self-declared upstream is pretrends 15.1). Only 18 MP is fully tested; 17 is the declared minimum - report any upstream error on 17.
Q: How do I cite this?
For the wrapper: see CITATION.cff. More importantly: cite the original paper and the Stata package for every estimator you report. Paper DOIs are in docs/en/estimators-and-diagnostics.md.
Q: What's NOT supported?
Multi-valued treatments, continuous doses, synthetic DID, spillover effects, triple differences, and treatment reversal. Some route to appropriate designs; others need custom code.
Q: Is the automatic variable inference reliable?
The natural-language Skill only runs automatically when evidence is sufficient and consistent. If two equally plausible outcome or treatment definitions exist, it stops and asks you. All inference is recorded in project_inference.json.
- Software validation ≠ identification: passing all tests does not prove your parallel trends or no-anticipation assumptions hold.
- Validated on: Windows + Stata 18 MP. Declared minimum Stata 17 (dependency-audit supported); 17 not separately tested - report upstream errors.
- CI coverage: GitHub Actions validates repo structure; 17 Stata regression tests pass locally on Windows + Stata 18 MP.
- Overall estimands differ: different methods target different weighted averages. Don't claim they should be equal.
- This package does not implement estimators: all estimation runs through upstream Stata commands. This is a router + auditor + reporter.
- Not a Stata Journal published package: currently a MIT-licensed SSC submission candidate.
Full validation evidence, the test matrix, and validated boundaries are in VALIDATION.md and Output provenance and testing.
The eight estimators this wrapper routes to (each is an independent upstream Stata package):
- Callaway-Sant'Anna 2021 -
csdid- difference-in-differences with multiple time periods (Journal of Econometrics) - Borusyak-Jaravel-Spiess 2024 -
did_imputation- revisiting event-study designs (Review of Economic Studies) - Sun-Abraham 2021 -
eventstudyinteract- event-study estimators with heterogeneous treatment effects (Journal of Econometrics) - Gardner 2022 -
did2s- two-stage difference-in-differences - Cengiz et al. 2019 -
stackedev- stacked regression for event studies - de Chaisemartin-D'Haultfoeuille 2024 -
did_multiplegt_dyn- difference-in-differences estimators (Review of Economic Studies) - Wooldridge 2021 -
jwdid- ETWFE (interaction-weighted estimator) - TWFE -
reghdfe- traditional two-way fixed effects (diagnostic benchmark only)
These modern estimators address heterogeneous treatment effects that naive TWFE mishandles under staggered adoption. The primary reference is Callaway-Sant'Anna (2021; group-time ATT), with robustness from Borusyak-Jaravel-Spiess (2024; imputation) and Sun-Abraham (2021; interaction-weighted). Always cite the original paper and the Stata package for every estimator you report; see docs/en/estimators-and-diagnostics.md for DOIs.
- Contributing — bug reports, feature requests, pull requests
- CITATION.cff
- CHANGELOG.md
- License: MIT
- Author: 潘王雨昂 (Yuang Panwang) - panwangyuang.com - pwya1998@126.com
- Full technical docs: docs/
Follow our WeChat Official Account: 计算公共治理


