Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
9c08693
builder: implement edid() -- Efficient DiD estimator (no-covariate path)
marcelortizv Apr 11, 2026
aaec7fc
builder: fix 4 edid bugs (EIF centering, inference validity, bootstra…
marcelortizv Apr 11, 2026
9266703
scriber: add edid() NEWS entry and architecture record
marcelortizv Apr 11, 2026
2a6c4ae
Add edid(): efficient DiD estimator (Chen, Sant'Anna & Xie 2025)
marcelortizv Apr 11, 2026
e4fe95d
Align edid() arguments with att_gt() conventions and add aggte_edid()
marcelortizv Apr 13, 2026
10af087
builder: fix PT-All pair enumeration -- remove never-treated as compa…
marcelortizv Apr 14, 2026
c699fbc
adding edid estimation feature without covariates
marcelortizv Apr 17, 2026
bc3edfb
builder: wire covariate path into edid() dispatcher
marcelortizv Apr 17, 2026
5915a80
builder: write implementation.md and mailbox.md for covariate wiring …
marcelortizv Apr 17, 2026
7aa8a0c
fix warning with covariates
marcelortizv Apr 20, 2026
5d0ca5d
chore: clean up repo for draft PR
marcelortizv May 6, 2026
811fcb3
fix: Rd parsing errors and S3 generic mismatch breaking CI
marcelortizv May 6, 2026
a32ae39
Fix aggte_edid aggregation overalls and PT-Post pair enumeration
pedrohcgs May 27, 2026
5a20376
Align edid() covariate path with paper Eqs. (3.12) and (4.4)
pedrohcgs May 28, 2026
1d94438
Use plug-in nuisance estimation; disable cross-fitting pending diagno…
pedrohcgs May 28, 2026
915c6dd
edid: drop propensity-ratio clipping to preserve Neyman orthogonality
pedrohcgs May 29, 2026
6cfe397
edid: correct covariate-path EIF centering (ratio-estimator influence…
pedrohcgs May 29, 2026
85c7206
Remove benchmark/ scratch directory
pedrohcgs Jun 1, 2026
ccfe738
edid: aggregation/inference hardening on the covariate path
pedrohcgs Jun 1, 2026
8cb8bf3
edid: add did-compatible MP construction (as_MP_edid)
pedrohcgs Jun 1, 2026
a3f258d
edid: route aggte_edid() through did::aggte (via as_MP_edid)
pedrohcgs Jun 1, 2026
e409c87
edid: full att_gt-compatibility -- aggregations are did::AGGTEobj; de…
pedrohcgs Jun 1, 2026
f333afb
edid: docs to roxygen2 8.0.0; remove vestigial store_eif; document AG…
pedrohcgs Jun 1, 2026
0867d6c
mboot: cluster-robust bootstrap uses cluster sums (Callaway & Sant'An…
pedrohcgs Jun 1, 2026
8d4fb92
edid: route bootstrap through did::mboot + aggte(bstrap=TRUE); delete…
pedrohcgs Jun 1, 2026
1888714
edid: merge tiny utility files into edid-utils.R
pedrohcgs Jun 1, 2026
aed984a
edid/did: clean R CMD check NOTEs (docs + .lm.fit + .Rbuildignore)
pedrohcgs Jun 1, 2026
dc621fd
Merge bcallaway11/did master (incl. PR #261) into feature-efficient-D…
pedrohcgs Jun 1, 2026
79ed795
edid: quiet the covariate-path O(n^2) performance notice
pedrohcgs Jun 1, 2026
a88aa6b
tests: make the suite robust under coverage instrumentation (fix test…
pedrohcgs Jun 1, 2026
83fe20f
edid: correct_first_step (ACH 2012) + fix ACH sign, PT-Post base peri…
pedrohcgs Jun 1, 2026
027eb9b
edid: rename correct_first_step -> estimation_effect; make ACH sign t…
pedrohcgs Jun 2, 2026
25f064c
edid: analytic sup-t uniform bands (cband_method) + opt-in higher-ord…
pedrohcgs Jun 2, 2026
6d05b4d
edid: fix bstrap=TRUE under the analytic default + 2.6x faster covari…
pedrohcgs Jun 3, 2026
bb3ca7b
edid: add misspec_robust SE (weight-estimation channel) for efficient…
pedrohcgs Jun 4, 2026
b062b4a
edid: optimize the misspec_robust weight-channel path (~15-25% faster…
pedrohcgs Jun 4, 2026
92d7fb0
edid: make the higher_order aggregate SEs higher-order-aware (not jus…
pedrohcgs Jun 4, 2026
c871dfa
edid: fix R CMD check WARNING/NOTE from markdown-mangled math in roxygen
pedrohcgs Jun 4, 2026
ff5762b
edid: API cleanup (Tier 2) — weight_scheme, drop control_group, honor…
pedrohcgs Jun 4, 2026
6509eba
edid: correctness fixes (Tier 3) — reproducibility, degenerate-data g…
pedrohcgs Jun 4, 2026
655c3aa
edid: tests for the API + correctness changes (Tier 4)
pedrohcgs Jun 4, 2026
392eb5a
edid: overlap trimming (DRDID-style) + scale-invariant averaged-Omega…
pedrohcgs Jun 5, 2026
5eb35e0
edid: misspec_robust = TRUE by default (master switch for all estimat…
pedrohcgs Jun 5, 2026
3a5cb91
edid: overlap trimming -- unit-level + DRDID renormalization (fixes t…
pedrohcgs Jun 5, 2026
b9c2726
edid: account for the kept-treated-mass (pi_g,kept) estimation effect…
pedrohcgs Jun 6, 2026
454dd2b
edid: regenerate Rd + document new trim args (fix R CMD check codoc W…
pedrohcgs Jun 6, 2026
5a0a401
edid: sup-t band coordinate-mask consistency + collinear-X validation…
pedrohcgs Jun 8, 2026
cebadef
edid: performance overhaul — analytic Hessian, block-sparse variance,…
pedrohcgs Jun 9, 2026
f905917
edid: stabilize inference under kernel-FP changes + 8-13x faster kern…
pedrohcgs Jun 9, 2026
44b7bb7
edid: fix R CMD check codoc WARNING from the Hessian-signature change
pedrohcgs Jun 9, 2026
016f6bb
edid: cache per-(vector,group) conditional means in the misspec_robus…
pedrohcgs Jun 9, 2026
80c646d
edid: exact analytic ACH first-step correction (replaces per-coeffici…
pedrohcgs Jun 9, 2026
ea2e867
edid: share per-cell kernel slices + hoist cell-invariant m_eff
pedrohcgs Jun 9, 2026
c6ce4e5
edid: merge-hardening — declare imports, fix NEWS default, memoize si…
pedrohcgs Jun 9, 2026
2498405
edid: fix silent sieve mixed-estimator + promote mc_cores to a `cores…
pedrohcgs Jun 9, 2026
3a66c39
edid: proper sieve weight-estimation channel (Sigma_Omega) for the ef…
pedrohcgs Jun 9, 2026
5db26fd
edid: document SE-option small-sample calibration (doc-only)
pedrohcgs Jun 9, 2026
a3612a0
edid: averaged+sieve weight channel + numerical-stability guard for t…
pedrohcgs Jun 9, 2026
75d66fb
edid: harden inference and aggregation paths
pedrohcgs Jun 10, 2026
5133060
edid: comprehensive audit round -- estimand-safe trimming, Section-5 …
pedrohcgs Jun 10, 2026
898a7e8
update gitignore
marcelortizv Jun 11, 2026
e4103c6
edid: AKS inference layer, small-n machinery, with-X ratio-channel re…
pedrohcgs Jun 12, 2026
669e177
edid: ratio_method default = "exp"; remove the multinomial-system engine
pedrohcgs Jun 12, 2026
89bdaa7
Merge branch 'master' (did 2.5.0 perf round, PR #263) into the effici…
pedrohcgs Jun 12, 2026
f19d84a
edid: round-3 stability guards and diagnostics (8 items; guards/messa…
pedrohcgs Jun 12, 2026
46eba84
edid: add experimental AR1 no-cov shrink target
pedrohcgs Jun 13, 2026
14c87fa
Revert "edid: add experimental AR1 no-cov shrink target"
pedrohcgs Jun 13, 2026
70263bc
edid: ignore local vscode build metadata
pedrohcgs Jun 13, 2026
1ab1763
Reset no-covariate shrinkage default
pedrohcgs Jun 13, 2026
0974b1a
Rerun package CI
pedrohcgs Jun 13, 2026
1aa0786
Print coverage test failures
pedrohcgs Jun 13, 2026
4369592
Fix coverage-only CI failures
pedrohcgs Jun 13, 2026
aafd8c6
Relax covr-only thin-cohort fingerprints
pedrohcgs Jun 13, 2026
c812339
Harden coverage workflow upload
pedrohcgs Jun 13, 2026
9a3562e
update gitignore
marcelortizv Jun 14, 2026
a0e07a3
edid: effective-n ridge, over-id eigen-ridge variance fix, covariate …
pedrohcgs Jun 16, 2026
ed57835
edid: full observation-weight support on the covariate path (weightsn…
pedrohcgs Jun 16, 2026
9ec10fa
Merge overnight-aggfix-robust into feature-efficient-DiD-estimator: f…
pedrohcgs Jun 16, 2026
40d2ff2
edid: regenerate roxygen docs for the weighted-covariate nuisance fit…
pedrohcgs Jun 16, 2026
217518a
Merge branch 'overnight-aggfix-robust' into feature-efficient-DiD-est…
pedrohcgs Jun 16, 2026
3571b74
Merge branch 'master' into feature-efficient-DiD-estimator (sync to d…
pedrohcgs Jun 16, 2026
856a956
edid: fix weighted-cov Omega unit-test never-treated convention + dro…
pedrohcgs Jun 16, 2026
14cb481
edid: support designs with no never-treated group + fix R CMD check /…
pedrohcgs Jun 17, 2026
6194166
README: static badges instead of badger; drop the now-unnecessary bad…
pedrohcgs Jun 17, 2026
fa58d26
_pkgdown.yml: add the 16 edid exports to the reference index (build-c…
pedrohcgs Jun 17, 2026
b1fdf34
edid: over-identification engine — AHT effective-df F, Omega-EE harmo…
pedrohcgs Jun 17, 2026
2a3e48f
edid: memoize plug-in refit in the over-id toolkit (refit each leg once)
pedrohcgs Jun 18, 2026
3246281
Fix notyettreated control-cohort deletion when no never-treated group
pedrohcgs Jun 18, 2026
b0910c9
test: rename T param to n_periods in invariance test helper
pedrohcgs Jun 18, 2026
c4e48ba
test: clarify comment sentinels and section dividers
pedrohcgs Jun 18, 2026
bf4c3ad
README: drop unused ggpubr/gridExtra, use static badges (fix build-ch…
pedrohcgs Jun 18, 2026
4135a91
Merge pull request #266 from bcallaway11/fix-latest-control-cohort-de…
pedrohcgs Jun 18, 2026
fccbf89
Bump version to 2.5.1.1 (PR #266)
github-actions[bot] Jun 18, 2026
4e9de53
Fix fix_weights="varying" 2x SE, aggte empty-selection crashes, negat…
pedrohcgs Jun 19, 2026
a4da32c
Set version to 2.5.1 for the CRAN release
pedrohcgs Jun 19, 2026
3249c13
Merge pull request #267 from bcallaway11/fix-audit-fixweights-aggte-v…
pedrohcgs Jun 19, 2026
ed8b675
Round-2 audit fixes: parallel bootstrap RNG + chunk guard, calendar m…
pedrohcgs Jun 19, 2026
329493c
Address Copilot review on #268: <- assignments, &&, mc.cores cap, exp…
pedrohcgs Jun 19, 2026
e68fca1
Address Copilot 2nd pass: accurate calendar warning, RNGkind restore-…
pedrohcgs Jun 19, 2026
7a6e4ad
NEWS: clarify the parallel-bootstrap trigger is influence-function ro…
pedrohcgs Jun 19, 2026
d66f832
Merge pull request #268 from bcallaway11/fix-parallel-bootstrap-calen…
pedrohcgs Jun 19, 2026
f114c6f
Harden scalar argument validation
pedrohcgs Jun 19, 2026
9d161ff
Expand scalar validation hardening
pedrohcgs Jun 19, 2026
1835bb0
Harden validation and preprocessing edge cases
pedrohcgs Jun 19, 2026
622d7ce
Preserve gname = Inf never-treated units in finite-data filter
pedrohcgs Jun 19, 2026
9aba07d
Merge pull request #270 from bcallaway11/release/scalar-argument-vali…
pedrohcgs Jun 19, 2026
858e31d
edid: add edid_overid() -- omnibus over-identification J (low-rank, A…
pedrohcgs Jun 19, 2026
75b253d
edid: cluster-align efficient weights under clustering (efficient inf…
pedrohcgs Jun 21, 2026
df2f040
edid: aggregate var_add for single-cohort dynamic ES_avg (overall wei…
pedrohcgs Jun 23, 2026
3c2b462
edid: cluster-metric eigen-floor for rank-deficient over-id (H>=G_act…
pedrohcgs Jun 23, 2026
aae91f6
Merge remote-tracking branch 'origin/master' into feature-efficient-D…
pedrohcgs Jun 23, 2026
09995f9
edid: regenerate stale Rd + index edid_overid -- fix R-CMD-check codo…
pedrohcgs Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,20 @@ Icon?
^benchmark-results$
^dev$
^\.revdeplite$
^CSX2026\.pdf$
^Efficient_DiD\.pdf$
^Rplots\.pdf$
^CODEX_AUDIT_HANDOFF\.md$
^EDID_implemention\.md$
^IMPLEMENTATION_PLAN\.md$
^METHODOLOGY_REVIEW\.md$
^audit\.md$
^audit_cov\.md$
^comprehension\.md$
^implementation\.md$
^mailbox\.md$
^spec\.md$
^test-spec\.md$
^data-raw$
^updates_made\.md$
^quality_reports$
45 changes: 41 additions & 4 deletions .github/workflows/test-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,47 @@ jobs:
needs: coverage

- name: Test coverage
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: |
covr::codecov(
quiet = FALSE,
clean = FALSE,
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
fail_root <- file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")

print_failures <- function() {
fail_files <- if (dir.exists(fail_root)) {
list.files(fail_root, pattern = "\\.Rout\\.fail$", recursive = TRUE, full.names = TRUE)
} else {
character()
}
for (fail_file in fail_files) {
cat("\n===== ", fail_file, " =====\n", sep = "")
cat(readLines(fail_file, warn = FALSE), sep = "\n")
cat("\n===== end ", fail_file, " =====\n", sep = "")
}
}

coverage <- tryCatch(
covr::package_coverage(
quiet = FALSE,
clean = FALSE,
install_path = fail_root
),
error = function(e) {
print_failures()
stop(e)
}
)

print(coverage)

codecov_token <- Sys.getenv("CODECOV_TOKEN", "")
if (nzchar(codecov_token)) {
tryCatch(
covr::codecov(coverage = coverage, quiet = FALSE, token = codecov_token),
error = function(e) {
warning("Codecov upload failed: ", conditionMessage(e), call. = FALSE)
}
)
} else {
message("CODECOV_TOKEN is not set; skipping Codecov upload.")
}
shell: Rscript {0}
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ dev/
.revdeplite/*.md
.revdeplite/github/
.revdeplite/results/

# --- edid working-file ignores ---
CSX2026.pdf
Efficient_DiD.pdf
Rplots.pdf
CODEX_AUDIT_HANDOFF.md
EDID_implemention.md
IMPLEMENTATION_PLAN.md
METHODOLOGY_REVIEW.md
audit.md
audit_cov.md
comprehension.md
implementation.md
mailbox.md
spec.md
test-spec.md
11 changes: 9 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
Package: did
Title: Treatment Effects with Multiple Periods and Groups
Version: 2.5.0
Version: 2.5.1
Authors@R: c(person("Brantly", "Callaway", email = "brantly.callaway@uga.edu", role = c("aut", "cre")), person("Pedro H. C.", "Sant'Anna", email="pedro.santanna@emory.edu", role = c("aut")))
URL: https://bcallaway11.github.io/did/, https://github.com/bcallaway11/did/
Description: The standard Difference-in-Differences (DID) setup involves two periods and two groups -- a treated group and untreated group. Many applications of DID methods involve more than two periods and have individuals that are treated at different points in time. This package contains tools for computing average treatment effect parameters in Difference in Differences setups with more than two periods and with variation in treatment timing using the methods developed in Callaway and Sant'Anna (2021) <doi:10.1016/j.jeconom.2020.12.001>. The main parameters are group-time average treatment effects which are the average treatment effect for a particular group at a particular time. These can be aggregated into a fewer number of treatment effect parameters, and the package deals with the cases where there is selective treatment timing, dynamic treatment effects, calendar time effects, or combinations of these. There are also functions for testing the Difference in Differences assumption, and plotting group-time average treatment effects.
Depends:
R (>= 4.1.0)
License: GPL-3
Copyright: The package authors, except the lookup tables in
inst/extdata/aks_lookup/, which are Copyright (c) 2023 Sophie Sun (MIT
License), vendored from the MissAdapt replication package of Armstrong,
Kline & Sun (2025, Econometrica). See inst/COPYRIGHTS.
Encoding: UTF-8
LazyData: true
Imports:
Expand All @@ -17,6 +21,8 @@ Imports:
DRDID (>= 1.3.0),
generics,
methods,
parallel,
splines,
tidyr,
fastglm,
data.table (>= 1.15.4),
Expand All @@ -33,6 +39,7 @@ Suggests:
broom,
testthat (>= 3.0.0),
withr,
callr,
remotes,
callr
R.matlab
Config/roxygen2/version: 8.0.0
27 changes: 27 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Generated by roxygen2: do not edit by hand

S3method(as.data.frame,edid_fit)
S3method(coef,edid_fit)
S3method(ggdid,AGGTEobj)
S3method(ggdid,MP)
S3method(glance,AGGTEobj)
Expand All @@ -8,22 +10,45 @@ S3method(nobs,AGGTEobj)
S3method(nobs,MP)
S3method(print,AGGTEobj)
S3method(print,MP)
S3method(print,edid_adaptive)
S3method(print,edid_fit)
S3method(print,edid_frontier)
S3method(print,edid_hausman)
S3method(print,edid_overid)
S3method(print,edid_perturbation_bootstrap)
S3method(print,edid_refit_bootstrap)
S3method(print,edid_sargan)
S3method(summary,AGGTEobj)
S3method(summary,MP)
S3method(summary,MP.TEST)
S3method(summary,edid_fit)
S3method(tidy,AGGTEobj)
S3method(tidy,MP)
S3method(vcov,edid_fit)
export(AGGTEobj)
export(DIDparams)
export(MP)
export(MP.TEST)
export(aggte)
export(aggte_edid)
export(as_MP_edid)
export(att_gt)
export(build_sim_dataset)
export(compute.aggte)
export(compute.att_gt)
export(compute.att_gt2)
export(conditional_did_pretest)
export(edid)
export(edid_adaptive)
export(edid_clear_plugin_cache)
export(edid_frontier)
export(edid_hausman)
export(edid_overid)
export(edid_perturbation_bootstrap)
export(edid_refit_bootstrap)
export(edid_sargan)
export(edid_weight_plot)
export(edid_weights)
export(ggdid)
export(glance)
export(gplot)
Expand Down Expand Up @@ -59,6 +84,7 @@ importFrom(generics,tidy)
importFrom(methods,as)
importFrom(methods,is)
importFrom(stats,aggregate)
importFrom(stats,as.formula)
importFrom(stats,binomial)
importFrom(stats,complete.cases)
importFrom(stats,cov)
Expand All @@ -74,6 +100,7 @@ importFrom(stats,predict)
importFrom(stats,qnorm)
importFrom(stats,quantile)
importFrom(stats,rnorm)
importFrom(stats,sd)
importFrom(stats,setNames)
importFrom(stats,var)
importFrom(tidyr,gather)
Expand Down
145 changes: 145 additions & 0 deletions NEWS.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions R/AGGTEobj.R
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ summary.AGGTEobj <- function(object, ...) {

cat("\n")
cband_text1a <- paste0(100*(1-object$DIDparams$alp),"% ")
cband_text1b <- if (object$DIDparams$bstrap) {
if (object$DIDparams$cband) "Simult. " else "Pointwise "
} else {
"Pointwise "
}
# DIDparams$cband stores the effective band type: did's bootstrap path coerces it to FALSE
# whenever the simultaneous crit falls back to pointwise, and edid's analytic sup-t path sets
# it to TRUE when it installs a simultaneous crit without the bootstrap. Label from it directly
# (the old bstrap && cband rule mislabeled edid's analytic simultaneous bands as pointwise).
cband_text1b <- ifelse(isTRUE(object$DIDparams$cband), "Simult. ", "Pointwise ")
cband_text1 <- paste0("[", cband_text1a, cband_text1b)

cband_lower <- object$att.egt - object$crit.val.egt*object$se.egt
Expand Down
34 changes: 25 additions & 9 deletions R/att_gt.R
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,34 @@ att_gt <- function(yname,
# Capture extra arguments for custom est_method
extra_args <- list(...)

if (missing(control_group)) control_group <- "nevertreated"
validate_choice_scalar(
control_group,
"control_group",
c("nevertreated", "notyettreated"),
"control_group must be either 'nevertreated' or 'notyettreated'"
)
validate_choice_scalar(
base_period,
"base_period",
c("universal", "varying"),
"base_period must be either 'universal' or 'varying'."
)

# Validate compute_inffunc (point-estimates-only switch)
if (!is.logical(compute_inffunc) || length(compute_inffunc) != 1 || is.na(compute_inffunc)) {
stop("compute_inffunc must be a single logical (TRUE or FALSE).")
}
validate_logical_scalar(panel, "panel")
validate_logical_scalar(allow_unbalanced_panel, "allow_unbalanced_panel")
validate_logical_scalar(bstrap, "bstrap")
validate_logical_scalar(cband, "cband")
validate_logical_scalar(faster_mode, "faster_mode")
validate_logical_scalar(print_details, "print_details")
validate_logical_scalar(pl, "pl")
validate_positive_whole_number(cores, "cores")
validate_anticipation(anticipation)
validate_alp(alp)
# When influence functions are not computed there are no standard errors, no
# uniform bands, and no parallel-trends pre-test, so the bootstrap is moot.
if (!compute_inffunc) {
Expand Down Expand Up @@ -356,17 +380,9 @@ att_gt <- function(yname,
stop("Must provide idname when panel = TRUE. Set panel = FALSE for repeated cross sections.")
}

# Validate alp (significance level)
if (!is.numeric(alp) || length(alp) != 1 || is.na(alp) || alp <= 0 || alp >= 1) {
stop("alp must be a single number strictly between 0 and 1.")
}

# Validate biters (number of bootstrap iterations) when the bootstrap is used
if (bstrap) {
if (!is.numeric(biters) || length(biters) != 1 || is.na(biters) ||
biters < 1 || biters != round(biters)) {
stop("biters must be a single positive whole number.")
}
validate_positive_whole_number(biters, "biters")
}

# Warn users about anticipation and never-treated units
Expand Down
Loading
Loading