Skip to content

Repository files navigation

中文 · English

version Stata ≥ 17 MIT License

staggered-did

一条命令跑完 8 种主流交叠 DID 估计量 + 全套诊断。 不做选择,不用记语法,不写 300 行 do-file。
One command: 8 estimators, full diagnostics, publication-ready figures.

目录 / Table of Contents

中文

English


中文

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.jsonstatus.csvgraph_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["返回主要结论、稳健性差异、警告和结果文件"]
Loading

程序不会自动缩尾、插补、聚合或改写源数据。不能运行的方法会被明确标记为 skippedfailed,不会悄悄从图里消失。

完整系统架构与每一步工作流见 系统架构与工作流

具体来说,省了什么?

你当然可以手动跑 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、时间、谁被处理了。 剩下的预检、路由、估计、诊断、制图、写表——全都自动完成。

30 秒跑起来

需要 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 行代码分别调用、聚合、画图。

8 种估计量,一张表讲清楚

方法 命令 论文 默认? 什么时候用?
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 安装(档案收录后)

ssc install staggered_did, replace
help staggered_did

SSC 只分发本项目自己的 .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,会自动:

  1. .ado.sthlp 安装到 Stata PERSONAL 目录
  2. 为当前 agent 创建 skill junction
  3. 安装缺失的上游依赖(csdiddid_imputation 等)
  4. 运行六方法自检,验证一切就绪

或手动运行:powershell -ExecutionPolicy Bypass -File scripts\setup.ps1

安装完成后,同一句话就能跑分析:

帮我对这个数据进行交叠DID分析。

Skill 自动发现变量、推断设计、运行估计、返回报告。你不需要手动指定任何参数。

方式二:纯 Stata 手动安装

从解压后的目录安装:

* 将 <你的仓库路径> 替换为实际路径
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|bothauto 保持历史默认行为:有从未处理组就用从未处理组,否则用尚未处理组。显式 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

每个产物的字段定义、来源与测试覆盖见 输出来源与测试体系

两张独立图

动态效应和总体效应是不同的统计对象,永远分两幅图输出:

动态效应

动态效应对比 — 每个方法按事件时间的 ATT

每个方法一条线,作者-年份标注。BJS 的处理后期包含在内(但它的 pre# 系数留在前趋势诊断里,因为参照期不同于常规 event -1 基准)。

总体效应

总体效应对比 — 按 estimand 类别分组

不同 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 文件里推断变量映射,再生成上面这条命令。

全部 8 种方法 + 完整诊断

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.csvhonestdid_sensitivity.csv

FAQ

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 许可证


(回到顶部 / back to top)

关注微信公众号

微信公众号:计算公共治理

欢迎关注微信公众号:计算公共治理


English

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.md frontmatter. Any text-based model (including weak non-vision models) can run the automatic analysis as long as it can read SKILL.md and execute PowerShell; no image understanding or screenshot parsing is needed.

Why Use It?

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 .ado runs 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 .do file may be lighter.

What Happens After That Request

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"]
Loading

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.

Specifically, What Does It Save?

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.

30-Second Quick Start

Requires Stata ≥ 17. The .ado declares version 17. Audit: the wrapper uses no Stata 18-specific features; the highest self-declared upstream floor is pretrends (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.

The 8 Estimators at a Glance

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.

Installation & Prerequisites

The SSC submission bundle is ready; ssc install staggered_did will work after the archive accepts it. Until then, use the GitHub/local installation paths below.

Runtime

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 Installation (After Archive Acceptance)

ssc install staggered_did, replace
help staggered_did

SSC 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.

Method 1: Download + One-Sentence Install (Recommended)

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:

  1. Installs .ado and .sthlp to your Stata PERSONAL directory
  2. Creates a skill junction for the current agent
  3. Installs missing upstream dependencies (csdid, did_imputation, etc.)
  4. 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.

Method 2: Pure Stata Manual Install

From your unzipped directory:

* Replace <your-repo-path> with the actual path
net install staggered_did, from("<your-repo-path>/stata") replace

Or 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/") replace

Verify:

which staggered_did
help staggered_did

Manual 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/") replace

Full dependency list, install troubleshooting, and runtime in Dependencies and runtime.

Command Syntax

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 diagnostics

For 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.

Pre-estimation Checks

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.

Output Files

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.

Two Independent Figures

Dynamic paths and overall effects are different statistical objects — always two separate figures:

Dynamic Effects

Dynamic effect comparison — ATT by event time per method

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.

Overall Effects

Overall effect comparison — grouped by estimand class

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.

What You'll See

All methods succeed

[staggered_did] completed: successes=6 failures=0 skipped=0
[staggered_did] status: results/status.csv
[staggered_did] estimates: results/estimates_long.csv

Some methods skip or fall back

[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.

More Examples

With Controls and Custom Window

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.

All 8 Methods with Full Diagnostics

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 conclusion

Of 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.

FAQ

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.

Limitations & Boundaries

  • 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.

References

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 & Citation

Follow Us on WeChat

WeChat Official Account: 计算公共治理

Follow our WeChat Official Account: 计算公共治理


(back to top / 回到顶部)

About

Automatic data-aware staggered-adoption DID analysis in Stata - 8 estimators + full diagnostics via one command or one sentence

Topics

Resources

Contributing

Stars

14 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages