diff_diff.TreatmentDoseShape#

class diff_diff.TreatmentDoseShape[source]#

Bases: object

Distributional shape of a continuous treatment dose.

Populated on PanelProfile only when treatment_type == "continuous"; None otherwise. Most fields are descriptive distributional context.

profile_panel only sees the dose column, not the separate first_treat column ContinuousDiD.fit() consumes. In the canonical ContinuousDiD setup (Callaway, Goodman-Bacon, Sant’Anna 2024) the dose D_i is time-invariant per unit (D_i = 0 for never-treated, D_i > 0 constant across all periods for treated unit i) and first_treat is a separate column the caller supplies — not derived from the dose column. Under that canonical setup, several profile-side facts on the dose column predict ContinuousDiD.fit() outcomes:

  1. PanelProfile.has_never_treated == True (some unit has dose 0 in every period). Predicts the estimator’s P(D=0) > 0 requirement under both control_group="never_treated" and control_group="not_yet_treated" (Remark 3.1 lowest-dose-as-control not yet implemented), because the canonical setup ties first_treat == 0 to D_i == 0. Failure means no never-treated controls exist on the dose column; see routing notes below.

  2. PanelProfile.treatment_varies_within_unit == False (per-unit full-path dose constancy on the dose column). This IS the actual fit-time gate, matching ContinuousDiD.fit()’s df.groupby(unit)[dose].nunique() > 1 rejection at line 222-228; holds regardless of first_treat. True rules ContinuousDiD out — for graded-adoption panels with dose changes use HeterogeneousAdoptionDiD.

  3. PanelProfile.is_balanced == True. Actual fit-time gate (continuous_did.py:329-338); not first_treat-dependent.

  4. Absence of the duplicate_unit_time_rows alert. 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 because ContinuousDiD will otherwise overwrite silently.

  5. treatment_dose.dose_min > 0 (over non-zero doses). Predicts ContinuousDiD.fit()’s strictly-positive-treated- dose requirement (raises ValueError on negative dose for first_treat > 0 units, 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): ContinuousDiD does not apply (Remark 3.1 lowest-dose-as-control is not implemented). HeterogeneousAdoptionDiD IS 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): HeterogeneousAdoptionDiD is 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 ContinuousDiD and HeterogeneousAdoptionDiD become candidates again on the re-encoded scale.

  • Do not relabel positive- or negative-dose units as first_treat == 0: that triggers ContinuousDiD.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_treat column independently: it must contain at least one first_treat == 0 unit (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_panel does not see first_treat and cannot validate it.

has_zero_dose is a row-level fact (“at least one observation has dose == 0”); it is NOT a substitute for has_never_treated, which is the unit-level field. A panel can have has_zero_dose == True (pre-treatment zero rows) while has_never_treated == False (every unit eventually treated), in which case the standard-workflow agent would conclude no never-treated controls exist before calling ContinuousDiD.fit().

Methods

__init__(n_distinct_doses, has_zero_dose, ...)

Attributes

n_distinct_doses

has_zero_dose

dose_min

dose_max

dose_mean

__init__(n_distinct_doses, has_zero_dose, dose_min, dose_max, dose_mean)#
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)#