Formula-LHS marker that lets gllvmTMB() accept a wide data frame
(one row per unit, one column per trait) without making the user
pivot first to one row per (unit, trait) observation.
Value
A formula marker; never evaluated as a function call. The
parser recognises traits(...) on the LHS of a gllvmTMB()
formula and dispatches to the wide-format pivot pre-pass.
Details
The package teaches two shapes, long or wide data-frame:
long:
gllvmTMB(value ~ ..., data = df_long, ...)– one row per(unit, trait)observation.wide data frame:
gllvmTMB(traits(t1, t2, ...) ~ ..., data = df_wide, ...)– one row per unit, one column per trait, with compact formula syntax.
The soft-deprecated gllvmTMB_wide(Y, ...) wrapper remains exported
for legacy matrix-wrapper workflows, but new examples should use
traits(...) through gllvmTMB().
Both taught shapes reach the same stacked-trait model after internal stacking; the user picks whichever shape matches their data on disk.
Because the LHS already names the response traits, the RHS can use a
compact wide shorthand. 1 expands to the trait-specific intercepts
0 + trait; ordinary predictors such as env_temp expand to
(0 + trait):env_temp; and latent(1 | individual) expands to
the long covariance syntax latent(0 + trait | individual).
Ordinary latent() includes its \(\boldsymbol\Psi\) companion by
default. The same 1 | group shorthand is recognised for indep(),
dep(), bar-style phylo_indep() / phylo_dep(), and the
spatial_*() keywords. Species-axis phylogenetic keywords such as
phylo_latent(species, d = K) already name their phylogenetic axis
and pass through unchanged. Ordinary random-intercept terms such as
(1 | batch) also pass through unchanged.
gllvmTMB(
traits(sleep, mass, lifespan, brain) ~ 1 + env_temp +
latent(1 | individual, d = 2),
data = wide_df,
unit = "individual",
family = gaussian()
)Internally traits() is implemented as a tidyr::pivot_longer()
pre-pass: the wide data is pivoted to long format with trait as a
factor column (levels in the order the user supplied to traits())
and .y_wide_ as the response column; the LHS of the formula is
rewritten from traits(...) to .y_wide_; and the compact RHS is
expanded to the trait-stacked long syntax before dispatch. The
explicit long RHS remains accepted, so existing calls that already
write 0 + trait and latent(0 + trait | group) keep working.
Tidyselect verbs are supported because traits() forwards its
arguments to tidyr::pivot_longer(cols = ...):
traits(all_of(cols)), traits(starts_with("sp")),
traits(matches("^y[0-9]+$")), traits(any_of(c("a", "b"))), and
bare names all work.
Cells with NA responses are, by default, dropped via
pivot_longer(values_drop_na = TRUE) – the canonical complete-case
behaviour. Passing missing = miss_control(response = "include") to
gllvmTMB() instead keeps every (unit, trait) cell and masks the
NA cells out of the likelihood (the observed-response mask), preserving
per-cell identity and original-row accounting in fit$missing_data. Users
who want strict listwise drop should pre-filter the wide data before
calling.
Mixed-family fits (family = list(...) keyed by trait) use the same
family handling after internal stacking; traits() does not
intercept the family argument. Per-row weight vectors of length
nrow(data) are also replicated across traits automatically. For
per-cell weight matrices, pivot to long format and pass a weights
column aligned with (unit, trait) rows. The legacy matrix wrapper
gllvmTMB_wide() still accepts matrix weights for migration code.
See also
gllvmTMB() for model fitting. The legacy
matrix wrapper gllvmTMB_wide(Y, ...) is soft-deprecated in
0.2.0; wide-data examples now use the traits(...) LHS through
gllvmTMB().
Examples
if (FALSE) { # \dontrun{
# Wide format: one column per trait; traits() stacks them internally.
# The LHS *is* the trait spec, so no `trait =` argument is needed.
set.seed(1)
df_wide <- data.frame(
unit = factor(paste0("u", 1:40)),
t1 = rnorm(40), t2 = rnorm(40), t3 = rnorm(40)
)
fit <- gllvmTMB(
traits(t1, t2, t3) ~ 1 + latent(1 | unit, d = 2),
data = df_wide, unit = "unit"
)
extract_Sigma(fit)
} # }
