Getting started¶
Installation¶
dml-rs is published as the Python package dml-rs (Python
3.9 or newer, prebuilt wheels for Linux, macOS and Windows) and as the Rust crate
dml-core (Rust 1.80 or newer).
pip install dml-rs
cargo add dml-core
The Python package only requires NumPy. pandas is optional (summaries are returned as DataFrames when it is installed), and scikit-learn is needed only if you use its estimators as learners.
The workflow¶
Every DML estimation has the same three ingredients:
- A model, which defines the causal parameter and its orthogonal score, for example the
partially linear model
PLRfor a continuous treatment or the interactive modelIRMfor the average effect of a binary treatment. - Nuisance learners, machine-learning models for the conditional expectations the score needs (e.g. \(E[Y \mid X]\) and \(E[D \mid X]\)). Any learner works; the better it predicts, the more precise the estimate.
- Sample splitting. Each nuisance is fitted on \(K-1\) folds and evaluated on the held-out fold (cross-fitting), optionally repeated over several random splits.
import dml_rs
model = dml_rs.PLR(
ml_l=dml_rs.GradientBoostingRegressor(), # E[Y | X]
ml_m=dml_rs.GradientBoostingRegressor(), # E[D | X]
n_folds=5,
n_rep=1,
random_state=0,
)
model.fit(X, y, d) # X: (n, p), y: (n,), d: (n,) or (n, k) for k treatments
use dml_core::{DmlData, GradientBoostingRegressor, Plr};
let data = DmlData::new(y, d, x)?;
let fit = Plr::new(GradientBoostingRegressor::new(), GradientBoostingRegressor::new())
.n_folds(5)
.n_rep(1)
.seed(0)
.fit(&data)?;
Results¶
model.summary # coefficient table (pandas DataFrame)
print(model) # full report, including learner losses
model.coef_, model.se_ # estimates and standard errors, one per treatment
model.confint(0.95) # confidence intervals, shape (n_treat, 2)
model.pval_ # p-values
model.nuisance_loss_ # out-of-sample RMSE / log loss of each learner
model.predictions_ # cross-fitted nuisance predictions
println!("{fit}"); // full report
let (coef, se) = (fit.coef(), fit.se());
let ci = fit.confint(0.95)?;
let p = fit.p_value();
let loss = fit.nuisance_loss("ml_l");
PLR (partialling out) | n_obs = 2000, n_folds = 5, n_rep = 1
coef std err t P>|t| 2.5 % 97.5 %
d 0.4706 0.0222 21.157 0.0000 0.4270 0.5142
Nuisance learners (out-of-sample, mean over repetitions):
ml_l RMSE 1.1579
ml_m RMSE 1.0413
Choosing learners¶
The nuisance functions are prediction problems, so choose learners as you would for prediction and
compare them on the out-of-fold losses in nuisance_loss_. The built-in learners run entirely in
Rust; scikit-learn estimators are called from Rust for each fold. See Learners.
Reproducibility¶
Sample splits are drawn from random_state (Python) or seed (Rust), so results are reproducible.
To reproduce a DoubleML analysis exactly, pass its splits: fit(..., folds=model.smpls) in Python
or fit_with_folds in Rust.
Next steps¶
- The examples walk through complete analyses.
- The user guide describes every model and the inference options.
- The API reference lists every class and parameter.