Learn R Programming

kutils (version 1.69)

semTable: Creates Structural Equation Modeling Tables

Description

Creates LaTeX markup for structural equation modeling output tables in the style of the American Psychological Association(APA). Input objects should be created by the "lavaan" package.

Usage

semTable(object, file = NULL, paramSets = "all", paramSetLabels,
  columns = c(est = "Estimate", se = "SE", z = "z", p = "p"),
  columnLabels, fits = c("chisq", "cfi", "tli", "rmsea"),
  fitLabels = toupper(fits), varLabels = NULL, groups = NULL,
  type = "latex", table.float = FALSE, caption = NULL,
  label = NULL, longtable = FALSE, print.results = TRUE,
  centering = "siunitx", alpha = c(0.05, 0.01, 0.001))

Arguments

object

A lavaan object (e.g., returned by cfa() or sem()), or a named list of lavaan objects, e.g., list("Model A" = obj1, "Model B" = obj2). Results will be displayed side by side.

file

Base name for output file. This function will insert suffix, either "tex", "html" and "csv".

paramSets

Parameter sets to be included for each fitted object. Valid values of the vector are "all" or a any of the following: c("loadings", "slopes", "intercepts", "residualvariances", "residualcovariances", "latentmeans", "latentvariances", "latentcovariances", "thresholds", "constructed", "fits"). Default is "all", any of the estimates present in the fitted model that are listed in the previous sentence will be included in the output. For the sake of simplicity, we now allow one vector here, which applies to all models in the object list.

paramSetLabels

Named vector, used to supply alternative pretty printing labels for parameter sets. The default values are c("loadings"= "Factor Loadings", "slopes" = "Regression Slopes", "intercepts" = "Intercepts", "means"= "Means", "residualvariances" = "Residual Variances", "residualcovariances" = "Residual Covariances", "variances" = "Variances", "latentvariances" = "Latent Variances", "latentcovariances" = "Latent Covariances", "latentmeans" = "Latent Intercepts", "thresholds" = "Thresholds", "constructed" = "Constructed", "fits" = "Fit Indices"). The paramSetLabels argument must be a named vector that overrides some or all of the default names.

columns

A vector naming estimates to appear for each model. The allowed columns are "est", "se", "z", "p", "rsquare", "estse", "eststars", "estsestars". The first 5 have the usual meanings, while "estse" (can also be written "est(se)") displays as, for example "1.21(0.23)", and the last 2 are to include "significance stars". "eststars" shows as "1.21***" and "estsestars" (or "est(se)stars") displays as "1.21(0.23)**". See parameter alpha. One may request different columns for each model by providing a named list of vectors. Use model names in the list, list("Model A" = c("est", "se"), "Model B" = c("estse", "p")).

columnLabels

A named vector of "pretty labels" for the headings in the table. The default labels are c("est" = "Estimate", se = "Std. Err.", z = "z", p = "p", rsquare = "R Square", estse = "Estimate(Std.Err."), eststars = "Estimate", estsestars = "Estimate(Std.Err.)").

fits

Summary indicators to be included. May be a list, one for each model provided, otherwise the same fit indicators will be presented for each model. Any of the fit indicators provided by lavaan::fitMeasures(object) are allowed: c("npar", "fmin", "chisq", "df", "pvalue", "baseline.chisq", "baseline.df", "baseline.pvalue", "cfi", "tli", "nnfi", "rfi", "nfi", "pnfi", "ifi", "rni", "logl", "unrestricted.logl", "aic", "bic", "ntotal", "bic2", "rmsea", "rmsea.ci.lower", "rmsea.ci.upper", "rmsea.pvalue", "rmr", "rmr_nomean", "srmr", "srmr_bentler", "srmr_bentler_nomean", "srmr_bollen", "srmr_bollen_nomean", "srmr_mplus", "srmr_mplus_nomean", "cn_05", "cn_01", "gfi", "agfi", "pgfi", "mfi", "ecvi"). The return for "chisq" will include markup for degrees of freedom and p value. If user specifies NULL, or if "fits" is excluded from paramSets, all fit indicators are omitted.

fitLabels

Labels for some or all of the fit measures requested by the fits parameter, e.g. c(rmsea = "Root Mean Square Error of Approximation", cli = "CLI"). The default labels are the upper-case fits names (except for "chisq", where a Greek letter is supplied when possible).

varLabels

Named vector of labels to replace variable names in column 1 of SEM table.

groups

All groups will be printed, unless a subset is requested here. Estimates for all groups will be displayed side by side. If ONLY SOME groups should be included, then specify groups as either names of fit objects or as integers for elements of the groups vector.

type

Choose "latex", "html", or "csv"

table.float

If TRUE, create a LaTeX floating table object in which the tabular created here will reside. Default is FALSE.

caption

Caption for table (if table.float=TRUE) or longtable output. Ignored otherwise.

label

LaTeX label for this object (for cross-references). Only used if table.float = TRUE or longtable = TRUE.

longtable

If TRUE, use longtable for LaTeX documents. Default is FALSE. If true, table.float argument is ignored.

print.results

If TRUE, marked up result will be displayed within the session. Otherwise, result is returned silently and user can use cat to dislay it. Don't use print because it inserts unwanted decorations.

centering

Default "siunitx". For method used in previous editions, replace with "none".

alpha

Thresholds for p-values that determine number of stars. Defaults as c(0.05, 0.01, 0.001) for c("*", "**", "***").

Value

Markup for SEM table. Includes an attribute "markedResults", which can be converted to other markup formats by the function markupConvert.

Details

The argument paramSets determines the inclusion of estimate sections. One can specify "all", which means that all types of parameters that we can find in the fitted model are presented. Otherwise, a subset of parameter sets can be chosen by the user.

  • "loadings" are the factor loadings in the model.

  • "slopes" are the regression slopes in the model.

  • "intercepts" are the estimated constants in the measurement models.

  • "residualvariances" are the observed variable residual variances.

  • "residualcovariances" are the observed covariances among residuals of observed variables.

  • "latentvariances" are the variances of unobserved variables.

  • "latentcovariances" are the covariances between unobserved variables.

  • "latentmeans" are means of unobserved variables

  • "thresholds" arise in latent response variates (non-numeric indicator data).

  • "constructed" are parameters that are calculated from a formula in the model specification, such as an indirect path c=a*b.

  • "fits" the summary indicators of the mismatch between the theoretical and observed covariance matrices, such as RMSEA, CLI, TFI. While the fits are not technically parameters, they are displayed in the same block style as parameters

The columns parameter is used to specify different columns, while columnLabels will alter the displayed labels for them.

Examples

Run this code
# NOT RUN {
## These run longer than 5 seconds
## CFA model
require(lavaan)

tempdir <- tempdir()
## The example from lavaan's docs
HS.model <- ' visual  =~ x1 + x2 + x3
              textual =~ x4 + x5 + x6
              speed   =~ x7 + x8 + x9'
fit1 <- cfa(HS.model, data = HolzingerSwineford1939,
            std.lv = TRUE, meanstructure = TRUE)
## Try a LaTeX file first
fit1.t1 <- semTable(fit1, columns = c("estse", "p"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t1"),
                    varLabels = c("x1" = "hello"), type = "latex", print.results = FALSE)
## If you have a working version of pdflatex in your system path, 
if (interactive()) testtable("fit1.t1.tex", tempdir)
fit1.t1 <- semTable(fit1, columns = c("estse", "p"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t1.html"),
                    varLabels = c("x1" = "hello"), type = "html", print.results = FALSE)
## Use file attribute to get full file name
browseURL(attr(fit1.t1, "file"))
## Try CSV output next
fit1.t1 <- semTable(fit1, columns = c("estse", "p"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t1"),
                    varLabels = c("x1" = "hello"), type = "csv", print.results = FALSE)
## Go inspect this file with a spread sheet program:
attr(fit1.t1, "file")
## Now demonstrate variable labels
vl <- c(visual = "Visual", textual = "Textual", speed = "Speed",
       x1 = "V1", x2 = "V2", x3 = "V3")
fit1.t2 <- semTable(fit1, columns = c("est", "estse"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t2"),
                    varLabels = vl)
if (interactive()) testtable("fit1.t2", tempdir)
## floating table
fit1.t3 <- semTable(fit1, columns = c("est", "estse"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t3"),
                    varLabels = vl, table.float = TRUE,
                    caption = "Holzinger Swineford 1939",
                    label = "tab:hs1939")
if (interactive()) testtable("fit1.t3", tempdir)
fit1.t4 <- semTable(fit1, columns = c("est", "estse"),
                    fits = c("chisq", "rmsea"), file = file.path(tempdir, "fit1.t3"),
                    varLabels = vl, longtable = TRUE,
                    caption = "Holzinger Swineford 1939",
                    label = "tab:hs1939")
if (interactive()) testtable("fit1.t4", tempdir)
fit1.t5 <- semTable(fit1, fits = c("chisq", "rmsea"),
               columns = c("est", "se"), columnLabels = c(se = "S.E."),
               file = file.path(tempdir, "fit1.t5"))
if (interactive()) testtable("fit1.t5", tempdir)
fit1.t6 <- semTable(fit1, fits = c("chisq", "rmsea"),
               columns = c("estsestars"),
               columnLabels = c("estsestars" = "Est(SE)"),
               file = file.path(tempdir, "fit1.t6"))
if (interactive()) testtable("fit1.t6", tempdir)
vl <- c(x1 = "happy 1", x2 = "happy 2", x3 = "happy 3",
        visual = "Seeing", textual = "Thumb Texting")
fit1.t7 <- semTable(fit1, fits = c("chisq", "rmsea"),
               columns = c("eststars", "p"),
               columnLabels = c("eststars" = "Est(SE)"),
               file = file.path(tempdir, "fit1.t7"),
               varLabels = vl, longtable = TRUE, type = "latex")

if (interactive()) testtable("fit1.t7", tempdir)
# Change output format to csv
cat(markupConvert(attr(fit1.t7, "markedResults"), type = "csv"))

## 2 groups
fit1.g <- cfa(HS.model, data = HolzingerSwineford1939,
                        std.lv = TRUE, group = "school", estimator = "MLR")
fit1.gt1 <- semTable(fit1.g, columns = c("estsestars", "p"),
               columnLabels = c(estsestars = "Est w/stars", p = "p-value"),
               file = file.path(tempdir, "fit1.gt1"))
if (interactive()) testtable("fit1.gt1", tempdir)
## Now name particular group by name
fit1.gt2 <- semTable(fit1.g, columns = c("estsestars", "p"),
               columnLabels = c(estsestars = "Est w/stars", p = "p-value"),
               file = file.path(tempdir, "fit1.gt2"), groups = "Pasteur")
if (interactive()) testtable("fit1.gt2", tempdir)
## Name particular group by number
fit1.gt3 <- semTable(fit1.g, columns = c("estsestars", "p"),
               columnLabels = c(estsestars = "Est w/stars", p = "p-value"),
               file = file.path(tempdir, "fit1.gt3"), groups = 1)
if (interactive()) testtable("fit1.gt3", tempdir)

## Fit same model with standardization
fit1.std <- update(fit1, std.lv = TRUE, std.ov = TRUE, meanstructure = TRUE) 
## include 2 models in table request
fit1.t2 <- semTable(list("Ordinary" = fit1, "Standardized" = fit1.std),
                    file = file.path(tempdir, "fit1.2.1"))
semTable(list("Ordinary" = fit1, "Standardized" = fit1.std),
    columns = list("Ordinary" = c("est", "se"), "Standardized" = c("est")),
    columnLabels = c(est = "Est", se = "SE"), file = file.path(tempdir, "fit1.2.2"))
if (interactive()) testtable("fit1.2.2", tempdir)

fit1.t2 <- semTable(fit1, fits = c("chisq", "rmsea"), print.results = FALSE)
cat(fit1.t2)
fit1.t3 <- semTable(fit1, fits = c("chisq", "rmsea", "tli"),
               columns = c("est", "se"))

## Can create file with cat
cat(fit1.t3, file = file.path(tempdir, "fit1.t3.tex"))

## Basic SEM
regmodel1 <- 'visual  =~ x1 + x2 + x3 
             textual =~ x4 + x5 + x6
             speed   =~ x7 + x8 + x9
             visual ~ textual + speed
'

fit2 <- sem(regmodel1, data = HolzingerSwineford1939, std.lv = TRUE,
            meanstructure = TRUE)

fit2.std <- update(fit2, std.lv = TRUE, std.ov = TRUE, meanstructure = TRUE) 

fit2.t <- semTable(list("Ordinary" = fit2, "Standardized" = fit2.std), fits = "rmsea",
                   columns = list("Ordinary" = c("est", "se", "p"),
                                   "Standardized" = c("estsestars")),
                   columnLabels = c("est" = "Est", "se" = "Std.Err.", "p" = "p",
                                    "estsestars" = "Standardized Est."),
                   paramSets = c("loadings", "intercepts", "slopes", "latentcovariances"),
                   file = file.path(tempdir, "fit2.t1"), type = "latex")

if (interactive()) testtable("fit2.t1", tempdir)

# Change output format to csv
cat(markupConvert(attr(fit2.t, "markedResults"), type = "csv"))
cat(markupConvert(attr(fit2.t, "markedResults"), type = "html"))

fit2.t <- semTable(list("Ordinary" = fit2, "Standardized" = fit2.std),
              type = c("html"),
              file = file.path(tempdir, "fit2.t"),
              varLabels = c(x1 = "happy 1", x2 = "happy 2", x3 = "happy 3"),
              print.results = FALSE)

if (interactive()) browseURL(file.path(tempdir, "fit2.t.html"))


regmodel2 <- 'visual  =~ x1 + x2 + x3
              textual =~ x4 +  x6
              speed   =~  x8 + x9
              visual ~ textual 
'
fit3 <- sem(regmodel2, data = HolzingerSwineford1939, std.lv = TRUE,
            meanstructure = TRUE)

fit3.t1 <-  semTable(fit3, type = c("latex"),
                     columns = c("estsestars", "rsquare"), 
                     file = file.path(tempdir, "fit3.1"),
                     print.results = FALSE)

cat(fit3.t1)
if (interactive()) testtable("fit3.1", tempdir)

fit3.std <- update(fit2, std.lv = TRUE, std.ov = TRUE)

fit3.std.t1 <- semTable(list("Mod 1" = fit2, "Mod 1 std" = fit2.std, "Mod 2" = fit3,
              "Mod 3 std" = fit3.std), columns = c("estsestars"), type = "html",
               file = file.path(tempdir, "fit3.std.t1"), print.results = FALSE)
if(interactive()) browseURL(file.path(tempdir, "fit3.std.t1.html"))

fit3 <- sem(regmodel1, data = HolzingerSwineford1939, group = "school")
## if specify 2 types, get a list of them back
fit3.t1 <- semTable(fit3, type = "latex")
fit3.t2 <- semTable(fit3, columns = c("est", "se"),
                     columnLabels = c(est = "Est.", se = "S.E."))

fit3.t2 <- semTable(fit3, fits = c("chisq", "rmsea", "cfi"), print.results = FALSE)
cat(fit3.t2)

fit3.t2 <- semTable(fit3, columns = c("estsestars"),
            fits = c("chisq", "rmsea", "cfi"), type = "html",
            file = file.path(tempdir, "fit3.t2"))
if(interactive()) browseURL(file.path(tempdir, "fit3.t2.html"))
 
fit3.t2 <- semTable(fit3, fits = c("rmsea", "cfi"))

model <- "factor =~ .7*y1 + .7*y2 + .7*y3 + .7*y4
                 y1 | -1*t1 + 1*t2
                 y2 | -.5*t1 + 1*t2
                 y3 | -.2*t1 + 1*t2
                 y4 | -1*t1 + 1*t2"
dat <- simulateData(model, sample.nobs = 300)

testmodel <- "ExampleFactor =~ y1 + y2 + y3 + y4"

fit4 <- cfa(testmodel, data = dat, ordered = colnames(dat),
            std.lv = FALSE)

fit4.t1 <- semTable(fit4, paramSets = c("loadings", "thresholds",
    "residualvariances"), fits = c("tli", "chisq"),
    fitLabels = c(tli = "TLI", chisq = "chisq"), type = "html")

fit4.t2 <- semTable(fit4, fits = c("rmsea", "cfi", "chisq"),
              fitLabels = c(rmsea = "Root M.SQ.E.A", cfi = "CompFitIdx", chisq = "chisq"),
              type = "latex")

## Model 5 - Mediation model with equality constraints
model5 <-
    '
    # latent variable definitions
    ind60 =~ x1 + x2 + x3
    dem60 =~ y1 + e*y2 + d*y3 + y4
    dem65 =~ y5 + e*y6 + d*y7 + y8
    # regressions
    dem60 ~ a*ind60
    dem65 ~ c*ind60 + b*dem60
    # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8

    # indirect effect (a*b)
    ## := operator defines new parameters
    ab := a*b

    ## total effect
    total := c + (a*b)
    '

fit5 <- sem(model5, data=PoliticalDemocracy)
fit5boot <- sem(model5, data=PoliticalDemocracy, se = "bootstrap", bootstrap = 100)

fit5.t1 <- semTable(list("Democracy" = fit5), columns = c("estsestars", "rsquare"),
           file = file.path(tempdir, "fit5.1t"), type = "html",
           print.results = FALSE)
if(interactive()) browseURL(file.path(tempdir, "fit5.t1.html"))

## convert same object to LaTeX, save file, then test
cat(markupConvert(attr(fit5.t1, "markedResults"), type = "latex"),
         file = file.path(tempdir, "fit5.t1.tex"))
if (interactive()) testtable("fit5.t1", tempdir)

semTable(list("Democracy" = fit5, "Bootstrapped SE" = fit5boot),
         columns = c("estsestars", "rsquare"),
         file = file.path(tempdir, "fit5.2"), type = "latex",
         longtable = TRUE)

semTable(list("Democracy" = fit5, "Bootstrapped SE" = fit5boot),
         columns = c("estsestars", "rsquare"),
         paramSets = c("loadings", "slopes", "residualvariances", "constructed"),
         file = file.path(tempdir, "fit5.3"), type = "latex",
         longtable = TRUE)

## Model 5b - Revision of Model 5s
model5b <-
    '
    # Cut some indicators from the measurement model
    ind60 =~ x1 + x2 
    dem60 =~ y1 + e*y2 + d*y3 + y4
    dem65 =~ y5 + e*y6 + d*y7 
    # regressions
    dem60 ~ a*ind60
    dem65 ~ c*ind60 + b*dem60
    # cut out the residual correlations
    # indirect effect (a*b)
    ## := operator defines new parameters
    ab := a*b

    ## total effect
    total := c + (a*b)
    '

fit5b <- sem(model5b, data=PoliticalDemocracy, se = "bootstrap",
bootstrap = 100)
semTable(list("Model 5" = fit5boot, "Model 5b" = fit5b),
         columns = c("estsestars", "rsquare"),
         file = file.path(tempdir, "fit5.5"),
          type = "latex",
         longtable = TRUE)
if(interactive()) testtable("fit5.5", tempdir)

list.files(tempdir)
# }

Run the code above in your browser using DataLab