diff_diff.TreatmentDoseShape#
- class diff_diff.TreatmentDoseShape[source]#
Bases:
objectDistributional shape of a continuous treatment dose.
Populated on
PanelProfileonly whentreatment_type == "continuous";Noneotherwise. Most fields are descriptive distributional context.profile_panel only sees the dose column, not the separate
first_treatcolumnContinuousDiD.fit()consumes. In the canonicalContinuousDiDsetup (Callaway, Goodman-Bacon, Sant’Anna 2024) the doseD_iis time-invariant per unit (D_i = 0for never-treated,D_i > 0constant across all periods for treated unit i) andfirst_treatis a separate column the caller supplies — not derived from the dose column. Under that canonical setup, several profile-side facts on the dose column predictContinuousDiD.fit()outcomes:PanelProfile.has_never_treated == True(some unit has dose 0 in every period). Predicts the estimator’sP(D=0) > 0requirement under the defaultcontrol_group="never_treated"/control_group="not_yet_treated"(the canonical setup tiesfirst_treat == 0toD_i == 0). When it isFalse,control_group="lowest_dose"(Remark 3.1) is the route — the lowest-dose group becomes the comparison (needs a mass point at the minimum dose) — so failure of (1) no longer rulesContinuousDiDout; see routing notes below.PanelProfile.treatment_varies_within_unit == False(per-unit full-path dose constancy on the dose column). This IS the actual fit-time gate, matchingContinuousDiD.fit()’sdf.groupby(unit)[dose].nunique() > 1rejection at line 222-228; holds regardless offirst_treat.TruerulesContinuousDiDout — for graded-adoption panels with dose changes useHeterogeneousAdoptionDiD.PanelProfile.is_balanced == True. Actual fit-time gate (continuous_did.py:329-338); notfirst_treat-dependent.Absence of the
duplicate_unit_time_rowsalert. The precompute path silently resolves duplicate(unit, time)cells via last-row-wins (continuous_did.py:818-823); not a fit-time raise. The agent must deduplicate before fit becauseContinuousDiDwill otherwise overwrite silently.treatment_dose.dose_min > 0(over non-zero doses). PredictsContinuousDiD.fit()’s strictly-positive-treated- dose requirement (raisesValueErroron negative dose forfirst_treat > 0units,continuous_did.py:287-294). Failure means some treated units have negative dose; see routing notes below.
Routing alternatives when (1) or (5) fails:
When (1) fails (no never-treated controls but all observed doses non-negative): use
control_group="lowest_dose"(Remark 3.1) if there is a mass point at the minimum dose (>= 2units atd_L) — the lowest-dose group becomes the comparison and the estimand isATT(d) - ATT(d_L). OtherwiseHeterogeneousAdoptionDiDIS a candidate for graded-adoption designs (HAD’s contract requires non-negative dose, satisfied here); linear DiD with the treatment as a continuous covariate is another.When (5) fails (negative treated doses):
HeterogeneousAdoptionDiDis not a fallback either — HAD raises on negative post-period dose (had.py:1450-1459, paper Section 2). Linear DiD with the treatment as a signed continuous covariate is the applicable routing alternative.Re-encoding the treatment column (shifting, absolute value, etc.) is an agent-side preprocessing choice that changes the estimand and is not documented in REGISTRY as a supported fallback; if the agent re-encodes to non-negative support, both
ContinuousDiDandHeterogeneousAdoptionDiDbecome candidates again on the re-encoded scale.Do not relabel positive- or negative-dose units as
first_treat == 0: that triggersContinuousDiD.fit()’s force-zero coercion path, which is implementation behavior for inconsistent inputs (e.g., an accidentally-nonzero row on a never-treated unit), not a documented routing option.
The agent must still validate the supplied
first_treatcolumn independently: it must contain at least onefirst_treat == 0unit (P(D=0) > 0), be non-negative integer-valued (or+inf/ 0 for never-treated), and be consistent with the dose column on per-unit treated/untreated status.profile_paneldoes not seefirst_treatand cannot validate it.has_zero_doseis a row-level fact (“at least one observation has dose == 0”); it is NOT a substitute forhas_never_treated, which is the unit-level field. A panel can havehas_zero_dose == True(pre-treatment zero rows) whilehas_never_treated == False(every unit eventually treated), in which case the standard-workflow agent would conclude no never-treated controls exist before callingContinuousDiD.fit().Methods
__init__(n_distinct_doses, has_zero_dose, ...)Attributes
n_distinct_doseshas_zero_dosedose_mindose_maxdose_mean- __init__(n_distinct_doses, has_zero_dose, dose_min, dose_max, dose_mean)#
- classmethod __new__(*args, **kwargs)#