I am using multiply-imputed data to run an Actor-Partner Interdependence Model (APIM; Cook & Kenny, 2005). I am using mice
to impute my data and for reference, I am using this resource guide for constructing my APIM with lavaan
.
As far as I can tell, there is only one other StackOverflow question that is related my issue. However, I was unable to directly apply that answer to my APIM. There is also a three-year-old Github issue with semPaths
that doesn't seem to have made any progress since the other StackOverflow user posted about it.
I tried adjusting the other StackOverflow answer for my APIM by filtering out everything except the two actor effects and two partner effects. I think I'm almost there; if you compare the summary output with my third attempt at plotting, the summary estimates are similar to what's plotted by semPaths()
but not exact.
What's causing the plotted estimates to be off? Is there a better way of plotting lavaan.mi
objects that feels less hacky?
library(mice)
library(lavaan)
library(tidyverse)
library(faux)
library(missMethods)
library(semTools)
library(semPlot)
set.seed(1234)
## prepwork
# generate data
df <- faux::rnorm_multi(n = 100, vars = 4, mu = 3, sd = 1, varnames = c("x1", "x2", "y1", "y2")) %>%
mutate(id = rep(1:100)) %>% select(id, everything()) %>%
missMethods::delete_MCAR(cols = c("x1", "x2", "y1", "y2"), p = .15)
# make lavaan object
myapim <- "x1 ~ a1*y1; x2 ~ a2*y2; x1 ~ p12*y2; x2 ~ p21*y1; y1 ~ mx1*1; y2 ~ mx2*1; x1 ~ my1*1; x2 ~ my2*1
y1 ~~ vx1*y1; y2 ~~ vx2*y2; x1 ~~ vy1*x1; x2 ~~ vy2*x2; y2 ~~ cx*y1; x2 ~~ cy*x1
a_diff := a1 - a2; p_diff := p12 - p21; k1 := p12/a1; k2 := p21/a2
k_diff := k1 - k2; i_diff := my1 - my2; a_ave := (a1 + a2)/2; p_ave := (p12 + p21)/2
i_ave := (my1 +my2)/2; sum1 := (p12 + a1)/2; sum2 := (p21 + a2)/2; cont1 := a1 - p12; cont2 := a2 - p21
"
apimd <- lavaan::sem(myapim, data = df, fixed.x = FALSE, missing = "default")
# impute data
imp <- mice::mice(df, m = 5, maxit = 10, seed = 1234, printFlag = FALSE)
# conduct SEM
sem_model <- runMI(model = myapim, data = imp, fun = "sem", miPackage = "mice", seed = 1234)
## plot
# try to plot lavaan.mi
semPaths(sem_model, what = "est", sizeMan = 25, sizeMan2 = 10,
label.cex = 1.2, edge.label.position = 0.45, edge.label.cex = 1.5) # error
#> Error in (function (classes, fdef, mtable) : unable to find an inherited method for function 'semPlotModel_S4' for signature '"lavaan.mi"'
# try to use other StackOverflow answer directly
other_model <- sem(myapim, data = df)
SEMPLOT <- semPlot::semPlotModel(other_model)
desired_output <- data.frame(standardizedsolution(sem_model))
SEMPLOT@Pars$std <- desired_output$est.std # error
#> Error in `$<-.data.frame`(`*tmp*`, std, value = structure(c(0.164373878998654, : replacement has 27 rows, data has 14
# try to adjust the other StackOverflow answer for my APIM
baseplot <- semPlot::semPlotModel(other_model)
desired_output <- data.frame(standardizedsolution(sem_model))
desired_output <- desired_output %>% filter(op != ":=")
baseplot@Pars$est <- desired_output$est.std
semPaths(baseplot,
nCharNodes = 0, what = "est",
fade = FALSE, layout = "tree2",
rotation = 2, style = "ram",
intercepts = FALSE, residuals = FALSE,
optimizeLatRes = TRUE, curve = 3.1, nDigits = 3,
sizeMan = 12, sizeMan2 = 10,
label.cex = 1.2, edge.label.position = 0.45, edge.label.cex = 1.5)
# I only pasted part of summary() to keep it short
> summary(sem_model)
lavaan.mi object based on 5 imputed data sets.
See class?lavaan.mi help page for available methods.
Convergence information:
The model converged on 5 imputed data sets
Rubin's (1987) rules were used to pool point and SE estimates across 5 imputed data sets, and to calculate degrees of freedom for each parameter's t test and CI.
Parameter Estimates:
Standard errors Standard
Information Expected
Information saturated (h1) model Structured
Regressions:
Estimate Std.Err t-value df P(>|t|)
x1 ~
y1 (a1) 0.162 0.111 1.456 12.345 0.170
x2 ~
y2 (a2) -0.005 0.112 -0.046 75.147 0.963
x1 ~
y2 (p12) 0.209 0.122 1.714 37.386 0.095
x2 ~
y1 (p21) 0.049 0.103 0.478 47.391 0.635
What's causing the plotted estimates to be off?
I just left a comment on the earlier post you linked to, informing them that standardizedSolution()
is not for lavaan.mi
objects. You should use an actual class?lavaan.mi
method, something like:
desired_output <- summary(sem_model, standardized = "std.all",
## so you can check it more easily:
output = "data.frame")
And carefully compare the rows and columns of desired_output
to those of baseplot@Pars
to make sure your replacement makes sense.
Also, you only need to set the miPackage=
when you want to impute within the runMI()
call, which is not recommended. You did the right thing by imputing first, but that is when setting your seed counts.
# impute data
imp <- mice::mice(df, m = 5, maxit = 10,
seed = 1234, printFlag = FALSE)
# conduct SEM
sem_model <- sem.mi(model = myapim, data = imp)
Is there a better way of plotting lavaan.mi objects that feels less hacky?
Not yet. Of course, the easiest and safest way to make this work would be if semPaths()
checked for the lavaan.mi
class and correctly extracted the necessary information itself. I'll make a note to send Sacha a pull request for semPlot
when I have time to work on semTools
again.