Chapter 16. Composite Models

Principles and Practice of Structural Equation Modeling (5e) by Rex B. Kline

Author

Sungkyun Cho

Published

September 17, 2024

Load libraries
library(tidyverse)
library(lavaan)
library(semTools)
library(cSEM)
library(psych)

Partial least squares path modeling (PLS-PM) algorithm

Software: cSEM

Source: Fig 16.3 (p. 295)

# set global seed for random number generation
set.seed(123)

# variable order is acculscl, status, percent, educ, income,
# interpers, job, scl90d
# read correlation matrix
shen.cor <- matrix(c(1.00, .44, .69, .21, .23, .12, .09, .03,
                      .44,1.00, .54, .08, .15, .08, .06, .02,
                      .69, .54,1.00, .16, .19, .08, .04,-.02,
                      .21, .08, .16,1.00, .19, .08, .01,-.07,
                      .23, .15, .19, .19,1.00,-.03,-.02,-.11,
                      .12, .08, .08, .08,-.03,1.00, .38, .37,
                      .09, .06, .04, .01,-.02, .38,1.00, .46,
                      .03, .02,-.02,-.07,-.11, .37, .46,1.00),
                      ncol = 8, nrow = 8)

# generate raw scores and save to dataframe
shen.data <- semTools::kd(shen.cor, 983, type="exact")

# rename columns in data frame and display correlation matrix
names(shen.data) <- c("acculscl", "status", "percent", "educ", "income",
 "interpers", "job", "scl90d")

# display correlation matrix
cor(shen.data) |> print()
          acculscl status percent  educ income interpers   job scl90d
acculscl      1.00   0.44    0.69  0.21   0.23      0.12  0.09   0.03
status        0.44   1.00    0.54  0.08   0.15      0.08  0.06   0.02
percent       0.69   0.54    1.00  0.16   0.19      0.08  0.04  -0.02
educ          0.21   0.08    0.16  1.00   0.19      0.08  0.01  -0.07
income        0.23   0.15    0.19  0.19   1.00     -0.03 -0.02  -0.11
interpers     0.12   0.08    0.08  0.08  -0.03      1.00  0.38   0.37
job           0.09   0.06    0.04  0.01  -0.02      0.38  1.00   0.46
scl90d        0.03   0.02   -0.02 -0.07  -0.11      0.37  0.46   1.00
# descriptive statistics
psych::describe(shen.data) |> print()
          vars   n mean sd median trimmed  mad   min  max range  skew kurtosis
acculscl     1 983    0  1  -0.03    0.00 1.04 -3.77 3.15  6.92  0.00     0.11
status       2 983    0  1  -0.01   -0.01 1.00 -3.19 3.05  6.25  0.06    -0.05
percent      3 983    0  1  -0.04   -0.01 1.04 -3.46 3.01  6.47  0.03    -0.10
educ         4 983    0  1   0.00    0.00 1.03 -3.67 3.37  7.04 -0.01     0.01
income       5 983    0  1   0.00    0.00 1.00 -3.14 3.45  6.60  0.03     0.22
interpers    6 983    0  1   0.01    0.01 0.99 -3.57 3.11  6.68 -0.05     0.00
job          7 983    0  1   0.02    0.00 1.06 -3.23 3.15  6.38 -0.03    -0.23
scl90d       8 983    0  1   0.02    0.00 1.01 -2.73 2.98  5.71  0.05    -0.10
            se
acculscl  0.03
status    0.03
percent   0.03
educ      0.03
income    0.03
interpers 0.03
job       0.03
scl90d    0.03
# specify composite model

shen.model <- '
  # outer model (measurement)
  # exogenous composites
  Acculturation <~ acculscl + status + percent
  SES <~ educ + income
  
  # endogenous composites
  Stress <~ interpers + job
  Depression <~ scl90d

  # inner model (structural)
  Stress ~ Acculturation
  Depression ~ SES + Stress
'
# fit model to data with package cSEM for cca

# the algorithm is basic PLS-PM 
# dominant indicators specified for all composites
# with multiple indicators
# by default, the single indicator for depression
# is the dominant indicator
# outer weights are PLS mode A (correlation weights)
# inner weights are factor (factorial)
# bootstrapped standard errors, 1000 generated samples
# seed for bootstrapping (123) initializes random
# number generation in cSEM functions for boostrapping
# the global seed in R is also set to the same value (123)
# thus, bootstrapped estimates of standard errors and
# percentiles for distributions of global fit test
# statistics are reproducible

shen <- cSEM::csem(.data = shen.data, .model = shen.model, 
 .dominant_indicators = c(Acculturation = "acculscl", SES = "educ",
  Stress = "interpers"), .approach_weights = "PLS-PM", 
 .PLS_modes = "modeA", .PLS_weight_scheme_inner = "factorial", 
 .resample_method = "bootstrap", .R = 1000, .seed = 123,
 .disattenuate = FALSE)
# check solution for problems
cSEM::verify(shen)
________________________________________________________________________________
Verify admissibility:

  admissible
Details:
  Code   Status    Description
  1      ok        Convergence achieved                                   
  2      ok        All absolute standardized loading estimates <= 1       
  3      ok        Construct VCV is positive semi-definite                
  4      ok        All reliability estimates <= 1                         
  5      ok        Model-implied indicator VCV is positive semi-definite  
________________________________________________________________________________
# parameter estimates with bootstrapped standard errors
cSEM::summarize(shen)
________________________________________________________________________________
----------------------------------- Overview -----------------------------------
    General information:
    ------------------------
    Estimation status                = Ok
    Number of observations           = 983
    Weight estimator                 = PLS-PM
    Inner weighting scheme           = "factorial"
    Type of indicator correlation    = Pearson
    Path model estimator             = OLS
    Second-order approach            = NA
    Type of path model               = Linear
    Disattenuated                    = No
    Resample information:
    ---------------------
    Resample method                  = "bootstrap"
    Number of resamples              = 1000
    Number of admissible results     = 1000
    Approach to handle inadmissibles = "drop"
    Sign change option               = "none"
    Random seed                      = 123
    Construct details:
    ------------------
    Name           Modeled as     Order         Mode      
    Acculturation  Composite      First order   "modeA"   
    SES            Composite      First order   "modeA"   
    Stress         Composite      First order   "modeA"   
    Depression     Composite      First order   "modeA"   
----------------------------------- Estimates ----------------------------------
Estimated path coefficients:
============================
                                                                        CI_percentile   
  Path                      Estimate  Std. error   t-stat.   p-value         95%        
  Stress ~ Acculturation      0.1166      0.0276    4.2251    0.0000 [ 0.0687; 0.1765 ] 
  Depression ~ SES           -0.1214      0.0298   -4.0779    0.0000 [-0.1798;-0.0713 ] 
  Depression ~ Stress         0.5039      0.0230   21.9210    0.0000 [ 0.4586; 0.5479 ] 
Estimated loadings:
===================
                                                                           CI_percentile   
  Loading                      Estimate  Std. error   t-stat.   p-value         95%        
  Acculturation =~ acculscl      0.8952      0.0393   22.8018    0.0000 [ 0.8105; 0.9668 ] 
  Acculturation =~ status        0.7509      0.0706   10.6426    0.0000 [ 0.5965; 0.8567 ] 
  Acculturation =~ percent       0.8582      0.0408   21.0136    0.0000 [ 0.7484; 0.8988 ] 
  SES =~ educ                    0.6440      0.1646    3.9115    0.0001 [ 0.2346; 0.8785 ] 
  SES =~ income                  0.8735      0.1293    6.7530    0.0000 [ 0.6337; 0.9974 ] 
  Stress =~ interpers            0.7942      0.0193   41.1856    0.0000 [ 0.7531; 0.8284 ] 
  Stress =~ job                  0.8639      0.0127   67.9643    0.0000 [ 0.8371; 0.8874 ] 
  Depression =~ scl90d           1.0000          NA        NA        NA [     NA;     NA ] 
Estimated weights:
==================
                                                                           CI_percentile   
  Weight                       Estimate  Std. error   t-stat.   p-value         95%        
  Acculturation <~ acculscl      0.5327      0.0946    5.6283    0.0000 [ 0.3908; 0.7721 ] 
  Acculturation <~ status        0.3551      0.1097    3.2366    0.0012 [ 0.1299; 0.5630 ] 
  Acculturation <~ percent       0.2989      0.0886    3.3740    0.0007 [ 0.0526; 0.4114 ] 
  SES <~ educ                    0.4959      0.1853    2.6766    0.0074 [ 0.0579; 0.7864 ] 
  SES <~ income                  0.7793      0.1556    5.0067    0.0000 [ 0.4826; 0.9885 ] 
  Stress <~ interpers            0.5446      0.0220   24.7726    0.0000 [ 0.5019; 0.5883 ] 
  Stress <~ job                  0.6569      0.0224   29.2846    0.0000 [ 0.6140; 0.7004 ] 
  Depression <~ scl90d           1.0000          NA        NA        NA [     NA;     NA ] 
Estimated construct correlations:
=================================
                                                                      CI_percentile   
  Correlation             Estimate  Std. error   t-stat.   p-value         95%        
  Acculturation ~~ SES      0.2745      0.0382    7.1933    0.0000 [ 0.1969; 0.3267 ] 
Estimated indicator correlations:
=================================
                                                                     CI_percentile   
  Correlation            Estimate  Std. error   t-stat.   p-value         95%        
  acculscl ~~ status       0.4400      0.0258   17.0772    0.0000 [ 0.3887; 0.4887 ] 
  acculscl ~~ percent      0.6900      0.0176   39.2730    0.0000 [ 0.6550; 0.7228 ] 
  status ~~ percent        0.5400      0.0219   24.6519    0.0000 [ 0.4945; 0.5799 ] 
  educ ~~ income           0.1900      0.0300    6.3323    0.0000 [ 0.1294; 0.2526 ] 
  interpers ~~ job         0.3800      0.0283   13.4416    0.0000 [ 0.3241; 0.4361 ] 
------------------------------------ Effects -----------------------------------
Estimated total effects:
========================
                                                                            CI_percentile   
  Total effect                  Estimate  Std. error   t-stat.   p-value         95%        
  Stress ~ Acculturation          0.1166      0.0276    4.2251    0.0000 [ 0.0687; 0.1765 ] 
  Depression ~ Acculturation      0.0588      0.0143    4.1068    0.0000 [ 0.0348; 0.0902 ] 
  Depression ~ SES               -0.1214      0.0298   -4.0779    0.0000 [-0.1798;-0.0713 ] 
  Depression ~ Stress             0.5039      0.0230   21.9210    0.0000 [ 0.4586; 0.5479 ] 
Estimated indirect effects:
===========================
                                                                            CI_percentile   
  Indirect effect               Estimate  Std. error   t-stat.   p-value         95%        
  Depression ~ Acculturation      0.0588      0.0143    4.1068    0.0000 [ 0.0348; 0.0902 ] 
________________________________________________________________________________
# test overall (global) model fit
# 1000 bootstrap replications
# seed value set
cSEM::testOMF(shen, .R = 1000, .seed = 123)
________________________________________________________________________________
--------- Test for overall model fit based on Beran & Srivastava (1985) --------
Null hypothesis:
       +------------------------------------------------------------------+
       |                                                                  |
       |   H0: The model-implied indicator covariance matrix equals the   |
       |   population indicator covariance matrix.                        |
       |                                                                  |
       +------------------------------------------------------------------+
Test statistic and critical value: 
                                        Critical value
    Distance measure    Test statistic    95%   
    dG                      0.0062      0.0088  
    SRMR                    0.0254      0.0408  
    dL                      0.0232      0.0600  
    dML                     0.0329      0.0462  
    
Decision: 
                            Significance level
    Distance measure             95%        
    dG                      Do not reject    
    SRMR                    Do not reject    
    dL                      Do not reject    
    dML                     Do not reject    
    
Additional information:
    Out of 1000 bootstrap replications 1000 are admissible.
    See ?verify() for what constitutes an inadmissible result.
    The seed used was: 123
________________________________________________________________________________
# model quality criteria
cSEM::assess(shen, .quality_criterion = c("df", "r2", "r2_adj", "f2", 
 "chi_square"))
________________________________________________________________________________

    Construct         R2          R2_adj    
    Stress          0.0136        0.0126    
    Depression      0.2684        0.2669    

--------------------------- Distance and fit measures --------------------------


    Chi_square     = 32.35609

    Degrees of freedom    = 15

-------------------------- Effect sizes (Cohen's f^2) --------------------------

  Dependent construct: 'Stress'

    Independent construct       f^2    
    Acculturation             0.0138   

  Dependent construct: 'Depression'

    Independent construct       f^2    
    SES                       0.0201   
    Stress                    0.3471   
________________________________________________________________________________
# model-implied correlations among composites
cSEM::fit(shen, .type_vcv = "construct") |> print()
              Acculturation         SES     Stress  Depression
Acculturation    1.00000000  0.27450941 0.11664735  0.02545829
SES              0.27450941  1.00000000 0.03202079 -0.10524925
Stress           0.11664735  0.03202079 1.00000000  0.50002142
Depression       0.02545829 -0.10524925 0.50002142  1.00000000
# model-implied correlations among indicators
predicted <- cSEM::fit(shen, .type_vcv = "indicator")
predicted |> round(3) |> print()
          acculscl status percent   educ income interpers   job scl90d
acculscl     1.000  0.440   0.690  0.158  0.215     0.083 0.090  0.023
status       0.440  1.000   0.540  0.133  0.180     0.070 0.076  0.019
percent      0.690  0.540   1.000  0.152  0.206     0.080 0.086  0.022
educ         0.158  0.133   0.152  1.000  0.190     0.016 0.018 -0.068
income       0.215  0.180   0.206  0.190  1.000     0.022 0.024 -0.092
interpers    0.083  0.070   0.080  0.016  0.022     1.000 0.380  0.397
job          0.090  0.076   0.086  0.018  0.024     0.380 1.000  0.432
scl90d       0.023  0.019   0.022 -0.068 -0.092     0.397 0.432  1.000
# calculate correlation residuals
# rounded to 3-decimal places

cor_residuals = shen.cor - predicted
round(cor_residuals, digits = 3) |> print()
          acculscl status percent   educ income interpers    job scl90d
acculscl     0.000  0.000   0.000  0.052  0.015     0.037  0.000  0.007
status       0.000  0.000   0.000 -0.053 -0.030     0.010 -0.016  0.001
percent      0.000  0.000   0.000  0.008 -0.016     0.000 -0.046 -0.042
educ         0.052 -0.053   0.008  0.000  0.000     0.064 -0.008 -0.002
income       0.015 -0.030  -0.016  0.000  0.000    -0.052 -0.044 -0.018
interpers    0.037  0.010   0.000  0.064 -0.052     0.000  0.000 -0.027
job          0.000 -0.016  -0.046 -0.008 -0.044     0.000  0.000  0.028
scl90d       0.007  0.001  -0.042 -0.002 -0.018    -0.027  0.028  0.000