Source code for lwdid.exceptions

"""
Exception hierarchy for the lwdid package.

Provides a structured exception hierarchy for difference-in-differences
estimation. All exceptions inherit from LWDIDError, enabling unified
error handling across the package.

The hierarchy covers parameter validation, data insufficiency, time series
requirements, randomization inference, visualization, and staggered designs.
"""


[docs] class LWDIDError(Exception): """ Base exception class for all lwdid package errors. All custom exceptions in the lwdid package inherit from this class, providing a common base for unified error handling. """ pass
[docs] class InvalidParameterError(LWDIDError): """ Exception raised when input parameter validation fails. This is a general exception for invalid parameter values that do not fall into more specific categories. Common triggers include: - Invalid data types for control variables - Cluster variable specified without vce='cluster' - Other parameter constraint violations See Also -------- InvalidRollingMethodError : For invalid rolling method values. InvalidVCETypeError : For invalid variance estimator types. """ pass
[docs] class InvalidRollingMethodError(InvalidParameterError): """ Exception raised when the rolling parameter has an invalid value. The rolling parameter must be one of: 'demean', 'detrend', 'demeanq', or 'detrendq'. This exception is raised during input validation when an unsupported transformation method is specified. See Also -------- InvalidParameterError : Parent class for parameter validation errors. """ pass
[docs] class InvalidVCETypeError(InvalidParameterError): """ Exception raised when the vce parameter has an invalid value. The vce (variance-covariance estimator) parameter must be one of: None, 'robust', 'hc0', 'hc1', 'hc2', 'hc3', 'hc4', or 'cluster'. This exception is raised during estimation when an unsupported variance estimator type is specified. See Also -------- InvalidParameterError : Parent class for parameter validation errors. """ pass
[docs] class UnbalancedPanelError(LWDIDError): """ Exception raised when balanced panel is required but data is unbalanced. This exception is raised when ``balanced_panel='error'`` is specified and the panel data contains units with different numbers of observations. Attributes ---------- min_obs : int Minimum observations per unit in the panel. max_obs : int Maximum observations per unit in the panel. n_incomplete_units : int Number of units with fewer than max_obs observations. Notes ----- Unbalanced panels arise when units have different numbers of observed time periods. Under standard selection assumptions, this is acceptable provided that missingness depends only on time-invariant unit heterogeneity and not on time-varying shocks. Users may want to enforce balanced panels for sensitivity analysis or when the selection mechanism is questionable. See Also -------- LWDIDError : Base exception class. diagnose_selection_mechanism : Diagnostic tools for selection bias. """ def __init__( self, message: str, min_obs: int, max_obs: int, n_incomplete_units: int, ): super().__init__(message) self.min_obs = min_obs self.max_obs = max_obs self.n_incomplete_units = n_incomplete_units
[docs] class InsufficientDataError(LWDIDError): """ Exception raised when sample size is insufficient for estimation. This is a general exception for data insufficiency issues. More specific subclasses indicate the exact nature of the insufficiency (e.g., no treated units, no control units, insufficient pre-periods). See Also -------- NoTreatedUnitsError : No units with treatment indicator d=1. NoControlUnitsError : No units with treatment indicator d=0. InsufficientPrePeriodsError : Insufficient pre-treatment periods. """ pass
[docs] class NoTreatedUnitsError(InsufficientDataError): """ Exception raised when there are no treated units in the data. Raised when all units have treatment indicator d=0 in the panel or regression sample. At least one treated unit (d=1) is required for difference-in-differences estimation. """ pass
[docs] class NoControlUnitsError(InsufficientDataError): """ Exception raised when there are no control units in the data. Raised when all units have treatment indicator d=1 in the panel or regression sample. At least one control unit (d=0) is required for difference-in-differences estimation. """ pass
[docs] class InsufficientPrePeriodsError(InsufficientDataError): """ Exception raised when pre-treatment periods are insufficient. Raised when the number of pre-treatment periods is too small for the chosen rolling transformation. Different methods have different minimum global and unit-level requirements: - demean: Each unit must have at least 1 pre-treatment observation. - detrend: (i) The panel must contain at least 2 pre-treatment periods in total (T0 >= 2); and (ii) Each unit must have at least 2 pre-treatment observations so that a unit-specific linear trend can be estimated. - demeanq: (i) The panel must contain at least 1 pre-treatment period in total (T0 >= 1); (ii) Each unit must have at least 1 pre-treatment observation; and (iii) For each unit, the number of pre-treatment observations must be at least the number of distinct pre-period quarters plus one, ``n_pre >= (#quarters_pre + 1)``, to ensure positive degrees of freedom when estimating quarterly fixed effects. - detrendq: (i) The panel must contain at least 2 pre-treatment periods in total (T0 >= 2); (ii) Each unit must have at least 2 pre-treatment observations; and (iii) For each unit, the number of pre-treatment observations must be at least 1 plus the number of distinct pre-period quarters, ``n_pre >= (1 + #quarters_pre)``, to avoid rank deficiency when estimating a linear trend with quarterly effects. This exception is also raised in staggered adoption designs when ``exclude_pre_periods`` is specified and the remaining pre-treatment periods are insufficient for the chosen transformation method. Attributes ---------- cohort : int or None The treatment cohort identifier that triggered the error. Only set in staggered adoption mode. available : int or None Number of pre-treatment periods remaining after exclusion. Only set when exclude_pre_periods is used. required : int or None Minimum number of pre-treatment periods required by the transformation method (1 for demean, 2 for detrend, etc.). excluded : int or None Number of periods excluded via exclude_pre_periods parameter. See Also -------- lwdid.transformations.apply_rolling_transform : Applies rolling transformations and enforces pre-period requirements. Notes ----- When the no-anticipation assumption may be violated, excluding periods immediately before treatment from the transformation window can provide robustness. For cohort g with ``exclude_pre_periods=k``, the pre-treatment window becomes {T_min, ..., g-1-k} instead of {T_min, ..., g-1}. """ def __init__( self, message: str, cohort: int | None = None, available: int | None = None, required: int | None = None, excluded: int | None = None, ): super().__init__(message) self.cohort = cohort self.available = available self.required = required self.excluded = excluded
[docs] class InsufficientQuarterDiversityError(InsufficientDataError): """ Exception raised when quarterly data requirements are not met. Raised for quarterly transformation methods (``demeanq``/``detrendq``) when quarter coverage is insufficient: post-treatment periods contain quarters that do not appear in the pre-treatment period for a given unit, preventing estimation of seasonal effects for those quarters. See Also -------- lwdid.validation.validate_quarter_coverage : Quarter coverage validation. """ pass
[docs] class TimeDiscontinuityError(LWDIDError): """ Exception raised when time series is discontinuous or post variable is non-monotone. This exception is raised in two scenarios: 1. **Time index discontinuity**: The time index has gaps, meaning there are missing periods in the sequence. A continuous time index is required for valid transformation and estimation. 2. **Post variable non-monotonicity**: The post-treatment indicator is not monotone non-decreasing in time, suggesting that the policy was reversed or suspended. The method assumes absorbing treatment states. Both scenarios violate the assumptions required for valid difference-in- differences estimation and will cause estimation to fail or produce incorrect results. See Also -------- LWDIDError : Base exception class. """ pass
[docs] class MissingRequiredColumnError(LWDIDError): """ Exception raised when input DataFrame is missing required columns. Required columns depend on the function call but typically include: - y: Outcome variable - d: Treatment indicator - ivar: Unit identifier - tvar: Time variable (single column or list of [year, quarter]) - post: Post-treatment indicator - controls: Control variables (if specified) See Also -------- LWDIDError : Base exception class. """ pass
[docs] class RandomizationError(LWDIDError): """ Exception raised when randomization inference (RI) fails. Common causes include invalid number of replications (rireps <= 0), missing required columns in input data, sample size too small for resampling (N < 3), invalid ri_method specification, or insufficient valid draws for reliable inference. See Also -------- LWDIDError : Base exception class. """ pass
[docs] class VisualizationError(LWDIDError): """ Exception raised for visualization-related errors. Common causes include plot data missing required columns (such as transformed outcome, treatment indicator, or time index) or missing plotting backend (matplotlib not installed). See Also -------- LWDIDError : Base exception class. """ pass
# ============================================================================= # Staggered DiD Exceptions # =============================================================================
[docs] class InvalidStaggeredDataError(LWDIDError): """ Exception raised when staggered data validation fails. This exception is raised in the following scenarios: 1. **Invalid gvar values**: - Negative values in gvar column - String values instead of numeric types - Values that cannot be interpreted as treatment cohort or never-treated 2. **No valid cohorts**: - All units are never-treated (no treated cohorts to estimate effects for) - All gvar values are NaN/0/inf with no positive integers 3. **Inconsistent gvar within unit**: - Same unit has different gvar values across time periods (gvar should be time-invariant within unit) Valid gvar values: - Positive integer: Treatment cohort (first treatment period) - 0: Never treated - np.inf: Never treated - NaN/None: Never treated See Also -------- LWDIDError : Base exception class. NoNeverTreatedError : Raised when never-treated units are required but absent. """ pass
[docs] class NoNeverTreatedError(InsufficientDataError): """ Exception raised when never-treated units are required but absent. This exception is raised when: - aggregate='cohort' is specified but no never-treated units exist - aggregate='overall' is specified but no never-treated units exist Never-treated units are required as control group for cohort and overall effect aggregation because different cohorts use different pre-treatment periods for transformation, and only never-treated units can serve as a consistent reference across cohorts. For (g,r)-specific effects, not-yet-treated units can serve as controls, so this exception is not raised when aggregate='none'. See Also -------- InsufficientDataError : Parent class for data insufficiency errors. InvalidStaggeredDataError : Raised for other staggered data validation failures. """ pass
# ============================================================================= # Repeated Cross-Section Aggregation Exceptions # =============================================================================
[docs] class AggregationError(LWDIDError): """ Base exception class for aggregation-related errors. This is the parent class for all exceptions related to repeated cross-sectional data aggregation. Specific subclasses indicate the exact nature of the aggregation failure. See Also -------- InvalidAggregationError : Raised when aggregation constraints are violated. InsufficientCellSizeError : Raised when all cells are below minimum size. """ pass
[docs] class InvalidAggregationError(AggregationError): """ Exception raised when aggregation constraints are violated. This exception is raised in the following scenarios: 1. **Treatment varies within cell**: Treatment status is not constant within a (unit, period) cell, violating the assumption that treatment is assigned at the aggregation unit level. 2. **gvar varies within unit**: Treatment timing (gvar) is not constant within a unit across all periods, violating the time-invariance assumption. 3. **Duplicate column names**: Aggregation would result in duplicate column names in the output. See Also -------- AggregationError : Parent class for aggregation errors. """ pass
[docs] class InsufficientCellSizeError(AggregationError): """ Exception raised when all cells are below minimum size threshold. This exception is raised when the min_cell_size parameter is specified and all (unit, period) cells have fewer observations than the threshold, resulting in an empty output panel. See Also -------- AggregationError : Parent class for aggregation errors. """ pass