Latent constructs and measurement models in drmSEM
Source:vignettes/latent-variables.Rmd
latent-variables.RmdIn observational and ecological research, many foundational concepts—such as individual quality, habitat degradation, boldness, or organismal size—cannot be measured directly with a single error-free variable. Instead, researchers measure a battery of correlated proxies (indicators) or combine multiple environmental drivers.
drmSEM supports latent, MIMIC, and composite
measurement models within its piecewise structural equation
modelling framework (Shipley 2000, 2009; Grace and
Bollen 2008). This vignette explains:
- The distinction between formative, reflective, and MIMIC constructs.
- The declaration syntax:
drm_latent(),drm_indicator(), anddrm_composite(). - Identification constraints: marker indicator () versus unit-variance ().
- Reliability metrics: Cronbach’s (Cronbach 1951) versus Raykov’s composite reliability (Raykov 1997; McDonald 1999).
- Distributional effect propagation: tracing causal interventions through latent constructs to downstream mean (), dispersion (), and zero-inflation () components.
- The honest piecewise paradigm: how factor scores are materialized and why indicator loadings are kept strictly separate from structural paths.
The three construct paradigms
Before writing formulas, it is critical to distinguish the causal direction between the latent variable and its observed variables (Bollen 1989; Bollen and Lennox 1991; Grace and Bollen 2005):
| Construct type | Causal topology | Interpretation | Declaration |
|---|---|---|---|
| Formative / Composite | Indicators Construct | Indicators define or cause the construct (e.g. Diet + Activity Body Condition). |
drm_composite() or
drm_latent(type = "formative")
|
| Reflective | Latent Indicators | Indicators are manifestations of a common underlying factor (e.g. Boldness Latency, Exploration, Aggression). | drm_latent(type = "reflective") |
| MIMIC | Causes Latent Indicators | Multiple causes drive an unobserved state, which is reflected in multiple observable proxies (Jöreskog & Goldberger 1975). | drm_latent(type = "mimic", causes = ...) |
1. Formative constructs: drm_composite()
A formative construct is a deterministic or weighted combination of observed causes (Bollen and Lennox 1991; Grace and Bollen 2008). Because the indicators cause the construct, indicators are not required to correlate positively with each other, and internal-consistency reliability is not a validity requirement.
# Simulated morphometric data for 250 birds
n <- 250
dat <- data.frame(
bill_length = rnorm(n, mean = 20, sd = 2),
tarsus_length = rnorm(n, mean = 35, sd = 3),
body_mass = rnorm(n, mean = 15, sd = 1.5),
temp = rnorm(n, mean = 18, sd = 4)
)
# Build an equal-weighted composite index of body size
size_comp <- drm_composite(
name = "structural_size",
indicators = c("bill_length", "tarsus_length", "body_mass"),
method = "fixed",
data = dat,
standardize = TRUE
)
size_comp
#> <composite construct> structural_size = fixed(bill_length, tarsus_length,
#> body_mass)
#> reliability (Cronbach's alpha): -0.01
#> score standardized (mean 0, sd 1)When passed to drm_sem(), the composite score is
materialized into the data frame prior to node fitting. Structural nodes
can use structural_size as an ordinary predictor or
response.
2. Reflective constructs:
drm_latent(type = "reflective")
In a reflective construct, an unobserved latent variable drives multiple observable indicators:
where is the factor loading for indicator , and is the unique (error) variance (Bollen 1989).
Identification constraints
Because
is unobserved, its scale and origin are indeterminate without an
identification constraint. drm_latent() provides two
standard identification options:
-
Marker variable identification
(
identification = "marker"): Fixes the loading of a reference indicator to . The scale of is identical to the scale of that marker indicator. -
Unit-variance identification
(
identification = "unit_variance"): Constrains the latent variance to . All indicator loadings are estimated freely as factor loadings.
# Generate data driven by a single latent trait: 'boldness'
boldness_true <- rnorm(n, mean = 0, sd = 1.5)
dat$exploratory_speed <- 1.0 * boldness_true + rnorm(n, sd = 0.5)
dat$novel_object_time <- 0.8 * boldness_true + rnorm(n, sd = 0.6)
dat$trap_latency <- 0.6 * boldness_true + rnorm(n, sd = 0.7)
# Declare a reflective latent with marker identification:
refl_marker <- drm_latent(
name = "boldness",
indicators = c("exploratory_speed", "novel_object_time", "trap_latency"),
type = "reflective",
identification = "marker",
marker = "exploratory_speed",
data = dat
)
summary(refl_marker)
#> <latent construct: reflective> boldness (identification: marker)
#> indicator loading std_loading
#> exploratory_speed 1.0000 0.9520
#> novel_object_time 0.8702 0.9235
#> trap_latency 0.6970 0.8866
#> Composite reliability (Raykov's rho): 0.947
#> Reliability (Cronbach's alpha): 0.907Notice that exploratory_speed has a loading of exactly
,
fixing the metric of the latent construct.
Alternatively, using
identification = "unit_variance":
refl_uvar <- drm_latent(
name = "boldness_std",
indicators = list(
drm_indicator("exploratory_speed"),
drm_indicator("novel_object_time"),
drm_indicator("trap_latency")
),
type = "reflective",
identification = "unit_variance",
data = dat
)
summary(refl_uvar)
#> <latent construct: reflective> boldness_std (identification: unit_variance)
#> indicator loading std_loading
#> exploratory_speed 1.3969 0.9520
#> novel_object_time 1.2156 0.9235
#> trap_latency 0.9736 0.8866
#> Composite reliability (Raykov's rho): 0.947
#> Reliability (Cronbach's alpha): 0.9073. MIMIC models: Multiple Indicators, Multiple Causes
A MIMIC (Multiple Indicators, Multiple Causes) construct (Jöreskog and Goldberger 1975) combines structural causes and reflective indicators in a single unified block:
Environmental causes predict the latent variable , which in turn manifests in observed proxy indicators .
# Environmental causes of condition
dat$food_availability <- rnorm(n)
dat$parasite_load <- rnorm(n)
condition_true <- 0.7 * dat$food_availability - 0.5 * dat$parasite_load + rnorm(n, sd = 0.4)
# Reflective physiological indicators of condition
dat$hematocrit <- 1.0 * condition_true + rnorm(n, sd = 0.3)
dat$glucose <- 0.75 * condition_true + rnorm(n, sd = 0.35)
dat$plasma_fat <- 0.55 * condition_true + rnorm(n, sd = 0.4)
# Downstream response: clutch size with location-scale effects
dat$clutch_size <- rnorm(
n,
mean = 4.0 + 0.8 * condition_true,
sd = exp(0.1 - 0.4 * condition_true)
)
# Declare MIMIC construct:
mimic_cond <- drm_latent(
name = "condition",
causes = c("food_availability", "parasite_load"),
indicators = list(
drm_indicator("hematocrit", marker = TRUE),
drm_indicator("glucose"),
drm_indicator("plasma_fat")
),
type = "mimic",
identification = "marker",
data = dat
)
summary(mimic_cond)
#> <latent construct: mimic> condition (identification: marker)
#> Structural causes: food_availability, parasite_load
#> indicator loading std_loading
#> hematocrit 1.0000 0.9453
#> glucose 0.7728 0.9247
#> plasma_fat 0.6464 0.8915
#> Composite reliability (Raykov's rho): 0.947
#> Reliability (Cronbach's alpha): 0.94. Construct reliability: versus Raykov’s
Assessing construct quality is essential before interpreting
structural pathways. drmSEM provides both classical
Cronbach’s
and modern composite reliability:
Cronbach’s
Cronbach’s (Cronbach 1951) assumes essential -equivalence (all indicators share identical factor loadings ). When loadings differ (as is almost universally true in real biological data), underestimates scale reliability (Raykov 1997).
Raykov’s composite reliability (McDonald’s )
Raykov’s (Raykov 1997; McDonald 1999) allows congeneric indicators with unequal loadings and unique error variances .
# Compute standalone metrics on an indicator matrix
ind_df <- dat[c("hematocrit", "glucose", "plasma_fat")]
alpha_val <- drm_cronbach_alpha(ind_df)
rho_val <- drm_raykov_rho(ind_df)
c("Cronbach's alpha" = alpha_val, "Raykov's rho" = rho_val)
#> Cronbach's alpha Raykov's rho
#> 0.9002408 0.9493847When indicator loadings vary substantially, Raykov’s gives the honest, unattenuated measure of composite reliability.
5. Assembling the SEM and estimating distributional pathways
In drmSEM, latent and composite constructs integrate
seamlessly into piecewise structural equation models via the
latents = or composites = arguments:
sem <- drm_sem(
# Node 1: Condition driven by environmental causes
condition = drm_node(
drmTMB::bf(condition ~ food_availability + parasite_load),
family = stats::gaussian()
),
# Node 2: Clutch size has location (mu) AND scale (sigma) predicted by condition
clutch_size = drm_node(
drmTMB::bf(
clutch_size ~ condition,
sigma ~ condition
),
family = stats::gaussian()
),
data = dat,
latents = mimic_cond
)
#> ℹ Fitting node "condition"
#> ✔ Fitting node "clutch_size" [98ms]
#>
#> ℹ Fitting node "clutch_size"
#> ✔ Fitting node "clutch_size" [73ms]
#>
summary(sem)
#>
#> ── drmSEM summary ──────────────────────────────────────────────────────────────
#> 2 endogenous nodes; order "condition" and "clutch_size"
#>
#> ── Edges by component ──
#>
#> mu: 3
#> sigma: 1
#>
#> ── Paths ──
#>
#> <drmSEM paths: 4 component-labelled coefficients>
#> from to component link term estimate
#> food_availability condition mu identity food_availability 0.6819
#> parasite_load condition mu identity parasite_load -0.4726
#> condition clutch_size mu identity condition 0.8078
#> condition clutch_size sigma log condition -0.4797
#> std.error statistic p.value endogenous
#> 0.0303 22.495 4.65e-112 FALSE
#> 0.0319 -14.802 1.43e-49 FALSE
#> 0.0622 12.981 1.58e-38 TRUE
#> 0.0507 -9.458 3.14e-21 TRUEInspecting structural paths vs indicator loadings
drmSEM strictly separates structural paths between nodes
from indicator measurement loadings:
# Structural regression coefficients between nodes
paths(sem)
#> <drmSEM paths: 4 component-labelled coefficients>
#> from to component link term estimate
#> food_availability condition mu identity food_availability 0.6819
#> parasite_load condition mu identity parasite_load -0.4726
#> condition clutch_size mu identity condition 0.8078
#> condition clutch_size sigma log condition -0.4797
#> std.error statistic p.value endogenous
#> 0.0303 22.495 4.65e-112 FALSE
#> 0.0319 -14.802 1.43e-49 FALSE
#> 0.0622 12.981 1.58e-38 TRUE
#> 0.0507 -9.458 3.14e-21 TRUE
# Indicator loadings (measurement model)
loadings(sem)
#> <drmSEM composite/latent loadings: 1 construct>
#> composite indicator loading std_loading method type identification
#> condition hematocrit 1.0000 0.9453 pca mimic marker
#> condition glucose 0.7728 0.9247 pca mimic marker
#> condition plasma_fat 0.6464 0.8915 pca mimic marker
# Construct reliability summary across all latent blocks
reliability(sem)
#> <drmSEM construct reliability: 1 construct>
#> composite type method n_indicators alpha rho ave prop_var
#> condition mimic pca 3 0.9002 0.9469 0.8478 0.84786. Distributional effect propagation through latent nodes
A central strength of drmSEM is that causal effects are
component-labelled. A path from a latent construct to a
downstream node can target the mean
(),
the dispersion / scale
(),
the shape
(),
or the zero-inflation
()
component.
Suppose we intervene on food_availability, shifting it
by
standard deviation. What happens downstream to
clutch_size?
# Simulate total interventional effects of food_availability on clutch_size
eff <- total_effects(sem, from = "food_availability", to = "clutch_size", nsim = 200)
eff
#> <drmSEM effect>
#> from to scale mediation target estimate conf.low
#> food_availability clutch_size response mean mean 0.5241 0.4337
#> conf.high
#> 0.6164Interpreting component-labelled pathways
-
Path
condition -> clutch_size (mu): Increases the expected mean clutch size. -
Path
condition -> clutch_size (sigma): Modulates the residual variation or reproductive risk among individuals. This is not a mean effect. A negative path indicates that birds in higher condition have more predictable, less erratic clutch sizes.
By simulating forward through the structural graph,
drmSEM correctly propagates interventions through the
latent construct to all modelled distributional parameters
simultaneously.
Summary of best practices
-
Choose the right construct model: Use
drm_composite()when indicators are causes/inputs (formative); usedrm_latent()when indicators are reflections/proxies of an underlying factor. -
Select an identification rule: Use
markerwhen you want the latent construct to inherit the scale of a primary reference trait; useunit_variancewhen you want a standardized factor score. - Check composite reliability: Report Raykov’s alongside Cronbach’s to ensure proxy indicators measure a congeneric construct reliably ().
- Distinguish structural paths from loadings: In publications, report measurement loadings () separately from structural regressions ().
-
Embrace distributional targets: Use location-scale
formulas (
sigma ~ eta) when latent quality stabilizes or destabilizes downstream biological traits.