Skip to main content

Python-first access to R's brms with proper parameter names, ArviZ support, and cmdstanr performance

Project description

brmspy

Python-first access to R's brms with proper parameter names, ArviZ support, and cmdstanr performance. The easiest way to run brms models from Python.

Github repo and issues

Python 3.10+ License: Apache 2.0 Documentation

Coverage main Coverage r dependencies python-test-matrix r-dependencies-tests

Installation

R Configuration

R>=4 is required before installing brmspy.

On Linux and macOS brmspy will usually auto-detect R_HOME, and the session layer attempts to prepend $R_HOME/lib to LD_LIBRARY_PATH when needed for rpy2 ABI mode.

If you run into errors like “cannot find libR” (or similar dynamic loader issues), set these explicitly:

# Set R_HOME and add lib directory to LD_LIBRARY_PATH (Unix)
export R_HOME=$(R RHOME)
export LD_LIBRARY_PATH="${R_HOME}/lib:${LD_LIBRARY_PATH}"

# Recommended for stability
export RPY2_CFFI_MODE=ABI

Python

pip install brmspy

First-time setup (installs brms, cmdstanr, and CmdStan in R):

from brmspy import brms

with brms.manage() as ctx: # requires R to be installed already
    ctx.install_brms()

Prebuilt Runtimes (Optional)

For faster installation (~20-60 seconds vs 20-30 minutes), use prebuilt runtime bundles:

from brmspy import brms

with brms.manage() as ctx:
    ctx.install_brms(use_prebuilt=True)

Windows RTools

In case you don't have RTools installed, you can use the flag install_rtools = True. This is disabled by default, because the flag runs the full rtools installer and modifies system path. Use with caution!

from brmspy import brms

with brms.manage() as ctx:
    ctx.install_brms(
        use_prebuilt=True,
        install_rtools=True,  # works for both prebuilt and compiled installs
    )

System Requirements

Linux (x86_64):

  • glibc >= 2.27 (Ubuntu 18.04+, Debian 10+, RHEL 8+)
  • g++ >= 9.0
  • R >= 4.0

macOS (Intel & Apple Silicon):

  • Xcode Command Line Tools: xcode-select --install
  • clang >= 11.0
  • R >= 4.0

Windows (x86_64):

  • Rtools
  • R >= 4.0

Download Rtools from: https://cran.r-project.org/bin/windows/Rtools/

Key Features

  • Proper parameter names: Returns b_Intercept, b_zAge, sd_patient__Intercept instead of generic names like b_dim_0
  • ArviZ integration: Returns arviz.InferenceData by default for Python workflow
  • brms formula syntax: Full support for brms formula interface including random effects
  • Dual access: Results include .idata (ArviZ) plus a lightweight .r handle that can be passed back to brmspy to reference the underlying R object (the R object itself stays in the worker process)
  • No reimplementation: Delegates all modeling logic to real brms. No Python-side reimplementation, no divergence from native behavior
  • Prebuilt Binaries: Fast installation with precompiled runtimes (50x faster, ~25 seconds on Google Colab)
  • Stays true to brms: Function names, parameters, and returned objects are designed to be as close as possible to brms
  • Composable formula DSL: Build multivariate, non-linear, and distributional formulas by simply adding components together, identical to brms

Examples

1. Quick Start

Basic Bayesian regression with ArviZ diagnostics:

from brmspy import brms
from arviz_stats import summary

# Fit Poisson model with random effects
epilepsy = brms.get_brms_data("epilepsy")
model = brms.brm("count ~ zAge + (1|patient)", data=epilepsy, family="poisson")

# Proper parameter names automatically!
print(summary(model.idata))
#                  mean     sd  hdi_3%  hdi_97%  ...  r_hat
# b_Intercept     1.234  0.123   1.012    1.456  ...   1.00
# b_zAge          0.567  0.089   0.398    0.732  ...   1.00
# sd_patient__... 0.345  0.067   0.223    0.467  ...   1.00

2. Multivariate Models (Python vs R)

Model multiple responses simultaneously with seamless ArviZ integration:

Python (brmspy)R (brms)
from brmspy import brms
from brmspy.brms import bf, set_rescor
from arviz_stats import loo
from arviz_plots import plot_ppc_dist

# Fit multivariate model
mv = brms.brm(
    bf("mvbind(tarsus, back) ~ sex + (1|p|fosternest)")
    + set_rescor(True),
    data=btdata
)

# ArviZ just works!
loo(mv.idata, var_names=["tarsus"])
loo(mv.idata, var_names=["back"])
plot_ppc_dist(mv.idata, var_names=["tarsus"])
library(brms)
library(loo)

# Fit multivariate model
fit <- brm(
  bf(mvbind(tarsus, back) ~ sex + (1|p|fosternest))
  + set_rescor(TRUE),
  data = BTdata
)

# Separate LOO for each response
loo_tarsus <- loo(fit, resp = "tarsus")
loo_back <- loo(fit, resp = "back")

3. Distributional Regression

Model heteroscedasticity (variance depends on predictors):

from brmspy import brms
from brmspy.brms import bf

# Model both mean AND variance
model = brms.brm(
    bf("reaction ~ days", sigma = "~ days"),  # sigma varies with days!
    data=sleep_data,
    family="gaussian"
)

# Extract distributional parameters
print(model.idata.posterior.data_vars)
# b_Intercept, b_days, b_sigma_Intercept, b_sigma_days, ...

4. Complete Diagnostic Workflow with ArviZ

Full model checking in ~10 lines:

from brmspy import brms
from arviz_stats import rhat, ess, compare
from arviz_plots import plot_ppc_dist

model = brms.brm("count ~ zAge * Trt + (1|patient)", data=epilepsy, family="poisson")

# Check convergence
assert rhat(model.idata).to_array().values.max() < 1.01, "Convergence issues!"
assert ess(model.idata).to_array().values.min() > 400, "Low effective sample size!"

# Posterior predictive check
plot_ppc_dist(model.idata, num_samples=100)

# Model comparison
model2 = brms.brm("count ~ zAge + Trt + (1|patient)", data=epilepsy, family="poisson")
comparison = compare({"interaction": model.idata, "additive": model2.idata})
print(comparison)
#              rank  loo    p_loo  d_loo  weight
# interaction     0 -456.2   12.3    0.0    0.89
# additive        1 -461.5   10.8    5.3    0.11

5. Advanced Formulas: Splines & Non-linear Effects

Smooth non-linear relationships with splines:

from brmspy import brms

# Generalized additive model (GAM) with spline
model = brms.brm(
    "y ~ s(x, bs='cr', k=10) + (1 + x | group)",
    data=data,
    family="gaussian"
)

# Polynomial regression
poly_model = brms.brm(
    "y ~ poly(x, 3) + (1|group)",
    data=data
)

# Extract and visualize smooth effects
conditional_effects = brms.call("conditional_effects", model, "x")

Additional Features

Custom Priors:

from brmspy.brms import prior

model = brms.brm(
    "count ~ zAge + (1|patient)",
    data=epilepsy,
    priors=[
        prior("normal(0, 0.5)", class_="b"),
        prior("exponential(1)", class_="sd", group="patient")
    ],
    family="poisson"
)

Predictions:

import pandas as pd
from arviz_plots import plot_dist

new_data = pd.DataFrame({"zAge": [-1, 0, 1], "patient": [999, 999, 999]})

# Expected value (without observation noise)
epred = brms.posterior_epred(model, newdata=new_data)

# Posterior predictive (with noise)
ypred = brms.posterior_predict(model, newdata=new_data)

# Access as InferenceData for ArviZ
plot_dist(epred.idata)

6. Maximalist Example: Kitchen Sink

Everything at once - multivariate responses, different families, distributional parameters, splines, and complete diagnostics:

from brmspy.brms import bf, lf, set_rescor, skew_normal, gaussian
from brmspy import brms
import arviz as az

# Load data
btdata = brms.get_data("BTdata", package="MCMCglmm")

bf_tarsus = (
    bf("tarsus ~ sex + (1|p|fosternest) + (1|q|dam)") +
    lf("sigma ~ 0 + sex") +
    skew_normal()
)

bf_back = (
    bf("back ~ s(hatchdate) + (1|p|fosternest) + (1|q|dam)") +
    gaussian()
)

model = brms.brm(
    bf_tarsus + bf_back + set_rescor(False),
    data=btdata,
    chains=2,
    control={"adapt_delta": 0.95}
)

# ArviZ diagnostics work seamlessly
for response in ["tarsus", "back"]:
    print(f"\n=== {response.upper()} ===")
    
    # Model comparison
    loo = az.loo(model.idata, var_name=response)
    print(f"LOO: {loo.loo:.1f} ± {loo.loo_se:.1f}")
    
    # Posterior predictive check
    az.plot_ppc(model.idata, var_names=[response])
    
    # Parameter summaries
    print(az.summary(
        model.idata,
        var_names=[f"b_{response}"],
        filter_vars="like"
    ))

# Visualize non-linear effect
conditional = brms.call("conditional_effects", model, "hatchdate", resp="back")
# Returns proper pandas DataFrame ready for plotting!

Output shows:

  • Proper parameter naming: b_tarsus_Intercept, b_tarsus_sex, b_sigma_sex, sd_fosternest__tarsus_Intercept, etc.
  • Separate posterior predictive for each response
  • Per-response LOO for model comparison
  • All parameters accessible via ArviZ

API Reference (partial)

brmspy documentation

brms documentation

Setup Functions

Any operation that installs/uninstalls R packages or changes the runtime/environment should be done via brms.manage() (it restarts the worker, giving you a fresh embedded R session).

  • brms.manage() - Context manager for safe installation and environment management
    • ctx.install_brms(...) - Install brms + toolchain (from source or using a prebuilt runtime)
    • ctx.install_runtime(...) - Install the latest prebuilt runtime for the current system
    • ctx.install_rpackage(...) / ctx.uninstall_rpackage(...) - Manage extra R packages in an environment user library
  • environment_exists(name) - Check if an environment exists
  • environment_activate(name) - Activate an existing environment (switches worker session state)
  • get_brms_version() - Get installed brms version
  • find_local_runtime() - Find a matching locally installed runtime
  • get_active_runtime() - Get the configured runtime path
  • status() - Inspect runtime/toolchain status

Data Functions

  • get_brms_data() - Load example datasets from brms
  • get_data() - Load example datasets from any package
  • save_rds() - Save an R object to .rds (executed in the worker)
  • read_rds_fit() - Load saved brmsfit object as FitResult (with idata)
  • read_rds_raw() - Load an R object (raw)

Model Functions

  • bf, lf, nlf, acformula, set_rescor, set_mecor, set_nl - formula functions
  • brm() - Fit Bayesian regression model
  • make_stancode() - Generate Stan code for model

Diagnostics Functions

  • summary() - Comprehensive model summary as SummaryResult dataclass
  • fixef() - Extract population-level (fixed) effects
  • ranef() - Extract group-level (random) effects as xarray
  • posterior_summary() - Summary statistics for all parameters
  • prior_summary() - Extract prior specifications used in model
  • validate_newdata() - Validate new data for predictions
  • For loo, waic etc use arviz!

Prior Functions

  • prior() - Define a prior with same syntax as r-s prior_string
  • get_prior() - Get pd.DataFrame describing default priors
  • default_prior() - Get pd.DataFrame describing default priors

Families Functions

  • family() - Get family object of FitResult
  • brmsfamily() - Construct family object from kwargs
  • gaussian(), ...bernoulli(), ...beta_binomial(), etc - Wrappers around brmsfamily for faster family object construction

Prediction Functions

  • posterior_epred() - Expected value predictions (without noise)
  • posterior_predict() - Posterior predictive samples (with noise)
  • posterior_linpred() - Linear predictor values
  • log_lik() - Log-likelihood values

Generic Function Access

  • call() - Call any brms/R function by name with automatic type conversion

Known issues

  • If you have multiple R installations, explicitly setting R_HOME can help avoid “wrong R” / loader issues.

Requirements

Python: 3.10-3.14

R packages (auto-installed via ctx.install_brms() inside brms.manage()):

  • brms >= 2.20.0
  • cmdstanr
  • posterior

Python dependencies:

  • rpy2 >= 3.5.0
  • pandas >= 1.3.0
  • numpy >= 1.20.0
  • arviz (optional, for InferenceData)

Development

git clone https://github.com/kaitumisuuringute-keskus/brmspy.git
cd brmspy
sh script/init-venv.sh
./run_tests.sh

Architecture

brmspy uses:

  • brms::brm() with cmdstanr backend for fitting (ensures proper parameter naming)
  • posterior R package for conversion to draws format
  • arviz for Python-native analysis and visualization
  • rpy2 for Python-R communication

The current architecture isolates embedded R inside a worker process; the main Python process exposes the brms API surface and forwards calls to the worker. This improves stability (crash containment) and enables “resettable” R sessions for installs/environment changes.

License

Apache License 2.0

Credits

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

brmspy-0.4.0.tar.gz (184.0 kB view details)

Uploaded Source

Built Distribution

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

brmspy-0.4.0-py3-none-any.whl (157.4 kB view details)

Uploaded Python 3

File details

Details for the file brmspy-0.4.0.tar.gz.

File metadata

  • Download URL: brmspy-0.4.0.tar.gz
  • Upload date:
  • Size: 184.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for brmspy-0.4.0.tar.gz
Algorithm Hash digest
SHA256 5ad76f5b2628e215cc8542b9591c62cf620c661236709af57ea0a7bc31d4d163
MD5 9a6db941086af9c4f03aa4b63e9afe15
BLAKE2b-256 84fed7f93dc5095afe77701e87bb58d88897f92abd0d4bc552cd27e6225dc7ee

See more details on using hashes here.

File details

Details for the file brmspy-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: brmspy-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 157.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for brmspy-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1d4112027f904f46c554cc27b92ce3bfa1b2c14a1d4710203772fe50841bf4fe
MD5 753fdcf4cba588fa8e02e94aa439004c
BLAKE2b-256 f5769e1f810742f6ac87444e3850eeb1c390469c14e8f69e0da962bd2a464d4b

See more details on using hashes here.

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