Zum Inhalt springen / Skip to content

How to apply survey weights in structural equation modeling (SEM) with lavaan.

rstats
SEM
Fitting structural equation models with lavaan and applying survey weights via the survey and lavaan.survey packages.
Author

Niels Ohlsen

Published

March 17, 2015

The R-Package lavaan is my favourite tool for fitting structural equation models (SEM). Its biggest advantages: It´s free, it´s open source and its range of functions is growing steadily. Before lavaan, i used MPLUS, which still has the widest functionality of all SEM-Tools and is the most sophisticated software for latent variable modeling. The only problem is, that the software isn´t free and without a license you can´t get any of the support. For me, one drawback of lavaan is, that it can´t model latent class models or mixture models …yet! Yves Rosseel is planning to add this in the next two years.

lavaan stands for „latent variable analysis“. The package is available via CRAN and has a good tutorial on the lavaan project homepage. Models are specified via syntax. Thankfully, the lavaan-syntax is kept pretty simple. At least, it´s a lot easier than the LISREL-syntax. If you don´t like working with syntax, i recommend having a look at Onyx – a graphical interface for structural equation modeling by Andreas Brandmaier.

Install lavaan If you want to use survey weights, you have to install lavaan, the survey package and lavaan.survey. Lavaan is the package used for modeling and the survey-package converts your data into a survey-design-object. After you specified the model in a lavaan fit object and you have generated a survey-design-object from your data, these two objects are passed to the lavaan.survey function, which will calculate the weighted model.

#Install lavaan
install.packages("lavaan", dependencies=TRUE)
library(lavaan)

#install lavaan.survey
install.packages("lavaan.survey")
library(lavaan.survey)

#Install survey-package
install.packages("survey")
library(survey)

Generate the survey-design object

library("survey") #load survey package
data<- read.csv(file = "data.csv", header=T, sep=",") #read data

#if necessary - recode missing value "9" to NA
df[df== 9] <- NA

#generate survey-design object
svy.df<-svydesign(id=~ID,
                  weights=~weight_trunc,
                  data=data)

Specifying the model I´ll use a simple structural equation model with two latent variables, measured by three and two indicator-variables. The exogenous latent variable „latent_a“ is measured by x1-x3, the endogenous latent variable „latent_b“ is measured by y1-y2. „latent_b“ is regressed on (predicted by) „latent_a“.

library(lavaan)
model_1 <- '# measurement model
              latent_a =~ F09_a + F09_b + F09_c
              latent_b =~ F12_a + F12_b

             # regressions
              latent_b ~ latent_a
            '

lavaan.fit <- sem(model_1,
                     data=data,
                     estimator="MLR", # robust fit / when you have missing data
                     missing = "ml",  #fiml for missing data
                     mimic="Mplus")

#you can run the model (unweighted) at this point and inspect it
summary(lavaan.fit,fit.measures=TRUE, standardized=TRUE)

Normally, i would use MLM as estimator to get robust estimates, but in this case i chose MLR, because FIML is not available with MLM. FIML (defined with missing=“ml”) is regarded as equally efficient to multiple imputation in handling item-nonresponse. When using the lavaan.survey-package, you can´t use fiml (yet). You have to do a multiple imputation for your data, if you have missings, and instead of MLR lavaan.survey uses MLM as default.

Fitting the model

library(lavaan.survey)

#Fit the model using weighted data
survey.fit <- lavaan.survey(lavaan.fit,
                            survey.design,
                            estimator="ML")

#inspect output
summary(survey.fit,
        fit.measures=TRUE,
        standardized=TRUE,
        rsquare=TRUE)

# missing data patterns
inspect(fit, 'patterns')
# coverage of the covariance matrix (like in MPLUS)
inspect(fit, 'coverage')

Results I wouldn´t have expected that using weights in a SEM-analysis with lavaan is so easy to accomplish. Example fit-indices of the weighted SEM:

lavaan (0.5-17) converged normally after  24 iterations
  Number of observations                           577
  Estimator                                         ML
  Minimum Function Test Statistic               11.664
  Degrees of freedom                                 4
  P-value (Chi-square)                           0.020
  Comparative Fit Index (CFI)                    0.992
  Tucker-Lewis Index (TLI)                       0.980
  RMSEA                                          0.058
  90 Percent Confidence Interval          0.021  0.097
  SRMR                                           0.022

It´s common to show the parameter-estimates in a path-diagram. In my next blogging-session i´ll demonstrate how to draw path diagrams of a lavaan-model with SEMPLOT.