PanelRegression#

class causalpy.experiments.panel_regression.PanelRegression[source]#

Panel regression with fixed effects estimation.

Enables panel-aware visualization and diagnostics, with support for both dummy variable and within-transformation approaches to fixed effects.

Parameters:
  • data (pd.DataFrame) – A pandas dataframe with panel data. Each row is an observation for a unit at a time period.

  • formula (str) – A statistical model formula using patsy syntax. For dummy variable approach, include C(unit_var) in the formula. For within transformation, do NOT include C(unit_var) as it will be automatically removed.

  • unit_fe_variable (str) – Column name for the unit identifier (e.g., “state”, “id”, “country”).

  • time_fe_variable (str, optional) – Column name for the time identifier (e.g., “year”, “wave”, “period”). If provided, time fixed effects will be included. Default is None.

  • fe_method ({"dummies", "within"}, default="dummies") –

    Method for handling fixed effects: - “dummies”: Use dummy variables (C(unit) in formula). Gets individual

    unit effect estimates but creates N-1 dummy columns. Best for small N.

    • ”within”: Use within transformation (demeaning). Scales to large N but doesn’t directly estimate individual unit effects.

  • model (PyMCModel or RegressorMixin, optional) – A PyMC (Bayesian) or sklearn (OLS) model. If None, a model must be provided.

n_units#

Number of unique units in the panel.

Type:

int

n_periods#

Number of unique time periods (None if time_fe_variable not provided).

Type:

int or None

fe_method#

The fixed effects method used (“dummies” or “within”).

Type:

str

_group_means#

Stored group means for recovering unit effects (within method only).

Type:

dict

Examples

Small panel with dummy variables:

>>> import causalpy as cp
>>> import pandas as pd
>>> # Create small panel: 10 units, 20 time periods
>>> np.random.seed(42)
>>> units = [f"unit_{i}" for i in range(10)]
>>> periods = range(20)
>>> data = pd.DataFrame(
...     [
...         {
...             "unit": u,
...             "time": t,
...             "treatment": int(t >= 10 and u in units[:5]),
...             "x1": np.random.randn(),
...             "y": np.random.randn(),
...         }
...         for u in units
...         for t in periods
...     ]
... )
>>> result = cp.PanelRegression(
...     data=data,
...     formula="y ~ C(unit) + C(time) + treatment + x1",
...     unit_fe_variable="unit",
...     time_fe_variable="time",
...     fe_method="dummies",
...     model=cp.pymc_models.LinearRegression(
...         sample_kwargs={"random_seed": 42, "progressbar": False}
...     ),
... )

Large panel with within transformation:

>>> # Create larger panel: 1000 units, 10 time periods
>>> np.random.seed(42)
>>> units = [f"unit_{i}" for i in range(1000)]
>>> periods = range(10)
>>> data = pd.DataFrame(
...     [
...         {
...             "unit": u,
...             "time": t,
...             "treatment": int(t >= 5),
...             "x1": np.random.randn(),
...             "y": np.random.randn(),
...         }
...         for u in units
...         for t in periods
...     ]
... )
>>> result = cp.PanelRegression(
...     data=data,
...     formula="y ~ treatment + x1",  # No C(unit) needed
...     unit_fe_variable="unit",
...     time_fe_variable="time",
...     fe_method="within",
...     model=cp.pymc_models.LinearRegression(
...         sample_kwargs={"random_seed": 42, "progressbar": False}
...     ),
... )

Notes

The within transformation demeans all numeric and boolean variables by group, which removes time-invariant confounders but also drops time-invariant covariates from the model. For the dummy approach, individual unit effects can be extracted from the coefficients. For the within approach, unit effects can be recovered post-hoc using the stored group means (_group_means), which are always computed from the original (pre-demeaning) data.

Two-way fixed effects (unit + time) control for both unit-specific and time-specific unobserved heterogeneity. This is the standard approach in difference-in-differences estimation.

Balanced panels: When both unit and time fixed effects are requested with fe_method="within", the sequential demeaning (first by unit, then by time) is algebraically equivalent to the standard two-way within transformation only for balanced panels (every unit observed in every period). For unbalanced panels, iterative alternating demeaning would be needed for exact convergence; the single-pass approximation may introduce small biases.

Methods

PanelRegression.__init__(data, formula, ...)

PanelRegression.algorithm()

Run the experiment algorithm: fit the model.

PanelRegression.effect_summary(*[, window, ...])

Generate a decision-ready summary of causal effects.

PanelRegression.fit(*args, **kwargs)

PanelRegression.get_plot_data(*args, **kwargs)

Recover the data of an experiment along with the prediction and causal impact information.

PanelRegression.get_plot_data_bayesian(**kwargs)

Get plot data for Bayesian model.

PanelRegression.get_plot_data_ols(**kwargs)

Get plot data for OLS model.

PanelRegression.input_validation()

Validate input parameters.

PanelRegression.plot(*args, **kwargs)

Plot the model.

PanelRegression.plot_coefficients([var_names])

Plot coefficient estimates with credible/confidence intervals.

PanelRegression.plot_residuals([kind])

Plot residual diagnostics.

PanelRegression.plot_trajectories([units, ...])

Plot unit-level time series trajectories.

PanelRegression.plot_unit_effects([...])

Plot distribution of unit fixed effects.

PanelRegression.print_coefficients([round_to])

Ask the model to print its coefficients.

PanelRegression.summary([round_to])

Print a summary of the panel regression results.

Attributes

idata

Return the InferenceData object of the model.

supports_bayes

supports_ols

labels

__init__(data, formula, unit_fe_variable, time_fe_variable=None, fe_method='dummies', model=None, **kwargs)[source]#
Parameters:
Return type:

None

classmethod __new__(*args, **kwargs)#