Skip to content

chc.lalonde

Stability tier: experimental.

LaLonde-Dehejia-Wahba job-training benchmark: a randomized ATE recovered from confounded data.

The National Supported Work (NSW) demonstration randomized a job-training treatment; its experimental effect on 1978 earnings is ~$1794 (Dehejia & Wahba 1999) -- an author-independent truth, not a self-authored DGP. The LaLonde (1986) challenge swaps the experimental controls for a non-experimental CPS comparison pool: the naive treated-minus-control gap is then catastrophically biased (wrong sign, ~ -$8500), and a causal estimator's job is to recover the experimental number by covariate adjustment. This exercises CHC's causal backbone (chc.estimators) on real external data -- an external-validity check.

Data (public domain, ~445 + 15992 rows) is fetched from the Rdatasets mirror on first use and cached under ~/.cache/chc; no dataset ships in the repo and no package dependency is added (urllib + NumPy only). Gated like the BOPTEST tasks: if the fetch fails (offline), callers skip.

LalondeData dataclass

The observational LaLonde sample (NSW-treated + CPS controls) plus the randomized truth.

naive_ate property

Unadjusted treated-minus-control earnings gap -- badly confounded on the CPS pool.

load_lalonde(cache_dir=None)

Load the LaLonde benchmark: experimental NSW ATE + the confounded observational sample.

Fetches nsw_mixtape (randomized) and cps_mixtape (CPS controls) from Rdatasets, caching under cache_dir (default ~/.cache/chc). Raises on a failed fetch -- callers skip when offline, like the BOPTEST tasks.

lalonde_ate(data, estimator)

Adjusted ATE (dollars) from a CHC estimator on the confounded observational sample.

Covariates are standardised (raw re74/re75 earnings would blow up polynomial nuisances) and the outcome scaled to $1000s for conditioning, then the estimate scaled back to dollars. Each covariate reaches the estimator at unit spread in any units, and one whose spread is rounding (chc._units) at 0 in every row: a floor of 1e-9 added to the spread was in the covariate's units, and earnings with a spread of 5,000 dollars, logged in units of 1e13 dollars, reached it at a third of their spread.

The treatment, the outcome and each covariate are read as float64, as a panel's columns are, where they hold real numbers (chc.frames._real_numbers); the estimator casts them to its own dtype after. NumPy sums a matrix's columns row by row, so in float32 their means were rounded more with every row: at a million rows, a covariate of 10,000 give or take 1 reached the estimator up to 1.7 times its spread off, and at ten million one of 14,000 give or take 9,500 up to 4.4% of its spread.

Raises:

Type Description
ValueError

when a covariate is named treat or re78, the names the estimator reads the treatment and the outcome by: the covariate would replace either one; or when the treatment, the outcome or a covariate holds a value that is not a real number, or is masked, naming it.

lalonde_report(data, estimators)

Format the experimental truth, the naive bias, and each estimator's adjusted ATE.