sam: Fit Structural Equation Models using the SAM approach

View source: R/xxx_sam.R

samR Documentation

Fit Structural Equation Models using the SAM approach

Description

Fit a Structural Equation Model (SEM) using the Structural After Measurement (SAM) approach.

Usage

sam(model = NULL, data = NULL, aux = NULL, cmd = "sem", se = "twostep",
    mm_list = NULL, mm_args = list(bounds = "wide.zerovar"), 
    struc_args = list(estimator = "ML"), 
    sam_method = "local", ..., 
    local_options = list(m_method = "ML", lambda_correction = TRUE,
                         lambda1_floor = "debias", lambda2_floor = "debias",
                         alpha_correction = 0L, twolevel_method = "h1",
                         gamma_eta = "casewise"),
    global_options = list(), 
    bootstrap = list(R = 1000L, type = "ordinary", show_progress = FALSE),
    output = "lavaan",
    bootstrap_args = bootstrap
)

Arguments

model

A description of the user-specified model. Typically, the model is described using the lavaan model syntax. See model.syntax for more information. Alternatively, a parameter table (e.g., the output of the lavParTable() function) is also accepted.

data

A data frame containing the observed variables used in the model.

aux

Character vector. Names of auxiliary observed variables, forwarded to the underlying measurement and structural model fits. See the lavaan function for more details.

cmd

Character. Which command is used to run the sem models. The possible choices are "sem", "cfa" or "lavaan", determining how we deal with default options.

se

Character. The type of standard errors that are used in the final (structural) model. If "twostep" (the default), the standard errors take the estimation uncertainty of the first (measurement) stage into account. If "standard", this uncertainty is ignored, and we treat the measurement information as known. If "none", no standard errors are computed. The experimental "twostep.huber.white" variant computes the two-step correction as a stacked (Murphy-Topel type) sandwich with casewise-score based (first-order) meat – the two-step analogue of se = "robust.huber.white" in sem; it is currently available for continuous complete or missing = "ml" data (single- or multigroup, single level, no equality constraints), and falls back to "twostep.robust" or "twostep" otherwise. If the data are clustered (a cluster= argument is provided, without multilevel model syntax), se = "twostep" is automatically replaced by se = "twostep.robust", and the standard errors (also for se = "local" and se = "bootstrap", which resamples whole clusters) account for the clustering. For two-level models, se = "local" is available for the single-group continuous complete-data setting; it uses the cluster-sandwich covariance of the saturated (h1) estimates, and also enables the corrected structural test statistic. For estimator = "PML", se = "local" is the only available two-step correction (single group, complete data, all-ordinal indicators): it combines the casewise influences of the saturated thresholds/polychoric correlations with the casewise pairwise-likelihood influences of the measurement-block estimates, and also enables the corrected structural test statistic.

mm_list

List. Define the measurement blocks. Each element of the list should be either a single name of a latent variable, or a vector of latent variable names. If omitted, a separate measurement block is used for each latent variable.

mm_args

List. Optional arguments for the fitting function(s) of the measurement block(s) only. See lavOptions for a complete list. The measurement blocks may also be estimated by the noniterative MGM estimator (mm.args = list(estimator = "MGM"); continuous, single-level data only): the two-step corrected standard errors (for both sam_method = "local" and "global") then use the robust (Yuan-Chan) correction with the MGM influence matrix, and se = "twostep" is silently upgraded to "twostep.robust"; se = "local" is available as well.

struc_args

List. Optional arguments for the fitting function of the structural part only. See lavOptions for a complete list. Note that when se = "local", the bread of the local sandwich standard errors is (since 0.7-2) by default the observed information matrix of the structural model, while all other ingredients (and the structural test statistic) are based on the expected information. This makes the local standard errors more robust against misspecification of the structural part; whenever every structural equation reproduces the sample moments of its own predictors (for example, when all predictors are exogenous), both versions are identical. The summary header shows an additional Information bread line. The classic (expected-information) bread can be requested by adding struc_args = list(information_bread = "default") (or "expected"). A small-sample (leverage) correction of the local (and twostep.huber.white) standard errors can be requested with the global information_meat_hc option ("HC2" is a good choice for N < 500); see lavOptions. The correction is not applied whenever a lambda truncation engaged (see lambda1_floor and lambda2_floor above); the summary then says so.

sam_method

Character. Can be set to "local", "global" or "fsr". In the latter case, the results are the same as if Bartlett factor scores were used, without any bias correction.

...

Many more options can be specified, using 'name = value'. See lavOptions for a complete list. These options affect both the measurement blocks and the structural part.

local_options

List. Options specific for local SAM method (these options may change over time; both the dot.case and the snake_case spelling of the element names are accepted, e.g., lambda_correction or lambda_correction). If lambda_correction = TRUE, we ensure that the variance matrix of the latent variables (VETA) is positive definite. When the lambda correction must truncate, the truncation margin (floor) is controlled by lambda1_floor for the first-order correction (of VETA itself) and by lambda2_floor for the second-order correction (of the product-term augmented VETA2, only when the model contains latent quadratic/interaction terms). Both options accept the same values, and both default to "debias": a gated rule that, whenever the sample margin is comfortably positive (the vast majority of well-behaved analyses), reproduces the historical behavior exactly; only when the smallest generalized eigenvalue of the pencil is within (twice) its own sampling noise of the boundary is the truncation margin replaced by a bootstrap debiased estimate of the population margin. "default" requests the historical (vanishing-with-N) rules: margin 1/(N-1) for the first order, and P2/(N-1) for the second order, where P2 is the number of latent variables plus the number of product terms; in weakly-identified product-term models the second-order historical margin can make the accuracy of the structural estimates deteriorate with increasing N. A single nonnegative number fixes the margin at that value. The gated rules require raw, complete, single-level continuous data (no conditional_x); in all other settings they automatically fall back to "default". (Before September 2026, lambda2_floor was briefly called lambda.floor – with "default" as its default – and the old name is still accepted with a warning.) The standard errors of the local family (and the corrected structural test statistic) account for the sampling variability of a binding truncation – both the first-order one and, for models with latent product terms, the second-order one; when no truncation binds, they are unaffected. Note that when the debiased truncation engages, large product-term coefficients are somewhat shrunken, and their confidence intervals can be slightly anti-conservative. When a truncation engaged, summary() prints a note with the truncated multiplier(s) and (where available) two diagnostics for the regression coefficients, also expressed in standard error units: a delta-method approximation of the maximal shrinkage bias, and the maximal (exact) shift away from the estimates that the historical rule would have produced; the per-coefficient values are stored in the fitted object (object@internal$sam_trunc$bias and $move). As long as neither of them exceeds half a standard error, the truncation is of little practical consequence, and the note is merely informative. This is the typical situation when the truncation engages because a latent outcome is (almost) perfectly predicted (a residual variance close to zero, or equivalently a very high correlation between a latent predictor and its outcome), as opposed to (almost) collinear latent predictors, where the truncation can change the estimates substantially. (To reproduce the results of older versions of lavaan, use lambda1_floor = "default" and lambda2_floor = "default".) The option gamma_eta is only relevant if the model contains latent quadratic/interaction terms. It determines how the sampling variability of the step 1 (measurement) estimates enters the asymptotic variance matrix (Gamma.eta) of the summary statistics of the latent variables, which is used for the standard errors of the local family and for the corrected structural test statistic. If "casewise" (the default), Gamma.eta is computed from the casewise contributions to these statistics, augmented with the casewise influence of the step 1 estimates (based on the casewise scores of the measurement blocks). If "additive" (the only available method before 0.7-2), the step 1 variability is added as a separate term, which ignores the covariance between the two sources; this is not innocent for the intercepts of the indicators (which depend on the factor scores), and results in standard errors that are too large for the covariances between the latent variables and their product terms; the additive form also ignores the covariances between the estimates of different measurement blocks, and relies on the (usually normal-theory) standard errors of the measurement blocks. The alpha_correction option must be an integer. Acceptable values are in the range 0 to N-1. If zero (the default), no small sample correction is performed, and the bias-correction is the same as with local SAM. When equal to N-1, the bias-correction is eliminated, and the results are the same as naive FSR. Typical values are 0, P+1 (where P is the number of predictors in the structural model), P+5, and (N-1)/2.

global_options

List. Options specific for global SAM method (not used for now).

bootstrap

List. Only used when se = "bootstrap". Typical elements of this list are R: the number of bootstrap samples, and type, which can be set to "ordinary" (the default) or "parametric". A single number is also accepted (as in sem()) and is interpreted as R, the number of bootstrap draws.

output

Character. If "lavaan", a lavaan object is returned. If "list", a list is returned with all the ingredients from the different stages.

bootstrap_args

List. Deprecated, use bootstrap instead.

Details

The sam function automates the SAM approach by first estimating the measurement part of the model and then the structural part of the model. See the reference for more details.

Note that in the current implementation, all indicators of latent variables must be observed. As a result, second-order factor structures are not yet supported.

If sam_method is "local", "fsr" or "cfsr", the structural part of the model is fitted to the estimated latent (co)variances (and means) from step 1. This step-2 structural model is stored in the @internal slot of the returned object, and can be retrieved by lavInspect(fit, "sam.struc.fit.object"). The functions fitMeasures (and summary(fit, fit_measures = TRUE)), lavResiduals (and summary(fit, residuals = TRUE)), modindices and lavTestLRT (when all compared models are local SAM models) operate on this structural model: the reported quantities describe the fit of the structural part only, conditional on the (fixed) measurement model of step 1. When available, the reported (scaled) test statistic and the robust fit measures are corrected for the estimation of the measurement model in step 1: by default, this is the Satorra-Bentler scaled structural test, driven by the asymptotic covariance matrix of the latent moments (which accounts for step 1). The test argument may request another member of the Satorra-Bentler family for the structural test instead: "mean.var.adjusted", "scaled.shifted", or their "corrected" versions "mean.var.adjusted.corrected" and "scaled.shifted.corrected" (see lavTest), which use the unbiased estimator of the second-order trace of Hayakawa (2018), computed from the casewise contributions of the observations to the latent moments (available for single-group, single-level models with continuous indicators, except with se = "local.nt"; in other settings the corrected tests are dropped with a warning). Several tests can be combined, for instance test = c("satorra.bentler", "mean.var.adjusted.corrected").

If sam.method is "global", the returned object is an ordinary (joint) lavaan object, and fitMeasures returns the usual fit measures of the joint model. Note, however, that the parameters were estimated using a two-step procedure; the behavior of fit measures in this setting has not been studied, and they should be interpreted with caution.

Value

If output = "lavaan", an object of class lavaan, for which several methods are available, including a summary method. If output = "list", a list.

Exogenous covariates and conditional_x

Observed exogenous covariates may enter the structural part of the model. With continuous data, they are simply treated as (pseudo) latent variables (the default conditional_x = FALSE), but conditional_x = TRUE is supported as well (single-group, single-level models): the structural part is then fitted to the latent intercepts, the latent-on-covariate slopes, and the residual latent (co)variances, all conditional on the covariates.

With categorical (ordered) data, conditional_x = TRUE is the lavaan default (when exogenous covariates are present), and this is also the recommended setting: the covariates are conditioned on (as in a probit regression), rather than being absorbed into the joint polychoric correlation matrix – which would (wrongly) treat them as jointly normal, a real concern for binary/dummy covariates. In this setting, the measurement blocks of step 1 are estimated with conditional_x = TRUE as well (each block latent variable is regressed on all exogenous covariates, MIMIC style), so that the measurement parameters live on the same conditional latent-response scale (Var(y*|x) = 1, delta parameterization) as the joint model. An ordered endogenous variable in the structural part is supported too (with or without covariates): it enters the structural model on its latent-response scale, while its thresholds are fixed at their sample-based values.

When conditional_x = TRUE is combined with a local sam.method, the default se = "twostep" is automatically replaced by se = "twostep.robust": the classic two-step formula is built on the joint information matrix, which does not describe the local step-2 estimator in this setting (it underestimates the sampling variability, especially for binary covariates). The robust variant is computed as a structural-space sandwich here, and coincides with se = "local".

When latent interactions are combined with missing = "ml" (local sam.method), the factor scores that feed the second-order (interaction) moments are computed per missing-data pattern, using pattern-specific mapping matrices and measurement-error covariances. The first-order moments are based on the EM (h1) statistics and remain consistent under MAR; the second-order (interaction) layer is moment-based, and is consistent under MCAR. Cases for which some factor has no observed indicators cannot be scored: they are excluded from the interaction moments (with a warning), although they still contribute to the first-order moments.

Not (yet) available in combination with conditional_x = TRUE: multiple groups (for the local standard errors and the corrected test statistic), multilevel models, latent interactions, and the Yuan-Chan scaled global test statistic of sam_method = "global" (the standard, unscaled test is reported instead, with a warning).

References

Rosseel and Loh (2021). A structural-after-measurement approach to Structural Equation Modeling. Psychological Methods. Advance online publication. https://dx.doi.org/10.1037/met0000503

See Also

lavaan

Examples

## The industrialization and Political Democracy Example 
## Bollen (1989), page 332
model <- ' 
  # latent variable definitions
     ind60 =~ x1 + x2 + x3
     dem60 =~ y1 + a*y2 + b*y3 + c*y4
     dem65 =~ y5 + a*y6 + b*y7 + c*y8

  # regressions
    dem60 ~ ind60
    dem65 ~ ind60 + dem60

  # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8
'

fit.sam <- sam(model, data = PoliticalDemocracy,
               mm.list = list(ind = "ind60", dem = c("dem60", "dem65")))
summary(fit.sam)

lavaan documentation built on Oct. 8, 2026, 5:06 p.m.