diff_diff.LWDiD#

class diff_diff.LWDiD[source]#

Bases: BaseEstimator

Lee & Wooldridge rolling-transformation DiD estimator.

Parameters:
  • rolling ({'demean', 'detrend', 'demeanq', 'detrendq'}, default 'demean') – Unit-specific transformation method. ‘demean’: subtract pre-treatment mean ‘detrend’: subtract pre-treatment linear trend ‘demeanq’: subtract unit-specific seasonal (quarterly) means ‘detrendq’: subtract unit-specific linear trend + seasonal effects

  • estimation_method ({'reg', 'ipw', 'dr', 'psm'}, default 'reg') – Treatment effect estimation method. ‘reg’: regression adjustment (OLS) ‘ipw’: inverse probability weighting ‘dr’: doubly robust (augmented IPW) ‘psm’: propensity score matching (1:n_neighbors nearest-neighbor, 1:1 by default); POINT ESTIMATES ONLY - inference is NaN pending an Abadie-Imbens matching variance (see DEFERRED.md)

  • vcov_type ({'classical', 'hc1', 'hc2', 'hc3'}, default 'hc1') – Variance-covariance estimator. ‘hc2’: leverage-corrected (u_i^2 / (1-h_ii)) ‘hc3’: jackknife-style leverage correction (u_i^2 / (1-h_ii)^2) The full set applies to estimation_method='reg' only: ‘ipw’/’dr’/’psm’ accept ‘hc1’ alone (the influence-function variance on ipw/dr; psm reports NaN inference - see below). Cluster-robust (CR1) inference activates via the cluster= parameter, not through a vcov_type value, composes only with ‘hc1’, and is rejected for ‘psm’.

  • cluster (str or None, default None) – Column name for cluster-robust (CR1) standard errors. When set, clustered inference is active for the whole fit.

  • control_group ({'never_treated', 'not_yet_treated'}, default 'not_yet_treated') – Control group definition for staggered designs. Both options require never-treated units: ‘never_treated’ needs at least two, and ‘not_yet_treated’ needs at least one so that every cohort-time cell keeps a valid control pool. Panels where all units are eventually treated are rejected with a ValueError.

  • alpha (float, default 0.05) – Significance level for confidence intervals.

  • n_bootstrap (int, default 0) – Number of bootstrap replications (0 = analytical inference).

  • seed (int or None, default None) – Random seed for bootstrap inference.

  • pscore_trim (float, default 0.01) – Propensity score trimming threshold. Scores below this value or above (1 - pscore_trim) are clipped. Used by IPW/DR/PSM. Must be in (0, 0.5) and large enough that 1 - pscore_trim < 1 in float64 (a sub-ulp trim would disable the upper clip).

  • n_neighbors (int, default 1) – Number of nearest neighbors for PSM matching.

  • caliper (float or None, default None) – Maximum allowable distance for PSM matches. Unmatched treated units (no control within caliper) receive NaN.

  • with_replacement (bool, default True) – Whether PSM matching is done with replacement.

  • n_jobs (int, default 1) – Execution parallelism for the common-timing bootstrap (ThreadPoolExecutor when > 1; experimental). Purely an execution setting: seeded bootstrap draws are identical for every value (per-replicate SeedSequence streams), so it never affects any reported number and is not stored in result provenance.

Notes

Parameter mapping from lwdid-py to diff-diff:

The standalone lwdid-py package (from lwdid import lwdid) uses a functional interface with separate d (ever-treated indicator) and post (post-period indicator) columns. In diff-diff, the treatment column is the time-varying binary indicator D_i * post_t—i.e., the product of the two lwdid-py columns.

# lwdid-py (functional API):
lwdid(data, y='y', d='d', ivar='unit', tvar='time', post='post',
      rolling='demean', estimator='ra', vce=None)

# Equivalent in diff-diff (class-based API):
LWDiD(rolling='demean', estimation_method='reg',
      vcov_type='classical').fit(
    data, outcome='y', unit='unit', time='time', treatment='treat')
# where data['treat'] == data['d'] * data['post']

Parameter correspondence:

lwdid-py

diff-diff

Notes

y

outcome

Outcome column name

d + post

treatment

Binary D_it (ever-treated × post)

ivar

unit

Unit identifier

tvar

time

Time variable

gvar

first_treat

Cohort (first treatment period)

rolling

rolling

Same values

estimator=’ra’

estimation_method

‘ra’ -> ‘reg’, ‘ipwra’ -> ‘dr’

vce=None

vcov_type

Homoskedastic == ‘classical’

vce=’hc1’

vcov_type=’hc1’

Heteroskedasticity-robust

vce=’cluster’

cluster=<column>

Constructor cluster= parameter

cluster_var

cluster

Cluster variable name

controls

covariates

fit() covariates= parameter

control_group

control_group

Same values

Results mapping:

lwdid-py

diff-diff

Notes

result.att

result.att

ATT point estimate

result.se_att

result.se

Standard error

result.t_stat

result.t_stat

t-statistic

result.pvalue

result.p_value

p-value (note underscore)

result.ci_lower

result.conf_int[0]

CI lower bound

result.ci_upper

result.conf_int[1]

CI upper bound

result.nobs

result.n_obs

Number of observations

result.n_treated

result.n_treated

Treated units

result.n_control

result.n_control

Control units

result.vce_type

result.vcov_type

Variance family

result.cluster_var

result.cluster_name

Cluster variable name

result.n_clusters

result.n_clusters

Number of clusters

Examples

>>> import numpy as np, pandas as pd
>>> from diff_diff.lwdid import LWDiD
>>> from diff_diff import generate_staggered_data
>>> data = generate_staggered_data(n_units=100, n_periods=8, seed=0)
>>> model = LWDiD(rolling='demean', estimation_method='reg')
>>> result = model.fit(data, outcome='outcome', unit='unit',
...                    time='period', treatment='treated',
...                    first_treat='first_treat')
>>> result.att != 0
True

Methods

__init__([rolling, estimation_method, ...])

fit(data, outcome, unit, time, treatment[, ...])

Fit the LWDiD estimator.

get_params([deep])

Get estimator parameters (sklearn-compatible).

get_transformation_diagnostics(data, ...[, ...])

Run the transformation step and return diagnostics without full estimation.

set_params(**params)

Set estimator parameters (sklearn-compatible, transactional).

__init__(rolling='demean', estimation_method='reg', vcov_type='hc1', cluster=None, control_group='not_yet_treated', alpha=0.05, n_bootstrap=0, seed=None, pscore_trim=0.01, n_neighbors=1, caliper=None, with_replacement=True, n_jobs=1)[source]#
Parameters:
  • rolling (str)

  • estimation_method (str)

  • vcov_type (str)

  • cluster (str | None)

  • control_group (str)

  • alpha (float)

  • n_bootstrap (int)

  • seed (int | None)

  • pscore_trim (float)

  • n_neighbors (int)

  • caliper (float | None)

  • with_replacement (bool)

  • n_jobs (int)

Return type:

None

classmethod __new__(*args, **kwargs)#