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
- Calonico, Cattaneo, Farrell, Palomba, and Titiunik (2025a): Treatment Effect Heterogeneity in Regression Discontinuity Designs.
- Calonico, Cattaneo, Farrell, Palomba, and Titiunik (2025b): rdhte: Conditional Average Treatment Effects in RD Designs.
- Cattaneo and Titiunik (2022): Regression Discontinuity Designs.
- Calonico, Cattaneo, and Farrell (2020): Optimal Bandwidth Choice for Robust Bias Corrected Inference in Regression Discontinuity Designs.
- Calonico, Cattaneo, Farrell, and Titiunik (2019): Regression Discontinuity Designs using Covariates.
- Calonico, Cattaneo, and Titiunik (2014): Robust Nonparametric Confidence Intervals for Regression-Discontinuity Designs.
- Granzier, Pons, and Tricaud (2023): Coordination and Bandwagon Effects: How Past Rankings Shape the Behavior of Voters and Candidates.
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.
Metadata
Release files for rdhte 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rdhte-0.1.0.tar.gz | 40.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rdhte-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 73.1 kB
Release files / rdhte-0.1.0.tar.gz
| Download URL | rdhte-0.1.0.tar.gz |
|---|---|
| Size | 40.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
18cf474acbf6d5f5374dcedcc3779743cc831c35e65b3cf7497a8cdee592cdf6
|
|
BLAKE2b-256 checksum How to use checksums |
addf1a80eee360cc406c8e536b817b6082a421f608a351e4ef9cd51c257aa2bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 22, 2026.
Transparency logRelease files / rdhte-0.1.0-py3-none-any.whl
| Download URL | rdhte-0.1.0-py3-none-any.whl |
|---|---|
| Size | 32.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
08f701b7c2f40c40290426828c13af7cbc370408b2ac24357387e0c63d3955a8
|
|
BLAKE2b-256 checksum How to use checksums |
732791b7e0c23546e02a6a857581e83de9117a3962efedadd2a83082f0c6e404
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 22, 2026.
Transparency log