Generalized Additive Mixed Models (GAMM)¶
GAMMs extend GAMs by adding random effects for hierarchical, longitudinal, or clustered data:
This chapter covers random effects, covariance structures, estimation methods, and the formula interface.
Quick start¶
Use fit_gamm() with a
formula string and a DataFrame:
import numpy as np
import pandas as pd
from aurora.models.gamm import fit_gamm
# Generate hierarchical data: 10 subjects, 20 observations each
np.random.seed(42)
n_groups, n_per = 10, 20
n = n_groups * n_per
data = pd.DataFrame({
"y": np.random.randn(n),
"x1": np.random.randn(n),
"subject": np.repeat(np.arange(n_groups), n_per),
})
# Random intercept model
result = fit_gamm(
formula="y ~ x1 + (1 | subject)",
data=data,
covariance="identity",
)
print(result.summary())
# Access results
print(result.beta_parametric) # Fixed effects
print(result.variance_components) # Random effect variance
print(result.residual_variance) # sigma^2
print(result.aic) # AIC
Random effects¶
Random effects are specified using
RandomEffect:
from aurora.models.gamm import fit_gamm, RandomEffect
# Random intercept: (1 | subject)
re1 = RandomEffect(grouping="subject")
# Random intercept + slope: (1 + time | subject)
re2 = RandomEffect(grouping="subject", variables=("time",))
# In formula mode, random effects are parsed automatically
result = fit_gamm(formula="y ~ x + (1 + time | subject)", data=data)
Covariance structures¶
Control how random effects co-vary:
Structure |
Parameters |
Use case |
R equivalent |
|---|---|---|---|
|
1 |
Equal variance, no correlation |
|
|
q |
Independent heterogeneous variances |
|
|
q(q+1)/2 |
Full covariance matrix |
|
|
2 |
Temporal correlation, rho^|t-s| |
|
|
2 |
Equal within-cluster correlation |
|
|
band+1 |
Banded temporal correlations |
|
|
2 |
Spatial exponential decay |
Exp Cov |
|
2 |
Spatial Matern covariance |
Matern Cov |
# AR(1) for longitudinal data
result = fit_gamm(
formula="y ~ time + (1 | subject)",
data=data,
covariance="ar1",
)
# Compound symmetry for clustered data
result = fit_gamm(
formula="y ~ x + (1 | clinic)",
data=data,
covariance="compound_symmetry",
)
# Unstructured for random intercept + slope
result = fit_gamm(
formula="y ~ x + (1 + x | subject)",
data=data,
covariance="unstructured",
)
Estimation methods¶
Gaussian family: Direct REML estimation via Henderson’s mixed model equations. This is the default and recommended method.
Non-Gaussian families: Two approximations are available:
PQL (Penalized Quasi-Likelihood): Iteratively fits a working model using a linearized approximation.
Laplace approximation: More accurate for small cluster sizes or binary outcomes.
# Gaussian (direct REML) - default, most accurate
result = fit_gamm(formula="y ~ x + (1 | subject)", data=data, family="gaussian")
# Poisson GAMM (uses PQL automatically)
result = fit_gamm(formula="y ~ x + (1 | subject)", data=data, family="poisson")
Prediction¶
# Population-level predictions (fixed effects only)
pred_pop = result.predict(include_random=False)
# Conditional predictions (fixed + random effects)
pred_cond = result.predict(include_random=True)
# New data prediction
from aurora.models.gamm import predict_from_gamm
X_new = np.column_stack([np.ones(20), np.random.randn(20)])
pred = predict_from_gamm(result, X_new)
GAMM with smooth terms¶
Combine smooth terms and random effects in one model:
from aurora.models.gamm import fit_gamm
# Smooth time trend + subject random intercept
result = fit_gamm(
formula="y ~ s(time) + (1 | subject)",
data=data,
)
This fits a penalized spline for the time effect alongside a random intercept for each subject. The smoothing parameter and variance components are estimated jointly by REML.
For direct control, use
fit_gamm_with_smooth():
from aurora.models.gamm import fit_gamm_with_smooth, RandomEffect
result = fit_gamm_with_smooth(
y=y,
X_parametric=np.ones((n, 1)),
X_smooth=X_smooth,
S_smooth=S_smooth,
random_effects=[RandomEffect(grouping="subject")],
groups_data={"subject": groups},
lambda_smooth={"s(time)": 0.1},
)