diff_diff.TripleDifference

class diff_diff.TripleDifference[source]

Bases: object

Triple Difference (DDD) estimator.

Estimates the Average Treatment effect on the Treated (ATT) when treatment requires satisfying two criteria: belonging to a treated group AND being in an eligible partition of the population.

This implementation follows Ortiz-Villavicencio & Sant’Anna (2025), which shows that naive DDD implementations (difference of two DiDs, three-way fixed effects) are invalid when covariates are needed for identification.

Parameters:
  • estimation_method (str, default="dr") –

    Estimation method to use: - “dr”: Doubly robust (recommended). Consistent if either the outcome

    model or propensity score model is correctly specified.

    • ”reg”: Regression adjustment (outcome regression).

    • ”ipw”: Inverse probability weighting.

  • robust (bool, default=True) – Whether to use heteroskedasticity-robust standard errors. Note: influence function-based SEs are inherently robust to heteroskedasticity, so this parameter has no effect. Retained for API compatibility.

  • cluster (str, optional) – Column name for cluster-robust standard errors. When provided, SEs are computed using the Liang-Zeger cluster-robust variance estimator on the influence function.

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

  • pscore_trim (float, default=0.01) – Trimming threshold for propensity scores. Scores below this value or above (1 - pscore_trim) are clipped to avoid extreme weights.

  • rank_deficient_action (str, default="warn") – Action when design matrix is rank-deficient (linearly dependent columns): - “warn”: Issue warning and drop linearly dependent columns (default) - “error”: Raise ValueError - “silent”: Drop columns silently without warning

results_

Estimation results after calling fit().

Type:

TripleDifferenceResults

is_fitted_

Whether the model has been fitted.

Type:

bool

Examples

Basic usage with a DataFrame:

>>> import pandas as pd
>>> from diff_diff import TripleDifference
>>>
>>> # Data where treatment affects women (partition=1) in states
>>> # that enacted a policy (group=1)
>>> data = pd.DataFrame({
...     'outcome': [...],
...     'group': [1, 1, 0, 0, ...],      # 1=policy state, 0=control state
...     'partition': [1, 0, 1, 0, ...],  # 1=women, 0=men
...     'post': [0, 0, 1, 1, ...],       # 1=post-treatment period
... })
>>>
>>> # Fit using doubly robust estimation
>>> ddd = TripleDifference(estimation_method="dr")
>>> results = ddd.fit(
...     data,
...     outcome='outcome',
...     group='group',
...     partition='partition',
...     time='post'
... )
>>> print(results.att)  # ATT estimate

With covariates (properly handled unlike naive DDD):

>>> results = ddd.fit(
...     data,
...     outcome='outcome',
...     group='group',
...     partition='partition',
...     time='post',
...     covariates=['age', 'income']
... )

Notes

The DDD estimator is appropriate when:

  1. Treatment affects only units satisfying BOTH criteria: - Belonging to a treated group (G=1), e.g., states with a policy - Being in an eligible partition (P=1), e.g., women, low-income

  2. The DDD parallel trends assumption holds: the differential trend between eligible and ineligible partitions would have been the same across treated and control groups, absent treatment.

This is weaker than requiring separate parallel trends for two DiDs, as biases can cancel out in the differencing.

References

__init__(estimation_method='dr', robust=True, cluster=None, alpha=0.05, pscore_trim=0.01, rank_deficient_action='warn')[source]
Parameters:
  • estimation_method (str)

  • robust (bool)

  • cluster (str | None)

  • alpha (float)

  • pscore_trim (float)

  • rank_deficient_action (str)

Methods

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

fit(data, outcome, group, partition, time[, ...])

Fit the Triple Difference model.

get_params()

Get estimator parameters (sklearn-compatible).

print_summary()

Print summary to stdout.

set_params(**params)

Set estimator parameters (sklearn-compatible).

summary()

Get summary of estimation results.

__init__(estimation_method='dr', robust=True, cluster=None, alpha=0.05, pscore_trim=0.01, rank_deficient_action='warn')[source]
Parameters:
  • estimation_method (str)

  • robust (bool)

  • cluster (str | None)

  • alpha (float)

  • pscore_trim (float)

  • rank_deficient_action (str)

fit(data, outcome, group, partition, time, covariates=None)[source]

Fit the Triple Difference model.

Parameters:
  • data (pd.DataFrame) – DataFrame containing all variables.

  • outcome (str) – Name of the outcome variable column.

  • group (str) – Name of the group indicator column (0/1). 1 = treated group (e.g., states that enacted policy). 0 = control group.

  • partition (str) – Name of the partition/eligibility indicator column (0/1). 1 = eligible partition (e.g., women, targeted demographic). 0 = ineligible partition.

  • time (str) – Name of the time period indicator column (0/1). 1 = post-treatment period. 0 = pre-treatment period.

  • covariates (list of str, optional) – List of covariate column names to adjust for. These are properly incorporated using the selected estimation method (unlike naive DDD implementations).

Returns:

Object containing estimation results.

Return type:

TripleDifferenceResults

Raises:

ValueError – If required columns are missing or data validation fails.

get_params()[source]

Get estimator parameters (sklearn-compatible).

Returns:

Estimator parameters.

Return type:

Dict[str, Any]

set_params(**params)[source]

Set estimator parameters (sklearn-compatible).

Parameters:

**params – Estimator parameters.

Return type:

self

summary()[source]

Get summary of estimation results.

Returns:

Formatted summary.

Return type:

str

print_summary()[source]

Print summary to stdout.

Return type:

None