R interface

Each implementation package ships an R6 wrapper that calls the Python implementation through reticulate. Predictions are bitwise-identical to the Python results on the same input.

The R API is covered throughout the guide via the bilingual tabsets: see install for setup, the landing-page quickstart for the standard fit pattern, and estimands for the full estimand inventory. This page is the focused R reference.

Setup

After installing the Python packages (see install):

# Option A: env var, set before R starts
Sys.setenv(RETICULATE_PYTHON = "/path/to/RieszReg/.venv/bin/python")

# Option B: explicit, after R has started
use_python_rieszboost("/path/to/RieszReg/.venv/bin/python")
# or, for the kernel backend:
use_python_krrr("/path/to/RieszReg/.venv/bin/python")
# or, for the forest backend:
use_python_forestriesz("/path/to/RieszReg/.venv/bin/python")
# or, for the neural backend:
use_python_riesznet("/path/to/RieszReg/.venv/bin/python")

use_python_<pkg>() accepts a path to a Python interpreter or a virtualenv directory and forwards to the appropriate reticulate function. It also imports the Python package so subsequent calls have no per-call import overhead.

API surface (rieszboost)

R Python equivalent
ATE(treatment, covariates) rieszreg.ATE(treatment, covariates)
ATT(treatment, covariates) rieszreg.ATT(treatment, covariates)
TSM(level, treatment, covariates) rieszreg.TSM(level, treatment, covariates)
AdditiveShift(delta, treatment, covariates) rieszreg.AdditiveShift(...)
LocalShift(delta, threshold, treatment, covariates) rieszreg.LocalShift(...)
SquaredLoss() rieszreg.SquaredLoss()
KLLoss(max_eta) rieszreg.KLLoss(max_eta)
BernoulliLoss(max_abs_eta) rieszreg.BernoulliLoss(...)
BoundedSquaredLoss(lo, hi, max_abs_eta) rieszreg.BoundedSquaredLoss(...)
XGBoostBackend(hessian_floor, gradient_only) rieszboost.XGBoostBackend(...)
SklearnBackend(base_learner_factory) rieszboost.SklearnBackend(...)
RieszBooster$new(estimand, ...) rieszboost.RieszBooster(estimand, ...)
  $fit(Z, y=, eval_set=, eval_y=) .fit(Z, y=, eval_set=, eval_y=)
  $predict(Z) .predict(Z)
  $score(Z, y=) .score(Z, y=)
  $riesz_loss(Z, y=) .riesz_loss(Z, y=)
  $diagnose(Z) .diagnose(Z) (returns a list with $summary as a string)
  $save(path) .save(path)
load_riesz_booster(path) RieszBooster.load(path)
  $py the underlying Python object

API surface (krrr)

R Python equivalent
Gaussian(length_scale) krrr.Gaussian(length_scale)
Matern(nu, length_scale) krrr.Matern(nu, length_scale)
Linear() / Polynomial(...) krrr.Linear() / krrr.Polynomial(...)
KernelRidgeBackend(kernel, lambda_grid, solver, ...) krrr.KernelRidgeBackend(...)
KernelRieszRegressor$new(estimand, kernel, ...) krrr.KernelRieszRegressor(...)
  $fit(df, eval_set=) .fit(Z, eval_set=)
  $predict(df) .predict(Z)
  $diagnose(df) .diagnose(Z)

API surface (forestriesz)

R Python equivalent
ForestRieszRegressor$new(estimand, n_estimators, max_depth, min_samples_leaf, honest, inference, ...) forestriesz.ForestRieszRegressor(...)
  $fit(df) .fit(Z)
  $predict(df) .predict(Z)
  $predict_interval(df, alpha) .predict_interval(Z, alpha=) (single-basis fits with honest=TRUE, inference=TRUE)
  $diagnose(df) .diagnose(Z)
load_forest_riesz_regressor(path) ForestRieszRegressor.load(path)

The R wrapper exposes the moment-style ForestRieszRegressor (single-basis fits — handles ATE/ATT/TSM via the auto-resolved sieve in Python). The augmentation-style AugForestRieszRegressor is Python-only in v1; call it from R via reticulate when you need a fully estimand-agnostic forest fit.

API surface (riesznet)

R Python equivalent
RieszNet$new(estimand, hidden_sizes, activation, dropout, learning_rate, weight_decay, epochs, batch_size, device, dtype, ...) riesznet.RieszNet(...)
  $fit(df, eval_set=) .fit(Z, eval_set=)
  $predict(df) .predict(Z)
  $diagnose(df) .diagnose(Z)
load_riesz_net(path) RieszNet.load(path)

The R wrapper exposes the simple-MLP convenience surface only. Custom torch architectures (custom nn.Module factory, custom optimizer, custom scheduler) are Python-only — define the factory in Python at module top level and call into Python via reticulate. TorchBackend direct construction is also Python-only in v1.

R6’s $method() syntax maps to Python’s .method(). Integer-typed kwargs (n_estimators, max_depth, random_state, early_stopping_rounds, n_landmarks, n_features, cg_max_iter, min_samples_leaf, subforest_size, epochs, batch_size) take an L suffix in R: n_estimators = 200L. Other kwargs are auto-converted by reticulate.

Parity

The same fit produces the same predictions in both languages. The R test suites verify this with tolerance = 1e-12.

import numpy as np, pandas as pd
from rieszboost import RieszBooster
from rieszreg import ATE

rng = np.random.default_rng(0)
n = 500
x = rng.uniform(0, 1, n)
a = rng.binomial(1, 1 / (1 + np.exp(-(2 * x - 1))))
df_py = pd.DataFrame({"a": a.astype(float), "x": x})

booster_py = RieszBooster(estimand=ATE(), n_estimators=50, learning_rate=0.05,
                          max_depth=3, random_state=42).fit(df_py)
alpha_py = booster_py.predict(df_py)
alpha_py[:5]
array([ 1.4076698,  1.4764098,  1.6470399, -1.1931754,  1.1374395],
      dtype=float32)
set.seed(0)
n  <- 500
x  <- runif(n)
a  <- rbinom(n, 1, 1 / (1 + exp(-(2 * x - 1))))
df_r <- data.frame(a = as.numeric(a), x = x)

booster_r <- RieszBooster$new(
  estimand = ATE(), n_estimators = 50L, learning_rate = 0.05,
  max_depth = 3L, random_state = 42L
)
booster_r$fit(df_r)
alpha_r <- booster_r$predict(df_r)
head(alpha_r, 5)
[1]  1.212691 -1.263393  1.486148 -1.365574  1.212691

The two columns above use different RNGs (default_rng(0) versus set.seed(0)), so the df and the predictions differ between tabs. To verify parity end-to-end, fit on the same data in both languages and compare predictions row-by-row.

Cross-fitting and tuning from R

Use rsample::vfold_cv for splits and a for loop over $fit() / $predict() / $score() on the R6 estimator. See Tuning and cross-fitting for full examples.

Custom Python objects from R

The LinearForm tracer is Python-only. To use a custom estimand or Loss subclass from R, define it in Python via reticulate:

reticulate::py_run_string("
def m_my_thing(alpha):
    def inner(z, y=None):
        return 2.0 * (alpha(a=2, x=z['x']) - alpha(a=0, x=z['x']))
    return inner

from rieszreg import FiniteEvalEstimand
my_estimand = FiniteEvalEstimand(feature_keys=('a', 'x'), m=m_my_thing, name='MyThing')
")

est <- reticulate::py$my_estimand
booster <- RieszBooster$new(estimand = est, n_estimators = 100L, max_depth = 3L)
booster$fit(df_r)
booster$predict(df_r)[1:5]

The same pattern works for custom Loss subclasses: subclass in Python and pass the instance via loss=.

Reaching the Python object directly

booster$py is the underlying Python estimator. Use it for attributes the R6 wrapper doesn’t expose:

booster <- RieszBooster$new(estimand = ATE(), n_estimators = 50L)
booster$fit(df_r)

reticulate::py_to_r(booster$py$best_iteration_)
reticulate::py_to_r(booster$py$best_score_)
reticulate::py_to_r(booster$py$base_score_)