I have uploaded kutils-1.25, a beta testing version on KRAN. If your computer is not set to automatically pull updates from KRAN already, run this:
CRAN <- "http://rweb.crmda.ku.edu/cran" KRAN <- "http://rweb.crmda.ku.edu/kran" options(repos = c(KRAN, CRAN)) update.packages(ask = FALSE, checkBuilt = TRUE)
In case you don't have
kutils before now (how could that possibly be?), run
install.packages("kutils", dep = TRUE)
The special feature we are testing now is the enhanced
semTable function. In a nutshell, here is what this can do. Provide one or more structural equation models fitted with
lavaan. Then summary tables can be presented that display the models side by side. Using the arguments
paramSets, the user is able to control which columns are displayed for which model, and which model sections are included.
examples section of the help page for semTable includes 20 examples. It also demonstrates a new function called
testtable, which can compile and display the PDF output from LaTeX tables.
To whet your appetite, this code fits a multi-group CFA:
require(lavaan) tempdir <- tempdir() HS.model <- ' visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9' fit1.g <- cfa(HS.model, data = HolzingerSwineford1939, std.lv = TRUE, group = "school") 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)
The default settings will display all of the groups, side by side. That might become crowded, so we allow the user to select which columns are to be displayed for the groups. It is possible also to use the
groups argument to name one or more groups to be displayed in the presentation (helps when there are too many groups to fit in one table). In the multi group model, we do not allow different columns to be displayed for the various groups.
If the user has several SEM, they can be displayed side by side and the user is allowed to customize the list of displayed columns for each separate model. As we demonstrate here, we can fit a non-standardized SEM and a standardized model and display them side by side.
## Fit same model with standardization fit1 <- cfa(HS.model, data = HolzingerSwineford1939, std.lv = TRUE, meanstructure = TRUE) 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)
These examples demonstrate the ability to create LaTeX tables, because that's what I use. However, the function
is designed to also create tables in HTML and CSV formats, The magic recipe for that it to insert, say,
type = c("latex", "html", "csv") into the semTable funciton call. If you also add
file="whatever" then three files will be created, "whatever.tex", "whatever.html" and "whatever.csv". The HTML file can be viewed within the R session by running
The appeal of the HTML output is that an HTML file can be opened by a word processor, say LibreOffice or MS Word, and it will generally be turned into a table object which can be edited. The CSV file may be used in the same way, in conjunction with a spread sheet program.
semTable edition is intended to allow one to fit different models that can be combined into one table. The standardized example above is perhaps not the most persuasive demonstration. Consider the following instead.
## 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", boot = 100) ## Model 5b - Revision of Model 5s model5b <- ' # latent variable definitions ind60 =~ x1 + x2 dem60 =~ y1 + e*y2 + d*y3 + y4 dem65 =~ y5 + e*y6 + d*y7 + y8 # regressions dem60 ~ a*ind60 dem65 ~ c*ind60 + b*dem60 # no 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 = c("latex", "html", "csv"), longtable = TRUE) testtable("fit5.5", tempdir)
It seems to me this result is not exactly perfect. I wish it did not print "NA" on the omitted loadings. But I'm headed in the right direction. This lines up the parts of the models, makes it plain which pieces are included in each model.