Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Model fitting, diagnostics, convergence, varying parameters, and prediction.

Call chain:

model.fit() -> fit_model() -> resolve_solver() -> fit_ols_qr / fit_glm_irls / fit_lmer_pls / fit_glmer_pirls

Attributes:

NameTypeDescription
VALID_SOLVERSfrozenset[str]

Classes:

NameDescription
FitResultImmutable result of the fit lifecycle.

Functions:

NameDescription
augment_data_with_diagnosticsAugment raw data with diagnostic columns after fit.
build_mixed_post_fit_stateCompute BLUPs, variance components, and emit convergence warnings.
build_predict_gridBuild a Cartesian-product prediction grid.
check_convergenceRun convergence diagnostics on a fitted mixed model.
compute_diagnosticsCompute model-level diagnostics as a single-row DataFrame.
compute_metadataCompute model metadata as a single-row DataFrame.
compute_optimizer_diagnosticsCompute optimizer convergence diagnostics as a single-row DataFrame.
compute_predictions_from_formulaParse a predict formula, build the grid, compute predictions, and attach grid columns.
compute_r_squaredCompute R-squared and adjusted R-squared from raw arrays.
compute_varying_spread_stateCompute VaryingSpreadState (variance components) from theta parameters.
compute_varying_stateCompute VaryingState (BLUPs) from fitted random effects parameters.
execute_fitExecute the full fit lifecycle: bundle rebuild → fit → post-fit state → diagnostics.
fit_glm_irlsFit generalized linear model using Iteratively Reweighted Least Squares.
fit_glmer_pirlsFit generalized linear mixed model using Penalized IRLS.
fit_lmer_plsFit linear mixed-effects model using Penalized Least Squares.
fit_modelDispatch to appropriate fitter based on model specification.
fit_ols_qrFit ordinary or weighted least squares using QR decomposition.
get_theta_lower_boundsGet lower bounds for theta parameters.
parse_fit_kwargsValidate and extract fitting parameters from **kwargs.
parse_predict_formulaParse an explore-style formula and build a prediction grid.
per_factor_re_infoSplit global RE metadata into per-factor structures and names.
resolve_condition_valuesResolve a :class:Condition to concrete values or None.
resolve_solverSelect the appropriate solver for a model configuration.
validate_fit_methodValidate and apply a user-specified fitting method to a ModelSpec.

Modules:

NameDescription
convergenceConvergence diagnostics for fitted mixed-effects models.
diagnosticsModel-level diagnostics computation.
dispatchSolver dispatch for model fitting.
glmGLM fitting via Iteratively Reweighted Least Squares (IRLS).
glmerGLMM fitting via Penalized IRLS (PIRLS).
gridPrediction grid construction for formula-mode predictions.
lifecycleFit lifecycle orchestration.
lmerLMM fitting via Penalized Least Squares (PLS).
olsOLS fitting via QR decomposition.
predictPrediction operations on containers.
varyingVarying parameter extraction for mixed-effects models.

Attributes

VALID_SOLVERS

VALID_SOLVERS: frozenset[str] = frozenset({'qr', 'irls', 'pls', 'pirls'})

Classes

FitResult

Immutable result of the fit lifecycle.

Attributes:

NameTypeDescription
fitFitStateFitted model state (coefficients, residuals, etc.).
bundleDataBundleData bundle used for fitting (may be rebuilt).
formula_specobjectLearned formula spec for newdata evaluation.
raw_dataDataFrame | NoneOriginal data snapshot (pre-augmentation).
augmented_dataDataFrame | NoneData with diagnostic columns, or None.
varying_offsetsVaryingState | NoneBLUPs for mixed models, or None.
varying_spreadVaryingSpreadState | NoneVariance components for mixed models, or None.

Attributes

augmented_data
augmented_data: pl.DataFrame | None
bundle
bundle: DataBundle
fit
fit: FitState
formula_spec
formula_spec: object
raw_data
raw_data: pl.DataFrame | None
varying_offsets
varying_offsets: VaryingState | None = None
varying_spread
varying_spread: VaryingSpreadState | None = None

Functions

augment_data_with_diagnostics

augment_data_with_diagnostics(*, raw_data: pl.DataFrame, fit: FitState, bundle: DataBundle) -> pl.DataFrame

Augment raw data with diagnostic columns after fit.

Adds fitted, resid, hat, std_resid, cooksd columns (names from AugmentedDataCols schema). Values are NaN for rows dropped due to missing data.

Parameters:

NameTypeDescriptionDefault
raw_dataDataFrameOriginal data DataFrame (pre-NA-drop).required
fitFitStateFitted state with residuals, fitted values, leverage.required
bundleDataBundleData bundle with valid_mask, n_total, p.required

Returns:

TypeDescription
DataFrameDataFrame with diagnostic columns appended.

build_mixed_post_fit_state

build_mixed_post_fit_state(fit: FitState, bundle: DataBundle, data: pl.DataFrame, *, stacklevel: int = 3) -> tuple[VaryingState | None, VaryingSpreadState | None]

Compute BLUPs, variance components, and emit convergence warnings.

Orchestrates the post-fit assembly for mixed-effects models: computes VaryingState (BLUPs) and VaryingSpreadState (variance components) from the fitted parameters, then checks for convergence issues.

Parameters:

NameTypeDescriptionDefault
fitFitStateFitted model state containing theta, u, sigma.required
bundleDataBundleData bundle with RE metadata and valid mask.required
dataDataFrameOriginal training data (used for group level labels).required
stacklevelintWarning stacklevel for convergence warnings. Default 3 accounts for: user → model.fit()build_mixed_post_fit_state().3

Returns:

TypeDescription
VaryingState | NoneA tuple (varying_offsets, varying_spread) where either may be
VaryingSpreadState | NoneNone if the required fitted parameters are missing.

build_predict_grid

build_predict_grid(data: pl.DataFrame, focal_var: str, response_col: str, grouping_factors: tuple[str, ...], *, focal_values: list[float | str] | None = None, n_points: int | Literal['data'] = 50, varying_vars: list[str] | None = None, at: dict[str, Any] | None = None) -> pl.DataFrame

Build a Cartesian-product prediction grid.

Creates a grid where the focal variable is varied, condition variables are expanded, and all other predictors are held at reference values (mean for continuous, first sorted level for categorical). Grouping factors and the response column are excluded.

Parameters:

NameTypeDescriptionDefault
dataDataFrameTraining data (Polars DataFrame).required
focal_varstrThe predictor to vary across the grid.required
response_colstrResponse column name (excluded from grid).required
grouping_factorstuple[str, ...]Random-effect grouping variables (excluded).required
focal_valueslist[float | str] | NoneExplicit values for the focal variable. Overrides default linspace/unique-levels logic.None
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables. Use "data" to use actual observed unique values.50
varying_varslist[str] | NoneCondition variables to expand (all unique levels).None
atdict[str, Any] | NoneDict of pinned values. Scalar = single constant, list = expand.None

Returns:

TypeDescription
DataFramePolars DataFrame with the Cartesian-product prediction grid.

check_convergence

check_convergence(fit: FitState, re_meta: REInfo) -> list[ConvergenceMessage]

Run convergence diagnostics on a fitted mixed model.

Extracts theta and sigma from FitState, computes theta lower bounds and per-factor RE info from REInfo, and delegates to diagnose_convergence() for the actual diagnostic checks.

Parameters:

NameTypeDescriptionDefault
fitFitStateFitted model state (must have theta, sigma, converged).required
re_metaREInfoRandom effects metadata from the DataBundle.required

Returns:

TypeDescription
list[ConvergenceMessage]List of ConvergenceMessage objects. Empty if theta is None.

compute_diagnostics

compute_diagnostics(*, model_type: str, spec: ModelSpec, bundle: DataBundle, fit: FitState, coef_for_predict: np.ndarray, varying_spread: VaryingSpreadState | None, cv: CVState | None, has_intercept: bool = True) -> pl.DataFrame

Compute model-level diagnostics as a single-row DataFrame.

Builds goodness-of-fit diagnostics from fitted model state, with columns varying by model type (lm, glm, lmer, glmer).

Parameters:

NameTypeDescriptionDefault
model_typestrOne of “lm”, “glm”, “lmer”, “glmer”.required
specModelSpecModel specification (for family).required
bundleDataBundleData bundle (for n, rank, X, y, re_metadata).required
fitFitStateFitted state (for coefficients, residuals, loglik, etc.).required
coef_for_predictndarrayCoefficients safe for matrix multiplication (NaN replaced by 0 for rank-deficient models).required
varying_spreadVaryingSpreadState | NoneRandom effects variance components (mixed models).required
cvCVState | NoneCross-validation state, or None.required
has_interceptboolWhether the model includes an intercept. Affects R² computation (centered vs uncentered SS_tot).True

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with model diagnostics. See
DataFramemodel.diagnostics for full column documentation.

compute_metadata

compute_metadata(*, bundle: DataBundle) -> pl.DataFrame

Compute model metadata as a single-row DataFrame.

Returns sample/structural info about the model: observation counts, parameter count, and group counts (for mixed models).

Parameters:

NameTypeDescriptionDefault
bundleDataBundleData bundle (for n, n_total, p, re_metadata).required

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with model metadata.

compute_optimizer_diagnostics

compute_optimizer_diagnostics(*, model_type: str, fit: FitState) -> pl.DataFrame

Compute optimizer convergence diagnostics as a single-row DataFrame.

Parameters:

NameTypeDescriptionDefault
model_typestrOne of “lm”, “glm”, “lmer”, “glmer”.required
fitFitStateFitted state with convergence info, theta, dispersion.required

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with optimizer diagnostics.

compute_predictions_from_formula

compute_predictions_from_formula(formula: str, data: pl.DataFrame, spec: object, bundle: object, fit: object, formula_spec: object, pred_type: str, varying: str, allow_new_levels: bool, n_points: int | Literal['data']) -> 'PredictionState'

Parse a predict formula, build the grid, compute predictions, and attach grid columns.

Combines parse_predict_formula, compute_predictions, and grid-column attachment into a single call for model.predict() formula mode.

Parameters:

NameTypeDescriptionDefault
formulastrExplore-style formula (e.g. "wt ~ cyl").required
dataDataFrameTraining data.required
specobjectModel specification.required
bundleobjectData bundle.required
fitobjectFitted model state.required
formula_specobjectLearned formula spec for newdata evaluation.required
pred_typestrPrediction scale ("response" or "link").required
varyingstrRE handling ("exclude" or "include").required
allow_new_levelsboolIf True, new groups predict at population level.required
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables.required

Returns:

TypeDescription
‘PredictionState’PredictionState with grid columns attached.

compute_r_squared

compute_r_squared(y: np.ndarray, residuals: np.ndarray, n: int, p: int, has_intercept: bool = True) -> tuple[float, float]

Compute R-squared and adjusted R-squared from raw arrays.

For models with an intercept, uses centered SS_tot = sum((y - mean(y))^2). For no-intercept models, uses uncentered SS_tot = sum(y^2), matching R’s summary.lm() behavior.

Parameters:

NameTypeDescriptionDefault
yndarrayResponse vector of shape (n,).required
residualsndarrayResidual vector of shape (n,).required
nintNumber of observations.required
pintNumber of parameters (including intercept if present).required
has_interceptboolWhether the model includes an intercept.True

Returns:

TypeDescription
tuple[float, float]Tuple of (R-squared, adjusted R-squared).

compute_varying_spread_state

compute_varying_spread_state(theta: NDArray[np.floating], sigma: float, re_meta: REInfo) -> VaryingSpreadState

Compute VaryingSpreadState (variance components) from theta parameters.

Extracts residual variance (sigma²), random effect variances (tau²), correlations (rho), and intraclass correlation (ICC) from the fitted theta vector using the random effects structure.

Parameters:

NameTypeDescriptionDefault
thetaNDArray[floating]Variance component parameters from the fitted model.required
sigmafloatResidual standard deviation from the fitted model.required
re_metaREInfoRandom effects metadata (grouping vars, structure, etc.).required

Returns:

TypeDescription
VaryingSpreadStateVaryingSpreadState container with components DataFrame and
VaryingSpreadStatedecomposed variance quantities.

compute_varying_state

compute_varying_state(theta: NDArray[np.floating], u: NDArray[np.floating], re_meta: REInfo, data: pl.DataFrame | None = None) -> VaryingState

Compute VaryingState (BLUPs) from fitted random effects parameters.

Converts spherical random effects u to BLUPs b = Lambda @ u using the relative covariance factor Lambda built from theta. Constructs a grid of group/level combinations and maps BLUP values to named effects.

Parameters:

NameTypeDescriptionDefault
thetaNDArray[floating]Variance component parameters from the fitted model.required
uNDArray[floating]Spherical random effects vector from the fitted model.required
re_metaREInfoRandom effects metadata (grouping vars, structure, etc.).required
dataDataFrame | NoneOriginal training data, used to extract unique group levels. If None, levels are labeled "0", "1", etc.None

Returns:

TypeDescription
VaryingStateVaryingState container with grid, effects dict, and group info.

execute_fit

execute_fit(spec: ModelSpec, bundle: DataBundle | None, data: pl.DataFrame, raw_data: pl.DataFrame | None, formula: str, custom_contrasts: dict | None, weights_col: str | None, offset_col: str | None, missing: str, is_mixed: bool, solver_override: str | None, fit_kwargs: dict) -> FitResult

Execute the full fit lifecycle: bundle rebuild → fit → post-fit state → diagnostics.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification.required
bundleDataBundle | NoneExisting data bundle, or None to force rebuild.required
dataDataFrameCurrent data (raw_data-restored by caller).required
raw_dataDataFrame | NoneOriginal pre-augmentation snapshot, or None.required
formulastrFormula string for bundle building.required
custom_contrastsdict | NoneUser contrast matrices, or None.required
weights_colstr | NoneWeights column name, or None.required
offset_colstr | NoneOffset column name, or None.required
missingstrMissing value handling ("drop" or "fail").required
is_mixedboolWhether this is a mixed-effects model.required
solver_overridestr | NoneExplicit solver, or None for auto.required
fit_kwargsdictAdditional kwargs for fit_model().required

Returns:

TypeDescription
FitResultFitResult with all state the model needs to assign.

fit_glm_irls

fit_glm_irls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 25, tol: float = 1e-08) -> FitState

Fit generalized linear model using Iteratively Reweighted Least Squares.

This adapter wraps the IRLS implementation in IRLS solves GLMs by iterating between computing working weights and solving a weighted least squares problem.

  1. Initialize mu from y (or link function default)

  2. Initialize mu from y (or link function default)

  3. For each iteration: a. Compute working weights: W = 1 / (V(mu) * g’(mu)^2) b. Compute working response: z = eta + (y - mu) * g’(mu) c. Solve weighted least squares: beta = (X’WX)^{-1} X’Wz d. Update eta = X @ beta, mu = g^{-1}(eta)

  4. Continue until convergence (change in deviance < tol)

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - family: Distribution family (determines variance function) - link: Link function (determines g and g’)required
bundleDataBundleData bundle containing X, y, and optional weights.required
max_iterintMaximum IRLS iterations (default: 25).25
tolfloatConvergence tolerance on deviance (default: 1e-8).1e-08

Returns:

TypeDescription
FitStateFitState containing: - coef: Coefficient estimates - vcov: Variance-covariance (observed Fisher information) - fitted: Predicted values on response scale - residuals: Response residuals (y - mu) - leverage: Hat matrix diagonal - df_resid: Residual degrees of freedom - loglik: Log-likelihood - dispersion: Estimated dispersion parameter - converged: Whether IRLS converged - n_iter: Number of IRLS iterations

See Also:

fit_glmer_pirls

fit_glmer_pirls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 25, max_outer_iter: int = 10000, tol: float = 1e-07, verbose: bool = False, nAGQ: int = 1, use_hessian: bool = False) -> FitState

Fit generalized linear mixed model using Penalized IRLS.

This adapter wraps the PIRLS implementation from PIRLS combines IRLS (for the GLM part) with PLS (for random effects), using Laplace approximation to integrate out the random effects.

Outer loop (BOBYQA optimization over theta): Outer loop (BOBYQA optimization over theta): For each theta: 1. Build Lambda from theta

Inner loop (PIRLS iterations):
    a. Compute working weights from current eta/mu
    b. Compute working response
    c. Solve weighted PLS for beta and u
    d. Update eta = X @ beta + Z @ Lambda @ u
    e. Update mu = g^{-1}(eta)
    f. Step-halving if deviance increased
    g. Check convergence

2. Return Laplace deviance

Select theta minimizing Laplace deviance

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - family: Distribution family - link: Link function - random_terms: Parsed random effect specificationsrequired
bundleDataBundleData bundle containing: - X: Fixed effects design matrix - Z: Random effects design matrix (sparse) - y: Response vector - re_metadata: Grouping structurerequired
max_iterintMaximum PIRLS iterations per theta (default: 25).25
max_outer_iterintMaximum BOBYQA iterations (default: 10000).10000
tolfloatPIRLS convergence tolerance (default: 1e-7).1e-07
verboseboolPrint optimization progress (default: False).False
nAGQintQuadrature points (0 or 1, default: 1).1
use_hessianboolUse Hessian-based vcov (default: False). The default Schur complement approach matches lme4’s vcov() with use.hessian=FALSE and avoids expensive numerical differentiation. Set to True for observed-information vcov.False

Returns:

TypeDescription
FitStateFitState containing: - coef: Fixed effect coefficient estimates - vcov: Variance-covariance (observed information or Schur complement) - fitted: Predicted values on response scale (mu) - residuals: Response residuals (y - mu) - leverage: Approximate leverage values - df_resid: Residual degrees of freedom - loglik: Laplace-approximated log-likelihood - dispersion: Dispersion (1.0 for binomial/poisson) - theta: Optimized relative covariance parameters - u: Spherical random effects - converged: Whether both PIRLS and BOBYQA converged - n_iter: Number of optimizer evaluations

See Also:

fit_lmer_pls

fit_lmer_pls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 10000, verbose: bool = False) -> FitState

Fit linear mixed-effects model using Penalized Least Squares.

This adapter wraps the PLS implementation from PLS is the algorithm from Bates et al. (2015) used in R’s lme4 package.

Outer loop (BOBYQA optimization over theta): Outer loop (BOBYQA optimization over theta): For each theta (relative covariance parameters): 1. Build Lambda (block-diagonal Cholesky factor from theta) 2. Form S_22 = Lambda’ Z’ Z Lambda + I 3. Sparse Cholesky factorization of S_22 4. Compute Schur complement for fixed effects 5. Solve for beta (fixed effects) and u (spherical RE) 6. Compute REML or ML deviance

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - method: “reml” or “ml” (determines objective function) - random_terms: Parsed random effect specificationsrequired
bundleDataBundleData bundle containing: - X: Fixed effects design matrix (n x p) - Z: Random effects design matrix (n x q, sparse CSC) - y: Response vector - re_metadata: Grouping structure informationrequired
max_iterintMaximum BOBYQA iterations (default: 10000).10000
verboseboolPrint optimization progress (default: False).False

Returns:

TypeDescription
FitStateFitState containing: - coef: Fixed effect coefficient estimates - vcov: Variance-covariance of fixed effects - fitted: Predicted values (fixed + random) - residuals: Response residuals (y - fitted) - leverage: Approximate leverage values - df_resid: Residual degrees of freedom - loglik: REML or ML log-likelihood - sigma: Residual standard deviation - theta: Optimized relative covariance parameters - u: Spherical random effects (unit variance) - converged: Whether optimizer converged - n_iter: Number of optimizer iterations

See Also:

fit_model

fit_model(spec: ModelSpec, bundle: DataBundle, *, solver: str | None = None, max_iter: int | None = None, max_outer_iter: int = 10000, tol: float | None = None, verbose: bool = False, nAGQ: int = 1, use_hessian: bool = False) -> FitState

Dispatch to appropriate fitter based on model specification.

This is the main entry point for fitting models. It examines the ModelSpec to determine the appropriate solver and delegates to the corresponding fitter function.

If the design matrix is rank-deficient (detected during bundle construction), the X matrix is reduced to estimable columns before fitting. After fitting, coefficients and vcov are expanded back to full size with NaN for dropped columns (matching R’s lm() behavior).

The solver selection follows the estimation method matrix:

FamilyRandom EffectsMethodSolverDescription
gaussianNoolsqrQR decomposition
gaussianNomlirlsMaximum likelihood
non-gaussNomlirlsGLM via IRLS
gaussianYesreml/mlplsPenalized least squares
non-gaussYesmlpirlsPenalized IRLS

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing formula, family, link, method, and parsed formula components.required
bundleDataBundlePrepared data bundle containing design matrices (X, y, Z), column names, valid observation mask, and optional weights/offset.required
solverstr | NoneOverride solver selection. If None, auto-selected via resolve_solver(). Must be one of "qr", "irls", "pls", "pirls".None
max_iterint | NoneMaximum iterations (solver-specific defaults if None).None
max_outer_iterintMaximum outer (BOBYQA) iterations for GLMER (default: 10000).10000
tolfloat | NoneConvergence tolerance (solver-specific defaults if None).None
verboseboolPrint optimization progress (default: False).False
nAGQintQuadrature points for GLMER (default: 1).1
use_hessianboolUse Hessian-based vcov for GLMER (default: False).False

Returns:

TypeDescription
FitStateFitState containing all fitting results.

Examples:

>>> import numpy as np
>>> from containers import build_model_spec, DataBundle
>>> spec = build_model_spec(
...     formula="y ~ x",
...     response_var="y",
...     fixed_terms=["Intercept", "x"],
... )
>>> bundle = DataBundle(
...     X=np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]]),
...     y=np.array([2.0, 4.0, 6.0]),
...     X_names=["Intercept", "x"],
...     y_name="y",
...     valid_mask=np.array([True, True, True]),
...     n_total=3,
... )
>>> state = fit_model(spec, bundle)
>>> state.converged
True
>>> state.coef  # [Intercept, x] = [0, 2]
array([0., 2.])

fit_ols_qr

fit_ols_qr(spec: ModelSpec, bundle: DataBundle) -> FitState

Fit ordinary or weighted least squares using QR decomposition.

Supports observation weights (WLS) and offset terms. When weights are present, solves the transformed system sqrt(W)*X, sqrt(W)*y via QR decomposition, which yields WLS coefficients and vcov directly. Offsets are subtracted from y before fitting and added back to fitted values.

  1. Subtract offset from y (if present): y_adj = y - offset

  2. Subtract offset from y (if present): y_adj = y - offset

  3. Apply weights (if present): X_w = sqrt(w)*X, y_w = sqrt(w)*y_adj

  4. QR decompose X_w with column pivoting for stability

  5. Solve R * beta = Q.T @ y_w via back-substitution

  6. Recompute original-scale: fitted = X @ beta + offset, resid = y - fitted

  7. vcov = sigma_w^2 * (X’WX)^{-1}

  8. Leverage from (possibly weighted) hat matrix

Matches R’s logLik.lm formula:: Matches R’s logLik.lm formula::

L = 0.5*sum(log(w)) - n/2 * (log(2*pi) + log(RSS_w/n) + 1)

The 0.5*sum(log(w)) term is the Jacobian from the weight transformation.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification (unused for OLS, included for interface consistency with other fitters).required
bundleDataBundleData bundle containing: - X: Design matrix (n x p) - y: Response vector (n,) - weights: Observation weights (n,) or None for OLS - offset: Offset vector (n,) or Nonerequired

Returns:

TypeDescription
FitStateFitState containing: - coef: Coefficient estimates, shape (p,) - vcov: Variance-covariance matrix, shape (p, p) - fitted: Fitted values X @ coef + offset, shape (n,) - residuals: y - fitted, shape (n,) - leverage: Hat matrix diagonal, shape (n,) - df_resid: Residual degrees of freedom (n - rank) - loglik: Gaussian log-likelihood (weighted if applicable) - sigma: Residual standard deviation - converged: Always True (closed-form solution) - n_iter: Always 1 (single step)

Examples:

>>> import numpy as np
>>> from containers import build_model_spec, DataBundle
>>> spec = build_model_spec(
...     formula="y ~ x",
...     response_var="y",
...     fixed_terms=["Intercept", "x"],
... )
>>> bundle = DataBundle(
...     X=np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]]),
...     y=np.array([2.0, 4.0, 6.0]),
...     X_names=["Intercept", "x"],
...     y_name="y",
...     valid_mask=np.array([True, True, True]),
...     n_total=3,
... )
>>> state = fit_ols_qr(spec, bundle)
>>> np.allclose(state.fitted + state.residuals, bundle.y)
True
>>> np.allclose(state.coef, [0.0, 2.0])  # Perfect fit: y = 2x
True

get_theta_lower_bounds

get_theta_lower_bounds(n_theta: int, re_structure: str, metadata: dict | None = None) -> list[float]

Get lower bounds for theta parameters.

Diagonal elements of Cholesky factor must be non-negative. Off-diagonal elements are unbounded.

Parameters:

NameTypeDescriptionDefault
n_thetaintNumber of theta parametersrequired
re_structurestrRandom effects structure typerequired
metadatadict | NoneOptional metadata dict with ‘re_structures_list’ for crossed/nested/mixed structuresNone

Returns:

TypeDescription
list[float]List of lower bounds

parse_fit_kwargs

parse_fit_kwargs(spec: ModelSpec, kwargs: dict[str, object], nAGQ: int | None) -> tuple[ModelSpec, str | None, dict[str, object]]

Validate and extract fitting parameters from **kwargs.

Pops solver, method, and nAGQ from kwargs, validates each, and assembles the remaining fit-specific keyword arguments into a dict suitable for fit_model().

Parameters:

NameTypeDescriptionDefault
specModelSpecCurrent model specification (may be evolved if method is set).required
kwargsdict[str, object]Mutable dict of user-supplied keyword arguments. Recognized keys are popped: solver, method, max_iter, max_outer_iter, tol, verbose, nAGQ, use_hessian.required
nAGQint | NoneExplicit nAGQ parameter from the fit() signature (takes precedence over any value in kwargs).required

Returns:

TypeDescription
ModelSpecA tuple (updated_spec, solver_override, fit_kwargs) where:
str | None- updated_spec has the validated method applied (if method was set).
dict[str, object]- solver_override is the validated solver string, or None.
tuple[ModelSpec, str | None, dict[str, object]]- fit_kwargs is a dict ready to splat into fit_model().

parse_predict_formula

parse_predict_formula(formula: str, data: pl.DataFrame, response_col: str, grouping_factors: tuple[str, ...], *, n_points: int | Literal['data'] = 50) -> tuple[pl.DataFrame, list[str]]

Parse an explore-style formula and build a prediction grid.

Translates the formula via :func:parse_explore_formula, rejects contrast formulas, and delegates to :func:build_predict_grid.

Parameters:

NameTypeDescriptionDefault
formulastrExplore-style formula (e.g. "wt ~ cyl").required
dataDataFrameTraining data.required
response_colstrResponse column name.required
grouping_factorstuple[str, ...]Random-effect grouping variables.required
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables.50

Returns:

TypeDescription
DataFrameTuple of (grid DataFrame, list of grid column names for output).
list[str]The grid column names are the focal var plus any condition vars
tuple[DataFrame, list[str]](the columns that vary across the grid, excluding reference-value
tuple[DataFrame, list[str]]columns).

per_factor_re_info

per_factor_re_info(re_meta: REInfo, group_names: list[str]) -> tuple[str | list[str], list[str] | dict[str, list[str]]]

Split global RE metadata into per-factor structures and names.

For crossed/nested/mixed models, the global re_structure is a single string (e.g. “crossed”) and random_names is a concatenated list across all factors. This function splits them into per-factor structures and per-factor name dicts suitable for BLUP decomposition and convergence diagnostics.

For single-factor models, returns the originals unchanged.

Parameters:

NameTypeDescriptionDefault
re_metaREInfoRandom effects metadata from the fitted model’s DataBundle.required
group_nameslist[str]Ordered list of grouping variable names (e.g. ["subject"] or ["subject", "item"]).required

Returns:

TypeDescription
str | list[str]A tuple (re_structure, random_names) where:
list[str] | dict[str, list[str]]- For single-factor models: (str, list[str]) — the originals.
tuple[str | list[str], list[str] | dict[str, list[str]]]- For multi-factor models: (list[str], dict[str, list[str]]) — per-factor structure list and a dict mapping group name to its random effect names.

resolve_condition_values

resolve_condition_values(cond: Condition, data: pl.DataFrame) -> list | None

Resolve a :class:Condition to concrete values or None.

Parameters:

NameTypeDescriptionDefault
condConditionA Condition from :func:parse_explore_formula.required
dataDataFrameThe model’s training data.required

Returns:

TypeDescription
list | NoneA list of concrete values if the condition specifies explicit
list | Nonevalues (at_values, at_range, at_quantile), or
list | NoneNone for bare conditions (use all unique levels).

resolve_solver

resolve_solver(spec: ModelSpec) -> str

Select the appropriate solver for a model configuration.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification.required

Returns:

TypeDescription
strSolver name: “qr”, “irls”, “pls”, or “pirls”.

validate_fit_method

validate_fit_method(spec: ModelSpec, method_str: str) -> ModelSpec

Validate and apply a user-specified fitting method to a ModelSpec.

Checks that the method is compatible with the model’s family and random-effects structure, then returns an evolved spec with the new method.

Parameters:

NameTypeDescriptionDefault
specModelSpecCurrent model specification.required
method_strstrUser-supplied method string (e.g. "ols", "ml", "reml"). Will be lowercased.required

Returns:

TypeDescription
ModelSpecEvolved ModelSpec with the validated method applied.

Modules

convergence

Convergence diagnostics for fitted mixed-effects models.

Encapsulates the repeated pattern of extracting theta, computing bounds, assembling per-factor RE info, and running diagnose_convergence().

Functions:

NameDescription
check_convergenceRun convergence diagnostics on a fitted mixed model.

Classes

Functions

check_convergence
check_convergence(fit: FitState, re_meta: REInfo) -> list[ConvergenceMessage]

Run convergence diagnostics on a fitted mixed model.

Extracts theta and sigma from FitState, computes theta lower bounds and per-factor RE info from REInfo, and delegates to diagnose_convergence() for the actual diagnostic checks.

Parameters:

NameTypeDescriptionDefault
fitFitStateFitted model state (must have theta, sigma, converged).required
re_metaREInfoRandom effects metadata from the DataBundle.required

Returns:

TypeDescription
list[ConvergenceMessage]List of ConvergenceMessage objects. Empty if theta is None.

diagnostics

Model-level diagnostics computation.

Pure functions that compute model diagnostics from containers. These were extracted from model/core.py to keep the model class as thin glue.

Attributes

Classes

Functions

augment_data_with_diagnostics
augment_data_with_diagnostics(*, raw_data: pl.DataFrame, fit: FitState, bundle: DataBundle) -> pl.DataFrame

Augment raw data with diagnostic columns after fit.

Adds fitted, resid, hat, std_resid, cooksd columns (names from AugmentedDataCols schema). Values are NaN for rows dropped due to missing data.

Parameters:

NameTypeDescriptionDefault
raw_dataDataFrameOriginal data DataFrame (pre-NA-drop).required
fitFitStateFitted state with residuals, fitted values, leverage.required
bundleDataBundleData bundle with valid_mask, n_total, p.required

Returns:

TypeDescription
DataFrameDataFrame with diagnostic columns appended.
compute_diagnostics
compute_diagnostics(*, model_type: str, spec: ModelSpec, bundle: DataBundle, fit: FitState, coef_for_predict: np.ndarray, varying_spread: VaryingSpreadState | None, cv: CVState | None, has_intercept: bool = True) -> pl.DataFrame

Compute model-level diagnostics as a single-row DataFrame.

Builds goodness-of-fit diagnostics from fitted model state, with columns varying by model type (lm, glm, lmer, glmer).

Parameters:

NameTypeDescriptionDefault
model_typestrOne of “lm”, “glm”, “lmer”, “glmer”.required
specModelSpecModel specification (for family).required
bundleDataBundleData bundle (for n, rank, X, y, re_metadata).required
fitFitStateFitted state (for coefficients, residuals, loglik, etc.).required
coef_for_predictndarrayCoefficients safe for matrix multiplication (NaN replaced by 0 for rank-deficient models).required
varying_spreadVaryingSpreadState | NoneRandom effects variance components (mixed models).required
cvCVState | NoneCross-validation state, or None.required
has_interceptboolWhether the model includes an intercept. Affects R² computation (centered vs uncentered SS_tot).True

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with model diagnostics. See
DataFramemodel.diagnostics for full column documentation.
compute_metadata
compute_metadata(*, bundle: DataBundle) -> pl.DataFrame

Compute model metadata as a single-row DataFrame.

Returns sample/structural info about the model: observation counts, parameter count, and group counts (for mixed models).

Parameters:

NameTypeDescriptionDefault
bundleDataBundleData bundle (for n, n_total, p, re_metadata).required

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with model metadata.
compute_optimizer_diagnostics
compute_optimizer_diagnostics(*, model_type: str, fit: FitState) -> pl.DataFrame

Compute optimizer convergence diagnostics as a single-row DataFrame.

Parameters:

NameTypeDescriptionDefault
model_typestrOne of “lm”, “glm”, “lmer”, “glmer”.required
fitFitStateFitted state with convergence info, theta, dispersion.required

Returns:

TypeDescription
DataFrameSingle-row Polars DataFrame with optimizer diagnostics.
compute_r_squared
compute_r_squared(y: np.ndarray, residuals: np.ndarray, n: int, p: int, has_intercept: bool = True) -> tuple[float, float]

Compute R-squared and adjusted R-squared from raw arrays.

For models with an intercept, uses centered SS_tot = sum((y - mean(y))^2). For no-intercept models, uses uncentered SS_tot = sum(y^2), matching R’s summary.lm() behavior.

Parameters:

NameTypeDescriptionDefault
yndarrayResponse vector of shape (n,).required
residualsndarrayResidual vector of shape (n,).required
nintNumber of observations.required
pintNumber of parameters (including intercept if present).required
has_interceptboolWhether the model includes an intercept.True

Returns:

TypeDescription
tuple[float, float]Tuple of (R-squared, adjusted R-squared).

dispatch

Solver dispatch for model fitting.

Provides fit_model() which dispatches to the appropriate fitter based on model specification, and resolve_solver() which determines the solver type.

Handles rank-deficient design matrices by reducing X before fitting and expanding coefficients/vcov after, inserting NaN for dropped columns.

Functions:

NameDescription
fit_modelDispatch to appropriate fitter based on model specification.
parse_fit_kwargsValidate and extract fitting parameters from **kwargs.
resolve_solverSelect the appropriate solver for a model configuration.
validate_fit_methodValidate and apply a user-specified fitting method to a ModelSpec.

Attributes:

NameTypeDescription
VALID_SOLVERSfrozenset[str]

Attributes

VALID_SOLVERS
VALID_SOLVERS: frozenset[str] = frozenset({'qr', 'irls', 'pls', 'pirls'})

Classes

Functions

fit_model
fit_model(spec: ModelSpec, bundle: DataBundle, *, solver: str | None = None, max_iter: int | None = None, max_outer_iter: int = 10000, tol: float | None = None, verbose: bool = False, nAGQ: int = 1, use_hessian: bool = False) -> FitState

Dispatch to appropriate fitter based on model specification.

This is the main entry point for fitting models. It examines the ModelSpec to determine the appropriate solver and delegates to the corresponding fitter function.

If the design matrix is rank-deficient (detected during bundle construction), the X matrix is reduced to estimable columns before fitting. After fitting, coefficients and vcov are expanded back to full size with NaN for dropped columns (matching R’s lm() behavior).

The solver selection follows the estimation method matrix:

FamilyRandom EffectsMethodSolverDescription
gaussianNoolsqrQR decomposition
gaussianNomlirlsMaximum likelihood
non-gaussNomlirlsGLM via IRLS
gaussianYesreml/mlplsPenalized least squares
non-gaussYesmlpirlsPenalized IRLS

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing formula, family, link, method, and parsed formula components.required
bundleDataBundlePrepared data bundle containing design matrices (X, y, Z), column names, valid observation mask, and optional weights/offset.required
solverstr | NoneOverride solver selection. If None, auto-selected via resolve_solver(). Must be one of "qr", "irls", "pls", "pirls".None
max_iterint | NoneMaximum iterations (solver-specific defaults if None).None
max_outer_iterintMaximum outer (BOBYQA) iterations for GLMER (default: 10000).10000
tolfloat | NoneConvergence tolerance (solver-specific defaults if None).None
verboseboolPrint optimization progress (default: False).False
nAGQintQuadrature points for GLMER (default: 1).1
use_hessianboolUse Hessian-based vcov for GLMER (default: False).False

Returns:

TypeDescription
FitStateFitState containing all fitting results.

Examples:

>>> import numpy as np
>>> from containers import build_model_spec, DataBundle
>>> spec = build_model_spec(
...     formula="y ~ x",
...     response_var="y",
...     fixed_terms=["Intercept", "x"],
... )
>>> bundle = DataBundle(
...     X=np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]]),
...     y=np.array([2.0, 4.0, 6.0]),
...     X_names=["Intercept", "x"],
...     y_name="y",
...     valid_mask=np.array([True, True, True]),
...     n_total=3,
... )
>>> state = fit_model(spec, bundle)
>>> state.converged
True
>>> state.coef  # [Intercept, x] = [0, 2]
array([0., 2.])
parse_fit_kwargs
parse_fit_kwargs(spec: ModelSpec, kwargs: dict[str, object], nAGQ: int | None) -> tuple[ModelSpec, str | None, dict[str, object]]

Validate and extract fitting parameters from **kwargs.

Pops solver, method, and nAGQ from kwargs, validates each, and assembles the remaining fit-specific keyword arguments into a dict suitable for fit_model().

Parameters:

NameTypeDescriptionDefault
specModelSpecCurrent model specification (may be evolved if method is set).required
kwargsdict[str, object]Mutable dict of user-supplied keyword arguments. Recognized keys are popped: solver, method, max_iter, max_outer_iter, tol, verbose, nAGQ, use_hessian.required
nAGQint | NoneExplicit nAGQ parameter from the fit() signature (takes precedence over any value in kwargs).required

Returns:

TypeDescription
ModelSpecA tuple (updated_spec, solver_override, fit_kwargs) where:
str | None- updated_spec has the validated method applied (if method was set).
dict[str, object]- solver_override is the validated solver string, or None.
tuple[ModelSpec, str | None, dict[str, object]]- fit_kwargs is a dict ready to splat into fit_model().
resolve_solver
resolve_solver(spec: ModelSpec) -> str

Select the appropriate solver for a model configuration.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification.required

Returns:

TypeDescription
strSolver name: “qr”, “irls”, “pls”, or “pirls”.
validate_fit_method
validate_fit_method(spec: ModelSpec, method_str: str) -> ModelSpec

Validate and apply a user-specified fitting method to a ModelSpec.

Checks that the method is compatible with the model’s family and random-effects structure, then returns an evolved spec with the new method.

Parameters:

NameTypeDescriptionDefault
specModelSpecCurrent model specification.required
method_strstrUser-supplied method string (e.g. "ols", "ml", "reml"). Will be lowercased.required

Returns:

TypeDescription
ModelSpecEvolved ModelSpec with the validated method applied.

glm

GLM fitting via Iteratively Reweighted Least Squares (IRLS).

Functions:

NameDescription
fit_glm_irlsFit generalized linear model using Iteratively Reweighted Least Squares.

Classes

Functions

fit_glm_irls
fit_glm_irls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 25, tol: float = 1e-08) -> FitState

Fit generalized linear model using Iteratively Reweighted Least Squares.

This adapter wraps the IRLS implementation in IRLS solves GLMs by iterating between computing working weights and solving a weighted least squares problem.

  1. Initialize mu from y (or link function default)

  2. Initialize mu from y (or link function default)

  3. For each iteration: a. Compute working weights: W = 1 / (V(mu) * g’(mu)^2) b. Compute working response: z = eta + (y - mu) * g’(mu) c. Solve weighted least squares: beta = (X’WX)^{-1} X’Wz d. Update eta = X @ beta, mu = g^{-1}(eta)

  4. Continue until convergence (change in deviance < tol)

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - family: Distribution family (determines variance function) - link: Link function (determines g and g’)required
bundleDataBundleData bundle containing X, y, and optional weights.required
max_iterintMaximum IRLS iterations (default: 25).25
tolfloatConvergence tolerance on deviance (default: 1e-8).1e-08

Returns:

TypeDescription
FitStateFitState containing: - coef: Coefficient estimates - vcov: Variance-covariance (observed Fisher information) - fitted: Predicted values on response scale - residuals: Response residuals (y - mu) - leverage: Hat matrix diagonal - df_resid: Residual degrees of freedom - loglik: Log-likelihood - dispersion: Estimated dispersion parameter - converged: Whether IRLS converged - n_iter: Number of IRLS iterations

See Also:

Modules

glmer

GLMM fitting via Penalized IRLS (PIRLS).

Functions:

NameDescription
fit_glmer_pirlsFit generalized linear mixed model using Penalized IRLS.

Classes

Functions

fit_glmer_pirls
fit_glmer_pirls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 25, max_outer_iter: int = 10000, tol: float = 1e-07, verbose: bool = False, nAGQ: int = 1, use_hessian: bool = False) -> FitState

Fit generalized linear mixed model using Penalized IRLS.

This adapter wraps the PIRLS implementation from PIRLS combines IRLS (for the GLM part) with PLS (for random effects), using Laplace approximation to integrate out the random effects.

Outer loop (BOBYQA optimization over theta): Outer loop (BOBYQA optimization over theta): For each theta: 1. Build Lambda from theta

Inner loop (PIRLS iterations):
    a. Compute working weights from current eta/mu
    b. Compute working response
    c. Solve weighted PLS for beta and u
    d. Update eta = X @ beta + Z @ Lambda @ u
    e. Update mu = g^{-1}(eta)
    f. Step-halving if deviance increased
    g. Check convergence

2. Return Laplace deviance

Select theta minimizing Laplace deviance

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - family: Distribution family - link: Link function - random_terms: Parsed random effect specificationsrequired
bundleDataBundleData bundle containing: - X: Fixed effects design matrix - Z: Random effects design matrix (sparse) - y: Response vector - re_metadata: Grouping structurerequired
max_iterintMaximum PIRLS iterations per theta (default: 25).25
max_outer_iterintMaximum BOBYQA iterations (default: 10000).10000
tolfloatPIRLS convergence tolerance (default: 1e-7).1e-07
verboseboolPrint optimization progress (default: False).False
nAGQintQuadrature points (0 or 1, default: 1).1
use_hessianboolUse Hessian-based vcov (default: False). The default Schur complement approach matches lme4’s vcov() with use.hessian=FALSE and avoids expensive numerical differentiation. Set to True for observed-information vcov.False

Returns:

TypeDescription
FitStateFitState containing: - coef: Fixed effect coefficient estimates - vcov: Variance-covariance (observed information or Schur complement) - fitted: Predicted values on response scale (mu) - residuals: Response residuals (y - mu) - leverage: Approximate leverage values - df_resid: Residual degrees of freedom - loglik: Laplace-approximated log-likelihood - dispersion: Dispersion (1.0 for binomial/poisson) - theta: Optimized relative covariance parameters - u: Spherical random effects - converged: Whether both PIRLS and BOBYQA converged - n_iter: Number of optimizer evaluations

See Also:

grid

Prediction grid construction for formula-mode predictions.

Provides parse_predict_formula() which translates an explore-style formula into a prediction grid (Polars DataFrame), and build_predict_grid() which assembles the Cartesian-product grid from column specifications.

Shared by model.predict() (formula mode) and viz/predict.py (plot_predict).

Functions:

NameDescription
build_predict_gridBuild a Cartesian-product prediction grid.
compute_predictions_from_formulaParse a predict formula, build the grid, compute predictions, and attach grid columns.
parse_predict_formulaParse an explore-style formula and build a prediction grid.
resolve_condition_valuesResolve a :class:Condition to concrete values or None.

Classes

Functions

build_predict_grid
build_predict_grid(data: pl.DataFrame, focal_var: str, response_col: str, grouping_factors: tuple[str, ...], *, focal_values: list[float | str] | None = None, n_points: int | Literal['data'] = 50, varying_vars: list[str] | None = None, at: dict[str, Any] | None = None) -> pl.DataFrame

Build a Cartesian-product prediction grid.

Creates a grid where the focal variable is varied, condition variables are expanded, and all other predictors are held at reference values (mean for continuous, first sorted level for categorical). Grouping factors and the response column are excluded.

Parameters:

NameTypeDescriptionDefault
dataDataFrameTraining data (Polars DataFrame).required
focal_varstrThe predictor to vary across the grid.required
response_colstrResponse column name (excluded from grid).required
grouping_factorstuple[str, ...]Random-effect grouping variables (excluded).required
focal_valueslist[float | str] | NoneExplicit values for the focal variable. Overrides default linspace/unique-levels logic.None
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables. Use "data" to use actual observed unique values.50
varying_varslist[str] | NoneCondition variables to expand (all unique levels).None
atdict[str, Any] | NoneDict of pinned values. Scalar = single constant, list = expand.None

Returns:

TypeDescription
DataFramePolars DataFrame with the Cartesian-product prediction grid.
compute_predictions_from_formula
compute_predictions_from_formula(formula: str, data: pl.DataFrame, spec: object, bundle: object, fit: object, formula_spec: object, pred_type: str, varying: str, allow_new_levels: bool, n_points: int | Literal['data']) -> 'PredictionState'

Parse a predict formula, build the grid, compute predictions, and attach grid columns.

Combines parse_predict_formula, compute_predictions, and grid-column attachment into a single call for model.predict() formula mode.

Parameters:

NameTypeDescriptionDefault
formulastrExplore-style formula (e.g. "wt ~ cyl").required
dataDataFrameTraining data.required
specobjectModel specification.required
bundleobjectData bundle.required
fitobjectFitted model state.required
formula_specobjectLearned formula spec for newdata evaluation.required
pred_typestrPrediction scale ("response" or "link").required
varyingstrRE handling ("exclude" or "include").required
allow_new_levelsboolIf True, new groups predict at population level.required
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables.required

Returns:

TypeDescription
‘PredictionState’PredictionState with grid columns attached.
parse_predict_formula
parse_predict_formula(formula: str, data: pl.DataFrame, response_col: str, grouping_factors: tuple[str, ...], *, n_points: int | Literal['data'] = 50) -> tuple[pl.DataFrame, list[str]]

Parse an explore-style formula and build a prediction grid.

Translates the formula via :func:parse_explore_formula, rejects contrast formulas, and delegates to :func:build_predict_grid.

Parameters:

NameTypeDescriptionDefault
formulastrExplore-style formula (e.g. "wt ~ cyl").required
dataDataFrameTraining data.required
response_colstrResponse column name.required
grouping_factorstuple[str, ...]Random-effect grouping variables.required
n_pointsint | Literal[‘data’]Number of grid points for continuous focal variables.50

Returns:

TypeDescription
DataFrameTuple of (grid DataFrame, list of grid column names for output).
list[str]The grid column names are the focal var plus any condition vars
tuple[DataFrame, list[str]](the columns that vary across the grid, excluding reference-value
tuple[DataFrame, list[str]]columns).
resolve_condition_values
resolve_condition_values(cond: Condition, data: pl.DataFrame) -> list | None

Resolve a :class:Condition to concrete values or None.

Parameters:

NameTypeDescriptionDefault
condConditionA Condition from :func:parse_explore_formula.required
dataDataFrameThe model’s training data.required

Returns:

TypeDescription
list | NoneA list of concrete values if the condition specifies explicit
list | Nonevalues (at_values, at_range, at_quantile), or
list | NoneNone for bare conditions (use all unique levels).

lifecycle

Fit lifecycle orchestration.

Owns the multi-step fit sequence: bundle rebuild → fit → post-fit state → diagnostics augmentation. Called by model.fit() so the model class stays a thin facade.

Classes:

NameDescription
FitResultImmutable result of the fit lifecycle.

Functions:

NameDescription
execute_fitExecute the full fit lifecycle: bundle rebuild → fit → post-fit state → diagnostics.

Classes

FitResult

Immutable result of the fit lifecycle.

Attributes:

NameTypeDescription
fitFitStateFitted model state (coefficients, residuals, etc.).
bundleDataBundleData bundle used for fitting (may be rebuilt).
formula_specobjectLearned formula spec for newdata evaluation.
raw_dataDataFrame | NoneOriginal data snapshot (pre-augmentation).
augmented_dataDataFrame | NoneData with diagnostic columns, or None.
varying_offsetsVaryingState | NoneBLUPs for mixed models, or None.
varying_spreadVaryingSpreadState | NoneVariance components for mixed models, or None.
Attributes
augmented_data
augmented_data: pl.DataFrame | None
bundle
bundle: DataBundle
fit
fit: FitState
formula_spec
formula_spec: object
raw_data
raw_data: pl.DataFrame | None
varying_offsets
varying_offsets: VaryingState | None = None
varying_spread
varying_spread: VaryingSpreadState | None = None

Functions

execute_fit
execute_fit(spec: ModelSpec, bundle: DataBundle | None, data: pl.DataFrame, raw_data: pl.DataFrame | None, formula: str, custom_contrasts: dict | None, weights_col: str | None, offset_col: str | None, missing: str, is_mixed: bool, solver_override: str | None, fit_kwargs: dict) -> FitResult

Execute the full fit lifecycle: bundle rebuild → fit → post-fit state → diagnostics.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification.required
bundleDataBundle | NoneExisting data bundle, or None to force rebuild.required
dataDataFrameCurrent data (raw_data-restored by caller).required
raw_dataDataFrame | NoneOriginal pre-augmentation snapshot, or None.required
formulastrFormula string for bundle building.required
custom_contrastsdict | NoneUser contrast matrices, or None.required
weights_colstr | NoneWeights column name, or None.required
offset_colstr | NoneOffset column name, or None.required
missingstrMissing value handling ("drop" or "fail").required
is_mixedboolWhether this is a mixed-effects model.required
solver_overridestr | NoneExplicit solver, or None for auto.required
fit_kwargsdictAdditional kwargs for fit_model().required

Returns:

TypeDescription
FitResultFitResult with all state the model needs to assign.

lmer

LMM fitting via Penalized Least Squares (PLS).

Functions:

NameDescription
fit_lmer_plsFit linear mixed-effects model using Penalized Least Squares.

Classes

Functions

fit_lmer_pls
fit_lmer_pls(spec: ModelSpec, bundle: DataBundle, *, max_iter: int = 10000, verbose: bool = False) -> FitState

Fit linear mixed-effects model using Penalized Least Squares.

This adapter wraps the PLS implementation from PLS is the algorithm from Bates et al. (2015) used in R’s lme4 package.

Outer loop (BOBYQA optimization over theta): Outer loop (BOBYQA optimization over theta): For each theta (relative covariance parameters): 1. Build Lambda (block-diagonal Cholesky factor from theta) 2. Form S_22 = Lambda’ Z’ Z Lambda + I 3. Sparse Cholesky factorization of S_22 4. Compute Schur complement for fixed effects 5. Solve for beta (fixed effects) and u (spherical RE) 6. Compute REML or ML deviance

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification containing: - method: “reml” or “ml” (determines objective function) - random_terms: Parsed random effect specificationsrequired
bundleDataBundleData bundle containing: - X: Fixed effects design matrix (n x p) - Z: Random effects design matrix (n x q, sparse CSC) - y: Response vector - re_metadata: Grouping structure informationrequired
max_iterintMaximum BOBYQA iterations (default: 10000).10000
verboseboolPrint optimization progress (default: False).False

Returns:

TypeDescription
FitStateFitState containing: - coef: Fixed effect coefficient estimates - vcov: Variance-covariance of fixed effects - fitted: Predicted values (fixed + random) - residuals: Response residuals (y - fitted) - leverage: Approximate leverage values - df_resid: Residual degrees of freedom - loglik: REML or ML log-likelihood - sigma: Residual standard deviation - theta: Optimized relative covariance parameters - u: Spherical random effects (unit variance) - converged: Whether optimizer converged - n_iter: Number of optimizer iterations

See Also:

ols

OLS fitting via QR decomposition.

Functions:

NameDescription
fit_ols_qrFit ordinary or weighted least squares using QR decomposition.

Classes

Functions

fit_ols_qr
fit_ols_qr(spec: ModelSpec, bundle: DataBundle) -> FitState

Fit ordinary or weighted least squares using QR decomposition.

Supports observation weights (WLS) and offset terms. When weights are present, solves the transformed system sqrt(W)*X, sqrt(W)*y via QR decomposition, which yields WLS coefficients and vcov directly. Offsets are subtracted from y before fitting and added back to fitted values.

  1. Subtract offset from y (if present): y_adj = y - offset

  2. Subtract offset from y (if present): y_adj = y - offset

  3. Apply weights (if present): X_w = sqrt(w)*X, y_w = sqrt(w)*y_adj

  4. QR decompose X_w with column pivoting for stability

  5. Solve R * beta = Q.T @ y_w via back-substitution

  6. Recompute original-scale: fitted = X @ beta + offset, resid = y - fitted

  7. vcov = sigma_w^2 * (X’WX)^{-1}

  8. Leverage from (possibly weighted) hat matrix

Matches R’s logLik.lm formula:: Matches R’s logLik.lm formula::

L = 0.5*sum(log(w)) - n/2 * (log(2*pi) + log(RSS_w/n) + 1)

The 0.5*sum(log(w)) term is the Jacobian from the weight transformation.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification (unused for OLS, included for interface consistency with other fitters).required
bundleDataBundleData bundle containing: - X: Design matrix (n x p) - y: Response vector (n,) - weights: Observation weights (n,) or None for OLS - offset: Offset vector (n,) or Nonerequired

Returns:

TypeDescription
FitStateFitState containing: - coef: Coefficient estimates, shape (p,) - vcov: Variance-covariance matrix, shape (p, p) - fitted: Fitted values X @ coef + offset, shape (n,) - residuals: y - fitted, shape (n,) - leverage: Hat matrix diagonal, shape (n,) - df_resid: Residual degrees of freedom (n - rank) - loglik: Gaussian log-likelihood (weighted if applicable) - sigma: Residual standard deviation - converged: Always True (closed-form solution) - n_iter: Always 1 (single step)

Examples:

>>> import numpy as np
>>> from containers import build_model_spec, DataBundle
>>> spec = build_model_spec(
...     formula="y ~ x",
...     response_var="y",
...     fixed_terms=["Intercept", "x"],
... )
>>> bundle = DataBundle(
...     X=np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]]),
...     y=np.array([2.0, 4.0, 6.0]),
...     X_names=["Intercept", "x"],
...     y_name="y",
...     valid_mask=np.array([True, True, True]),
...     n_total=3,
... )
>>> state = fit_ols_qr(spec, bundle)
>>> np.allclose(state.fitted + state.residuals, bundle.y)
True
>>> np.allclose(state.coef, [0.0, 2.0])  # Perfect fit: y = 2x
True

predict

Prediction operations on containers.

Pure functions for computing predictions on new data, including random effects contribution for mixed models. Extracted from model/core.py.

Classes

Functions

build_X_for_newdata
build_X_for_newdata(formula_spec: FormulaSpec | None, X_names: tuple[str, ...], newdata: pl.DataFrame) -> NDArray[np.float64]

Build design matrix X for new data.

Uses the stored FormulaSpec to properly handle factors, transformations (log, poly, center), and interactions. This ensures new data is encoded consistently with the training data.

When no FormulaSpec is available (e.g. simulation-only workflows), falls back to manual column stacking.

Parameters:

NameTypeDescriptionDefault
formula_specFormulaSpec | NoneEncoding state from build_design_matrices(), or None for the fallback path.required
X_namestuple[str, ...]Column names of the training design matrix.required
newdataDataFrameNew data for prediction as a Polars DataFrame.required

Returns:

TypeDescription
NDArray[float64]Design matrix with same columns as training X, shape
NDArray[float64](n_new, p).
build_re_covariates
build_re_covariates(newdata: pl.DataFrame, factor_names: list[str], valid_indices: NDArray[np.intp], formula_spec: FormulaSpec | None, X_names: tuple[str, ...]) -> NDArray[np.float64]

Build random effects covariate matrix for valid newdata rows.

For each random effect term (e.g. Intercept, slope variable), extracts the appropriate covariate values from newdata for the valid rows.

Parameters:

NameTypeDescriptionDefault
newdataDataFrameNew data DataFrame.required
factor_nameslist[str]Names of random effect terms for this grouping factor (e.g. ["Intercept", "x"]).required
valid_indicesNDArray[intp]Integer indices of valid (non-NA) rows in newdata.required
formula_specFormulaSpec | NoneFormulaSpec for proper design matrix encoding, or None for the fallback path.required
X_namestuple[str, ...]Column names from the training design matrix.required

Returns:

TypeDescription
NDArray[float64]Array of shape (n_valid, n_re) with covariate values.
compute_predictions
compute_predictions(spec: ModelSpec, bundle: DataBundle, fit: FitState, formula_spec: FormulaSpec | None, training_data: pl.DataFrame | None, newdata: pl.DataFrame | None, pred_type: Literal['response', 'link'], *, varying: Literal['exclude', 'include'] = 'exclude', allow_new_levels: bool = False) -> PredictionState

Compute predictions for given data.

For training data (newdata=None), returns fitted values directly. For new data, builds the design matrix, computes the linear predictor, optionally adds random effects, and applies the inverse link function.

Parameters:

NameTypeDescriptionDefault
specModelSpecModel specification (family, link, etc.).required
bundleDataBundleTraining data bundle (X, X_names, rank_info, re_metadata).required
fitFitStateFitted state (coefficients, theta, u, fitted values).required
formula_specFormulaSpec | NoneFormulaSpec for encoding new data, or None.required
training_dataDataFrame | NoneOriginal training DataFrame for group-level mapping, or None.required
newdataDataFrame | NoneData for prediction. If None, uses training data fitted values.required
pred_typeLiteral[‘response’, ‘link’]Prediction scale ("response" or "link").required
varyingLiteral[‘exclude’, ‘include’]How to handle random effects for mixed models. "exclude" for population-level, "include" for conditional predictions with BLUPs.‘exclude’
allow_new_levelsboolIf True, new groups predict at population level. If False, raises ValueError for unseen groups.False

Returns:

TypeDescription
PredictionStatePredictionState with fitted values and optional link-scale values.
compute_re_contribution
compute_re_contribution(re_meta: REInfo, theta: NDArray[np.floating], u: NDArray[np.floating], training_data: pl.DataFrame | None, newdata: pl.DataFrame, valid_mask: NDArray[np.bool_], allow_new_levels: bool, formula_spec: FormulaSpec | None, X_names: tuple[str, ...]) -> NDArray[np.float64]

Compute random effects contribution for new data predictions.

For each valid observation in newdata, maps group labels to trained group indices and computes the BLUP contribution as the dot product of the random effects design row and the group’s estimated BLUPs.

Parameters:

NameTypeDescriptionDefault
re_metaREInfoRandom effects metadata from the DataBundle.required
thetaNDArray[floating]Variance parameters (relative scale) from FitState.required
uNDArray[floating]Spherical random effects from FitState.required
training_dataDataFrame | NoneOriginal training data (Polars DataFrame) for extracting known group levels, or None.required
newdataDataFrameNew data with grouping columns.required
valid_maskNDArray[bool_]Boolean mask of valid (non-NA) rows in the design matrix, shape (n,).required
allow_new_levelsboolIf True, new groups get 0 RE contribution (population-level prediction). If False, raises ValueError.required
formula_specFormulaSpec | NoneFormulaSpec for proper design matrix encoding (passed through to build_re_covariates).required
X_namestuple[str, ...]Column names of the training design matrix (passed through to build_re_covariates).required

Returns:

TypeDescription
NDArray[float64]Array of RE contributions for valid rows only, shape (n_valid,).
resolve_coef_for_predict
resolve_coef_for_predict(coef: NDArray[np.floating], rank_info: RankInfo | None) -> NDArray[np.floating]

Coefficients safe for matrix multiplication (NaN -> 0).

When rank-deficient columns produce NaN coefficients, those NaN values must be zeroed out for X @ coef to work correctly. The dropped columns contribute nothing to predictions.

Parameters:

NameTypeDescriptionDefault
coefNDArray[floating]Coefficient array, shape (p,).required
rank_infoRankInfo | NoneRank deficiency info from the DataBundle, or None if the design is full rank.required

Returns:

TypeDescription
NDArray[floating]Coefficient array with NaN replaced by 0 when the design is
NDArray[floating]rank-deficient, or the original array unchanged.
validate_newdata_groups
validate_newdata_groups(re_meta: REInfo, training_data: pl.DataFrame | None, newdata: pl.DataFrame, allow_new_levels: bool) -> None

Validate grouping columns and levels in new data for mixed models.

Ensures that all grouping variables exist in newdata and that group levels are a subset of those seen during training (unless allow_new_levels is True).

Called from compute_predictions when varying="include" for mixed models, ensuring group structure is valid before attempting to compute BLUP contributions.

Parameters:

NameTypeDescriptionDefault
re_metaREInfoRandom effects metadata from the DataBundle.required
training_dataDataFrame | NoneOriginal training DataFrame for extracting known group levels, or None.required
newdataDataFrameNew data for prediction as a Polars DataFrame.required
allow_new_levelsboolIf True, skip the unseen-level check.required

varying

Varying parameter extraction for mixed-effects models.

Extracts BLUP (Best Linear Unbiased Predictor) computation and variance component decomposition from the model class into pure functions on containers. These operations convert fitted spherical random effects into interpretable group-level parameters.

per_factor_re_info: Split global RE metadata into per-factor structures. per_factor_re_info: Split global RE metadata into per-factor structures. compute_varying_state: Compute BLUPs from theta and u via Lambda matrix. compute_varying_spread_state: Extract variance components (tau², rho, ICC).

Functions:

NameDescription
build_mixed_post_fit_stateCompute BLUPs, variance components, and emit convergence warnings.
compute_varying_spread_stateCompute VaryingSpreadState (variance components) from theta parameters.
compute_varying_stateCompute VaryingState (BLUPs) from fitted random effects parameters.
per_factor_re_infoSplit global RE metadata into per-factor structures and names.

Attributes

Classes

Functions

build_mixed_post_fit_state
build_mixed_post_fit_state(fit: FitState, bundle: DataBundle, data: pl.DataFrame, *, stacklevel: int = 3) -> tuple[VaryingState | None, VaryingSpreadState | None]

Compute BLUPs, variance components, and emit convergence warnings.

Orchestrates the post-fit assembly for mixed-effects models: computes VaryingState (BLUPs) and VaryingSpreadState (variance components) from the fitted parameters, then checks for convergence issues.

Parameters:

NameTypeDescriptionDefault
fitFitStateFitted model state containing theta, u, sigma.required
bundleDataBundleData bundle with RE metadata and valid mask.required
dataDataFrameOriginal training data (used for group level labels).required
stacklevelintWarning stacklevel for convergence warnings. Default 3 accounts for: user → model.fit()build_mixed_post_fit_state().3

Returns:

TypeDescription
VaryingState | NoneA tuple (varying_offsets, varying_spread) where either may be
VaryingSpreadState | NoneNone if the required fitted parameters are missing.
compute_varying_spread_state
compute_varying_spread_state(theta: NDArray[np.floating], sigma: float, re_meta: REInfo) -> VaryingSpreadState

Compute VaryingSpreadState (variance components) from theta parameters.

Extracts residual variance (sigma²), random effect variances (tau²), correlations (rho), and intraclass correlation (ICC) from the fitted theta vector using the random effects structure.

Parameters:

NameTypeDescriptionDefault
thetaNDArray[floating]Variance component parameters from the fitted model.required
sigmafloatResidual standard deviation from the fitted model.required
re_metaREInfoRandom effects metadata (grouping vars, structure, etc.).required

Returns:

TypeDescription
VaryingSpreadStateVaryingSpreadState container with components DataFrame and
VaryingSpreadStatedecomposed variance quantities.
compute_varying_state
compute_varying_state(theta: NDArray[np.floating], u: NDArray[np.floating], re_meta: REInfo, data: pl.DataFrame | None = None) -> VaryingState

Compute VaryingState (BLUPs) from fitted random effects parameters.

Converts spherical random effects u to BLUPs b = Lambda @ u using the relative covariance factor Lambda built from theta. Constructs a grid of group/level combinations and maps BLUP values to named effects.

Parameters:

NameTypeDescriptionDefault
thetaNDArray[floating]Variance component parameters from the fitted model.required
uNDArray[floating]Spherical random effects vector from the fitted model.required
re_metaREInfoRandom effects metadata (grouping vars, structure, etc.).required
dataDataFrame | NoneOriginal training data, used to extract unique group levels. If None, levels are labeled "0", "1", etc.None

Returns:

TypeDescription
VaryingStateVaryingState container with grid, effects dict, and group info.
per_factor_re_info
per_factor_re_info(re_meta: REInfo, group_names: list[str]) -> tuple[str | list[str], list[str] | dict[str, list[str]]]

Split global RE metadata into per-factor structures and names.

For crossed/nested/mixed models, the global re_structure is a single string (e.g. “crossed”) and random_names is a concatenated list across all factors. This function splits them into per-factor structures and per-factor name dicts suitable for BLUP decomposition and convergence diagnostics.

For single-factor models, returns the originals unchanged.

Parameters:

NameTypeDescriptionDefault
re_metaREInfoRandom effects metadata from the fitted model’s DataBundle.required
group_nameslist[str]Ordered list of grouping variable names (e.g. ["subject"] or ["subject", "item"]).required

Returns:

TypeDescription
str | list[str]A tuple (re_structure, random_names) where:
list[str] | dict[str, list[str]]- For single-factor models: (str, list[str]) — the originals.
tuple[str | list[str], list[str] | dict[str, list[str]]]- For multi-factor models: (list[str], dict[str, list[str]]) — per-factor structure list and a dict mapping group name to its random effect names.