Skip to content

stan

Stan code helpers.

This module exposes wrappers for generating Stan code from brms models without running sampling.

Notes

Executed inside the worker process that hosts the embedded R session.

Attributes

_formula_fn = bf module-attribute

Classes

FormulaConstruct dataclass

A composite formula expression built from parts.

FormulaConstruct stores a tree of nodes (FormulaPart and/or R objects) representing expressions combined with +. It is primarily created by calling the public formula helpers exposed by brmspy.brms.

Notes

The + operator supports grouping:

  • a + b + c becomes a single summand (one “group”)
  • (a + b) + (a + b) becomes two summands (two “groups”)

Use iter_summands() to iterate over these groups in a deterministic way.

Source code in brmspy/types/formula_dsl.py
@dataclass
class FormulaConstruct:
    """
    A composite formula expression built from parts.

    `FormulaConstruct` stores a tree of nodes (`FormulaPart` and/or R objects)
    representing expressions combined with `+`. It is primarily created by
    calling the public formula helpers exposed by [`brmspy.brms`][brmspy.brms].

    Notes
    -----
    The `+` operator supports grouping:

    - `a + b + c` becomes a single summand (one “group”)
    - `(a + b) + (a + b)` becomes two summands (two “groups”)

    Use [`iter_summands()`][brmspy.types.formula_dsl.FormulaConstruct.iter_summands]
    to iterate over these groups in a deterministic way.
    """

    _parts: list[Node]

    @classmethod
    def _formula_parse(cls, obj: Other) -> "FormulaConstruct":
        """
        Convert a supported value into a `FormulaConstruct`.

        Parameters
        ----------
        obj
            One of: `FormulaConstruct`, `FormulaPart`, string (interpreted as `bf(<string>)`),
            or `ProxyListSexpVector`.

        Returns
        -------
        FormulaConstruct
        """
        if isinstance(obj, FormulaConstruct):
            return obj
        if isinstance(obj, ProxyListSexpVector):
            return FormulaConstruct(_parts=[obj])
        if isinstance(obj, FormulaPart):
            return FormulaConstruct(_parts=[obj])
        if isinstance(obj, str):
            part = FormulaPart(_fun="bf", _args=[obj], _kwargs={})
            return FormulaConstruct(_parts=[part])
        raise TypeError(
            f"Cannot parse object of type {type(obj)!r} into FormulaConstruct"
        )

    def __add__(self, other: Other):
        """
        Combine two formula expressions with `+`.

        Parameters
        ----------
        other
            Value to add. Strings are treated as `bf(<string>)`.

        Returns
        -------
        FormulaConstruct
            New combined expression.
        """
        if isinstance(other, (FormulaPart, str, ProxyListSexpVector)):
            other = FormulaConstruct._formula_parse(other)

        if not isinstance(other, FormulaConstruct):
            raise ArithmeticError(
                "When adding values to formula, they must be FormulaConstruct or parseable to FormulaConstruct"
            )

        if len(other._parts) <= 1:
            return FormulaConstruct(_parts=self._parts + other._parts)
        else:
            return FormulaConstruct(_parts=[self._parts, other._parts])

    def __radd__(self, other: Other) -> "FormulaConstruct":
        """Support `"y ~ x" + bf("z ~ 1")` by coercing the left operand."""
        return self._formula_parse(other) + self

    def iter_summands(self) -> Iterator[Summand]:
        """
        Iterate over arithmetic groups (summands).

        Returns
        -------
        Iterator[tuple[FormulaPart | ProxyListSexpVector, ...]]
            Each yielded tuple represents one summand/group.

        Examples
        --------
        ```python
        from brmspy.brms import bf, gaussian, set_rescor

        f = bf("y ~ x") + gaussian() + set_rescor(True)
        for summand in f.iter_summands():
            print(summand)
        ```
        """

        def _groups(node: Node) -> Iterator[list[FormulaPart | ProxyListSexpVector]]:
            # Leaf node: single bf/family/etc
            if isinstance(node, (FormulaPart, ProxyListSexpVector)):
                return ([node],)  # one group with one element

            if isinstance(node, list):
                # If any child is a list, this node represents a "+"
                # between sub-expressions, so recurse into each child.
                if any(isinstance(child, list) for child in node):
                    for child in node:
                        yield from _groups(child)
                else:
                    # All children are leaves -> one summand
                    out: list[FormulaPart | ProxyListSexpVector] = []
                    for child in node:
                        if isinstance(child, (FormulaPart, ProxyListSexpVector, Sexp)):
                            child = cast(FormulaPart | ProxyListSexpVector, child)
                            out.append(child)
                        else:
                            raise TypeError(
                                f"Unexpected leaf node type in FormulaConstruct: {type(child)!r}"
                            )
                    yield out
                return

            raise TypeError(f"Unexpected node type in FormulaConstruct: {type(node)!r}")

        # self._parts is always a list[Node]
        for group in _groups(self._parts):
            yield tuple(group)

    # Make __iter__ return summands by default
    def __iter__(self) -> Iterator[Summand]:
        """Alias for [`iter_summands()`][brmspy.types.formula_dsl.FormulaConstruct.iter_summands]."""
        return self.iter_summands()

    def iterate(self) -> Iterator[FormulaPart | ProxyListSexpVector]:
        """
        Iterate over all leaf nodes in left-to-right order.

        This flattens the expression tree, unlike
        [`iter_summands()`][brmspy.types.formula_dsl.FormulaConstruct.iter_summands], which
        respects grouping.

        Returns
        -------
        Iterator[FormulaPart | ProxyListSexpVector]
        """

        def _walk(node: Node) -> Iterator[FormulaPart | ProxyListSexpVector]:
            if isinstance(node, FormulaPart):
                yield node
            elif isinstance(node, ProxyListSexpVector):
                yield node
            elif isinstance(node, list):
                for child in node:
                    yield from _walk(child)
            else:
                raise TypeError(
                    f"Unexpected node type in FormulaConstruct: {type(node)!r}"
                )

        for root in self._parts:
            yield from _walk(root)

    def __str__(self) -> str:
        return self._pretty(self._parts)

    def _pretty(self, node, _outer=True) -> str:
        if isinstance(node, FormulaPart):
            return str(node)

        if isinstance(node, (ProxyListSexpVector, Sexp)):
            return _sexp_to_str(node)

        if isinstance(node, list):
            # Pretty-print each child
            rendered = [self._pretty(child, _outer=False) for child in node]

            # If only one child, no parentheses needed
            if len(rendered) == 1:
                return rendered[0]

            # Multiple children → join with " + "
            inner = " + ".join(rendered)
            if _outer:
                return inner
            else:
                return f"({inner})"

        raise TypeError(f"Unexpected node type {type(node)!r} in pretty-printer")

    def __repr__(self) -> str:
        return self.__str__()

Attributes

_parts instance-attribute

Functions

_formula_parse(obj) classmethod

Convert a supported value into a FormulaConstruct.

Parameters:

Name Type Description Default
obj Other

One of: FormulaConstruct, FormulaPart, string (interpreted as bf(<string>)), or ProxyListSexpVector.

required

Returns:

Type Description
FormulaConstruct
Source code in brmspy/types/formula_dsl.py
@classmethod
def _formula_parse(cls, obj: Other) -> "FormulaConstruct":
    """
    Convert a supported value into a `FormulaConstruct`.

    Parameters
    ----------
    obj
        One of: `FormulaConstruct`, `FormulaPart`, string (interpreted as `bf(<string>)`),
        or `ProxyListSexpVector`.

    Returns
    -------
    FormulaConstruct
    """
    if isinstance(obj, FormulaConstruct):
        return obj
    if isinstance(obj, ProxyListSexpVector):
        return FormulaConstruct(_parts=[obj])
    if isinstance(obj, FormulaPart):
        return FormulaConstruct(_parts=[obj])
    if isinstance(obj, str):
        part = FormulaPart(_fun="bf", _args=[obj], _kwargs={})
        return FormulaConstruct(_parts=[part])
    raise TypeError(
        f"Cannot parse object of type {type(obj)!r} into FormulaConstruct"
    )
__add__(other)

Combine two formula expressions with +.

Parameters:

Name Type Description Default
other Other

Value to add. Strings are treated as bf(<string>).

required

Returns:

Type Description
FormulaConstruct

New combined expression.

Source code in brmspy/types/formula_dsl.py
def __add__(self, other: Other):
    """
    Combine two formula expressions with `+`.

    Parameters
    ----------
    other
        Value to add. Strings are treated as `bf(<string>)`.

    Returns
    -------
    FormulaConstruct
        New combined expression.
    """
    if isinstance(other, (FormulaPart, str, ProxyListSexpVector)):
        other = FormulaConstruct._formula_parse(other)

    if not isinstance(other, FormulaConstruct):
        raise ArithmeticError(
            "When adding values to formula, they must be FormulaConstruct or parseable to FormulaConstruct"
        )

    if len(other._parts) <= 1:
        return FormulaConstruct(_parts=self._parts + other._parts)
    else:
        return FormulaConstruct(_parts=[self._parts, other._parts])
__radd__(other)

Support "y ~ x" + bf("z ~ 1") by coercing the left operand.

Source code in brmspy/types/formula_dsl.py
def __radd__(self, other: Other) -> "FormulaConstruct":
    """Support `"y ~ x" + bf("z ~ 1")` by coercing the left operand."""
    return self._formula_parse(other) + self
iter_summands()

Iterate over arithmetic groups (summands).

Returns:

Type Description
Iterator[tuple[FormulaPart | ProxyListSexpVector, ...]]

Each yielded tuple represents one summand/group.

Examples:

from brmspy.brms import bf, gaussian, set_rescor

f = bf("y ~ x") + gaussian() + set_rescor(True)
for summand in f.iter_summands():
    print(summand)
Source code in brmspy/types/formula_dsl.py
def iter_summands(self) -> Iterator[Summand]:
    """
    Iterate over arithmetic groups (summands).

    Returns
    -------
    Iterator[tuple[FormulaPart | ProxyListSexpVector, ...]]
        Each yielded tuple represents one summand/group.

    Examples
    --------
    ```python
    from brmspy.brms import bf, gaussian, set_rescor

    f = bf("y ~ x") + gaussian() + set_rescor(True)
    for summand in f.iter_summands():
        print(summand)
    ```
    """

    def _groups(node: Node) -> Iterator[list[FormulaPart | ProxyListSexpVector]]:
        # Leaf node: single bf/family/etc
        if isinstance(node, (FormulaPart, ProxyListSexpVector)):
            return ([node],)  # one group with one element

        if isinstance(node, list):
            # If any child is a list, this node represents a "+"
            # between sub-expressions, so recurse into each child.
            if any(isinstance(child, list) for child in node):
                for child in node:
                    yield from _groups(child)
            else:
                # All children are leaves -> one summand
                out: list[FormulaPart | ProxyListSexpVector] = []
                for child in node:
                    if isinstance(child, (FormulaPart, ProxyListSexpVector, Sexp)):
                        child = cast(FormulaPart | ProxyListSexpVector, child)
                        out.append(child)
                    else:
                        raise TypeError(
                            f"Unexpected leaf node type in FormulaConstruct: {type(child)!r}"
                        )
                yield out
            return

        raise TypeError(f"Unexpected node type in FormulaConstruct: {type(node)!r}")

    # self._parts is always a list[Node]
    for group in _groups(self._parts):
        yield tuple(group)
__iter__()

Alias for iter_summands().

Source code in brmspy/types/formula_dsl.py
def __iter__(self) -> Iterator[Summand]:
    """Alias for [`iter_summands()`][brmspy.types.formula_dsl.FormulaConstruct.iter_summands]."""
    return self.iter_summands()
iterate()

Iterate over all leaf nodes in left-to-right order.

This flattens the expression tree, unlike iter_summands(), which respects grouping.

Returns:

Type Description
Iterator[FormulaPart | ProxyListSexpVector]
Source code in brmspy/types/formula_dsl.py
def iterate(self) -> Iterator[FormulaPart | ProxyListSexpVector]:
    """
    Iterate over all leaf nodes in left-to-right order.

    This flattens the expression tree, unlike
    [`iter_summands()`][brmspy.types.formula_dsl.FormulaConstruct.iter_summands], which
    respects grouping.

    Returns
    -------
    Iterator[FormulaPart | ProxyListSexpVector]
    """

    def _walk(node: Node) -> Iterator[FormulaPart | ProxyListSexpVector]:
        if isinstance(node, FormulaPart):
            yield node
        elif isinstance(node, ProxyListSexpVector):
            yield node
        elif isinstance(node, list):
            for child in node:
                yield from _walk(child)
        else:
            raise TypeError(
                f"Unexpected node type in FormulaConstruct: {type(node)!r}"
            )

    for root in self._parts:
        yield from _walk(root)
__str__()
Source code in brmspy/types/formula_dsl.py
def __str__(self) -> str:
    return self._pretty(self._parts)
_pretty(node, _outer=True)
Source code in brmspy/types/formula_dsl.py
def _pretty(self, node, _outer=True) -> str:
    if isinstance(node, FormulaPart):
        return str(node)

    if isinstance(node, (ProxyListSexpVector, Sexp)):
        return _sexp_to_str(node)

    if isinstance(node, list):
        # Pretty-print each child
        rendered = [self._pretty(child, _outer=False) for child in node]

        # If only one child, no parentheses needed
        if len(rendered) == 1:
            return rendered[0]

        # Multiple children → join with " + "
        inner = " + ".join(rendered)
        if _outer:
            return inner
        else:
            return f"({inner})"

    raise TypeError(f"Unexpected node type {type(node)!r} in pretty-printer")
__repr__()
Source code in brmspy/types/formula_dsl.py
def __repr__(self) -> str:
    return self.__str__()
__init__(_parts)

FormulaPart dataclass

A single formula helper invocation.

Instances of this type represent a call like bf("y ~ x") or set_rescor(True) without executing anything. They are primarily used as nodes inside a FormulaConstruct.

Parameters:

Name Type Description Default
_fun Literal[...]

Whitelisted formula helper name.

required
_args Sequence[Primitive]

Positional arguments for the helper.

required
_kwargs Mapping[str, Primitive]

Keyword arguments for the helper.

required
Notes

This is a low-level type. Most users should construct these via the public helper functions in brmspy.brms.

Source code in brmspy/types/formula_dsl.py
@dataclass
class FormulaPart:
    """
    A single formula helper invocation.

    Instances of this type represent a call like `bf("y ~ x")` or `set_rescor(True)`
    without executing anything. They are primarily used as nodes inside a
    [`FormulaConstruct`][brmspy.types.formula_dsl.FormulaConstruct].

    Parameters
    ----------
    _fun : Literal[...]
        Whitelisted formula helper name.
    _args : Sequence[Primitive]
        Positional arguments for the helper.
    _kwargs : Mapping[str, Primitive]
        Keyword arguments for the helper.

    Notes
    -----
    This is a low-level type. Most users should construct these via the public
    helper functions in [`brmspy.brms`][brmspy.brms].
    """

    _fun: _FORMULA_FUNCTION_WHITELIST
    _args: Sequence[Primitive]
    _kwargs: Mapping[str, Primitive]

    def __post_init__(self):
        """Validate `_fun`, `_args`, and `_kwargs` types after construction."""
        # Validate function name first
        if self._fun not in get_args(_FORMULA_FUNCTION_WHITELIST):
            raise ValueError(
                f"FormulaPart._fun must be one of {_FORMULA_FUNCTION_WHITELIST!r}, "
                f"got {self._fun!r}"
            )

        # Enforce _args is a list
        if not isinstance(self._args, Sequence):
            raise TypeError(
                f"FormulaPart._args must be a Sequence, got {type(self._args).__name__}"
            )

        # Enforce _kwargs is a dict
        if not isinstance(self._kwargs, Mapping):
            raise TypeError(
                f"FormulaPart._kwargs must be a Mapping, got {type(self._kwargs).__name__}"
            )

    def __str__(self) -> str:
        """Render a readable `fun(arg1, ..., kw=...)` representation."""
        args = ", ".join(repr(a) for a in self._args)
        kwargs = ", ".join(f"{k}={v!r}" for k, v in self._kwargs.items())
        inner = ", ".join(x for x in (args, kwargs) if x)
        return f"{self._fun}({inner})"

    def __repr__(self) -> str:
        return self.__str__()

Attributes

_fun instance-attribute
_args instance-attribute
_kwargs instance-attribute

Functions

__post_init__()

Validate _fun, _args, and _kwargs types after construction.

Source code in brmspy/types/formula_dsl.py
def __post_init__(self):
    """Validate `_fun`, `_args`, and `_kwargs` types after construction."""
    # Validate function name first
    if self._fun not in get_args(_FORMULA_FUNCTION_WHITELIST):
        raise ValueError(
            f"FormulaPart._fun must be one of {_FORMULA_FUNCTION_WHITELIST!r}, "
            f"got {self._fun!r}"
        )

    # Enforce _args is a list
    if not isinstance(self._args, Sequence):
        raise TypeError(
            f"FormulaPart._args must be a Sequence, got {type(self._args).__name__}"
        )

    # Enforce _kwargs is a dict
    if not isinstance(self._kwargs, Mapping):
        raise TypeError(
            f"FormulaPart._kwargs must be a Mapping, got {type(self._kwargs).__name__}"
        )
__str__()

Render a readable fun(arg1, ..., kw=...) representation.

Source code in brmspy/types/formula_dsl.py
def __str__(self) -> str:
    """Render a readable `fun(arg1, ..., kw=...)` representation."""
    args = ", ".join(repr(a) for a in self._args)
    kwargs = ", ".join(f"{k}={v!r}" for k, v in self._kwargs.items())
    inner = ", ".join(x for x in (args, kwargs) if x)
    return f"{self._fun}({inner})"
__repr__()
Source code in brmspy/types/formula_dsl.py
def __repr__(self) -> str:
    return self.__str__()
__init__(_fun, _args, _kwargs)

PriorSpec dataclass

Python representation of a brms prior specification.

This dataclass provides a typed interface to brms::prior_string() arguments, allowing Python developers to specify priors with IDE autocomplete and type checking. Use the prior() factory function to create instances.

Attributes:

Name Type Description
prior str

Prior distribution as string (e.g., "normal(0, 1)", "exponential(2)").

class_ (str, optional)

Parameter class: "b" (fixed effects), "sd" (group SD), "Intercept", "sigma", "cor", etc.

coef (str, optional)

Specific coefficient name for class-level priors.

group (str, optional)

Grouping variable for hierarchical effects.

dpar (str, optional)

Distributional parameter (e.g., "sigma", "phi", "zi").

resp (str, optional)

Response variable for multivariate models.

nlpar (str, optional)

Non-linear parameter name.

lb (float, optional)

Lower bound for truncated priors.

ub (float, optional)

Upper bound for truncated priors.

See Also

prior : Factory function to create PriorSpec instances. brms::prior_string : R documentation

Examples:

Create prior specifications (prefer using prior()):

from brmspy.types import PriorSpec

# Fixed effect prior
p1 = PriorSpec(prior="normal(0, 1)", class_="b")

# Group-level SD prior
p2 = PriorSpec(prior="exponential(2)", class_="sd", group="patient")

# Coefficient-specific prior with bounds
p3 = PriorSpec(prior="normal(0, 1)", class_="b", coef="age", lb=0)
Source code in brmspy/types/brms_results.py
@dataclass(frozen=True)
class PriorSpec:
    """
    Python representation of a brms prior specification.

    This dataclass provides a typed interface to `brms::prior_string()` arguments,
    allowing Python developers to specify priors with IDE autocomplete and type
    checking. Use the [`prior()`][brmspy.brms.prior] factory function to create
    instances.

    Attributes
    ----------
    prior : str
        Prior distribution as string (e.g., ``"normal(0, 1)"``, ``"exponential(2)"``).
    class_ : str, optional
        Parameter class: ``"b"`` (fixed effects), ``"sd"`` (group SD),
        ``"Intercept"``, ``"sigma"``, ``"cor"``, etc.
    coef : str, optional
        Specific coefficient name for class-level priors.
    group : str, optional
        Grouping variable for hierarchical effects.
    dpar : str, optional
        Distributional parameter (e.g., ``"sigma"``, ``"phi"``, ``"zi"``).
    resp : str, optional
        Response variable for multivariate models.
    nlpar : str, optional
        Non-linear parameter name.
    lb : float, optional
        Lower bound for truncated priors.
    ub : float, optional
        Upper bound for truncated priors.

    See Also
    --------
    prior : Factory function to create `PriorSpec` instances.
    brms::prior_string : [R documentation](https://paulbuerkner.com/brms/reference/prior_string.html)

    Examples
    --------
    Create prior specifications (prefer using [`prior()`][brmspy.brms.prior]):

    ```python
    from brmspy.types import PriorSpec

    # Fixed effect prior
    p1 = PriorSpec(prior="normal(0, 1)", class_="b")

    # Group-level SD prior
    p2 = PriorSpec(prior="exponential(2)", class_="sd", group="patient")

    # Coefficient-specific prior with bounds
    p3 = PriorSpec(prior="normal(0, 1)", class_="b", coef="age", lb=0)
    ```
    """

    prior: str
    class_: str | None = None
    coef: str | None = None
    group: str | None = None
    dpar: str | None = None
    resp: str | None = None
    nlpar: str | None = None
    lb: float | None = None
    ub: float | None = None

    def to_brms_kwargs(self) -> dict[str, Any]:
        """
        Convert PriorSpec to keyword arguments for brms::prior_string().

        Maps Python dataclass fields to R function arguments, handling
        the `class_` -> `class` parameter name conversion.

        Returns
        -------
        dict
            Keyword arguments ready for brms::prior_string()

        Examples
        --------
        ```python
        from brmspy import prior
        p = prior("normal(0, 1)", class_="b", coef="age")
        kwargs = p.to_brms_kwargs()
        print(kwargs)
        # {'prior': 'normal(0, 1)', 'class': 'b', 'coef': 'age'}
        ```
        """
        out: dict[str, Any] = {"prior": self.prior}
        if self.class_ is not None:
            out["class"] = self.class_
        if self.coef is not None:
            out["coef"] = self.coef
        if self.group is not None:
            out["group"] = self.group
        if self.dpar is not None:
            out["dpar"] = self.dpar
        if self.resp is not None:
            out["resp"] = self.resp
        if self.nlpar is not None:
            out["nlpar"] = self.nlpar
        if self.lb is not None:
            out["lb"] = self.lb
        if self.ub is not None:
            out["ub"] = self.ub
        return out

Attributes

prior instance-attribute
class_ = None class-attribute instance-attribute
coef = None class-attribute instance-attribute
group = None class-attribute instance-attribute
dpar = None class-attribute instance-attribute
resp = None class-attribute instance-attribute
nlpar = None class-attribute instance-attribute
lb = None class-attribute instance-attribute
ub = None class-attribute instance-attribute

Functions

to_brms_kwargs()

Convert PriorSpec to keyword arguments for brms::prior_string().

Maps Python dataclass fields to R function arguments, handling the class_ -> class parameter name conversion.

Returns:

Type Description
dict

Keyword arguments ready for brms::prior_string()

Examples:

from brmspy import prior
p = prior("normal(0, 1)", class_="b", coef="age")
kwargs = p.to_brms_kwargs()
print(kwargs)
# {'prior': 'normal(0, 1)', 'class': 'b', 'coef': 'age'}
Source code in brmspy/types/brms_results.py
def to_brms_kwargs(self) -> dict[str, Any]:
    """
    Convert PriorSpec to keyword arguments for brms::prior_string().

    Maps Python dataclass fields to R function arguments, handling
    the `class_` -> `class` parameter name conversion.

    Returns
    -------
    dict
        Keyword arguments ready for brms::prior_string()

    Examples
    --------
    ```python
    from brmspy import prior
    p = prior("normal(0, 1)", class_="b", coef="age")
    kwargs = p.to_brms_kwargs()
    print(kwargs)
    # {'prior': 'normal(0, 1)', 'class': 'b', 'coef': 'age'}
    ```
    """
    out: dict[str, Any] = {"prior": self.prior}
    if self.class_ is not None:
        out["class"] = self.class_
    if self.coef is not None:
        out["coef"] = self.coef
    if self.group is not None:
        out["group"] = self.group
    if self.dpar is not None:
        out["dpar"] = self.dpar
    if self.resp is not None:
        out["resp"] = self.resp
    if self.nlpar is not None:
        out["nlpar"] = self.nlpar
    if self.lb is not None:
        out["lb"] = self.lb
    if self.ub is not None:
        out["ub"] = self.ub
    return out
__init__(prior, class_=None, coef=None, group=None, dpar=None, resp=None, nlpar=None, lb=None, ub=None)

Functions

py_to_r(obj)

Convert arbitrary Python objects to R objects via rpy2.

Comprehensive converter that handles nested structures (dicts, lists), DataFrames, arrays, and scalars. Uses rpy2's converters with special handling for dictionaries (→ R named lists) and lists of dicts.

Parameters:

Name Type Description Default
obj any

Python object to convert. Supported types: - None → R NULL - dict → R named list (ListVector), recursively - list/tuple of dicts → R list of named lists - list/tuple (other) → R vector or list - pd.DataFrame → R data.frame - np.ndarray → R vector/matrix - scalars (int, float, str, bool) → R atomic types

required

Returns:

Type Description
rpy2 R object

R representation of the Python object

Notes

Conversion Rules:

  1. None: → R NULL
  2. DataFrames: → R data.frame (via pandas2ri)
  3. Dictionaries: → R named list (ListVector), recursively converting values
  4. Lists of dicts: → R list with 1-based indexed names containing named lists
  5. Other lists/tuples: → R vectors or lists (via rpy2 default)
  6. NumPy arrays: → R vectors/matrices (via numpy2ri)
  7. Scalars: → R atomic values

Recursive Conversion:

Dictionary values are recursively converted, allowing nested structures:

{'a': {'b': [1, 2, 3]}}    list(a = list(b = c(1, 2, 3)))

List of Dicts:

Lists containing only dicts are converted to R lists with 1-based indexing:

[{'x': 1}, {'x': 2}]    list("1" = list(x = 1), "2" = list(x = 2))

Examples:

from brmspy.helpers.conversion import py_to_r
import numpy as np
import pandas as pd

# Scalars
py_to_r(5)        # R: 5
py_to_r("hello")  # R: "hello"
py_to_r(None)     # R: NULL

# Arrays
py_to_r(np.array([1, 2, 3]))  # R: c(1, 2, 3)

# DataFrames
df = pd.DataFrame({'x': [1, 2], 'y': [3, 4]})
py_to_r(df)  # R: data.frame(x = c(1, 2), y = c(3, 4))
See Also

r_to_py : Convert R objects back to Python kwargs_r : Convert keyword arguments dict for R function calls brmspy.brms.fit : Uses this for converting data to R

Source code in brmspy/helpers/_rpy2/_converters/_dispatch.py
def py_to_r(obj: PyObject) -> Sexp:
    """
    Convert arbitrary Python objects to R objects via rpy2.

    Comprehensive converter that handles nested structures (dicts, lists),
    DataFrames, arrays, and scalars. Uses rpy2's converters with special
    handling for dictionaries (→ R named lists) and lists of dicts.

    Parameters
    ----------
    obj : any
        Python object to convert. Supported types:
        - None → R NULL
        - dict → R named list (ListVector), recursively
        - list/tuple of dicts → R list of named lists
        - list/tuple (other) → R vector or list
        - pd.DataFrame → R data.frame
        - np.ndarray → R vector/matrix
        - scalars (int, float, str, bool) → R atomic types

    Returns
    -------
    rpy2 R object
        R representation of the Python object

    Notes
    -----
    **Conversion Rules:**

    1. **None**: → R NULL
    2. **DataFrames**: → R data.frame (via pandas2ri)
    3. **Dictionaries**: → R named list (ListVector), recursively converting values
    4. **Lists of dicts**: → R list with 1-based indexed names containing named lists
    5. **Other lists/tuples**: → R vectors or lists (via rpy2 default)
    6. **NumPy arrays**: → R vectors/matrices (via numpy2ri)
    7. **Scalars**: → R atomic values

    **Recursive Conversion:**

    Dictionary values are recursively converted, allowing nested structures:
    ```python
    {'a': {'b': [1, 2, 3]}}  →  list(a = list(b = c(1, 2, 3)))
    ```

    **List of Dicts:**

    Lists containing only dicts are converted to R lists with 1-based indexing:
    ```python
    [{'x': 1}, {'x': 2}]  →  list("1" = list(x = 1), "2" = list(x = 2))
    ```

    Examples
    --------

    ```python
    from brmspy.helpers.conversion import py_to_r
    import numpy as np
    import pandas as pd

    # Scalars
    py_to_r(5)        # R: 5
    py_to_r("hello")  # R: "hello"
    py_to_r(None)     # R: NULL

    # Arrays
    py_to_r(np.array([1, 2, 3]))  # R: c(1, 2, 3)

    # DataFrames
    df = pd.DataFrame({'x': [1, 2], 'y': [3, 4]})
    py_to_r(df)  # R: data.frame(x = c(1, 2), y = c(3, 4))
    ```

    See Also
    --------
    r_to_py : Convert R objects back to Python
    kwargs_r : Convert keyword arguments dict for R function calls
    brmspy.brms.fit : Uses this for converting data to R
    """
    import rpy2.robjects as ro

    if obj is None:
        return ro.NULL

    if isinstance(obj, ro.Sexp):
        return obj

    if isinstance(obj, RListVectorExtension) and isinstance(obj.r, ro.Sexp):
        return obj.r

    _type = type(obj)
    converter = None

    if _type in _registry._PY2R_CONVERTERS:
        # O(1) lookup first
        converter = _registry._PY2R_CONVERTERS[_type]
    else:
        for _type, _con in _registry._PY2R_CONVERTERS.items():
            if isinstance(obj, _type):
                converter = _con
                break

    assert len(_registry._PY2R_CONVERTERS) > 0, "NO PY2R CONVERTERS"
    assert (
        converter
    ), "object fallback must be in place in __init__.py! This is an issue with the library, not the user!"
    return converter(obj)

_build_priors(priors=None)

Build R brms prior object from Python PriorSpec specifications.

Converts a sequence of PriorSpec objects to a single combined R brms prior object by calling brms::prior_string() for each spec and combining with +. Used internally by fit() to translate Python prior specifications to R.

Parameters:

Name Type Description Default
priors sequence of PriorSpec

List of prior specifications. Each PriorSpec contains: - prior: Prior distribution string (e.g., "normal(0, 1)") - class_: Parameter class (e.g., "b", "Intercept", "sigma") - coef: Specific coefficient name (optional) - group: Group-level effects (optional)

If None or empty, returns empty list (brms uses default priors)

None

Returns:

Type Description
R brmsprior object or list

Combined R brms prior object if priors provided, empty list otherwise

Raises:

Type Description
AssertionError

If combined result is not a valid brmsprior object

Notes

Prior Combination:

Multiple priors are combined using R's + operator:

prior1 + prior2 + prior3

This creates a single brmsprior object containing all specifications.

brms Prior Classes:

Common parameter classes: - b: Population-level effects (regression coefficients) - Intercept: Model intercept - sigma: Residual standard deviation (for gaussian family) - sd: Standard deviation of group-level effects - cor: Correlation of group-level effects

Prior String Format:

brms uses Stan-style prior specifications: - Normal: "normal(mean, sd)" - Student-t: "student_t(df, location, scale)" - Cauchy: "cauchy(location, scale)" - Exponential: "exponential(rate)" - Uniform: "uniform(lower, upper)"

Examples:

from brmspy.types import PriorSpec
from brmspy.helpers.priors import _build_priors

# Single prior for regression coefficients
priors = [
    PriorSpec(
        prior="normal(0, 1)",
        class_="b"
    )
]
brms_prior = _build_priors(priors)
See Also

brmspy.types.PriorSpec : Prior specification class brmspy.brms.fit : Uses this to convert priors for model fitting brms::prior : R brms prior specification brms::set_prior : R function for setting priors

References

.. [1] brms prior documentation: https://paul-buerkner.github.io/brms/reference/set_prior.html .. [2] Stan prior choice recommendations: https://github.com/stan-dev/stan/wiki/Prior-Choice-Recommendations

Source code in brmspy/helpers/_rpy2/_priors.py
def _build_priors(
    priors: None | Sequence[PriorSpec] = None,
) -> list[Sexp]:
    """
    Build R brms prior object from Python PriorSpec specifications.

    Converts a sequence of PriorSpec objects to a single combined R brms prior
    object by calling brms::prior_string() for each spec and combining with `+`.
    Used internally by fit() to translate Python prior specifications to R.

    Parameters
    ----------
    priors : sequence of PriorSpec, optional
        List of prior specifications. Each PriorSpec contains:
        - prior: Prior distribution string (e.g., "normal(0, 1)")
        - class_: Parameter class (e.g., "b", "Intercept", "sigma")
        - coef: Specific coefficient name (optional)
        - group: Group-level effects (optional)

        If None or empty, returns empty list (brms uses default priors)

    Returns
    -------
    R brmsprior object or list
        Combined R brms prior object if priors provided, empty list otherwise

    Raises
    ------
    AssertionError
        If combined result is not a valid brmsprior object

    Notes
    -----
    **Prior Combination:**

    Multiple priors are combined using R's `+` operator:
    ```R
    prior1 + prior2 + prior3
    ```

    This creates a single brmsprior object containing all specifications.

    **brms Prior Classes:**

    Common parameter classes:
    - **b**: Population-level effects (regression coefficients)
    - **Intercept**: Model intercept
    - **sigma**: Residual standard deviation (for gaussian family)
    - **sd**: Standard deviation of group-level effects
    - **cor**: Correlation of group-level effects

    **Prior String Format:**

    brms uses Stan-style prior specifications:
    - Normal: "normal(mean, sd)"
    - Student-t: "student_t(df, location, scale)"
    - Cauchy: "cauchy(location, scale)"
    - Exponential: "exponential(rate)"
    - Uniform: "uniform(lower, upper)"

    Examples
    --------

    ```python
    from brmspy.types import PriorSpec
    from brmspy.helpers.priors import _build_priors

    # Single prior for regression coefficients
    priors = [
        PriorSpec(
            prior="normal(0, 1)",
            class_="b"
        )
    ]
    brms_prior = _build_priors(priors)
    ```

    See Also
    --------
    brmspy.types.PriorSpec : Prior specification class
    brmspy.brms.fit : Uses this to convert priors for model fitting
    brms::prior : R brms prior specification
    brms::set_prior : R function for setting priors

    References
    ----------
    .. [1] brms prior documentation: https://paul-buerkner.github.io/brms/reference/set_prior.html
    .. [2] Stan prior choice recommendations: https://github.com/stan-dev/stan/wiki/Prior-Choice-Recommendations
    """
    if not priors:
        return []
    import rpy2.robjects as ro

    from brmspy.helpers._rpy2._converters import r_to_py

    fun_prior_string = cast(Callable, ro.r("brms::prior_string"))

    prior_objs = []
    for p in priors:
        kwargs = p.to_brms_kwargs()
        # first argument is the prior string
        prior_str = kwargs.pop("prior")
        prior_obj = fun_prior_string(prior_str, **kwargs)
        prior_objs.append(prior_obj)

    brms_prior = prior_objs[0]
    for p in prior_objs[1:]:
        brms_prior = brms_prior + p

    return brms_prior

_execute_formula(formula)

Source code in brmspy/_brms_functions/formula.py
def _execute_formula(formula: FormulaConstruct | Sexp | str) -> Sexp:
    import rpy2.robjects as ro

    if isinstance(formula, Sexp):
        return formula
    if isinstance(formula, str):
        formula = FormulaConstruct._formula_parse(formula)

    # Must run for formula functions, e.g me() to register
    ro.r("library(brms)")

    fun_add = cast(Callable[[Sexp, Sexp], Sexp], ro.r("function (a, b) a + b"))

    result: Sexp | None = None
    for summand in formula:
        subresult: Sexp = py_to_r(summand[0])
        for part in summand[1:]:
            subresult = fun_add(subresult, py_to_r(part))

        if result is None:
            result = subresult
        else:
            result = fun_add(result, subresult)

    assert result is not None
    return result

bf(*formulas, **formula_args)

Build a brms model formula.

This is the primary entrypoint for specifying the mean model and can be combined with other formula parts (e.g. lf, nlf, acformula) using +.

Parameters:

Name Type Description Default
*formulas str

One or more brms formula strings (e.g. "y ~ x + (1|group)"). Multiple formulas are commonly used for multivariate models.

()
**formula_args

Keyword arguments forwarded to R brms::brmsformula() (for example decomp="QR", center=True, sparse=True, nl=True, loop=True).

{}

Returns:

Type Description
FormulaConstruct

A composable formula specification.

See Also

brms::brmsformula : R documentation

Examples:

Basic formula:

from brmspy.brms import bf

f = bf("y ~ x1 + x2 + (1|group)")

QR decomposition (often helps with collinearity):

from brmspy.brms import bf

f = bf("reaction ~ days + (days|subject)", decomp="QR")

Multivariate formula + residual correlation:

from brmspy.brms import bf, set_rescor

f = bf("mvbind(y1, y2) ~ x") + set_rescor(True)
Source code in brmspy/_brms_functions/formula.py
def bf(*formulas: str, **formula_args) -> FormulaConstruct:
    """
    Build a brms model formula.

    This is the primary entrypoint for specifying the mean model and can be
    combined with other formula parts (e.g. `lf`, `nlf`, `acformula`) using ``+``.

    Parameters
    ----------
    *formulas : str
        One or more brms formula strings (e.g. ``"y ~ x + (1|group)"``). Multiple
        formulas are commonly used for multivariate models.
    **formula_args
        Keyword arguments forwarded to R ``brms::brmsformula()`` (for example
        ``decomp="QR"``, ``center=True``, ``sparse=True``, ``nl=True``, ``loop=True``).

    Returns
    -------
    FormulaConstruct
        A composable formula specification.

    See Also
    --------
    brms::brmsformula : [R documentation](https://paulbuerkner.com/brms/reference/brmsformula.html)

    Examples
    --------
    Basic formula:

    ```python
    from brmspy.brms import bf

    f = bf("y ~ x1 + x2 + (1|group)")
    ```

    QR decomposition (often helps with collinearity):

    ```python
    from brmspy.brms import bf

    f = bf("reaction ~ days + (days|subject)", decomp="QR")
    ```

    Multivariate formula + residual correlation:

    ```python
    from brmspy.brms import bf, set_rescor

    f = bf("mvbind(y1, y2) ~ x") + set_rescor(True)
    ```
    """
    part = FormulaPart(_fun="bf", _args=list(formulas), _kwargs=formula_args)
    return FormulaConstruct._formula_parse(part)

make_stancode(formula, data, priors=None, family='poisson', sample_prior='no', formula_args=None)

Generate Stan code using R brms::make_stancode().

Useful for inspecting the generated Stan model before fitting.

Parameters:

Name Type Description Default
formula str or FormulaConstruct

Model formula.

required
data DataFrame

Model data.

required
priors Sequence[PriorSpec] or None

Optional prior specifications created via brmspy.brms.prior().

None
family str

Distribution family (e.g. "gaussian", "poisson").

"poisson"
sample_prior str

Prior sampling mode passed to brms ("no", "yes", "only").

"no"
formula_args dict or None

Reserved for future use. Currently ignored.

None

Returns:

Type Description
str

Complete Stan program as a string.

See Also

brms::make_stancode : R documentation

Examples:

from brmspy import brms

epilepsy = brms.get_brms_data("epilepsy")
code = brms.make_stancode(
    "count ~ zAge + zBase * Trt + (1|patient)",
    data=epilepsy,
    family="poisson",
)

assert isinstance(code, str)
Source code in brmspy/_brms_functions/stan.py
def make_stancode(
    formula: FormulaConstruct | str,
    data: pd.DataFrame,
    priors: typing.Sequence[PriorSpec] | None = None,
    family: str = "poisson",
    sample_prior: str = "no",
    formula_args: dict | None = None,
) -> str:
    """
    Generate Stan code using R ``brms::make_stancode()``.

    Useful for inspecting the generated Stan model before fitting.

    Parameters
    ----------
    formula : str or FormulaConstruct
        Model formula.
    data : pandas.DataFrame
        Model data.
    priors : Sequence[PriorSpec] or None, default=None
        Optional prior specifications created via `brmspy.brms.prior()`.
    family : str, default="poisson"
        Distribution family (e.g. ``"gaussian"``, ``"poisson"``).
    sample_prior : str, default="no"
        Prior sampling mode passed to brms (``"no"``, ``"yes"``, ``"only"``).
    formula_args : dict or None, default=None
        Reserved for future use. Currently ignored.

    Returns
    -------
    str
        Complete Stan program as a string.

    See Also
    --------
    brms::make_stancode : [R documentation](https://paulbuerkner.com/brms/reference/make_stancode.html)

    Examples
    --------
    ```python
    from brmspy import brms

    epilepsy = brms.get_brms_data("epilepsy")
    code = brms.make_stancode(
        "count ~ zAge + zBase * Trt + (1|patient)",
        data=epilepsy,
        family="poisson",
    )

    assert isinstance(code, str)
    ```
    """
    import rpy2.robjects as ro

    fun_make_stancode = typing.cast(typing.Callable, ro.r("brms::make_stancode"))

    data_r = py_to_r(data)
    priors_r = _build_priors(priors)
    if isinstance(formula, FormulaConstruct):
        formula_obj = _execute_formula(formula)
    else:
        if formula_args is None:
            formula_args = {}
        formula = FormulaConstruct._formula_parse(formula)
        formula_obj = _execute_formula(formula)

    if len(priors_r) > 0:
        return fun_make_stancode(
            formula=formula_obj,
            data=data_r,
            prior=priors_r,
            family=family,
            sample_prior=sample_prior,
        )[0]
    else:
        return fun_make_stancode(
            formula=formula_obj, data=data_r, family=family, sample_prior=sample_prior
        )[0]