
Response-specific predictor effects
Source:vignettes/articles/fixed-effect-zero-constraints.Rmd
fixed-effect-zero-constraints.RmdUse Xcoef_fixed when a direct predictor effect is fixed
at zero for some responses but estimated for others. This is an exact
equality constraint on the mean model—not shrinkage, variable selection,
or evidence that an estimated effect is null.
You can impose named zero constraints in maximum-likelihood fits.
Non-zero fixed values and REML = TRUE are not
supported.
Decide whether zero is structural
For unit (i) and response (t), consider
Xcoef_fixed pins one named entry of the direct mean
coefficient
.
It does not directly constrain the loadings, latent scores, Psi, or
other covariance parameters—although their fitted estimates can change
after the mean restriction changes.
Use an exact zero only when it is justified before seeing the fitted estimate, for example by experimental design, temporal ordering, measurement definition, preregistration, or a defensible structural model.
-
Known by design that the direct coefficient is
zero: use
Xcoef_fixed. - Want to learn whether the coefficient is near zero: leave it free and report its uncertainty or compare prespecified restricted and unrestricted models.
- Want shrinkage or data-driven selection: use a method designed for that purpose.
- Want to remove a response: change the response set.
- Want to constrain which traits load on an LV: use the separate loading- constraint interface; that changes , not .
Pinning a false zero can move the omitted mean pattern into other fixed effects, residual variation, or covariance components.
Worked example
Suppose treatment is assigned after a baseline measurement. By design, the treatment cannot cause the baseline response, while its effects on later growth and colour responses remain free. We encode that prespecified temporal zero.
library(gllvmTMB)
set.seed(20260623)
n_unit <- 36
site <- factor(seq_len(n_unit))
treatment <- rnorm(n_unit)
dat <- expand.grid(
site = site,
trait = factor(c("baseline", "growth", "colour")),
KEEP.OUT.ATTRS = FALSE
)
dat$treatment <- treatment[as.integer(dat$site)]
dat$eta <- with(dat, ifelse(
trait == "baseline", 0.3,
ifelse(trait == "growth", 0.4 + 0.8 * treatment,
-0.2 - 0.5 * treatment)
))
dat$y <- dat$eta + rnorm(nrow(dat), sd = 0.2)Inspect the expanded coefficient names
Constraint names must match the expanded fixed-effect design, not a guess based on the raw formula. Factor contrasts, interactions, transformations, and factor level order can all change these names.
fixed_formula <- ~ 0 + trait + (0 + trait):treatment
colnames(model.matrix(fixed_formula, dat))
#> [1] "traitbaseline" "traitcolour"
#> [3] "traitgrowth" "traitbaseline:treatment"
#> [5] "traitcolour:treatment" "traitgrowth:treatment"The baseline treatment coefficient is named
traitbaseline:treatment, so the constraint is:
fit_long <- gllvmTMB(
y ~ 0 + trait + (0 + trait):treatment,
data = dat,
trait = "trait",
unit = "site",
family = gaussian(),
Xcoef_fixed = c("traitbaseline:treatment" = 0),
silent = TRUE
)
long_fixed <- tidy(fit_long, "fixed")
long_fixed[grepl(":treatment$", long_fixed$term),
c("term", "estimate", "std.error", "status")]
#> term estimate std.error status
#> 4 traitbaseline:treatment 0.0000000 NA fixed
#> 5 traitcolour:treatment -0.4548902 0.03013778 estimated
#> 6 traitgrowth:treatment 0.8203414 0.03013778 estimatedThe zero is imposed, not estimated. Therefore
estimate = 0, std.error = NA, and
status = "fixed" are expected. The missing standard error
does not indicate a failed Hessian calculation. Inference for all other
parameters is conditional on the zero restriction being correct, and the
model degrees of freedom decrease by one for each pinned
coefficient.
The same constraint from wide data
The equivalent traits(...) call reaches the same stacked
design and therefore uses the same expanded coefficient name.
dat_wide <- stats::reshape(
dat[c("site", "trait", "treatment", "y")],
idvar = c("site", "treatment"),
timevar = "trait",
direction = "wide"
)
names(dat_wide) <- sub("^y\\.", "", names(dat_wide))
fit_wide <- gllvmTMB(
traits(baseline, growth, colour) ~ 1 + treatment,
data = dat_wide,
unit = "site",
family = gaussian(),
Xcoef_fixed = c("traitbaseline:treatment" = 0),
silent = TRUE
)
wide_fixed <- tidy(fit_wide, "fixed")
comparison <- merge(
long_fixed[grepl(":treatment$", long_fixed$term),
c("term", "estimate", "status")],
wide_fixed[grepl(":treatment$", wide_fixed$term),
c("term", "estimate", "status")],
by = "term",
suffixes = c("_long", "_wide")
)
comparison
#> term estimate_long status_long estimate_wide status_wide
#> 1 traitbaseline:treatment 0.0000000 fixed 0.0000000 fixed
#> 2 traitcolour:treatment -0.4548902 estimated -0.4548902 estimated
#> 3 traitgrowth:treatment 0.8203414 estimated 0.8203416 estimatedEquivalent designs and contrast settings produce the same names and estimates; arbitrarily different codings need not.
To pin several prespecified direct effects, supply several uniquely named entries, for example:
Xcoef_fixed = c(
"traitbaseline:treatment" = 0,
"traitcolour:temperature" = 0
)Errors and next actions
-
A name is not recognized: inspect
colnames(model.matrix(...))for the exact expanded design and check factor levels and contrasts. - A name is duplicated: provide each expanded coefficient once.
- A non-zero fixed value is requested: only structural zero is implemented; leave the coefficient free if zero is not the intended value.
-
REML = TRUEerrors: fixed-coefficient pinning is currently ML-only. - A fixed row has an unavailable SE or CI: this is expected because the value was imposed rather than estimated.
-
The scientific question is whether the effect equals
zero: do not impose the answer with
Xcoef_fixed; fit an appropriate unrestricted comparison.
Related tools
Xcoef_fixed acts on direct response-specific mean
coefficients. It is different from:
- Formula keyword grid, which chooses covariance structure;
- Pre-fit response screening, which checks data support and formula estimability rather than selecting responses or predictors;
-
gllvmTMB()reference, which documents the complete argument contract.