1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
|
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/emmeans_lavaan.R
\name{lavaan2emmeans}
\alias{lavaan2emmeans}
\alias{recover_data.lavaan}
\alias{emm_basis.lavaan}
\title{\code{emmeans} Support Functions for \code{lavaan} Models}
\usage{
recover_data.lavaan(object, lavaan.DV, data = NULL, ...)
emm_basis.lavaan(object, trms, xlev, grid, lavaan.DV, ...)
}
\arguments{
\item{object}{An object of class \code{\link[lavaan:lavaan]{lavaan::lavaan()}}.
See \strong{Details}.}
\item{lavaan.DV}{\code{character} string naming the variable(s) for which
expected marginal means / trends should be produced.
A vector of names indicates a multivariate outcome, treated by default
as repeated measures.}
\item{data}{An optional \code{data.frame} without missing values, to be passed
when \code{missing="FIML"} estimation was useed, thus avoiding a reference-grid
with missing values.}
\item{...}{Further arguments passed to \code{emmeans::recover_data.lm} or
\code{emmeans::emm_basis.lm}}
\item{trms, xlev, grid}{See \code{emmeans::emm_basis}}
}
\description{
Provide emmeans support for lavaan objects
}
\details{
\subsection{Supported DVs}{
\code{lavaan.DV} must be an \emph{endogenous variable}, by appearing on
the left-hand side of either a regression operator (\code{"~"})
or an intercept operator (\code{"~1"}), or both.
\cr\cr
\code{lavaan.DV} can also be a vector of endogenous variable, in which
case they will be treated by \code{emmeans} as a multivariate outcome
(often, this indicates repeated measures) represented by an additional
factor named \code{rep.meas} by default. The \verb{mult.name=} argument
can be used to overwrite this default name.
}
\subsection{Unsupported Models}{
This functionality does not support the following models:
\itemize{
\item Multi-level models are not supported.
\item Models not fit to a \code{data.frame} (i.e., models fit to a
covariance matrix).
}
}
\subsection{Dealing with Fixed Parameters}{
Fixed parameters (set with \code{lavaan}'s modifiers) are treated as-is:
their values are set by the users, and they have a \emph{SE} of 0 (as such,
they do not co-vary with any other parameter).
}
\subsection{Dealing with Multigroup Models}{
If a multigroup model is supplied, a factor is added to the reference grid,
the name matching the \code{group} argument supplied when fitting the model.
\emph{Note that you must set} \code{nesting = NULL}.
}
\subsection{Dealing with Missing Data}{
Limited testing suggests that these functions do work when the model was fit
to incomplete data.
}
\subsection{Dealing with Factors}{
By default \code{emmeans} recognizes binary variables (0,1) as a "factor"
with two levels (and not a continuous variable). With some clever contrast
defenitions it should be possible to get the desired emmeans / contasts.
See example below.
}
}
\examples{
\dontrun{
library(lavaan)
library(emmeans)
#### Moderation Analysis ####
mean_sd <- function(x) mean(x) + c(-sd(x), 0, sd(x))
model <- '
# regressions
Sepal.Length ~ b1 * Sepal.Width + b2 * Petal.Length + b3 * Sepal.Width:Petal.Length
# define mean parameter label for centered math for use in simple slopes
Sepal.Width ~ Sepal.Width.mean * 1
# define variance parameter label for centered math for use in simple slopes
Sepal.Width ~~ Sepal.Width.var * Sepal.Width
# simple slopes for condition effect
SD.below := b2 + b3 * (Sepal.Width.mean - sqrt(Sepal.Width.var))
mean := b2 + b3 * (Sepal.Width.mean)
SD.above := b2 + b3 * (Sepal.Width.mean + sqrt(Sepal.Width.var))
'
semFit <- sem(model = model,
data = iris)
## Compare simple slopes
# From `emtrends`
test(
emtrends(semFit, ~ Sepal.Width, "Petal.Length",
lavaan.DV = "Sepal.Length",
cov.red = mean_sd)
)
# From lavaan
parameterEstimates(semFit, output = "pretty")[13:15, ]
# Identical slopes.
# SEs differ due to lavaan estimating uncertainty of the mean / SD
# of Sepal.Width, whereas emmeans uses the mean+-SD as is (fixed).
#### Latent DV ####
model <- '
LAT1 =~ Sepal.Length + Sepal.Width
LAT1 ~ b1 * Petal.Width + 1 * Petal.Length
Petal.Length ~ Petal.Length.mean * 1
V1 := 1 * Petal.Length.mean + 1 * b1
V2 := 1 * Petal.Length.mean + 2 * b1
'
semFit <- sem(model = model,
data = iris, std.lv = TRUE)
## Compare emmeans
# From emmeans
test(
emmeans(semFit, ~ Petal.Width,
lavaan.DV = "LAT1",
at = list(Petal.Width = 1:2))
)
# From lavaan
parameterEstimates(semFit, output = "pretty")[15:16, ]
# Identical means.
# SEs differ due to lavaan estimating uncertainty of the mean
# of Petal.Length, whereas emmeans uses the mean as is.
#### Multi-Variate DV ####
model <- '
ind60 =~ x1 + x2 + x3
# metric invariance
dem60 =~ y1 + a*y2 + b*y3 + c*y4
dem65 =~ y5 + a*y6 + b*y7 + c*y8
# scalar invariance
y1 + y5 ~ d*1
y2 + y6 ~ e*1
y3 + y7 ~ f*1
y4 + y8 ~ g*1
# regressions (slopes differ: interaction with time)
dem60 ~ b1*ind60
dem65 ~ b2*ind60 + NA*1 + Mean.Diff*1
# residual correlations
y1 ~~ y5
y2 ~~ y4 + y6
y3 ~~ y7
y4 ~~ y8
y6 ~~ y8
# conditional mean differences (besides mean(ind60) == 0)
low := (-1*b2 + Mean.Diff) - (-1*b1) # 1 SD below M
high := (b2 + Mean.Diff) - b1 # 1 SD above M
'
semFit <- sem(model, data = PoliticalDemocracy)
## Compare contrasts
# From emmeans
emmeans(semFit, pairwise ~ rep.meas|ind60,
lavaan.DV = c("dem60","dem65"),
at = list(ind60 = c(-1,1)))[[2]]
# From lavaan
parameterEstimates(semFit, output = "pretty")[49:50, ]
#### Multi Group ####
model <- 'x1 ~ c(int1, int2)*1 + c(b1, b2)*ageyr
diff_11 := (int2 + b2*11) - (int1 + b1*11)
diff_13 := (int2 + b2*13) - (int1 + b1*13)
diff_15 := (int2 + b2*15) - (int1 + b1*15)
'
semFit <- sem(model, group = "school", data = HolzingerSwineford1939)
## Compare contrasts
# From emmeans (note `nesting = NULL`)
emmeans(semFit, pairwise ~ school | ageyr, lavaan.DV = "x1",
at = list(ageyr = c(11, 13, 15)), nesting = NULL)[[2]]
# From lavaan
parameterEstimates(semFit, output = "pretty")
#### Dealing with factors ####
warpbreaks <- cbind(warpbreaks,
model.matrix(~ wool + tension, data = warpbreaks))
model <- "
# Split for convenience
breaks ~ 1
breaks ~ woolB
breaks ~ tensionM + tensionH
breaks ~ woolB:tensionM + woolB:tensionH
"
semFit <- sem(model, warpbreaks)
## Compare contrasts
# From lm -> emmeans
lmFit <- lm(breaks ~ wool * tension, data = warpbreaks)
lmEM <- emmeans(lmFit, ~ tension + wool)
contrast(lmEM, method = data.frame(L_all = c(-1, .05, 0.5),
M_H = c(0, 1, -1)), by = "wool")
# From lavaan -> emmeans
lavEM <- emmeans(semFit, ~ tensionM + tensionH + woolB,
lavaan.DV = "breaks")
contrast(lavEM,
method = list(
"L_all|A" = c(c(-1, .05, 0.5, 0), rep(0, 4)),
"M_H |A" = c(c(0, 1, -1, 0), rep(0, 4)),
"L_all|A" = c(rep(0, 4), c(-1, .05, 0.5, 0)),
"M_H |A" = c(rep(0, 4), c(0, 1, -1, 0))
))
}
}
\author{
Mattan S. Ben-Shachar (Ben-Gurion University of the Negev;
\email{matanshm@post.bgu.ac.il})
}
|