diff_diff.ChaisemartinDHaultfoeuilleResults#
- class diff_diff.ChaisemartinDHaultfoeuilleResults[source]#
Bases:
BaseResults,AggregationMixinResults 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_attandunits. 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
- units#
Unit identifiers in the post-filter sample. (The deprecated read-only alias
groupswarns and returns this list; removed in 4.0 — row M-114.)- Type:
- 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
- event_study_df#
Inference degrees-of-freedom PROVENANCE for the event-study and placebo surfaces: the single df every stored row’s
safe_inferencereceived. One scalar suffices because both surfaces are computed from the same design df (and are refreshed together to the final effective df under replicate weights).Nonewhen the rows used normal theory (no survey design), when the df was undefined, or when bootstrap overrode the stored inference with percentile values that never used a df.- Type:
float, 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 whencovariatesis 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, ...[, ...])aggregate(type[, weights, balance_e])Re-aggregate this fit without refitting.
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.
to_dict()Convert headline results to a dictionary.
Attributes
L_maxattcoef_varSE / abs(DID_M); NaN when DID_M is 0 or SE non-finite.
conf_intgroupsDeprecated alias for
units(removed in 4.0).is_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, units, 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, event_study_df=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]])
units (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])
event_study_df (Optional[float])
- Return type:
None
- classmethod __new__(*args, **kwargs)#