Skip to main content

Heterogeneous treatment effects in regression discontinuity designs.

Project description

rdhte (Python)

Heterogeneous treatment effects in regression discontinuity (RD) designs. Point estimates, robust bias-corrected confidence intervals, and heteroskedastic or cluster-robust standard errors for conditional RD treatment effects.

The theoretical framework is developed in Calonico, Cattaneo, Farrell, Palomba, and Titiunik (2025). arXiv

Install (development)

cd Python/rdhte
pip install -e ".[plot,test]"

Hard dependencies: numpy, scipy, pandas, statsmodels, rdrobust. Plotting (rdhte.plot.plot) additionally requires plotnine.

Quick start

import numpy as np
from rdhte import rdhte, rdbwhte, rdhte_lincom

rng = np.random.default_rng(0)
n = 5000
x = rng.uniform(-1.0, 1.0, n)
W = np.where(rng.random(n) < 0.5, "A", "B")              # categorical W
y = ((x >= 0) * (1.0 + 0.5 * (W == "B"))                 # CATE = 1.0 (A), 1.5 (B)
     + 0.3 * x + rng.normal(0, 0.3, n))

# Per-cell CATEs with rdrobust-style bandwidth selection (default: mserd).
m = rdhte(y=y, x=x, c=0.0, covs_hte=W)
print(m)                 # short header
print(m.summary())       # full table with Nh/h columns
print(m.tidy())          # broom-style DataFrame: term, estimate, std.error, ...
print(m.glance())        # one-row metadata DataFrame

# Data-driven bandwidth per cell, without running the full estimator.
bw = rdbwhte(y=y, x=x, c=0.0, covs_hte=W)
print(bw.h)              # (n_lev, 2) array of left/right bandwidths

# Test H0: CATE(A) == CATE(B), and separately H0: CATE(A) == 0.
res = rdhte_lincom(m, linfct=["A - B = 0", "A = 0"])
print(res["individual"])
print(res["joint"])

Empirical illustration

The package includes a full empirical illustration using the bundled Granzier, Pons, and Tricaud election dataset. From the Python folder:

python rdhte_illustration.py

Command Reference

The companion commands are:

Command Purpose
rdhte() Estimate conditional RD treatment effects and robust bias-corrected inference.
rdbwhte() Compute data-driven left/right bandwidths without running the full estimator.
rdhte_lincom() Test linear restrictions of the fitted CATE vector.

Main estimation options:

Option Notes
covs_hte Heterogeneity covariates. Accepts no covariate, one categorical/continuous variable, a list/DataFrame/2-D array for Cartesian-product cells, or a formula string with data=.
c, p, q RD cutoff and local-polynomial orders. Default p=1; q=None resolves to p + 1.
h, h_l, h_r Manual common or side-specific bandwidths. If omitted, rdrobust.rdbwselect is called using bwselect.
kernel triangular, epanechnikov, or uniform (tri, epa, uni also accepted).
weights Non-negative unit weights multiplying the kernel weights.
covs_eff Efficiency-improving covariates, included additively and through W interactions; they do not define CATE cells.
vce, cluster hc0-hc3 without clusters; cr1-cr3 with cluster=. Legacy hc* plus cluster= is remapped to the matching cr* with a warning.
level Confidence level in percent, e.g. 95, not 0.95.
bwjoint For categorical covs_hte, force one shared bandwidth across groups. Continuous/formula paths already use a shared bandwidth.
data, subset Resolve column-name strings against a DataFrame and optionally keep a subset of rows.

bwselect accepts mserd, msetwo, msesum, msecomb1, msecomb2, cerrd, certwo, cersum, cercomb1, and cercomb2. MSE denotes mean square error; CER denotes coverage error rate.

Return Objects

rdhte() returns an RdhteResult with fields for estimates, inference results, bandwidths, labels, and metadata: Estimate, Estimate_bc, coef, coef_bc, se_rb, ci_rb, t_rb, pv_rb, vcov, W_lev, W_names, kernel, bwselect, vce, vce_select, c, h, p, q, N, Nh, level, and rdmodel. Convenience methods include summary(), tidy(), and glance().

rdbwhte() returns an RdbwhteResult with bandwidths h, effective sample sizes Nh, group labels, and the same metadata. rdhte_lincom() returns a dictionary with individual and joint test-result DataFrames.

Multiple heterogeneity variables

For factor-by-factor interactions and formula expressions, supply covs_hte as a list of arrays / DataFrame / formula string:

import pandas as pd
df = pd.DataFrame({"y": y, "x": x, "W1": W1, "W2": W2})

# Cartesian-product factor cells.
m_2d = rdhte(y="y", x="x", c=0.0, covs_hte=[df["W1"], df["W2"]], data=df)

# Formula syntax (linear-in-Wk CATE per term).
m_form = rdhte(y="y", x="x", c=0.0, covs_hte="W1 * W2", data=df)
# Coefs: T, T:W1, T:W2, T:W1.W2.

# factor() is accepted as an alias for patsy C().
m_mix = rdhte(y="y", x="x", c=0.0, covs_hte="factor(W1) * W2", data=df)

Supported Features

Feature Status
Categorical covs_hte (one variable) yes
Continuous covs_hte (single numeric column) yes
No covs_hte (ATE-only path) yes
Manual bandwidth (h=) or per-side h_l/h_r yes
rdrobust.rdbwselect automatic bandwidth yes
Cluster-robust SE (cluster=, vce="cr1"/cr2/cr3) yes
Heteroskedastic SE (vce="hc0"/hc1/hc2/hc3) yes
Robust bias correction (q-order parallel fit) yes
Linear-combination tests (rdhte_lincom) yes
Forest plot for categorical W (rdhte.plot.plot) yes
summary() / tidy() / glance() methods yes
Factor-by-factor interaction covs_hte yes (pass [W1, W2] / pd.DataFrame / 2-D ndarray)
Formula-string covs_hte (e.g. "w_left*w_strength") yes (requires data=; factor(X) is accepted as an alias for patsy C(X))
covs_eff (efficiency-improving covariates) yes (1-D / 2-D / DataFrame; enters additively + W interactions; never with T or Xp)

vce accepts: nn, hc0, hc1, hc2, hc3, cr1, cr2, cr3. When cluster= is supplied the default switches to cr1; passing an hc* value with a cluster issues a warning and upgrades to the matching cr* form.

References

Related RD software is available at https://rdpackages.github.io/.

Authors

Sebastian Calonico, Matias D. Cattaneo, Max H. Farrell, Filippo Palomba, Rocio Titiunik.

License

GPL-3; see LICENSE in this package and LICENSE.md at the repository root.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rdhte-0.1.0.tar.gz (40.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rdhte-0.1.0-py3-none-any.whl (32.6 kB view details)

Uploaded Python 3

File details

Details for the file rdhte-0.1.0.tar.gz.

File metadata

  • Download URL: rdhte-0.1.0.tar.gz
  • Upload date:
  • Size: 40.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rdhte-0.1.0.tar.gz
Algorithm Hash digest
SHA256 18cf474acbf6d5f5374dcedcc3779743cc831c35e65b3cf7497a8cdee592cdf6
MD5 f456918e8a92241b3f0758b324f2ef1a
BLAKE2b-256 addf1a80eee360cc406c8e536b817b6082a421f608a351e4ef9cd51c257aa2bb

See more details on using hashes here.

Provenance

The following attestation bundles were made for rdhte-0.1.0.tar.gz:

Publisher: publish-python.yml on rdpackages/rdhte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rdhte-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rdhte-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 32.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rdhte-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 08f701b7c2f40c40290426828c13af7cbc370408b2ac24357387e0c63d3955a8
MD5 67155551df5af794a0406b74b9fa2e5f
BLAKE2b-256 732791b7e0c23546e02a6a857581e83de9117a3962efedadd2a83082f0c6e404

See more details on using hashes here.

Provenance

The following attestation bundles were made for rdhte-0.1.0-py3-none-any.whl:

Publisher: publish-python.yml on rdpackages/rdhte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page