Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0c2ac24
feat: add R twfeweights compatibility utilities
Aug 6, 2026
49409a8
feat: add ptetools panel primitives
Aug 6, 2026
b38973d
feat: add linear bad-control imputation
Aug 6, 2026
846d218
test: add R parity checks for compatibility layers
Aug 6, 2026
1a34285
feat: add parametric bad-control DR estimator
Aug 6, 2026
19481ff
feat: add cross-fitted bad-control DR path
Aug 6, 2026
b8eba41
feat: add generic ptetools ATT loop
Aug 6, 2026
54ee9cc
feat: add covariate-aware ptetools ATT path
Aug 6, 2026
3f5c911
feat: add ptetools empirical bootstrap
Aug 6, 2026
648b5d5
feat: add twfeweights balance diagnostics
Aug 6, 2026
4378447
feat: add two-period twfe implicit weights
Aug 6, 2026
a3dc2db
feat: add bad-controls simulation DGPs
Aug 6, 2026
1790c77
feat: add binary bad-control imputation
Aug 6, 2026
70c5747
feat: add ptetools repeated cross-sections
Aug 6, 2026
003b878
feat: add full-history ptetools subsets
Aug 6, 2026
79960b5
feat: add two-period twfeweights AIPW path
Aug 6, 2026
746592a
feat: add ptetools convenience wrappers
Aug 6, 2026
31e542f
feat: add multi-period implicit twfe decomposition
Aug 6, 2026
51080ae
fix: align ptetools aggregation weights
Aug 6, 2026
3da0258
feat: add staggered bad-control imputation
Aug 6, 2026
821438b
feat: expand ptetools result surfaces
Aug 6, 2026
cdf0480
feat: add staggered bad-control DR loops
Aug 6, 2026
13a1def
feat: add bad-control overlap safeguards
Aug 6, 2026
442896e
feat: add bad-control bootstrap inference
Aug 6, 2026
ac4daa3
feat: support bad-control covariate changes
Aug 6, 2026
51c8116
feat: extend DR covariate change support
Aug 6, 2026
f12ee1c
feat: add local twfe group-time weights
Aug 6, 2026
c961c9b
feat: add ptetools bootstrap confidence intervals
Aug 6, 2026
e755d45
feat: add twfe covariate balance diagnostics
Aug 6, 2026
179969f
feat: add aipw covariate balance diagnostics
Aug 6, 2026
280a947
feat: add multiperiod balance summaries
Aug 6, 2026
529a513
feat: add twfeweights object factories
Aug 6, 2026
f275468
feat: add multiperiod implicit aipw weights
Aug 6, 2026
b5737f2
feat: add twfeweights post-lasso did
Aug 6, 2026
68b7600
feat: add post-lasso regression adjustment
Aug 6, 2026
f311c33
feat: add ptetools critical value checks
Aug 6, 2026
d491869
feat: add ptetools result factories
Aug 6, 2026
20d6e62
feat: expose ptetools bootstrap APIs
Aug 6, 2026
5bd7aad
feat: add ptetools constructor aliases
Aug 6, 2026
dc55cc5
chore: stabilize post-lasso sklearn options
Aug 6, 2026
193f5db
feat: add ptetools aggregation dispatch aliases
Aug 6, 2026
259c6d6
feat: add ptetools RCS main loop
Aug 6, 2026
553fdea
fix: stabilize ptetools RCS bootstrap
Aug 6, 2026
47f07d6
feat: add ptetools dose result surface
Aug 6, 2026
c8f256d
feat: port ptetools process_dose_gt with splines2-compatible basis
Aug 6, 2026
0adee2c
feat: complete ptetools QTT/QoTT block and R-compat surfaces
Aug 7, 2026
440e1c3
docs: note matplotlib-vs-ggplot deviation for ptetools plotting wrappers
Aug 7, 2026
2122117
feat: cross-fit parametric DR bad-control score with shared fold ingress
Aug 7, 2026
f1751d0
docs: record dropped-parity surfaces for twfeweights post-lasso + imp…
Aug 7, 2026
133410e
fix: pte() influence surface zero-pads off-support and scales by (n/n…
Aug 7, 2026
fe7de51
docs: add quality_reports/ tracking for twfeweights-r-compat port
Aug 7, 2026
db8b189
docs: add tutorial 28 for badcontrols/twfeweights/ptetools R ports
Aug 7, 2026
fd94391
docs: register R-package ports in llms catalog, changelog, and doc-deps
Aug 7, 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
117 changes: 117 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,123 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- **R/Python parity fixtures for the new compatibility layers.** The
`twfeweights`, `ptetools`, and linear `badcontrols` paths now execute the
installed R packages on identical CSV fixtures and compare point estimates,
weights, and standard errors at `1e-8` tolerance.
- **Initial R `badcontrols` port.** Added the explicit two-period
``didbc`` / ``imputation_bad_control`` API, the parametric doubly robust
score, random-forest cross-fitting path, influence-function output, and ATT
extraction helper. Unknown nuisance methods fail closed.
- Added ``simulate_bad_controls`` with dgp1--dgp5, continuous/binary bad
controls, and known group-time treatment effects.
- Binary bad-control imputation now uses the logistic first stage and
Bernoulli-information influence-function correction with R parity coverage.
- Added staggered linear imputation by looping over estimable ``(g,t)`` cells
and aggregating them with cohort/post-period weights.
- Added staggered parametric and random-forest DR cell loops with the same
cohort aggregation surface.
- Added DR overlap and minimum-treated-group safeguards with explicit
imputation fallback.
- Added seeded cohort-stratified empirical bootstrap, bootstrap SE, and
percentile confidence intervals to ``didbc``.
- Added imputation support for general and bad-control covariate changes.
- Extended the same covariate-change design matrices to parametric and
random-forest DR nuisance models.
- **R `ptetools` compatibility primitives.** Added panel setup, two-period
group-time subsetting, ATT(g,t) influence-function containers, unadjusted
DID estimation, the generic ``pte`` group-time loop, and group/dynamic
aggregation building blocks. Pre-period covariates now use a conditional
AIPW path in ``did_attgt`` and ``pte``. ``pte`` also supports a seeded
unit-level empirical bootstrap for overall ATT inference.
- Added repeated-cross-section ``two_by_two_rcs_subset`` and
``did_rcs_attgt`` compatibility primitives.
- Added full-history ``keep_all_untreated_subset`` and
``keep_all_pretreatment_subset`` helpers for multi-period estimators.
- Added ``setup_pte_basic``, ``pte_default``, and ``pte_attgt`` convenience
wrappers matching common R ``ptetools`` entry points.
- Corrected dynamic/group aggregation to use treated-unit cohort weights and
normalize dynamic weights separately at each event time.
- Expanded ``PTEResults`` and ``PTEAggregateResult`` with summary,
serialization, weight tables, and bootstrap-distribution surfaces.
- Added percentile ``overall_conf_int`` to bootstrapped ``PTEResults``.
- Added ``crit_val_checks`` simultaneous-band fallback utility.
- Added ``group_time_att`` and ``aggte_obj`` result-container factories.
- Added the R-compatible ``attgt_noif`` result-container factory for custom
group-time estimators without influence functions.
- Added ``covid_attgt``, reusing the DRDID-validated doubly-robust core for
Callaway--Li levels and first-difference outcomes.
- Added ``ggpte_cont`` as a matplotlib/Plotly-compatible wrapper for
``DoseResult`` curves.
- Added ``ggpte`` as an event-study plotting wrapper for ``PTEResults``.
- Extended ``PTEResults.aggregate()`` with influence-function standard errors,
normal-based confidence intervals, and ``to_dataframe(level=...)`` views for
ATT(g,t), group, and dynamic results.
- Added ``plot_qtt`` for overall and dynamic QTT visualization.
- Added ``mp_weights_obj`` and broader ``ggtwfeweights`` result support, plus
the R-style ``dr_ml_attgt`` bad-controls cell wrapper.
- Added callback hooks to ``pte`` for custom setup, subset, ATT(g,t), and
aggregation functions.
- Added explicit Python counterparts for the R ``autoplot``/``plot`` methods
on PTE, QTT, empirical-bootstrap, and dose-response result objects.
- Added DRDID covariate adjustment to ``did_rcs_attgt`` and optional
multiplier-bootstrap bands to dynamic ``PTEResults.aggregate()`` results.
- Added ``process_att_gt`` and ``attgt_pte_aggregations`` aggregation aliases.
- **Cross-fit parametric DR bad-control score** matching R's `dr_ml_attgt`:
per-arm fold splits, OLS/logit cross-fitted nuisances, a `fold_ids` ingress
for exact shared-fold R/Python parity, and the `max(propensity)>0.99`
imputation fallback.
- Documented dropped-parity surfaces as REGISTRY deviations: `implicit_twfe_weights`
(R `fixest` segfaults on the parity fixture) and `did_post_lasso` (incomplete R
source with a `browser()` debug path) are verified for internal consistency, not
byte-level R parity.
- Added tutorial 28 (`docs/tutorials/28_did_badcontrols.ipynb`) demonstrating the
R-package ports end-to-end: `pte` group-time ATT on the Callaway--Sant'Anna panel,
`twfe_weights` negative-weight decomposition, and `didbc` bad-control estimates on
`simulate_bad_controls` output.
- Added ``DoseResult``, ``dose_obj``, and ``pte_dose_results`` containers for
dose-response outputs.
- Added ``process_dose_gt``, which combines per-cell dose results into
ATT(d) / ACRT(d) curves and overall ATT/ACRT with multiplier-bootstrap
standard errors and (optionally) simultaneous critical values, plus a
``bspline_basis`` helper that reproduces ``splines2::bSpline``/``dbs``
design matrices and ``mboot_se_and_crit`` for R-style sup-t inference.
- **R `ptetools` QTT/QoTT compatibility.** Ported the quantile-treatment-effects
machinery: ``compute_pte`` runs the ``(g,t)`` loop over F0/F1 cumulative
distribution functions, ``qtt_pte_aggregations`` / ``qott_pte_aggregations``
mix per-cell CDFs into overall / dynamic / group quantile curves (with a
documented fix for R's latent merge-reorder misalignment), and
``qtt_empirical_bootstrap`` derives pointwise and uniform (sup-t) confidence
bands from seeded unit-level block bootstrap. ``pte_qtt`` / ``PTEQTTResult``
hold the resulting curves and ``block_boot_sample`` resamples a panel by unit.
Numeric parity with R ``ptetools`` verified on single- and two-cohort panels.
- Added ``panel=False`` repeated-cross-section support to the generic ``pte``
loop.
- Added ``panel=False`` repeated-cross-section support to the generic ``pte`` loop.
- Added public ``panel_empirical_bootstrap`` and ``mboot2`` bootstrap helpers.
- Added R-style ``pte_params``, ``pte_results``, and ``pte_emp_boot`` aliases.
- **R `twfeweights` compatibility layer.** Added `twfe_weights`, `attO_weights`,
`att_simple_weights`, `MPWeightsResult`, and `ggtwfeweights` for decomposing
ATT(g,t) tables with R-compatible output columns and normalization rules,
plus effective-sample-size and covariate-spread diagnostics.
- Added the two-period ``two_period_reg_weights`` Frisch-Waugh-Lovell
implicit-weight calculation with time-varying changes and time-invariant
covariates.
- Added the two-period ``two_period_aipw_weights`` ATT and implicit control
weights calculation with optional pre-period covariates.
- Added the no-covariate multi-period ``implicit_twfe_weights`` group-time
decomposition and pre-trends contribution surface.
- Added local ``GTWeightsResult`` objects and
``implicit_twfe_weights_gt`` / ``combine_twfe_weights_gt`` accessors.
- Added ``twfe_cov_bal`` and ``twfe_cov_bal_gt`` covariate-balance diagnostics.
- Added ``aipw_cov_bal`` and ``aipw_cov_bal_gt`` balance diagnostics.
- Added ``mp_covariate_bal_summary_helper`` for weighted multi-period balance
summaries.
- Added ``gt_weights`` and ``two_period_covs_obj`` result-object factories.
- Added ``did_post_lasso`` with post-Lasso outcome/propensity selection and
normalized AIPW influence-function inference.
- Added ``did_post_lasso_ra`` outcome-only regression-adjustment variant.
- Added multi-period ``implicit_aipw_weights`` group-time decomposition.
- **ContinuousDiD post-fit `aggregate()` - a MIXED view/recompute adopter**
(v4 program 2(b) PR-3c; ledger row [M-025]). `ContinuousDiD.fit(aggregate=)`
is deprecated (`FutureWarning`; removed in 4.0, and the no-underscore
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
- [Honest DiD](https://diff-diff.readthedocs.io/en/stable/api/honest_did.html) - Rambachan & Roth (2023) sensitivity analysis: robust CI under PT violations, breakdown values
- [Pre-Trends Power Analysis](https://diff-diff.readthedocs.io/en/stable/api/pretrends.html) - Roth (2022) minimum detectable violation and power curves
- [Power Analysis](https://diff-diff.readthedocs.io/en/stable/api/power.html) - analytical and simulation-based MDE, sample size, power curves for study design
- [TWFE ATT(g,t) weights](https://diff-diff.readthedocs.io/en/stable/api/twfeweights.html) - R `twfeweights`-compatible decompositions for TWFE, overall ATT, and simple ATT weights
- [MMM Calibration Export](https://diff-diff.readthedocs.io/en/stable/api/mmm.html) - convert experiment results into MMM calibration inputs: PyMC-Marketing lift-test frames and Google Meridian lognormal ROI priors
- Conley spatial HAC SE (`vcov_type="conley"`) on cross-sectional `LinearRegression` / `compute_robust_vcov` plus panel `DifferenceInDifferences` / `MultiPeriodDiD` / `TwoWayFixedEffects` (with `conley_lag_cutoff` for within-unit Bartlett temporal HAC) - Conley (1999) spatial-correlation-aware SEs with parity vs R `conleyreg` on cross-sectional + panel fixtures, optional combined spatial + cluster product kernel via explicit `cluster=`, auto-activating sparse k-d-tree fast path for `n > 5_000`

Expand Down
193 changes: 193 additions & 0 deletions diff_diff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@
Comparison2x2,
bacon_decompose,
)
from diff_diff.badcontrols import (
BadControlsResult,
didbc,
dr_ml_attgt,
dr_ml_bad_control,
extract_att,
imputation_bad_control,
simulate_bad_controls,
staggered_dr_bad_control,
staggered_imputation_bad_control,
)
from diff_diff.business_report import (
BUSINESS_REPORT_SCHEMA_VERSION,
BusinessContext,
Expand Down Expand Up @@ -216,6 +227,65 @@
TreatmentDoseShape,
profile_panel,
)
from diff_diff.ptetools import (
ATTGTResult,
DoseResult,
GTDataFrame,
PTEAggregateResult,
PTEParams,
PTEQTTResult,
PTEResults,
TwoByTwoSubset,
aggte_obj,
attgt_if,
attgt_noif,
attgt_pte_aggregations,
autoplot_dose_obj,
autoplot_pte_emp_boot,
autoplot_pte_qtt,
autoplot_pte_results,
block_boot_sample,
bspline_basis,
compute_pte,
covid_attgt,
crit_val_checks,
did_attgt,
did_rcs_attgt,
dose_obj,
ggpte,
ggpte_cont,
group_time_att,
gt_data_frame,
keep_all_pretreatment_subset,
keep_all_untreated_subset,
mboot2,
mboot_se_and_crit,
overall_weights,
panel_empirical_bootstrap,
plot_dose_obj,
plot_pte_emp_boot,
plot_pte_qtt,
plot_pte_results,
plot_qtt,
process_att_gt,
process_dose_gt,
pte,
pte_aggte,
pte_attgt,
pte_default,
pte_dose_results,
pte_emp_boot,
pte_params,
pte_qtt,
pte_results,
qott_pte_aggregations,
qtt_empirical_bootstrap,
qtt_pte_aggregations,
setup_pte,
setup_pte_basic,
two_by_two_rcs_subset,
two_by_two_subset,
)
from diff_diff.rdd import (
RegressionDiscontinuity,
RegressionDiscontinuityResults,
Expand Down Expand Up @@ -283,6 +353,38 @@
TROPResults,
trop,
)
from diff_diff.twfeweights import (
GTWeightsResult,
ImplicitAIPWResult,
ImplicitTWFEResult,
MPWeightsResult,
PostLassoResult,
TwoPeriodCovariatesResult,
aipw_cov_bal,
aipw_cov_bal_gt,
att_simple_weights,
attO_weights,
combine_twfe_weights_gt,
did_post_lasso,
did_post_lasso_ra,
effective_sample_size,
frac_treated_extreme,
ggtwfeweights,
gt_weights,
implicit_aipw_weights,
implicit_twfe_weights,
implicit_twfe_weights_gt,
log_ratio_sd,
mp_covariate_bal_summary_helper,
mp_weights_obj,
pooled_sd,
twfe_cov_bal,
twfe_cov_bal_gt,
twfe_weights,
two_period_aipw_weights,
two_period_covs_obj,
two_period_reg_weights,
)
from diff_diff.two_stage import (
TwoStageBootstrapResults,
TwoStageDiD,
Expand Down Expand Up @@ -416,6 +518,37 @@
"TWFEWeightsResult",
"chaisemartin_dhaultfoeuille",
"twowayfeweights",
# R twfeweights compatibility
"MPWeightsResult",
"PostLassoResult",
"ImplicitTWFEResult",
"ImplicitAIPWResult",
"GTWeightsResult",
"TwoPeriodCovariatesResult",
"twfe_weights",
"twfe_cov_bal",
"twfe_cov_bal_gt",
"aipw_cov_bal",
"aipw_cov_bal_gt",
"mp_covariate_bal_summary_helper",
"mp_weights_obj",
"attO_weights",
"att_simple_weights",
"ggtwfeweights",
"effective_sample_size",
"pooled_sd",
"log_ratio_sd",
"frac_treated_extreme",
"gt_weights",
"did_post_lasso",
"did_post_lasso_ra",
"two_period_reg_weights",
"implicit_twfe_weights",
"implicit_aipw_weights",
"implicit_twfe_weights_gt",
"combine_twfe_weights_gt",
"two_period_aipw_weights",
"two_period_covs_obj",
# WooldridgeDiD (ETWFE)
"WooldridgeDiD",
"WooldridgeDiDResults",
Expand Down Expand Up @@ -502,6 +635,57 @@
"PreTrendsPowerCurve",
"compute_pretrends_power",
"compute_mdv",
# ptetools compatibility primitives
"PTEParams",
"GTDataFrame",
"TwoByTwoSubset",
"ATTGTResult",
"PTEAggregateResult",
"PTEResults",
"DoseResult",
"setup_pte",
"setup_pte_basic",
"gt_data_frame",
"keep_all_pretreatment_subset",
"keep_all_untreated_subset",
"two_by_two_subset",
"two_by_two_rcs_subset",
"attgt_if",
"attgt_noif",
"covid_attgt",
"aggte_obj",
"autoplot_dose_obj",
"autoplot_pte_emp_boot",
"autoplot_pte_qtt",
"autoplot_pte_results",
"attgt_pte_aggregations",
"did_attgt",
"crit_val_checks",
"group_time_att",
"ggpte",
"ggpte_cont",
"process_att_gt",
"process_dose_gt",
"bspline_basis",
"mboot_se_and_crit",
"pte_attgt",
"did_rcs_attgt",
"dose_obj",
"overall_weights",
"mboot2",
"panel_empirical_bootstrap",
"pte_aggte",
"pte",
"pte_default",
"pte_dose_results",
"pte_params",
"plot_qtt",
"plot_dose_obj",
"plot_pte_emp_boot",
"plot_pte_qtt",
"plot_pte_results",
"pte_results",
"pte_emp_boot",
# Survey support
"SurveyDesign",
"SurveyMetadata",
Expand Down Expand Up @@ -590,6 +774,15 @@
"Diagnostic",
"EventStudyResults",
"AggregationResult",
"BadControlsResult",
"didbc",
"dr_ml_bad_control",
"dr_ml_attgt",
"extract_att",
"imputation_bad_control",
"simulate_bad_controls",
"staggered_imputation_bad_control",
"staggered_dr_bad_control",
]

# Agent-facing entrypoints surface first in dir(diff_diff). LLM agents
Expand Down
7 changes: 7 additions & 0 deletions diff_diff/_deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ class _NotSupplied:
def __repr__(self) -> str: # pragma: no cover - debugging aid
return "<not supplied>"

def __copy__(self) -> "_NotSupplied":
return self

def __deepcopy__(self, memo: dict[int, object]) -> "_NotSupplied":
memo[id(self)] = self
return self


NOT_SUPPLIED = _NotSupplied()

Expand Down
Loading