diff_diff.ChaisemartinDHaultfoeuilleResults#
- class diff_diff.ChaisemartinDHaultfoeuilleResults[source]#
Bases:
objectResults from de Chaisemartin-D’Haultfoeuille (dCDH) Phase 1 estimation.
Phase 1 ships the contemporaneous-switch estimator
DID_M(=DID_1at horizonl = 1of the dynamic companion paper) plus the joiners- only / leavers-only views, the single-lag placeboDID_M^pl, and optionally the TWFE decomposition diagnostic (per-cell weights, fraction negative,sigma_fe).Notes
The analytical confidence interval is conservative under Assumption 8 (independent groups) of the dynamic companion paper, and exact only under iid sampling. This is documented as a deliberate deviation from “default nominal coverage” in the methodology registry.
For binary treatment in Phase 1, multi-switch groups (i.e., groups that switch treatment more than once) are dropped before estimation when
drop_larger_lower=True(the default), matching the RDIDmultiplegtDYNreference. The number of dropped groups is exposed vian_groups_dropped_crossers.Inference-method switch when bootstrap is enabled. The
overall_p_value/overall_conf_int(and joiners/leavers analogues) fields are populated by normal-theory inference from the cohort-recentered analytical SE whenn_bootstrap=0(the default). Whenn_bootstrap > 0, the same fields are populated by percentile-based bootstrap inference from the multiplier bootstrap distribution computed by_compute_dcdh_bootstrap(). The t-stat (overall_t_stat, etc.) is computed from the SE in both cases, since percentile bootstrap does not define an alternative t-stat semantic.event_study_effects[1],summary(),to_dataframe(),is_significant, andsignificance_starsall read from these top-level fields and therefore reflect the bootstrap inference automatically. The single-period placebo (L_max=None) still has NaN bootstrap fields; multi-horizon placebos (L_max >= 1) have valid bootstrap SE/CI/p viaplacebo_horizon_ses/cis/p_values. See the methodology registryNote (bootstrap inference surface)for the full contract and library precedent.- n_joiner_cells#
Total number of joiner switching
(g, t)cells across all periods. Each cell counted once. Equalssum_t (#{g : D_{g,t-1}=0, D_{g,t}=1}).- Type:
- n_joiner_obs#
Total raw observation count across joiner cells, summing
n_gtover the same set of cells. For balanced one-observation-per-cell panels this equalsn_joiner_cells; for individual-level inputs with multiple observations per(g, t)it can be larger.- Type:
- per_period_effects#
Per-period decomposition. Keys are period values; each value is a dict with the following keys:
"did_plus_t"(float): joiner effect at this period (0.0if no joiners or A11 violation)"did_minus_t"(float): leaver effect at this period"n_10_t"(int): joiner cell count"n_01_t"(int): leaver cell count"n_00_t"(int): stable-untreated cell count"n_11_t"(int): stable-treated cell count"did_plus_t_a11_zeroed"(bool): True when joiners exist but no stable-untreated controls (Assumption 11 violation, period contributes 0 to numerator with non-zero weight in denominator)"did_minus_t_a11_zeroed"(bool): mirror for leavers
- Type:
- twfe_weights#
Per-cell TWFE decomposition weights from Theorem 1 of de Chaisemartin & D’Haultfoeuille (2020). Columns:
group,time,weight. Computed on the FULL pre-filter cell sample passed by the user (the same input the standalonetwowayfeweights()function uses) — NOT the post-filter estimation sample described byoverall_attandgroups. Whenfit()drops groups via the ragged-panel ordrop_larger_lowerfilters,results.twfe_*andresults.overall_attdescribe different samples and aUserWarningis emitted; see REGISTRY.mdChaisemartinDHaultfoeuilleNote (TWFE diagnostic sample contract)for the rationale. Only populated whentwfe_diagnostic=True.- Type:
pd.DataFrame, optional
- twfe_fraction_negative#
Fraction of treated-cell weights that are negative.
> 0is the diagnostic for the heterogeneous-treatment-effect bias of the plain TWFE estimator on the FULL pre-filter cell sample (NOT the post-filter estimation sample). See thetwfe_weightsdocstring above for the sample contract.- Type:
float, optional
- twfe_sigma_fe#
Smallest standard deviation of per-cell treatment effects that could flip the sign of the plain TWFE estimator (Corollary 1 of the AER 2020 paper). Computed on the FULL pre-filter cell sample.
- Type:
float, optional
- twfe_beta_fe#
The plain TWFE coefficient computed on the FULL pre-filter cell sample, for comparison with
overall_att. Note that the two are computed on different samples whenfit()filters drop groups — see thetwfe_weightsdocstring above for the sample contract.- Type:
float, optional
- n_switcher_cells#
When
L_max=None: number of switching(g, t)cells (N_S = sum_t (n_10_t + n_01_t)). WhenL_max >= 1: number of eligible switcher groups at horizon 1 (N_1). Previously this field always held the cell count; forL_max >= 1it was repurposed to hold the per-group count that matches theDID_1estimand. Originally equals once regardless of how many original observations fed into it. This is theN_Sdenominator ofDID_Munder the library’s equal-cell weighting convention (cell counts, not within-cell observation sums). The AER 2020 paper’s Equation 3 definesN_{d,d',t} = sum_g N_{g,t}(observation sums); the library’s choice is a documented deviation - seedocs/methodology/REGISTRY.md## ChaisemartinDHaultfoeuilleL517 for the full Note.- Type:
- n_groups_dropped_crossers#
Number of groups dropped because they were multi-switch (matches R’s
drop_larger_lower=TRUEbehavior).0whendrop_larger_lower=Falseor no crossers exist.- Type:
- n_groups_dropped_singleton_baseline#
Number of groups whose baseline
D_{g,1}is unique in the post-drop panel (footnote 15 of the dynamic paper). They are excluded from the cohort-recentered VARIANCE computation only — they remain in the point-estimate sample as period-based stable controls (see REGISTRY.mdChaisemartinDHaultfoeuillefor the period-vs-cohort deviation that makes this distinction matter).- Type:
- n_groups_dropped_never_switching#
Number of groups with
S_g = 0(never switched). Reported for backwards compatibility only. Per the Round 2 full influence-function fix, never-switching groups are NOT excluded from the variance: they contribute via their stable-control roles in the per-period IF formula. The field name retains “dropped” for API stability but no actual exclusion happens.- Type:
- event_study_effects#
Populated with horizon
1whenL_max=None, or horizons1..L_maxwhenL_max >= 1. WhenL_max >= 1, uses the per-groupDID_{g,l}path; whenL_max=None, uses the per-periodDID_Mpath.- Type:
dict, optional
- sup_t_bands#
Sup-t simultaneous confidence-band metadata for the OVERALL event-study surface. Holds
{"crit_value": float, "alpha": float, "n_bootstrap": int, "method": str}. Populated whenn_bootstrap > 0AND there are at least 2 valid horizons with finite bootstrap SE > 0 AND a strict majority (more than 50%) of sup-t draws are finite. The band itself is written per-horizon ascband_conf_intonevent_study_effects[l].Noneotherwise. Python-only library extension; Rdid_multiplegt_dynprovides no joint / sup-t bands.- Type:
dict, optional
- covariate_residuals#
DID^Xfirst-stage diagnostics: per-baselinetheta_hat,n_obs, andr_squared. Populated whencontrolsis set.- Type:
pd.DataFrame, optional
- linear_trends_effects#
Cumulated
DID^{fd}level effectsdelta^{fd}_l. Keyed by horizon. Populated whentrends_linear=True.- Type:
dict, optional
- heterogeneity_effects#
Per-horizon heterogeneity test results
beta^{het}_l. Populated whenheterogeneityis set.- Type:
dict, optional
- design2_effects#
Design-2 switch-in/switch-out descriptive summary. Populated when
design2=True.- Type:
dict, optional
- path_effects#
Per-path event-study effects keyed by observed treatment trajectory (tuple of int). Populated when
by_pathis a positive int ORpaths_of_interestis a list of int tuples at estimator construction. Each entry holds{"n_groups": int, "frequency_rank": int, "horizons": {l: {"effect", "se", "t_stat", "p_value", "conf_int", "n_obs"}}}forl = 1..L_max. Underpaths_of_interest, dict-insertion order matches the user- specified path order;frequency_rankis the within- selected-paths rank by descending observed-group count (decoupled from iteration order).- Type:
dict, optional
- path_placebo_event_study#
Per-path backward-horizon placebos
DID^{pl}_{path, l}forl = 1..L_max, keyed by observed treatment trajectory (tuple of int). Inner dict keys are negative ints (-lfor lagl) to mirror theplacebo_event_studyconvention so a unified{**path_effects[p]["horizons"], **path_placebo_event_study[p]}view is well-formed across forward and backward horizons. Each inner entry holds{"effect", "se", "t_stat", "p_value", "conf_int", "n_obs"}. Populated when (by_pathis a positive int ORpaths_of_interestis set) ANDplacebo=TrueANDL_max >= 1. Empty-state contract mirrorspath_effects:Nonewhenby_path / paths_of_interest + placebowas not requested;{}when requested but no observed path has a complete window[F_g-1, F_g-1+L_max]within the panel (the same regime wherepath_effectsreturns{}, with the sameUserWarningat fit-time). Downstream callers should distinguish the two states. Inherits the cross-path cohort-sharing SE deviation from R documented forpath_effects. See REGISTRY.mdNote (Phase 3 by_path ...)→ “Per-path placebos”.- Type:
dict, optional
- path_heterogeneity_effects#
Per-path heterogeneity test results (Web Appendix Section 1.5, Lemma 7) when
heterogeneityis set AND (by_path=korpaths_of_interest=[(...), ...]) is set. Inner dict keyed by horizon directly (no"horizons"wrapper); each entry holds{"beta", "se", "t_stat", "p_value", "conf_int", "n_obs"}, wherebetais the heterogeneity coefficient on the path- restricted switcher subsample - plain OLS on the non-survey path, WLS-on-pweights undersurvey_design. Cohort dummies in the design matrix absorb baseline by construction. Empty-state contract mirrorspath_effects:Nonewhen not requested;{}when requested but no path has eligible switchers. Mirrors Rdid_multiplegt_dyn(..., by_path, predict_het)per-by_level dispatch. See REGISTRY.mdNote (Phase 3 by_path ...)→ “Per-path heterogeneity testing”.- Type:
dict, optional
- path_cumulated_event_study#
Per-path cumulated level effects
delta_{path, l} = sum_{l'=1..l} DID^{fd}_{path, l'}forl = 1..L_max, keyed by observed treatment trajectory (tuple of int). Inner dict is keyed by horizon directly (no"horizons"wrapper); each entry holds{"effect", "se", "t_stat", "p_value", "conf_int", "n_obs"}. Populated when (by_pathis a positive int ORpaths_of_interestis set) ANDtrends_linear=TrueANDL_max >= 1;Noneotherwise. Mirrors the globallinear_trends_effectscumulation: SE on the cumulated layer is the conservative upper bound (sum of per-horizon component SEs frompath_effects[path]["horizons"][l]["se"], NaN-consistent). Built AFTER bootstrap propagation so the cumulated SE / t / p / CI are derived from the FINAL post-bootstrap per-horizon SEs whenn_bootstrap > 0. Surfaced ascumulated_effect/cumulated_secolumns onto_dataframe(level="by_path")(always-present, NaN-when- None) and as a per-path “Cumulated Level Effects” sub-section insummary(). See REGISTRY.mdNote (Phase 3 by_path ...)→ “Per-path linear-trends DID^{fd}”.- Type:
dict, optional
- path_sup_t_bands#
Per-path joint sup-t simultaneous-band metadata, keyed by observed treatment trajectory (tuple of int). Each entry holds
{"crit_value": float, "alpha": float, "n_bootstrap": int, "method": str, "n_valid_horizons": int}. Populated when (by_pathis a positive int ORpaths_of_interestis set) ANDn_bootstrap > 0. The band itself is applied per-horizon ascband_conf_intonpath_effects[path]["horizons"][l]and rendered ascband_lower/cband_uppercolumns onto_dataframe(level="by_path"). Empty-state contract:Nonewhen not requested (no bootstrap, or bothby_pathandpaths_of_interestareNone);{}when requested but no path passed both gates (>=2valid horizons with finite bootstrap SE> 0AND a strict majority — more than 50% — of finite sup-t draws). Bands cover joint inference WITHIN a single path across horizons; they do NOT provide simultaneous coverage across paths. Inherits the cross-path cohort-sharing SE deviation from R documented forpath_effects(the bootstrap SE used as the t-stat denominator carries the same deviation). Python-only library extension; Rdid_multiplegt_dynprovides no joint / sup-t bands at any surface. See REGISTRY.mdNote (Phase 3 by_path per-path joint sup-t bands).- Type:
dict, optional
- honest_did_results#
HonestDiD sensitivity analysis bounds (Rambachan & Roth 2023). Populated when
honest_did=Trueinfit()or by callingcompute_honest_did(results)post-hoc. Contains identified set bounds, robust confidence intervals, and breakdown analysis.- Type:
HonestDiDResults, optional
- survey_metadata#
Populated when
fit(..., survey_design=sd)is called;Noneotherwise. Carries the resolved survey design summary (weight_type, strata/PSU counts,df_survey, weight range, and replicate-method info when applicable).df_surveyis threaded into survey-aware inference (t-distribution at all analytical surfaces) and consumed bycompute_honest_did()to produce survey-aware critical values.- Type:
Any, optional
- bootstrap_results#
Bootstrap inference results when
n_bootstrap > 0.- Type:
DCDHBootstrapResults, optional
Methods
__init__(overall_att, overall_se, ...[, ...])print_summary([alpha])Print the formatted summary to stdout.
summary([alpha])Generate a formatted summary of dCDH estimation results.
to_dataframe([level])Convert results to a DataFrame at the requested level of aggregation.
Attributes
L_maxattcoef_varSE / abs(DID_M); NaN when DID_M is 0 or SE non-finite.
conf_intis_significantTrue iff overall
DID_Mp-value is belowalpha.p_valueplacebo_event_studysesignificance_starsSignificance stars for the overall
DID_M.t_stattrends_linear- __init__(overall_att, overall_se, overall_t_stat, overall_p_value, overall_conf_int, joiners_att, joiners_se, joiners_t_stat, joiners_p_value, joiners_conf_int, n_joiner_cells, n_joiner_obs, joiners_available, leavers_att, leavers_se, leavers_t_stat, leavers_p_value, leavers_conf_int, n_leaver_cells, n_leaver_obs, leavers_available, placebo_effect, placebo_se, placebo_t_stat, placebo_p_value, placebo_conf_int, placebo_available, per_period_effects, groups, time_periods, n_obs, n_treated_obs, n_switcher_cells, n_cohorts, n_groups_dropped_crossers, n_groups_dropped_singleton_baseline, n_groups_dropped_never_switching, event_study_effects=None, L_max=None, placebo_event_study=None, twfe_weights=None, twfe_fraction_negative=None, twfe_sigma_fe=None, twfe_beta_fe=None, alpha=0.05, normalized_effects=None, cost_benefit_delta=None, sup_t_bands=None, covariate_residuals=None, linear_trends_effects=None, trends_linear=None, heterogeneity_effects=None, design2_effects=None, path_effects=None, path_placebo_event_study=None, path_heterogeneity_effects=None, path_cumulated_event_study=None, path_sup_t_bands=None, honest_did_results=None, survey_metadata=None, bootstrap_results=None, _estimator_ref=None)#
- Parameters:
overall_att (float)
overall_se (float)
overall_t_stat (float)
overall_p_value (float)
joiners_att (float)
joiners_se (float)
joiners_t_stat (float)
joiners_p_value (float)
n_joiner_cells (int)
n_joiner_obs (int)
joiners_available (bool)
leavers_att (float)
leavers_se (float)
leavers_t_stat (float)
leavers_p_value (float)
n_leaver_cells (int)
n_leaver_obs (int)
leavers_available (bool)
placebo_effect (float)
placebo_se (float)
placebo_t_stat (float)
placebo_p_value (float)
placebo_available (bool)
per_period_effects (Dict[Any, Dict[str, Any]])
groups (List[Any])
time_periods (List[Any])
n_obs (int)
n_treated_obs (int)
n_switcher_cells (int)
n_cohorts (int)
n_groups_dropped_crossers (int)
n_groups_dropped_singleton_baseline (int)
n_groups_dropped_never_switching (int)
L_max (Optional[int])
twfe_weights (Optional[pd.DataFrame])
twfe_fraction_negative (Optional[float])
twfe_sigma_fe (Optional[float])
twfe_beta_fe (Optional[float])
alpha (float)
cost_benefit_delta (Optional[Dict[str, Any]])
sup_t_bands (Optional[Dict[str, Any]])
covariate_residuals (Optional[pd.DataFrame])
trends_linear (Optional[bool])
design2_effects (Optional[Dict[str, Any]])
path_effects (Optional[Dict[Tuple[int, ...], Dict[str, Any]]])
path_placebo_event_study (Optional[Dict[Tuple[int, ...], Dict[int, Dict[str, Any]]]])
path_heterogeneity_effects (Optional[Dict[Tuple[int, ...], Dict[int, Dict[str, Any]]]])
path_cumulated_event_study (Optional[Dict[Tuple[int, ...], Dict[int, Dict[str, Any]]]])
path_sup_t_bands (Optional[Dict[Tuple[int, ...], Dict[str, Any]]])
honest_did_results (Optional['HonestDiDResults'])
survey_metadata (Optional[Any])
bootstrap_results (Optional[DCDHBootstrapResults])
_estimator_ref (Optional[Any])
- Return type:
None
- classmethod __new__(*args, **kwargs)#