kutils package update: more powerful SEM table maker

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 columns and paramSets, the user is able to control which columns are displayed for which model, and which model sections are included.

The 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:

 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)

Creates a PDF file that shows the 2 groups side by side:

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.

This 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.

This entry was posted in Data Analysis, R. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *