Random effects model within-group correlation in hierarchical or
longitudinal data. Each random effect term specifies:
- Which variables have random coefficients
- What grouping structure to use
- Whether to include a random intercept
- The covariance structure among random effects
Parameters:
grouping (str or int) – Grouping variable name or column index. All observations with
the same value of this variable belong to the same group.
variables (tuple of (str or int), optional) – Variables with random slopes. If empty, only includes random
intercept (if include_intercept=True).
include_intercept (bool, default=True) – Whether to include a random intercept (1 | group).
random_effects (list of RandomEffect) – Random effects to validate
Raises:
TypeError – If random_effects is not a list or contains non-RandomEffect items
ValueError – If random effects specifications are invalid
Return type:
None
Examples
>>> re1=RandomEffect(grouping='subject')>>> re2=RandomEffect(grouping='clinic')>>> validate_random_effects([re1,re2])# OK
>>> validate_random_effects([re1,"invalid"])# Raises TypeErrorTraceback (most recent call last):...TypeError: All items in random_effects must be RandomEffect instances
Notes
This function checks:
- All items are RandomEffect instances
- Each RandomEffect is internally valid (via __post_init__)
Base class for random effect covariance structures.
A covariance structure defines how to parameterize the variance-covariance
matrix Ψ for random effects. Different structures impose different
constraints (e.g., diagonal, identity) to reduce the number of parameters.
The Cholesky parameterization:
- Ensures positive definiteness
- Provides unconstrained optimization (except L_ii > 0)
- Standard approach in lme4 and other mixed model software
>>> # All variances are equal>>> np.allclose(np.diag(psi),np.exp(0.5))True
Notes
This is the most constrained structure, useful when:
- Sample size is small
- Random effects are believed to be exchangeable
- Computational efficiency is important
The AR(1) structure is ideal for:
- Equally-spaced longitudinal data
- Time series with decaying correlations
- Repeated measures where adjacent observations are most correlated
The inverse of AR(1) covariance matrix is tridiagonal, enabling
efficient computation for large matrices.
This is a generalization of AR(1) that allows arbitrary values at
each lag, rather than assuming exponential decay.
For q random effects (time points), requires (band+1) parameters where
band is the number of non-zero off-diagonal bands:
- params[0]: log(r_0) - log-variance
- params[k]: arctanh(r_k / r_0) for k = 1, …, band (transformed correlations)
Parameters:
band (int, default=2) – Number of off-diagonal bands to include. Higher values allow
more complex temporal patterns but require more parameters.
- band=1: First-order correlations only (like AR(1) but with free parameter)
- band=2: First and second-order correlations
Toeplitz covariance is appropriate for:
- Equally-spaced longitudinal data
- Time series where correlations don’t follow AR(p) exactly
- Exploratory analysis before fitting structured models
The Toeplitz structure is a special case of a block structure and
allows efficient computation using banded matrix algorithms.
For each random effect term, Z has a block for each group. Within
each block, columns correspond to the random effects (intercept,
slopes) for that group.
Parameters:
X (ndarray, shape (n, p)) – Fixed effects design matrix (including intercept if needed)
random_effects (list of RandomEffect) – Random effect specifications
groups_data (dict) – Dictionary mapping grouping variable -> group indicators array.
Keys should match RandomEffect.grouping values.
Returns:
Z (ndarray, shape (n, q)) – Random effects design matrix where q = sum over terms of
(n_groups_i * n_effects_i)
Z_info (list of dict) – List of dictionaries (one per random effect term) containing:
- ‘grouping’: grouping variable
- ‘n_effects’: number of random effects per group
- ‘n_groups’: number of groups
- ‘groups’: array of unique group values
- ‘start_col’: starting column in Z
- ‘end_col’: ending column in Z (exclusive)
b (ndarray, shape (q,)) – Random effect coefficients (BLUPs) from model fitting
Z_info (list of dict) – Information about Z matrix structure (from construct_Z_matrix)
Returns:
random_effects – Dictionary mapping grouping_variable -> {group_id -> coefficients}.
For each group, coefficients are [intercept, slope1, slope2, …]
depending on the random effect specification.
This function is the inverse of construct_Z_matrix: it takes the
vector of random effect coefficients and organizes them by group
for interpretation and prediction.
X (ndarray, shape (n, p)) – Fixed effects design matrix.
Z (ndarray, shape (n, q)) – Random effects design matrix.
Z_info (list of dict) – Metadata about Z structure (from construct_Z_matrix).
Each dict contains: ‘n_effects’, ‘n_groups’, ‘grouping’, ‘start_col’, ‘end_col’.
covariance (str, default='unstructured') – Covariance structure: ‘unstructured’, ‘diagonal’, or ‘identity’.
Applied to all random effect terms.
method (str, default='L-BFGS-B') – Optimization method for scipy.optimize.minimize.
maxiter (int, default=1000) – Maximum number of iterations.
tol (float, default=1e-6) – Convergence tolerance.
initial_psi (ndarray or list of ndarray or None, optional) – Initial value(s) for Ψ. If None, uses identity for each term.
If list, one per random effect term. If single array, used for all terms.
initial_sigma2 (float or None, optional) – Initial value for σ². If None, uses variance of residuals.
store_matrices (bool, default=False) – Whether to store V and P matrices in result (memory intensive).
Returns:
result – REML estimation result. For multiple random effects, psi is block-diagonal
containing all variance-covariance matrices.
Return type:
REMLResult
Examples
>>> # Single random intercept model>>> n=100>>> y=np.random.randn(n)>>> X=np.ones((n,1))>>> groups=np.repeat(np.arange(20),5)>>> Z=np.zeros((n,20))>>> Z[np.arange(n),groups]=1>>> Z_info=[{'n_effects':1,'n_groups':20,'grouping':'subject',... 'start_col':0,'end_col':20}]>>> result=estimate_variance_components(y,X,Z,Z_info)
n_effects (int or None, optional) – Number of random effects per group. If provided and psi is
smaller than the full dimension q, the per-group psi is
expanded to a block-diagonal matrix with q//n_effects
repeated blocks.
Returns:
V – Marginal covariance matrix V = Z Ψ_full Z’ + σ² I.
For a random intercept model with m groups, psi is (1, 1) but Z
has m columns. This function automatically expands psi to
diag(σ²_b,...,σ²_b) when n_effects is provided.
Compute REML projection matrix P = V⁻¹ - V⁻¹X(X’V⁻¹X)⁻¹X’V⁻¹.
The REML projection matrix removes the contribution of fixed effects
from the inverse covariance, yielding the restricted (REML) likelihood
that accounts for degrees of freedom lost in estimating β.
Parameters:
V (ndarray, shape (n, n)) – Marginal covariance matrix V = ZΨZ’ + σ²I.
X (ndarray, shape (n, p)) – Fixed effects design matrix.
Returns:
P – REML projection matrix. Satisfies PX = 0 and is symmetric
positive semi-definite.
Computation uses Cholesky decomposition for numerical stability.
Both V⁻¹ and (X’V⁻¹X)⁻¹ are computed via Cholesky solves rather
than explicit inversion.
beta_smooth (dict[str, ndarray]) – Smooth term coefficients by term name.
random_effects (dict[str, ndarray]) – Random effect coefficients by grouping variable.
variance_components (list[ndarray]) – List of variance-covariance matrices Ψ, one per random effect term.
For single term models, this is a list with one element.
covariance_params (list[ndarray] | None) – List of raw covariance parameters for each random effect term.
For structured covariance (AR1, CS, etc.), contains transformed parameters.
For example, AR1 params are [log(σ²), arctanh(ρ)].
None for unstructured covariance.
smoothing_parameters (dict[str, float] | None) – Smoothing parameters λ by smooth term (if estimated).
edf_total (float) – Total effective degrees of freedom.
edf_parametric (float) – EDF for parametric terms.
edf_smooth (dict[str, float]) – EDF by smooth term.
fitted_values (ndarray) – Fitted values on the response scale (μ̂; for the Gaussian identity
link this equals η̂ = Xβ + Zb).
residuals (ndarray) – Response residuals y − μ̂.
log_likelihood (float) – Log-likelihood (or REML log-likelihood). For non-Gaussian PQL fits
this is the conditional log-likelihood at the BLUPs, not the
marginal likelihood.
include_random (bool, default=True) – Whether to include random effects in predictions.
If True, returns fitted values (fixed + random effects).
If False, returns only fixed effects (population-level predictions).
y = X_para β_para + Σ_k X_k β_k + Zb + ε
b ~ N(0, Ψ), ε ~ N(0, σ²I)
This is solved in closed form via penalized least squares with REML
estimation of variance components.
Algorithm:
1. Construct combined fixed effects matrix X = [X_para | X_smooth_1 | …]
2. Build combined penalty matrix S (block diagonal for smooth terms)
3. Estimate variance components Ψ, σ² via REML
4. Solve mixed model equations with estimated variances
5. Compute EDF, AIC, BIC
Examples
>>> # Random intercept with linear predictor>>> n=100>>> X_para=np.ones((n,1))>>> Z=np.eye(n)[:,:10]# 10 groups>>> y=X_para@[2.0]+Z@np.random.randn(10)+np.random.randn(n)>>> Z_info=[{'n_effects':1,'n_groups':10}]>>> result=fit_gamm_gaussian(X_para,None,Z,Z_info,y)
Covariance structure for random effects. Options:
- ‘identity’: Independent random effects (default)
- ‘diagonal’: Heterogeneous variances, no correlation
- ‘unstructured’: Full covariance matrix
- ‘ar1’: Autoregressive AR(1) for temporal correlation
- ‘compound_symmetry’ or ‘cs’: Exchangeable correlation
- ‘exponential’: Spatial decay (requires coordinates)
- ‘matern’: Matérn spatial covariance (requires coordinates)
For temporal/longitudinal data, use ‘ar1’. For clustered data
with equal correlations, use ‘compound_symmetry’.
maxiter (int, default=100) – Maximum iterations for optimization.
tol (float, default=1e-6) – Convergence tolerance.
use_sparse (bool, default=False) – Whether to use sparse matrix operations for smooth terms.
When True, uses sparse CSR format for basis matrices and sparse
linear solvers. Provides significant speedup (10-100×) and memory
reduction (6-8×) for large problems with B-spline basis functions.
Currently only supported when smooth terms use B-spline basis.
backend (str, default='numpy') – Computational backend: ‘numpy’, ‘torch’, or ‘jax’.
device (str, optional) – Device for computation (for torch backend): ‘cpu’, ‘cuda’, ‘cuda:0’, etc.
If None, uses CUDA if available, else CPU.
Returns:
result – Fitted GAMM result with coefficients, variance components,
and diagnostics.
This is a high-level interface that automatically constructs
design matrices for random effects and calls the appropriate
fitting function based on the family.
For Gaussian family, uses exact REML estimation.
For non-Gaussian families, uses Penalized Quasi-Likelihood (PQL;
Breslow & Clayton 1993) with uncorrected estimates.
Non-Gaussian likelihood quantities. For non-Gaussian families,
log_likelihood is the conditional log-likelihood
Σᵢ ℓ(yᵢ; μ̂ᵢ) evaluated at the BLUPs, not the marginal likelihood;
aic/bic are conditional information criteria derived from it.
Likelihood-ratio tests on fixed effects based on these quantities are
not valid (the anova module issues a warning).
Limitations.offset and prior weights are not currently
supported by fit_gamm; for models requiring them, use fit_glm
(no random effects) instead.
Formula mode supports:
- R-style formula syntax with lme4-style random effects
- Automatic design matrix construction from DataFrames
- Random intercepts: (1 | group)
- Random slopes: (1 + x | group)
- Nested effects: (1 | a/b)
- Crossed effects: (1 | a) + (1 | b)
For Gaussian family, this reduces to standard LMM which could be solved
more efficiently with REML. Use fit_gamm_gaussian() instead for Gaussian data.
The algorithm iterates between:
1. Updating (β, b) given Ψ via weighted least squares
2. Updating Ψ given (β, b) via empirical covariance
Convergence is determined by relative change in Ψ falling below tol_outer.
With update_psi=False (fixed Ψ), convergence is determined by the
inner (β, b) fixed point instead.
For the Gamma family, the working weights assume dispersion φ = 1;
the estimated dispersion is not propagated into the weighting
(documented limitation).
For the Binomial family, fitted probabilities numerically 0 or 1
indicate complete or quasi-complete separation; a warning is issued
and the result is reported as not converged.
Examples
>>> importnumpyasnp>>> fromaurora.models.gamm.pqlimportfit_pql>>> # Generate Poisson data with random intercepts>>> n_groups,n_per_group=5,20>>> n=n_groups*n_per_group>>> groups=np.repeat(np.arange(n_groups),n_per_group)>>> X=np.column_stack([np.ones(n),np.random.randn(n)])>>> Z=np.eye(n_groups)[groups]# Random intercept design>>> b_true=np.random.randn(n_groups)*0.5>>> eta=X@[1.0,0.3]+Z@b_true>>> y=np.random.poisson(np.exp(eta))>>> result=fit_pql(X,Z,y,family='poisson')>>> print(result.converged)True
psi_init (ndarray, shape (n_effects, n_effects), optional) – Initial variance-covariance matrix (default: identity).
Required for models with more than one random effect per group
(e.g. random intercept + slope): its dimension determines how the
columns of Z are grouped. Without it, random-intercept structure
(one effect per group) is assumed.
Laplace approximation is more accurate than PQL, especially for:
- Binary data with small group sizes
- Count data with small means
- Models with large random effects
However, it is computationally more expensive due to:
- Inner optimization to find conditional mode b̂
- Hessian computation at each iteration
Caterpillar plots show the estimated random effects (BLUPs) for each
group with confidence intervals, sorted by magnitude. They are useful
for identifying groups with unusually large or small effects.
grouping (str or int, optional) – Which grouping variable to plot. If None, uses the first random
effect term. Required if model has multiple grouping variables.
effect_index (int, default=0) – Which random effect to plot (0=intercept, 1=first slope, etc.).
confidence (float, default=0.95) – Confidence level for intervals (e.g., 0.95 for 95% CI).
sort (bool, default=True) – Whether to sort groups by effect magnitude.
figsize (tuple, optional) – Figure size (width, height) in inches.
ax (Axes, optional) – Matplotlib axes to plot on. If None, creates new figure.
Create Q-Q plot for random effects normality check.
Q-Q (quantile-quantile) plots compare the distribution of random effects
to a theoretical normal distribution. Points falling along the diagonal
line indicate normality.
The random effects are assumed to follow a normal distribution:
b ~ N(0, Ψ)
Departures from normality (heavy tails, skewness) can be identified
visually from Q-Q plots. Consider:
- S-shaped pattern: Heavy tails (outliers)
- Systematic curve: Skewness
- Points far from line: Individual outliers
The histogram shows the empirical distribution of random effects,
while the overlaid curve shows the theoretical normal distribution
N(0, Ψ) implied by the model.
Interpret variance-covariance components of random effects.
Produces a human-readable summary of the estimated variance-covariance
matrices for each grouping variable, including standard deviations,
correlations, and qualitative interpretation of effect sizes.
Parameters:
variance_components (list of ndarray) – List of variance-covariance matrices, one per grouping variable.
Each matrix has shape (q,q) where q is the number of
random effects for that grouping factor.
group_names (list of str, optional) – Names of grouping variables corresponding to the entries in
variance_components. If None, generic names
(Group_1, Group_2, …) are used.
Returns:
interpretation – Formatted multi-line string with variance, standard deviation,
covariance, and correlation estimates together with qualitative
interpretation (e.g., weak/strong correlation, compensatory effects).
Compute marginal and conditional R-squared for GAMM.
Implements the method of Nakagawa & Schielzeth (2013) for partitioning
variance explained in mixed models into fixed-effects-only (marginal)
and fixed-plus-random (conditional) components.
Parameters:
result (GAMMResult) – Fitted GAMM result. Must expose attributes _X_parametric,
beta_parametric, fitted_values, residuals,
residual_variance, and variance_components.
Returns:
r2_marginal (float) – Proportion of total variance explained by fixed effects alone,
computed as var_fixed/var_total.
r2_conditional (float) – Proportion of total variance explained by both fixed and random
effects, computed as (var_fixed+var_random)/var_total.
where var_fixed is the variance of the fixed-effects predictions,
var_random is the mean trace of the variance-covariance components,
and var_residual is the residual variance from the fitted model.
For non-Gaussian families, these R-squared values are approximate.
Covariance structure for random effects. Options:
- ‘identity’: Independent random effects (default)
- ‘diagonal’: Heterogeneous variances, no correlation
- ‘unstructured’: Full covariance matrix
- ‘ar1’: Autoregressive AR(1) for temporal correlation
- ‘compound_symmetry’ or ‘cs’: Exchangeable correlation
- ‘exponential’: Spatial decay (requires coordinates)
- ‘matern’: Matérn spatial covariance (requires coordinates)
For temporal/longitudinal data, use ‘ar1’. For clustered data
with equal correlations, use ‘compound_symmetry’.
maxiter (int, default=100) – Maximum iterations for optimization.
tol (float, default=1e-6) – Convergence tolerance.
use_sparse (bool, default=False) – Whether to use sparse matrix operations for smooth terms.
When True, uses sparse CSR format for basis matrices and sparse
linear solvers. Provides significant speedup (10-100×) and memory
reduction (6-8×) for large problems with B-spline basis functions.
Currently only supported when smooth terms use B-spline basis.
backend (str, default='numpy') – Computational backend: ‘numpy’, ‘torch’, or ‘jax’.
device (str, optional) – Device for computation (for torch backend): ‘cpu’, ‘cuda’, ‘cuda:0’, etc.
If None, uses CUDA if available, else CPU.
Returns:
result – Fitted GAMM result with coefficients, variance components,
and diagnostics.
This is a high-level interface that automatically constructs
design matrices for random effects and calls the appropriate
fitting function based on the family.
For Gaussian family, uses exact REML estimation.
For non-Gaussian families, uses Penalized Quasi-Likelihood (PQL;
Breslow & Clayton 1993) with uncorrected estimates.
Non-Gaussian likelihood quantities. For non-Gaussian families,
log_likelihood is the conditional log-likelihood
Σᵢ ℓ(yᵢ; μ̂ᵢ) evaluated at the BLUPs, not the marginal likelihood;
aic/bic are conditional information criteria derived from it.
Likelihood-ratio tests on fixed effects based on these quantities are
not valid (the anova module issues a warning).
Limitations.offset and prior weights are not currently
supported by fit_gamm; for models requiring them, use fit_glm
(no random effects) instead.
Formula mode supports:
- R-style formula syntax with lme4-style random effects
- Automatic design matrix construction from DataFrames
- Random intercepts: (1 | group)
- Random slopes: (1 + x | group)
- Nested effects: (1 | a/b)
- Crossed effects: (1 | a) + (1 | b)
For Gaussian family, this reduces to standard LMM which could be solved
more efficiently with REML. Use fit_gamm_gaussian() instead for Gaussian data.
The algorithm iterates between:
1. Updating (β, b) given Ψ via weighted least squares
2. Updating Ψ given (β, b) via empirical covariance
Convergence is determined by relative change in Ψ falling below tol_outer.
With update_psi=False (fixed Ψ), convergence is determined by the
inner (β, b) fixed point instead.
For the Gamma family, the working weights assume dispersion φ = 1;
the estimated dispersion is not propagated into the weighting
(documented limitation).
For the Binomial family, fitted probabilities numerically 0 or 1
indicate complete or quasi-complete separation; a warning is issued
and the result is reported as not converged.
Examples
>>> importnumpyasnp>>> fromaurora.models.gamm.pqlimportfit_pql>>> # Generate Poisson data with random intercepts>>> n_groups,n_per_group=5,20>>> n=n_groups*n_per_group>>> groups=np.repeat(np.arange(n_groups),n_per_group)>>> X=np.column_stack([np.ones(n),np.random.randn(n)])>>> Z=np.eye(n_groups)[groups]# Random intercept design>>> b_true=np.random.randn(n_groups)*0.5>>> eta=X@[1.0,0.3]+Z@b_true>>> y=np.random.poisson(np.exp(eta))>>> result=fit_pql(X,Z,y,family='poisson')>>> print(result.converged)True
result – Dictionary containing:
- ‘beta_parametric’: Parametric coefficients
- ‘beta_smooth’: Dictionary of smooth coefficients by term
- ‘random_effects’: Random effect estimates
- ‘variance_components’: List of Ψ matrices
- ‘smoothing_parameters’: Dictionary of λ by smooth term
- ‘fitted_values’: Fitted values on the response scale (μ = g⁻¹(η))
- ‘linear_predictor’: Fitted linear predictor (η)
- ‘converged’: Whether algorithm converged
- ‘n_iterations_outer’: Number of outer iterations
- ‘edf_smooth’: EDF by smooth term
beta_smooth (dict[str, ndarray]) – Smooth term coefficients by term name.
random_effects (dict[str, ndarray]) – Random effect coefficients by grouping variable.
variance_components (list[ndarray]) – List of variance-covariance matrices Ψ, one per random effect term.
For single term models, this is a list with one element.
covariance_params (list[ndarray] | None) – List of raw covariance parameters for each random effect term.
For structured covariance (AR1, CS, etc.), contains transformed parameters.
For example, AR1 params are [log(σ²), arctanh(ρ)].
None for unstructured covariance.
smoothing_parameters (dict[str, float] | None) – Smoothing parameters λ by smooth term (if estimated).
edf_total (float) – Total effective degrees of freedom.
edf_parametric (float) – EDF for parametric terms.
edf_smooth (dict[str, float]) – EDF by smooth term.
fitted_values (ndarray) – Fitted values on the response scale (μ̂; for the Gaussian identity
link this equals η̂ = Xβ + Zb).
residuals (ndarray) – Response residuals y − μ̂.
log_likelihood (float) – Log-likelihood (or REML log-likelihood). For non-Gaussian PQL fits
this is the conditional log-likelihood at the BLUPs, not the
marginal likelihood.
include_random (bool, default=True) – Whether to include random effects in predictions.
If True, returns fitted values (fixed + random effects).
If False, returns only fixed effects (population-level predictions).
Random effects model within-group correlation in hierarchical or
longitudinal data. Each random effect term specifies:
- Which variables have random coefficients
- What grouping structure to use
- Whether to include a random intercept
- The covariance structure among random effects
Parameters:
grouping (str or int) – Grouping variable name or column index. All observations with
the same value of this variable belong to the same group.
variables (tuple of (str or int), optional) – Variables with random slopes. If empty, only includes random
intercept (if include_intercept=True).
include_intercept (bool, default=True) – Whether to include a random intercept (1 | group).
Base class for random effect covariance structures.
A covariance structure defines how to parameterize the variance-covariance
matrix Ψ for random effects. Different structures impose different
constraints (e.g., diagonal, identity) to reduce the number of parameters.
The Cholesky parameterization:
- Ensures positive definiteness
- Provides unconstrained optimization (except L_ii > 0)
- Standard approach in lme4 and other mixed model software
>>> # All variances are equal>>> np.allclose(np.diag(psi),np.exp(0.5))True
Notes
This is the most constrained structure, useful when:
- Sample size is small
- Random effects are believed to be exchangeable
- Computational efficiency is important
The AR(1) structure is ideal for:
- Equally-spaced longitudinal data
- Time series with decaying correlations
- Repeated measures where adjacent observations are most correlated
The inverse of AR(1) covariance matrix is tridiagonal, enabling
efficient computation for large matrices.
This is a generalization of AR(1) that allows arbitrary values at
each lag, rather than assuming exponential decay.
For q random effects (time points), requires (band+1) parameters where
band is the number of non-zero off-diagonal bands:
- params[0]: log(r_0) - log-variance
- params[k]: arctanh(r_k / r_0) for k = 1, …, band (transformed correlations)
Parameters:
band (int, default=2) – Number of off-diagonal bands to include. Higher values allow
more complex temporal patterns but require more parameters.
- band=1: First-order correlations only (like AR(1) but with free parameter)
- band=2: First and second-order correlations
Toeplitz covariance is appropriate for:
- Equally-spaced longitudinal data
- Time series where correlations don’t follow AR(p) exactly
- Exploratory analysis before fitting structured models
The Toeplitz structure is a special case of a block structure and
allows efficient computation using banded matrix algorithms.