Learn R Programming

lavaan (version 0.7-2)

model.syntax: The Lavaan Model Syntax

Description

The lavaan model syntax describes a latent variable model. The function lavParTable turns it into a table that represents the full model as specified by the user. We refer to this table as the parameter table.

Usage

lavaanify(model = NULL, meanstructure = FALSE, int_ov_free = FALSE,
    int_lv_free = FALSE, marker_int_zero = FALSE,
    orthogonal = FALSE, orthogonal_y = FALSE,
    orthogonal_x = FALSE, orthogonal_efa = FALSE, std_lv = FALSE,
    correlation = FALSE, composites = TRUE, composites_cov_free = FALSE,
    effect_coding = "", conditional_x = FALSE,
    fixed_x = FALSE, parameterization = "delta", constraints = NULL,
    ceq_simple = FALSE, auto = FALSE, model_type = "sem",
    auto_fix_first = FALSE, marker = NULL,
    auto_fix_single = FALSE, auto_var = FALSE,
    auto_cov_lv_x = FALSE, auto_cov_x = FALSE, auto_cov_y = FALSE,
    auto_th = FALSE, auto_delta = FALSE, auto_efa = FALSE,
    var_table = NULL, ngroups = 1L, nthresholds = NULL,
    group_equal = NULL, group_partial = NULL, group_w_free = FALSE,
    debug = FALSE, warn = TRUE, as_data_frame = TRUE, ...)

lavParTable(model = NULL, meanstructure = FALSE, int_ov_free = FALSE, int_lv_free = FALSE, marker_int_zero = FALSE, orthogonal = FALSE, orthogonal_y = FALSE, orthogonal_x = FALSE, orthogonal_efa = FALSE, std_lv = FALSE, correlation = FALSE, composites = TRUE, composites_cov_free = FALSE, effect_coding = "", conditional_x = FALSE, fixed_x = FALSE, parameterization = "delta", constraints = NULL, ceq_simple = FALSE, auto = FALSE, model_type = "sem", auto_fix_first = FALSE, marker = NULL, auto_fix_single = FALSE, auto_var = FALSE, auto_cov_lv_x = FALSE, auto_cov_x = FALSE, auto_cov_y = FALSE, auto_th = FALSE, auto_delta = FALSE, auto_efa = FALSE, var_table = NULL, ngroups = 1L, nthresholds = NULL, group_equal = NULL, group_partial = NULL, group_w_free = FALSE, debug = FALSE, warn = TRUE, as_data_frame = TRUE, ...)

lavParseModelString(model_syntax = '', as_data_frame = FALSE, parser = "open", warn = TRUE, debug = FALSE, ...)

Arguments

model

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

model_syntax

The model syntax specifying the model. Must be a literal string.

meanstructure

If TRUE, intercepts/means will be added to the model for both observed and latent variables.

int_ov_free

If FALSE, the intercepts of the observed variables are fixed to zero.

int_lv_free

If FALSE, the intercepts of the latent variables are fixed to zero.

marker_int_zero

Logical. Only relevant if the metric of each latent variable is set by fixing the first factor loading to unity. If TRUE, it implies meanstructure = TRUE and std_lv = FALSE, and it fixes the intercepts of the marker indicators to zero, while freeing the means/intercepts of the latent variables. This only works correctly for single-group, single-level models.

orthogonal

If TRUE, all covariances among latent variables are set to zero.

orthogonal_y

If TRUE, all covariances among endogenous latent variables only are set to zero.

orthogonal_x

If TRUE, all covariances among exogenous latent variables only are set to zero.

orthogonal_efa

If TRUE, all covariances among latent variables involved in rotation only are set to zero.

std_lv

If TRUE, the metric of each latent variable is determined by fixing their variances to 1.0. If FALSE, the metric of each latent variable is determined by fixing the factor loading of the first indicator to 1.0. If there are multiple groups, std_lv = TRUE and "loadings" is included in the group.label argument, then only the latent variances of the first group will be fixed to 1.0, while the latent variances of the other groups are freely estimated.

correlation

If TRUE, a correlation structure is fitted. For continuous data, this implies that the (residual) variances are no longer parameters of the model.

composites

Logical. If TRUE, use the new (0.6-20) approach to handle composites.

composites_cov_free

Logical. Only relevant if the model contains composites. If TRUE, the composite-indicator (co)variances (the elements of the T matrix) are estimated as free parameters. If FALSE (the default), they are fixed to their sample values.

effect_coding

Can be logical or character string. If logical and TRUE, this implies effect_coding = c("loadings", "intercepts"). If logical and FALSE, it is set equal to the empty string. If "loadings" is included, equality constraints are used so that the average of the factor loadings (per latent variable) equals 1. Note that this should not be used together with std_lv = TRUE. If "intercepts" is included, equality constraints are used so that the sum of the intercepts (belonging to the indicators of a single latent variable) equals zero. As a result, the latent mean will be freely estimated and will usually equal the average of the means of the indicators involved.

conditional_x

If TRUE, we set up the model conditional on the exogenous `x' covariates; the model-implied sample statistics only include the non-x variables. If FALSE, the exogenous `x' variables are modeled jointly with the other variables, and the model-implied statistics reflect both sets of variables.

fixed_x

If TRUE, the exogenous `x' covariates are considered fixed variables and the means, variances and covariances of these variables are fixed to their sample values. If FALSE, they are considered random, and the means, variances and covariances are free parameters.

parameterization

Currently only used if data is categorical. If "delta", the delta parameterization is used. If "theta", the theta parameterization is used.

constraints

Additional (in)equality constraints. See details for more information.

ceq_simple

If TRUE, and no other general constraints are used in the model, simple equality constraints are represented in the parameter table as duplicated free parameters (instead of extra rows with op = "==").

auto

If TRUE, the default values are used for the auto.* arguments, depending on the value of model_type.

model_type

Either "sem" or "growth"; only used if auto=TRUE.

auto_fix_first

If TRUE, the factor loading of the first indicator is set to 1.0 for every latent variable.

marker

Optional named character vector mapping a latent variable (name) to the observed indicator (name) whose loading should be fixed to 1.0 (instead of the first indicator) when auto_fix_first = TRUE. Latent variables that are not named in this vector keep the first indicator as their marker. This is used internally by the bad.marker.crit mechanism.

auto_fix_single

If TRUE, the residual variance (if included) of an observed indicator is set to zero if it is the only indicator of a latent variable.

auto_var

If TRUE, the (residual) variances of both observed and latent variables are set free.

auto_cov_lv_x

If TRUE, the covariances of exogenous latent variables are included in the model and set free.

auto_cov_x

If TRUE, the covariances between exogenous latent variables and observed exogenous covariates are also included in the model and set free (this implies auto_cov_lv_x = TRUE). Ignored (with a warning) if conditional_x = TRUE.

auto_cov_y

If TRUE, the covariances of dependent variables (both observed and latent) are included in the model and set free.

auto_th

If TRUE, thresholds for limited dependent variables are included in the model and set free.

auto_delta

If TRUE, response scaling parameters for limited dependent variables are included in the model and set free.

auto_efa

If TRUE, the necessary constraints are imposed to make the (unrotated) exploratory factor analysis blocks identifiable: for each block, factor variances are set to 1, factor covariances are constrained to be zero, and factor loadings are constrained to follow an echelon pattern.

var_table

The variable table containing information about the observed variables in the model.

ngroups

The number of (independent) groups.

nthresholds

Either a single integer or a named vector of integers. If nthresholds is a single integer, all endogenous variables are assumed to be ordered with nthresholds indicating the number of thresholds needed in the model. If nthresholds is a named vector, it indicates the number of thresholds for these ordered variables only. This argument should not be used in combination with var_table.

group_equal

A vector of character strings. Only used in a multiple group analysis. Can be one or more of the following: "loadings", "intercepts", "means", "regressions", "residuals" or "covariances", specifying the pattern of equality constraints across multiple groups. When (in the model syntax) a vector of labels is used as a modifier for a certain parameter, this will override the group_equal setting if it applies to this parameter. See also the Multiple groups section below for using modifiers in multiple groups.

group_partial

A vector of character strings containing the labels of the parameters which should be free in all groups (thereby overriding the group_equal argument for some specific parameters).

group_w_free

Logical. If TRUE, the group frequencies are considered to be free parameters in the model. In this case, a Poisson model is fitted to estimate the group frequencies. If FALSE (the default), the group frequencies are fixed to their observed values.

as_data_frame

If TRUE, return the list of model parameters as a data.frame.

parser

Character. If "old", use the original/classic parser. If "new", use the new/ldw parser. If "open", use the newest, extensible parser. The default (as of version 0.7-1) is "open".

warn

If TRUE, some (possibly harmless) warnings are printed out.

debug

If TRUE, debugging information is printed out.

...

To accept old argument names with dots. No other arguments are accepted.

Fixing parameters

It is often desirable to fix a model parameter that is otherwise (by default) free. Any parameter in a model can be fixed by using a modifier resulting in a numerical constant. Here are some examples:

  • Fixing the regression coefficient of the predictor x2:

    y ~ x1 + 2.4*x2 + x3
  • Specifying an orthogonal (zero) covariance between two latent variables:

    f1 ~~ 0*f2
  • Specifying an intercept and a linear slope in a growth model:

    i =~ 1*y11 + 1*y12 + 1*y13 + 1*y14
    s =~ 0*y11 + 1*y12 + 2*y13 + 3*y14

Instead of a numeric constant, one can use a mathematical function that returns a numeric constant, for example sqrt(10). Multiplying with NA will force the corresponding parameter to be free.

Additionally, the == operator can be used to set a labeled parameter equal to a specific numeric value. This will be demonstrated in the section below about (In)equality constraints.

Starting values

User-provided starting values can be given by using the special function start(), containing a numeric constant. For example:

y ~ x1 + start(1.0)*x2 + x3

Note that if a starting value is provided, the parameter is not automatically considered to be free.

Parameter labels and equality constraints

Each free parameter in a model is automatically given a name (or label). The name given to a model parameter consists of three parts, coerced to a single character vector. The first part is the name of the variable in the left-hand side of the formula where the parameter was implied. The middle part is based on the special `operator' used in the formula. This can be either one of "=~", "~" or "~~". The third part is the name of the variable in the right-hand side of the formula where the parameter was implied, or "1" if it is an intercept. The three parts are pasted together in a single string. For example, the name of the fixed regression coefficient in the regression formula y ~ x1 + 2.4*x2 + x3 is the string "y~x2". The name of the parameter corresponding to the covariance between two latent variables in the formula f1 ~~ f2 is the string "f1~~f2".

Although this automatic labeling of parameters is convenient, the user may specify their own labels for specific parameters simply by pre-multiplying the corresponding term (on the right hand side of the operator only) by a character string (starting with a letter). For example, in the formula f1 =~ x1 + x2 + mylabel*x3, the parameter corresponding with the factor loading of x3 will be named "mylabel". An alternative way to specify the label is as follows: f1 =~ x1 + x2 + label("mylabel")*x3, where the label is the argument of special function label(); this can be useful if the label contains a space, or an operator (like "~").

There are two ways to constrain a parameter to be equal to another target parameter. If you have specified your own labels, you can use the fact that equal labels imply equal parameter values. If you rely on automatic parameter labels, you can use the special function equal(). The argument of equal() is the (automatic or user-specified) name of the target parameter. For example, in the confirmatory factor analysis example below, the intercepts of the three indicators of each latent variable are constrained to be equal to each other. For the first three, we have used the default names. For the last three, we have provided a custom label for the y2a intercept.

model <- '
  # two latent variables with fixed loadings
    f1 =~ 1*y1a + 1*y1b + 1*y1c
    f2 =~ 1*y2a + 1*y2b + 1*y2c

# intercepts constrained to be equal # using the default names y1a ~ 1 y1b ~ equal("y1a~1") * 1 y1c ~ equal("y1a~1") * 1

# intercepts constrained to be equal # using a custom label y2a ~ int2*1 y2b ~ int2*1 y2c ~ int2*1 '

Multiple groups

In a multiple group analysis, modifiers that contain a single element should be replaced by a vector, having the same length as the number of groups. If you provide a single element, it will be recycled for all the groups. This may be dangerous, in particular when the modifier is a label. In that case, the (same) label is copied across all groups, and this would imply an equality constraint across groups. Therefore, when using modifiers in a multiple group setting, it is always safer (and cleaner) to specify the same number of elements as the number of groups. Consider this example with two groups:


HS.model <- ' visual  =~ x1 + 0.5*x2 + c(0.6, 0.8)*x3
              textual =~ x4 + start(c(1.2, 0.6))*x5 + x6
              speed   =~ x7 + x8 + c(x9.group1, x9.group2)*x9 '

In this example, the factor loading of the `x2' indicator is fixed to the value 0.5 for both groups. However, the factor loadings of the `x3' indicator are fixed to 0.6 and 0.8 for group 1 and group 2 respectively. The same logic is used for all modifiers. Note that character vectors can contain unquoted strings.

Multiple modifiers

In the model syntax, you can specify a variable more than once on the right hand side of an operator; therefore, several `modifiers' can be applied simultaneously; for example, if you want to fix the value of a parameter and also label that parameter, you can use something like:

 f1 =~ x1 + x2 + 4*x3 + x3.loading*x3

Random slopes (two-level models)

In a two-level model (with a cluster= argument), the rv() modifier can be used in the level-1 (within) part of the model to turn a regression coefficient into a random slope: a latent variable at the second (between) level. The (quoted) argument of rv() is the name of this latent variable. At the between level, the random slope can then be given a (residual) variance, covariances, regressions on between-level covariates, and an intercept (which is the `fixed effect' of the slope). For example (cfr. ex9.8 in the Mplus User's Guide):

model <- '
  level: 1
    fw =~ y1 + y2 + y3 + y4
    fw ~ rv("s1")*x1 + rv("s2")*x2
  level: 2
    fb =~ y1 + y2 + y3 + y4
    s1 + s2 + fb ~ w
'

Here, the within-level regression coefficients of fw on the observed covariates x1 and x2 vary across clusters; the random slopes s1 and s2 are regressed on the between-level covariate w.

The covariate that carries a random slope may also be a latent (within-level) variable, as in

model <- '
  level: 1
    fxw =~ x1 + x2 + x3
    fyw =~ y1 + y2 + y3
    fyw ~ rv("s1")*fxw
  level: 2
    fxb =~ x1 + x2 + x3
    fyb =~ y1 + y2 + y3
    fyb ~ fxb
    fyb ~~ s1
'

Such a slope multiplies two random variables, and the marginal loglikelihood is no longer available in closed form: it is computed by (Gauss-Hermite) quadrature over the latent-covariate slopes only, while all other random effects are still integrated out analytically (Rockwood, 2020). The number of quadrature points (per dimension) can be set via the integration.ngh option (default: 21).

The same route is taken when the covariate is a `split' both-level observed variable (an observed variable that appears in both the level-1 and the level-2 part of the model): the random slope then multiplies its latent within-cluster component (latent centering), and the covariate is jointly modeled:

model <- '
  level: 1
    y ~ rv("s1")*x
  level: 2
    y ~ x
    y ~~ s1
'

This is the latent-covariate contextual model of Rockwood (2020, section 4). Note the contrast with a within-only covariate (a variable that does not appear in the level-2 part): there, the slope multiplies the observed values, which are conditioned upon.

The current implementation is limited to: two-level models, a single group, continuous data, estimator = "ML" and fixed.x = TRUE; the observed covariates that carry a random slope must be within-only (level-1) variables. Missing data can be handled by full-information maximum likelihood (missing = "ml"); the exogenous covariates themselves must be complete (cases with missing values on exogenous variables are removed). The (observed-data) loglikelihood is conditional on all exogenous covariates: the covariates carrying random slopes, any other within-only exogenous covariates (as in Mplus TYPE = TWOLEVEL RANDOM), and the between-level exogenous covariates. Between-only endogenous observed variables (level-2 outcomes, e.g., indicators of a between-level factor) are supported; they must be pure indicators (not involved in regressions), with a free (or fixed to a nonzero value) residual variance, and without missing values. No chi-square test statistic (or fit indices) are available for models with random slopes; fitMeasures() provides the loglikelihood-based measures only (npar, ntotal, logl, aic, bic, bic2), plus (with estimator = "MLR") the H0 scaling correction factor (scaling.factor.h0), which can be used for scaled (loglikelihood) difference tests. Standard errors can be "standard" (observed information) or "robust.huber.white" (sandwich; this is also what estimator = "MLR" implies). Both optim.method = "nlminb" and optim.method = "em" are supported; with complete data, "nlminb" is the default, while with missing = "ml", the EM algorithm is the default optimizer. lavPredict() provides empirical Bayes (posterior mean) predictions: with level = 2, the between-level latent variables including the random slopes (and, with se = "standard", their posterior standard deviations); with level = 1 (the default), the within-level factor scores. For latent-covariate models, these are EAP (expected a posteriori) estimates: the posterior is a mixture over the quadrature nodes.

For random slopes with latent covariates, some additional restrictions apply (for now): a random-slope label may not be attached to both an observed and a latent covariate, and the optimizer is always "nlminb". The latent-covariate slopes may be regressed on between-level covariates (e.g., s1 ~ w in the level-2 part), as in the PISA example of Rockwood (2020).

(In)equality constraints

The == operator can be used either to fix a parameter to a specific value, or to set an estimated parameter equal to another parameter. Adapting the example in the Parameter labels and equality constraints section, we could have used different labels for the second factor's intercepts:


    y2a ~ int1*1
    y2b ~ int2*1
    y2c ~ int3*1

Then, we could fix the first intercept to zero by including in the syntax an operation that indicates the parameter's label equals that value:


    int1 == 0

Whereas we could still estimate the other two intercepts under an equality constraint by setting their different labels equal to each other:


    int2 == int3

Optimization can be less efficient when constraining parameters this way (see the documentation linked under See also for more information). But the flexibility might be advantageous. For example, the constraints could be specified in a separate character-string object, which can be passed to the lavaan(..., constraints=) argument, enabling users to compare results with(out) the constraints.

Inequality constraints work in much the same way, using the < or > operator to indicate which estimated parameter is hypothesized to be greater or less than either a specific value or another estimated parameter. For example, a variance can be constrained to be nonnegative:


    y1a ~~ var1a*y1a
    ## hypothesized constraint:
    var1a > 0

Or the factor loading of a particular indicator might be expected to exceed other indicators' loadings:


    f1 =~ L1*y1a + L2*y1b + L3*y1c
    ## hypothesized constraints:
    L1 > L2
    L3 < L1

User-defined parameters

Functions of parameters can be useful to test particular hypotheses. Following from the Multiple groups example, we might be interested in which group's factor loading is larger (i.e., an estimate of differential item functioning (DIF) when the latent scales are linked by anchor items with equal loadings).


  speed =~ c(L7, L7)*x7 + c(L8, L8)*x8 + c(L9.group1, L9.group2)*x9 '
  ## user-defined parameter:
  DIF_L9 := L9.group1 - L9.group2

Note that this hypothesis is easily tested without a user-defined parameter by using the lavTestWald() function. However, a user-defined parameter additionally provides an estimate of the parameter being tested.

User-defined parameters are particularly useful for specifying indirect effects in models of mediation. For example:


model <- ' # direct effect
             Y ~ c*X
           # mediator
             M ~ a*X
             Y ~ b*M

# user defined parameters:

# indirect effect (a*b) ab := a*b # total effect (defined using another user-defined parameter) total := ab + c '

Details

The model syntax consists of one or more formula-like expressions, each one describing a specific part of the model. The model syntax can be read from a file (using readLines), or can be specified as a literal string enclosed by single quotes as in the example below.

myModel <- '
  # 1. latent variable definitions
    f1 =~ y1 + y2 + y3
    f2 =~ y4 + y5 + y6
    f3 =~ y7 + y8 +
          y9 + y10
    f4 =~ y11 + y12 + y13

! this is also a comment

# 2. regressions f1 ~ f3 + f4 f2 ~ f4 y1 + y2 ~ x1 + x2 + x3

# 3. (co)variances y1 ~~ y1 y2 ~~ y4 + y5 f1 ~~ f2

# 4. intercepts f1 ~ 1; y5 ~ 1

# 5. thresholds y11 | t1 + t2 + t3 y12 | t1 y13 | t1 + t2

# 6. scaling factors y11 ~*~ y11 y12 ~*~ y12 y13 ~*~ y13

# 7. composites f5 <~ z1 + z2 + z3 + z4 '

Blank lines and comments can be used in between the formulas, and formulas can be split over multiple lines. Both the sharp (#) and the exclamation (!) characters can be used to start a comment. Multiple formulas can be placed on a single line if they are separated by a semicolon (;).

There can be seven types of formula-like expressions in the model syntax:

  1. Latent variable definitions: The "=~" operator can be used to define (continuous) latent variables. The name of the latent variable is on the left of the "=~" operator, while the terms on the right, separated by "+" operators, are the indicators of the latent variable.

    The operator "=~" can be read as ``is manifested by''.

  2. Regressions: The "~" operator specifies a regression. The dependent variable is on the left of a "~" operator and the independent variables, separated by "+" operators, are on the right. These regression formulas are similar to the way ordinary linear regression formulas are used in R, but they may include latent variables. Interaction terms are currently not supported.

  3. Variance-covariances: The "~~" (`double tilde') operator specifies (residual) variances of an observed or latent variable, or a set of covariances between one variable and several other variables (either observed or latent). Several variables, separated by "+" operators, can appear on the right. In this way, several pairwise (co)variances involving the same left-hand variable can be expressed in a single expression. The distinction between variances and residual variances is made automatically.

  4. Intercepts: A special case of a regression formula can be used to specify an intercept (or a mean) of either an observed or a latent variable. The variable name is on the left of a "~" operator, and the only term on the right is the number "1", representing the intercept. Including an intercept formula in the model automatically implies meanstructure = TRUE. The distinction between intercepts and means is made automatically.

  5. Thresholds: The "|" operator can be used to define the thresholds of categorical endogenous variables (on the left-hand side of the operator). By convention, the thresholds (on the right-hand side, separated by the "+" operator) are named "t1", "t2", and so on.

  6. Scaling factors: The "~*~" operator defines a scale factor. The variable name on the left hand side must be the same as the variable name on the right hand side. Scale factors are used in the Delta parameterization, in a multiple group analysis when factor indicators are categorical.

  7. Composites: The "<~" operator can be used to define a composite (on the right hand side of the operator). A composite is a weighted linear combination of its composite indicators. The name of the composite variable is on the left of the "<~" operator, while the terms on the right, separated by "+" operators, are the indicators of the composite variable.

There are 4 additional operators, also with left- and right-hand sides, that can be included in model syntax. Three of them are used to specify (in)equality constraints on estimated parameters (==, >, and <), and those are demonstrated in a later section about (In)equality constraints. The final additional operator (:=) can be used to define ``new'' parameters that are functions of one or more other estimated parameters. The := operator is demonstrated in a section about User-defined parameters.

Usually, only a single variable name appears on the left side of an operator. However, if multiple variable names are specified, separated by the "+" operator, the formula is repeated for each element on the left side (as for example in the third regression formula in the example above). The only exception is scaling factors, where only a single element is allowed on the left-hand side.

In the right-hand side of these formula-like expressions, each element can be modified (using the "*" operator) by either a numeric constant, an expression resulting in a numeric constant, an expression resulting in a character vector, or one of three special functions: start(), label() and equal(). This provides the user with a mechanism to fix parameters, to provide alternative starting values, to label the parameters, and to define equality constraints among model parameters. All "*" expressions are referred to as modifiers. They are explained in more detail in the following sections.

References

Rockwood, N. J. (2020). Maximum likelihood estimation of multilevel structural equation models with random slopes for latent covariates. Psychometrika, 85(2), 275--300. tools:::Rd_expr_doi("10.1007/s11336-020-09702-9")

Rosseel, Y. (2012). lavaan: An R package for structural equation modeling. Journal of Statistical Software, 48(2), 1--36. tools:::Rd_expr_doi("10.18637/jss.v048.i02")