Chapter 2 Structural Equation Modeling

2.1 Introduction to Structural Equation Modeling (SEM)

2.2 What is Structural Equation Modeling?

Structural Equation Modeling (SEM) is a powerful multivariate statistical technique that combines factor analysis and multiple regression to test complex theoretical models. SEM allows researchers to:

  1. Model latent variables (constructs that cannot be directly observed)
  2. Test theoretical relationships between multiple variables simultaneously
  3. Account for measurement error in observed variables
  4. Examine direct and indirect effects (mediation pathways)
  5. Compare competing theoretical models

2.3 Core Concepts and Components

2.3.1 Latent vs. Observed Variables

  • Latent Variables: Unobserved constructs (e.g., intelligence, depression, satisfaction)
  • Observed Variables: Measured indicators (e.g., test scores, questionnaire items)

2.3.2 Two-Part Model Structure

SEM consists of two interconnected components:

  1. Measurement Model: Defines how latent variables are measured by observed indicators
  2. Structural Model: Specifies relationships between latent variables

2.4 When to Use Structural Equation Modeling

2.4.1 Research Questions Ideal for SEM:

  1. Complex Theoretical Models: When you have multiple constructs with hypothesized relationships
    • Example: “How do personality traits influence job performance through motivation and stress?”
  2. Mediation Analysis: When you want to test indirect effects
    • Example: “Does social support mediate the relationship between therapy and depression recovery?”
  3. Measurement Validation: When you need to test factor structure of scales
    • Example: “Does this new anxiety scale measure the intended construct?”
  4. Model Comparison: When you want to test competing theories
    • Example: “Which model better explains academic achievement: motivation-first or self-efficacy-first?”

2.4.2 Real-World Applications in Behavioral Science:

2.4.2.1 Clinical Psychology:

  • Question: “How do cognitive distortions, behavioral activation, and social support interact to influence depression recovery?”
  • SEM Application: Model depression as latent variable measured by symptoms, test mediation pathways
  • Advantage: Accounts for measurement error in symptom assessments

2.4.2.2 Educational Psychology:

  • Question: “What are the pathways from teaching quality to student achievement through motivation and engagement?”
  • SEM Application: Multi-level constructs with mediation analysis
  • Advantage: Simultaneously tests multiple theoretical pathways

2.4.2.3 Organizational Psychology:

  • Question: “How do leadership styles affect employee performance through job satisfaction and commitment?”
  • SEM Application: Complex mediation model with multiple mediators
  • Advantage: Separates direct and indirect effects of leadership

2.4.2.4 Health Psychology:

  • Question: “What factors predict health behavior change through theory of planned behavior?”
  • SEM Application: Test full TPB model with attitudes, norms, and control predicting intentions and behavior
  • Advantage: Tests complete theoretical model simultaneously

2.5 Advantages of SEM

2.5.1 Statistical Benefits:

  1. Measurement Error Correction: Accounts for unreliability in measures
  2. Simultaneous Testing: Tests all relationships at once (vs. multiple regressions)
  3. Model Fit Assessment: Provides multiple indices to evaluate model quality
  4. Flexible Modeling: Handles complex relationships and constraints

2.5.2 Theoretical Benefits:

  1. Theory Testing: Allows rigorous testing of theoretical models
  2. Mediation Analysis: Sophisticated indirect effect testing
  3. Model Comparison: Can compare competing theoretical explanations
  4. Construct Validation: Confirms measurement quality of psychological constructs

2.6 When NOT to Use SEM

2.6.1 Inappropriate Situations:

  1. Small Sample Sizes: Generally need n > 200 (preferably > 300)
  2. Exploratory Research: SEM is confirmatory; use EFA first for scale development
  3. Simple Relationships: Basic regression may suffice for single predictors
  4. Poor Measurement: Requires reliable, valid measures of constructs

2.7 Types of SEM Models

2.7.1 1. Confirmatory Factor Analysis (CFA)

  • Purpose: Test measurement model only
  • Use when: Validating scale structure or establishing measurement invariance
  • Example: “Does this personality scale have the expected 5-factor structure?”

2.7.2 2. Path Analysis

  • Purpose: Test structural relationships without latent variables
  • Use when: All variables are directly observed
  • Example: “How does income → education → health behavior?”

2.7.3 3. Full SEM Model

  • Purpose: Test both measurement and structural models
  • Use when: Complex theoretical model with latent variables
  • Example: Complete theoretical model of therapy effectiveness

2.7.4 4. Multi-Group SEM

  • Purpose: Test whether model works the same across groups
  • Use when: Examining group differences or measurement invariance
  • Example: “Does this depression model work the same for men and women?”

2.8 Research Questions Addressed by SEM

2.8.1 1. Direct Effects

  • Question: “What is the direct effect of social support on well-being?”
  • SEM Analysis: Structural coefficient from support to well-being

2.8.2 2. Indirect Effects (Mediation)

  • Question: “Does stress mediate the relationship between work demands and burnout?”
  • SEM Analysis: Test indirect pathway through stress

2.8.3 3. Total Effects

  • Question: “What is the total effect of personality on job performance?”
  • SEM Analysis: Sum of direct and indirect effects

2.8.4 4. Model Comparison

  • Question: “Which theory better explains academic achievement?”
  • SEM Analysis: Compare fit of competing models

2.9 How to Report SEM Results in APA Format

2.9.1 Model Specification:

“A structural equation model was fitted using maximum likelihood estimation with robust standard errors (MLR). The model included five latent variables measured by [X] observed indicators each.”

2.9.2 Model Fit Reporting:

“Model fit was assessed using multiple indices. The model demonstrated acceptable fit: χ²(df = 145) = 267.23, p < .001, CFI = .924, TLI = .908, RMSEA = .056, 90% CI [.047, .065], SRMR = .068.”

2.9.3 Path Coefficients:

“Results indicated a significant positive relationship between social support and well-being (β = .34, SE = .08, p < .001, 95% CI [.19, .49]).”

2.9.4 Indirect Effects:

“Bootstrap analysis (5,000 resamples) revealed a significant indirect effect of personality on well-being through social support (β = .12, SE = .04, p = .003, 95% CI [.04, .20]).”

2.9.5 Effect Sizes:

“According to Cohen’s guidelines, this represents a medium effect size (β = .34), explaining 12% of the variance in well-being.”

2.10 Common SEM Terminology

  • Endogenous Variables: Dependent variables (caused by other variables in the model)
  • Exogenous Variables: Independent variables (not caused by other variables in the model)
  • Factor Loadings: Relationships between latent variables and their indicators
  • Residuals: Unexplained variance in observed variables
  • Disturbances: Unexplained variance in latent variables
  • Modification Indices: Suggestions for improving model fit

Understanding these concepts is crucial for properly conducting and interpreting SEM analyses in behavioral science research.

2.11 Mathematical Foundation

SEM consists of two main components:

2.11.1 Measurement Model (Confirmatory Factor Analysis)

\[\mathbf{x} = \boldsymbol{\Lambda}_x \boldsymbol{\xi} + \boldsymbol{\delta}\] \[\mathbf{y} = \boldsymbol{\Lambda}_y \boldsymbol{\eta} + \boldsymbol{\varepsilon}\]

2.11.2 Structural Model

\[\boldsymbol{\eta} = \mathbf{B}\boldsymbol{\eta} + \boldsymbol{\Gamma}\boldsymbol{\xi} + \boldsymbol{\zeta}\]

Where: - \(\mathbf{x}\) and \(\mathbf{y}\) are observed variables - \(\boldsymbol{\xi}\) are exogenous latent variables - \(\boldsymbol{\eta}\) are endogenous latent variables - \(\boldsymbol{\Lambda}\) are factor loadings - \(\mathbf{B}\) and \(\boldsymbol{\Gamma}\) are structural coefficients - \(\boldsymbol{\delta}\), \(\boldsymbol{\varepsilon}\), and \(\boldsymbol{\zeta}\) are error terms

2.12 Required Packages

# Set CRAN mirror
options(repos = c(CRAN = "https://cran.rstudio.com/"))

# Install packages if not already installed
if (!require("lavaan")) install.packages("lavaan")
if (!require("semPlot")) install.packages("semPlot")
if (!require("report")) install.packages("report")
if (!require("ggplot2")) install.packages("ggplot2")
if (!require("dplyr")) install.packages("dplyr")
if (!require("tidyr")) install.packages("tidyr")
if (!require("corrplot")) install.packages("corrplot")
if (!require("psych")) install.packages("psych")
if (!require("MVN")) install.packages("MVN")
if (!require("semTools")) install.packages("semTools")

library(lavaan)
library(semPlot)
library(report)
library(ggplot2)
library(dplyr)
library(tidyr)
library(corrplot)
library(psych)
library(MVN)
library(semTools)

2.13 Data Simulation: Psychology Well-being Study

We’ll simulate a dataset examining the relationships between personality traits, social support, stress, and psychological well-being.

set.seed(456)
n <- 500

# Simulate latent variable scores
extraversion_lat <- rnorm(n, 0, 1)
neuroticism_lat <- rnorm(n, 0, 1)
social_support_lat <- 0.4 * extraversion_lat - 0.3 * neuroticism_lat + rnorm(n, 0, 0.8)
stress_lat <- -0.3 * extraversion_lat + 0.5 * neuroticism_lat - 0.4 * social_support_lat + rnorm(n, 0, 0.7)
wellbeing_lat <- 0.3 * extraversion_lat - 0.4 * neuroticism_lat + 0.5 * social_support_lat - 0.6 * stress_lat + rnorm(n, 0, 0.5)

# Create observed indicators for each latent variable

# Extraversion indicators (5 items)
extra1 <- 3 + 0.8 * extraversion_lat + rnorm(n, 0, 0.6)
extra2 <- 3 + 0.9 * extraversion_lat + rnorm(n, 0, 0.5)
extra3 <- 3 + 0.7 * extraversion_lat + rnorm(n, 0, 0.7)
extra4 <- 3 + 0.8 * extraversion_lat + rnorm(n, 0, 0.6)
extra5 <- 3 + 0.6 * extraversion_lat + rnorm(n, 0, 0.8)

# Neuroticism indicators (4 items)
neuro1 <- 3 + 0.9 * neuroticism_lat + rnorm(n, 0, 0.5)
neuro2 <- 3 + 0.8 * neuroticism_lat + rnorm(n, 0, 0.6)
neuro3 <- 3 + 0.7 * neuroticism_lat + rnorm(n, 0, 0.7)
neuro4 <- 3 + 0.8 * neuroticism_lat + rnorm(n, 0, 0.6)

# Social Support indicators (4 items)
support1 <- 3 + 0.9 * social_support_lat + rnorm(n, 0, 0.5)
support2 <- 3 + 0.8 * social_support_lat + rnorm(n, 0, 0.6)
support3 <- 3 + 0.8 * social_support_lat + rnorm(n, 0, 0.6)
support4 <- 3 + 0.7 * social_support_lat + rnorm(n, 0, 0.7)

# Stress indicators (3 items)
stress1 <- 3 + 0.9 * stress_lat + rnorm(n, 0, 0.5)
stress2 <- 3 + 0.8 * stress_lat + rnorm(n, 0, 0.6)
stress3 <- 3 + 0.8 * stress_lat + rnorm(n, 0, 0.6)

# Well-being indicators (4 items)
wellbeing1 <- 3 + 0.9 * wellbeing_lat + rnorm(n, 0, 0.5)
wellbeing2 <- 3 + 0.8 * wellbeing_lat + rnorm(n, 0, 0.6)
wellbeing3 <- 3 + 0.8 * wellbeing_lat + rnorm(n, 0, 0.6)
wellbeing4 <- 3 + 0.7 * wellbeing_lat + rnorm(n, 0, 0.7)

# Combine into dataset
sem_data <- data.frame(
  extra1, extra2, extra3, extra4, extra5,
  neuro1, neuro2, neuro3, neuro4,
  support1, support2, support3, support4,
  stress1, stress2, stress3,
  wellbeing1, wellbeing2, wellbeing3, wellbeing4
)

# Round to simulate Likert scale responses (1-7)
sem_data <- round(sem_data)
sem_data[sem_data < 1] <- 1
sem_data[sem_data > 7] <- 7

# Display first few rows
head(sem_data, 10)
##    extra1 extra2 extra3 extra4 extra5 neuro1 neuro2 neuro3 neuro4 support1
## 1       2      2      1      2      2      3      2      4      4        2
## 2       4      4      4      3      4      2      2      2      3        3
## 3       5      4      3      4      4      3      2      2      3        4
## 4       2      1      2      2      3      4      4      4      4        1
## 5       2      2      3      3      1      2      2      3      1        5
## 6       3      3      4      2      3      2      3      3      1        2
## 7       3      4      3      3      4      3      1      3      2        3
## 8       3      3      3      3      3      3      3      4      3        2
## 9       5      4      3      4      3      2      3      2      2        4
## 10      3      3      4      4      3      3      4      3      3        3
##    support2 support3 support4 stress1 stress2 stress3 wellbeing1 wellbeing2
## 1         1        3        2       3       3       4          2          2
## 2         4        3        3       3       3       2          3          3
## 3         5        4        3       1       3       2          4          4
## 4         1        1        2       4       4       4          1          2
## 5         4        5        4       1       1       1          6          5
## 6         2        3        3       3       4       3          4          4
## 7         2        2        3       4       3       3          2          4
## 8         3        3        2       4       3       3          3          2
## 9         4        4        4       1       1       2          5          5
## 10        2        4        3       3       4       3          3          4
##    wellbeing3 wellbeing4
## 1           3          2
## 2           2          2
## 3           5          4
## 4           1          2
## 5           3          5
## 6           4          3
## 7           3          2
## 8           3          4
## 9           5          5
## 10          3          4
# Summary statistics
summary(sem_data)
##      extra1          extra2          extra3          extra4     
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000  
##  Median :3.000   Median :3.000   Median :3.000   Median :3.000  
##  Mean   :3.086   Mean   :3.086   Mean   :3.098   Mean   :3.092  
##  3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :7.000   Max.   :6.000   Max.   :6.000   Max.   :6.000  
##      extra5          neuro1          neuro2          neuro3     
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000  
##  Median :3.000   Median :3.000   Median :3.000   Median :3.000  
##  Mean   :3.126   Mean   :3.046   Mean   :3.058   Mean   :2.984  
##  3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :6.000   Max.   :6.000   Max.   :6.000   Max.   :6.000  
##      neuro4         support1        support2        support3    
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:3.000  
##  Median :3.000   Median :3.000   Median :3.000   Median :3.000  
##  Mean   :3.036   Mean   :3.086   Mean   :3.082   Mean   :3.058  
##  3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :6.000   Max.   :6.000   Max.   :6.000   Max.   :5.000  
##     support4        stress1         stress2         stress3     
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000  
##  Median :3.000   Median :3.000   Median :3.000   Median :3.000  
##  Mean   :3.042   Mean   :2.972   Mean   :3.006   Mean   :2.996  
##  3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :6.000   Max.   :7.000   Max.   :6.000   Max.   :6.000  
##    wellbeing1      wellbeing2      wellbeing3      wellbeing4   
##  Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :1.000  
##  1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000   1st Qu.:2.000  
##  Median :3.000   Median :3.000   Median :3.000   Median :3.000  
##  Mean   :3.078   Mean   :3.134   Mean   :3.078   Mean   :3.094  
##  3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :7.000   Max.   :7.000   Max.   :7.000   Max.   :7.000

2.14 Exploratory Data Analysis

# Correlation matrix
cor_matrix <- cor(sem_data)
corrplot(cor_matrix, method = "color", type = "lower", 
         order = "hclust", tl.cex = 0.8, tl.col = "black")

# Descriptive statistics by construct
describe(sem_data)
##            vars   n mean   sd median trimmed  mad min max range  skew kurtosis
## extra1        1 500 3.09 0.99      3    3.07 1.48   1   7     6  0.15     0.02
## extra2        2 500 3.09 1.02      3    3.09 1.48   1   6     5 -0.06    -0.43
## extra3        3 500 3.10 1.04      3    3.06 1.48   1   6     5  0.13    -0.39
## extra4        4 500 3.09 1.07      3    3.06 1.48   1   6     5  0.16    -0.34
## extra5        5 500 3.13 0.99      3    3.12 1.48   1   6     5 -0.03    -0.26
## neuro1        6 500 3.05 1.05      3    3.04 1.48   1   6     5  0.11    -0.19
## neuro2        7 500 3.06 1.06      3    3.06 1.48   1   6     5 -0.03    -0.46
## neuro3        8 500 2.98 0.97      3    2.98 1.48   1   6     5  0.06    -0.33
## neuro4        9 500 3.04 1.02      3    3.04 1.48   1   6     5  0.02    -0.27
## support1     10 500 3.09 1.06      3    3.08 1.48   1   6     5  0.06    -0.33
## support2     11 500 3.08 1.05      3    3.09 1.48   1   6     5 -0.06    -0.31
## support3     12 500 3.06 0.91      3    3.10 1.48   1   5     4 -0.29    -0.24
## support4     13 500 3.04 1.00      3    3.04 1.48   1   6     5 -0.02    -0.38
## stress1      14 500 2.97 1.13      3    2.97 1.48   1   7     6  0.15    -0.19
## stress2      15 500 3.01 1.10      3    3.01 1.48   1   6     5  0.09    -0.24
## stress3      16 500 3.00 1.11      3    3.01 1.48   1   6     5 -0.02    -0.48
## wellbeing1   17 500 3.08 1.41      3    3.03 1.48   1   7     6  0.32    -0.43
## wellbeing2   18 500 3.13 1.33      3    3.12 1.48   1   7     6  0.18    -0.56
## wellbeing3   19 500 3.08 1.32      3    3.06 1.48   1   7     6  0.21    -0.45
## wellbeing4   20 500 3.09 1.34      3    3.06 1.48   1   7     6  0.29    -0.52
##              se
## extra1     0.04
## extra2     0.05
## extra3     0.05
## extra4     0.05
## extra5     0.04
## neuro1     0.05
## neuro2     0.05
## neuro3     0.04
## neuro4     0.05
## support1   0.05
## support2   0.05
## support3   0.04
## support4   0.04
## stress1    0.05
## stress2    0.05
## stress3    0.05
## wellbeing1 0.06
## wellbeing2 0.06
## wellbeing3 0.06
## wellbeing4 0.06
# Check for multivariate normality
mvn_test <- mvn(sem_data)
print(mvn_test$multivariateNormality)
## NULL
# Reliability analysis for each construct
alpha_extra <- alpha(sem_data[, paste0("extra", 1:5)])
alpha_neuro <- alpha(sem_data[, paste0("neuro", 1:4)])
alpha_support <- alpha(sem_data[, paste0("support", 1:4)])
alpha_stress <- alpha(sem_data[, paste0("stress", 1:3)])
alpha_wellbeing <- alpha(sem_data[, paste0("wellbeing", 1:4)])

print(paste("Extraversion Cronbach's α:", round(alpha_extra$total$std.alpha, 3)))
## [1] "Extraversion Cronbach's α: 0.845"
print(paste("Neuroticism Cronbach's α:", round(alpha_neuro$total$std.alpha, 3)))
## [1] "Neuroticism Cronbach's α: 0.835"
print(paste("Social Support Cronbach's α:", round(alpha_support$total$std.alpha, 3)))
## [1] "Social Support Cronbach's α: 0.838"
print(paste("Stress Cronbach's α:", round(alpha_stress$total$std.alpha, 3)))
## [1] "Stress Cronbach's α: 0.863"
print(paste("Well-being Cronbach's α:", round(alpha_wellbeing$total$std.alpha, 3)))
## [1] "Well-being Cronbach's α: 0.927"

2.15 Step 1: Confirmatory Factor Analysis (CFA)

First, we’ll test the measurement model to ensure our latent variables are well-defined.

# Define the measurement model
measurement_model <- '
  # Latent variable definitions
  extraversion =~ extra1 + extra2 + extra3 + extra4 + extra5
  neuroticism =~ neuro1 + neuro2 + neuro3 + neuro4
  social_support =~ support1 + support2 + support3 + support4
  stress =~ stress1 + stress2 + stress3
  wellbeing =~ wellbeing1 + wellbeing2 + wellbeing3 + wellbeing4
'

# Fit the CFA model
cfa_fit <- cfa(measurement_model, data = sem_data, estimator = "MLR")

# Model summary
summary(cfa_fit, fit.measures = TRUE, standardized = TRUE)
## lavaan 0.6-19 ended normally after 44 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        50
## 
##   Number of observations                           500
## 
## Model Test User Model:
##                                               Standard      Scaled
##   Test Statistic                               172.554     172.351
##   Degrees of freedom                               160         160
##   P-value (Chi-square)                           0.235       0.239
##   Scaling correction factor                                  1.001
##     Yuan-Bentler correction (Mplus variant)                       
## 
## Model Test Baseline Model:
## 
##   Test statistic                              6166.734    6108.463
##   Degrees of freedom                               190         190
##   P-value                                        0.000       0.000
##   Scaling correction factor                                  1.010
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.998       0.998
##   Tucker-Lewis Index (TLI)                       0.998       0.998
##                                                                   
##   Robust Comparative Fit Index (CFI)                         0.998
##   Robust Tucker-Lewis Index (TLI)                            0.998
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)             -12043.684  -12043.684
##   Scaling correction factor                                  0.967
##       for the MLR correction                                      
##   Loglikelihood unrestricted model (H1)     -11957.407  -11957.407
##   Scaling correction factor                                  0.993
##       for the MLR correction                                      
##                                                                   
##   Akaike (AIC)                               24187.368   24187.368
##   Bayesian (BIC)                             24398.098   24398.098
##   Sample-size adjusted Bayesian (SABIC)      24239.395   24239.395
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.013       0.012
##   90 Percent confidence interval - lower         0.000       0.000
##   90 Percent confidence interval - upper         0.025       0.025
##   P-value H_0: RMSEA <= 0.050                    1.000       1.000
##   P-value H_0: RMSEA >= 0.080                    0.000       0.000
##                                                                   
##   Robust RMSEA                                               0.012
##   90 Percent confidence interval - lower                     0.000
##   90 Percent confidence interval - upper                     0.025
##   P-value H_0: Robust RMSEA <= 0.050                         1.000
##   P-value H_0: Robust RMSEA >= 0.080                         0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.026       0.026
## 
## Parameter Estimates:
## 
##   Standard errors                             Sandwich
##   Information bread                           Observed
##   Observed information based on                Hessian
## 
## Latent Variables:
##                     Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   extraversion =~                                                        
##     extra1             1.000                               0.763    0.774
##     extra2             1.119    0.060   18.674    0.000    0.853    0.841
##     extra3             0.882    0.064   13.805    0.000    0.673    0.647
##     extra4             1.082    0.066   16.314    0.000    0.825    0.771
##     extra5             0.753    0.056   13.370    0.000    0.574    0.583
##   neuroticism =~                                                         
##     neuro1             1.000                               0.889    0.849
##     neuro2             0.869    0.050   17.322    0.000    0.772    0.726
##     neuro3             0.713    0.046   15.356    0.000    0.634    0.656
##     neuro4             0.876    0.048   18.114    0.000    0.779    0.761
##   social_support =~                                                      
##     support1           1.000                               0.893    0.846
##     support2           0.924    0.050   18.657    0.000    0.826    0.789
##     support3           0.743    0.041   18.254    0.000    0.664    0.730
##     support4           0.717    0.050   14.470    0.000    0.640    0.639
##   stress =~                                                              
##     stress1            1.000                               0.966    0.853
##     stress2            0.921    0.041   22.250    0.000    0.890    0.812
##     stress3            0.923    0.045   20.546    0.000    0.892    0.806
##   wellbeing =~                                                           
##     wellbeing1         1.000                               1.298    0.922
##     wellbeing2         0.899    0.025   36.657    0.000    1.167    0.880
##     wellbeing3         0.882    0.028   31.319    0.000    1.144    0.869
##     wellbeing4         0.846    0.031   27.616    0.000    1.098    0.818
## 
## Covariances:
##                     Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   extraversion ~~                                                        
##     neuroticism       -0.059    0.036   -1.657    0.098   -0.087   -0.087
##     social_support     0.275    0.040    6.857    0.000    0.404    0.404
##     stress            -0.319    0.043   -7.398    0.000   -0.433   -0.433
##     wellbeing          0.504    0.053    9.565    0.000    0.510    0.510
##   neuroticism ~~                                                         
##     social_support    -0.295    0.042   -7.039    0.000   -0.372   -0.372
##     stress             0.519    0.051   10.242    0.000    0.604    0.604
##     wellbeing         -0.768    0.066  -11.681    0.000   -0.666   -0.666
##   social_support ~~                                                      
##     stress            -0.515    0.052   -9.962    0.000   -0.597   -0.597
##     wellbeing          0.860    0.068   12.678    0.000    0.742    0.742
##   stress ~~                                                              
##     wellbeing         -1.091    0.076  -14.347    0.000   -0.870   -0.870
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .extra1            0.389    0.035   11.225    0.000    0.389    0.401
##    .extra2            0.302    0.030    9.985    0.000    0.302    0.293
##    .extra3            0.628    0.048   13.190    0.000    0.628    0.581
##    .extra4            0.463    0.037   12.576    0.000    0.463    0.405
##    .extra5            0.640    0.043   14.846    0.000    0.640    0.660
##    .neuro1            0.306    0.031    9.841    0.000    0.306    0.279
##    .neuro2            0.534    0.042   12.775    0.000    0.534    0.473
##    .neuro3            0.530    0.040   13.260    0.000    0.530    0.569
##    .neuro4            0.441    0.033   13.262    0.000    0.441    0.421
##    .support1          0.317    0.031   10.258    0.000    0.317    0.284
##    .support2          0.414    0.038   10.984    0.000    0.414    0.378
##    .support3          0.386    0.030   12.992    0.000    0.386    0.467
##    .support4          0.595    0.038   15.452    0.000    0.595    0.592
##    .stress1           0.350    0.032   11.070    0.000    0.350    0.273
##    .stress2           0.410    0.035   11.690    0.000    0.410    0.341
##    .stress3           0.429    0.036   12.027    0.000    0.429    0.351
##    .wellbeing1        0.296    0.032    9.159    0.000    0.296    0.149
##    .wellbeing2        0.398    0.032   12.626    0.000    0.398    0.226
##    .wellbeing3        0.423    0.029   14.539    0.000    0.423    0.244
##    .wellbeing4        0.595    0.043   13.969    0.000    0.595    0.330
##     extraversion      0.582    0.057   10.180    0.000    1.000    1.000
##     neuroticism       0.790    0.069   11.496    0.000    1.000    1.000
##     social_support    0.798    0.066   12.104    0.000    1.000    1.000
##     stress            0.933    0.077   12.048    0.000    1.000    1.000
##     wellbeing         1.684    0.112   14.988    0.000    1.000    1.000
# Model fit indices
fit_indices <- fitMeasures(cfa_fit, c("chisq", "df", "pvalue", "cfi", "tli", 
                                      "rmsea", "rmsea.ci.lower", "rmsea.ci.upper", 
                                      "srmr", "aic", "bic"))
print(fit_indices)
##          chisq             df         pvalue            cfi            tli 
##        172.554        160.000          0.235          0.998          0.998 
##          rmsea rmsea.ci.lower rmsea.ci.upper           srmr            aic 
##          0.013          0.000          0.025          0.026      24187.368 
##            bic 
##      24398.098

2.16 Step 2: Full Structural Equation Model

Now we’ll test the full structural model with hypothesized relationships.

# Define the full SEM model
structural_model <- '
  # Measurement model
  extraversion =~ extra1 + extra2 + extra3 + extra4 + extra5
  neuroticism =~ neuro1 + neuro2 + neuro3 + neuro4
  social_support =~ support1 + support2 + support3 + support4
  stress =~ stress1 + stress2 + stress3
  wellbeing =~ wellbeing1 + wellbeing2 + wellbeing3 + wellbeing4
  
  # Structural model
  social_support ~ extraversion + neuroticism
  stress ~ extraversion + neuroticism + social_support
  wellbeing ~ extraversion + neuroticism + social_support + stress
'

# Fit the SEM model
sem_fit <- sem(structural_model, data = sem_data, estimator = "MLR")

# Model summary
summary(sem_fit, fit.measures = TRUE, standardized = TRUE, rsquare = TRUE)
## lavaan 0.6-19 ended normally after 33 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        50
## 
##   Number of observations                           500
## 
## Model Test User Model:
##                                               Standard      Scaled
##   Test Statistic                               172.554     172.351
##   Degrees of freedom                               160         160
##   P-value (Chi-square)                           0.235       0.239
##   Scaling correction factor                                  1.001
##     Yuan-Bentler correction (Mplus variant)                       
## 
## Model Test Baseline Model:
## 
##   Test statistic                              6166.734    6108.463
##   Degrees of freedom                               190         190
##   P-value                                        0.000       0.000
##   Scaling correction factor                                  1.010
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.998       0.998
##   Tucker-Lewis Index (TLI)                       0.998       0.998
##                                                                   
##   Robust Comparative Fit Index (CFI)                         0.998
##   Robust Tucker-Lewis Index (TLI)                            0.998
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)             -12043.684  -12043.684
##   Scaling correction factor                                  0.967
##       for the MLR correction                                      
##   Loglikelihood unrestricted model (H1)     -11957.407  -11957.407
##   Scaling correction factor                                  0.993
##       for the MLR correction                                      
##                                                                   
##   Akaike (AIC)                               24187.368   24187.368
##   Bayesian (BIC)                             24398.098   24398.098
##   Sample-size adjusted Bayesian (SABIC)      24239.395   24239.395
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.013       0.012
##   90 Percent confidence interval - lower         0.000       0.000
##   90 Percent confidence interval - upper         0.025       0.025
##   P-value H_0: RMSEA <= 0.050                    1.000       1.000
##   P-value H_0: RMSEA >= 0.080                    0.000       0.000
##                                                                   
##   Robust RMSEA                                               0.012
##   90 Percent confidence interval - lower                     0.000
##   90 Percent confidence interval - upper                     0.025
##   P-value H_0: Robust RMSEA <= 0.050                         1.000
##   P-value H_0: Robust RMSEA >= 0.080                         0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.026       0.026
## 
## Parameter Estimates:
## 
##   Standard errors                             Sandwich
##   Information bread                           Observed
##   Observed information based on                Hessian
## 
## Latent Variables:
##                     Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   extraversion =~                                                        
##     extra1             1.000                               0.763    0.774
##     extra2             1.119    0.060   18.674    0.000    0.853    0.841
##     extra3             0.882    0.064   13.805    0.000    0.673    0.647
##     extra4             1.082    0.066   16.314    0.000    0.825    0.771
##     extra5             0.753    0.056   13.370    0.000    0.574    0.583
##   neuroticism =~                                                         
##     neuro1             1.000                               0.889    0.849
##     neuro2             0.869    0.050   17.322    0.000    0.772    0.726
##     neuro3             0.713    0.046   15.356    0.000    0.634    0.656
##     neuro4             0.876    0.048   18.114    0.000    0.779    0.761
##   social_support =~                                                      
##     support1           1.000                               0.893    0.846
##     support2           0.924    0.050   18.657    0.000    0.826    0.789
##     support3           0.743    0.041   18.254    0.000    0.664    0.730
##     support4           0.717    0.050   14.470    0.000    0.640    0.639
##   stress =~                                                              
##     stress1            1.000                               0.966    0.853
##     stress2            0.921    0.041   22.250    0.000    0.890    0.812
##     stress3            0.923    0.045   20.546    0.000    0.892    0.806
##   wellbeing =~                                                           
##     wellbeing1         1.000                               1.298    0.922
##     wellbeing2         0.899    0.025   36.657    0.000    1.167    0.880
##     wellbeing3         0.882    0.028   31.319    0.000    1.144    0.869
##     wellbeing4         0.846    0.031   27.616    0.000    1.098    0.818
## 
## Regressions:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   social_support ~                                                      
##     extraversion      0.439    0.057    7.728    0.000    0.375    0.375
##     neuroticism      -0.340    0.048   -7.167    0.000   -0.339   -0.339
##   stress ~                                                              
##     extraversion     -0.334    0.058   -5.789    0.000   -0.263   -0.263
##     neuroticism       0.503    0.051    9.883    0.000    0.463    0.463
##     social_support   -0.344    0.054   -6.382    0.000   -0.318   -0.318
##   wellbeing ~                                                           
##     extraversion      0.281    0.053    5.344    0.000    0.165    0.165
##     neuroticism      -0.381    0.057   -6.746    0.000   -0.261   -0.261
##     social_support    0.442    0.045    9.724    0.000    0.304    0.304
##     stress           -0.617    0.069   -8.986    0.000   -0.460   -0.460
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   extraversion ~~                                                       
##     neuroticism      -0.059    0.036   -1.657    0.098   -0.087   -0.087
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .extra1            0.389    0.035   11.225    0.000    0.389    0.401
##    .extra2            0.302    0.030    9.985    0.000    0.302    0.293
##    .extra3            0.628    0.048   13.190    0.000    0.628    0.581
##    .extra4            0.463    0.037   12.576    0.000    0.463    0.405
##    .extra5            0.640    0.043   14.846    0.000    0.640    0.660
##    .neuro1            0.306    0.031    9.841    0.000    0.306    0.279
##    .neuro2            0.534    0.042   12.775    0.000    0.534    0.473
##    .neuro3            0.530    0.040   13.260    0.000    0.530    0.569
##    .neuro4            0.441    0.033   13.262    0.000    0.441    0.421
##    .support1          0.317    0.031   10.258    0.000    0.317    0.284
##    .support2          0.414    0.038   10.984    0.000    0.414    0.378
##    .support3          0.386    0.030   12.992    0.000    0.386    0.467
##    .support4          0.595    0.038   15.452    0.000    0.595    0.592
##    .stress1           0.350    0.032   11.070    0.000    0.350    0.273
##    .stress2           0.410    0.035   11.690    0.000    0.410    0.341
##    .stress3           0.429    0.036   12.027    0.000    0.429    0.351
##    .wellbeing1        0.296    0.032    9.159    0.000    0.296    0.149
##    .wellbeing2        0.398    0.032   12.626    0.000    0.398    0.226
##    .wellbeing3        0.423    0.029   14.539    0.000    0.423    0.244
##    .wellbeing4        0.595    0.043   13.969    0.000    0.595    0.330
##     extraversion      0.582    0.057   10.180    0.000    1.000    1.000
##     neuroticism       0.790    0.069   11.496    0.000    1.000    1.000
##    .social_support    0.576    0.050   11.534    0.000    0.723    0.723
##    .stress            0.389    0.041    9.378    0.000    0.417    0.417
##    .wellbeing         0.196    0.027    7.200    0.000    0.116    0.116
## 
## R-Square:
##                    Estimate
##     extra1            0.599
##     extra2            0.707
##     extra3            0.419
##     extra4            0.595
##     extra5            0.340
##     neuro1            0.721
##     neuro2            0.527
##     neuro3            0.431
##     neuro4            0.579
##     support1          0.716
##     support2          0.622
##     support3          0.533
##     support4          0.408
##     stress1           0.727
##     stress2           0.659
##     stress3           0.649
##     wellbeing1        0.851
##     wellbeing2        0.774
##     wellbeing3        0.756
##     wellbeing4        0.670
##     social_support    0.277
##     stress            0.583
##     wellbeing         0.884
# Model report using report package
report(sem_fit)
## [1] "Support for lavaan not fully implemented yet :("

2.17 Model Diagnostics and Fit Assessment

# Comprehensive fit measures
fit_measures_sem <- fitMeasures(sem_fit, c("chisq", "df", "pvalue", "cfi", "tli", 
                                           "rmsea", "rmsea.ci.lower", "rmsea.ci.upper", 
                                           "srmr", "aic", "bic"))

# Create a fit measures table
fit_table <- data.frame(
  Measure = names(fit_measures_sem),
  Value = round(fit_measures_sem, 3),
  Interpretation = c(
    "Model Chi-square", "Degrees of freedom", "P-value", 
    "Comparative Fit Index (>0.95 excellent)", 
    "Tucker-Lewis Index (>0.95 excellent)", 
    "RMSEA (<0.06 excellent)", "RMSEA CI Lower", "RMSEA CI Upper",
    "SRMR (<0.08 good)", "AIC (lower is better)", "BIC (lower is better)"
  )
)

print(fit_table)
##                       Measure     Value                          Interpretation
## chisq                   chisq   172.554                        Model Chi-square
## df                         df   160.000                      Degrees of freedom
## pvalue                 pvalue     0.235                                 P-value
## cfi                       cfi     0.998 Comparative Fit Index (>0.95 excellent)
## tli                       tli     0.998    Tucker-Lewis Index (>0.95 excellent)
## rmsea                   rmsea     0.013                 RMSEA (<0.06 excellent)
## rmsea.ci.lower rmsea.ci.lower     0.000                          RMSEA CI Lower
## rmsea.ci.upper rmsea.ci.upper     0.025                          RMSEA CI Upper
## srmr                     srmr     0.026                       SRMR (<0.08 good)
## aic                       aic 24187.368                   AIC (lower is better)
## bic                       bic 24398.098                   BIC (lower is better)
# Modification indices
mod_indices <- modificationIndices(sem_fit, sort = TRUE, maximum.number = 10)
print("Top 10 Modification Indices:")
## [1] "Top 10 Modification Indices:"
print(mod_indices)
##              lhs op      rhs    mi    epc sepc.lv sepc.all sepc.nox
## 173       extra3 ~~   extra4 9.912  0.096   0.096    0.178    0.178
## 139       extra1 ~~   extra5 7.295  0.074   0.074    0.148    0.148
## 180       extra3 ~~ support2 7.163 -0.072  -0.072   -0.141   -0.141
## 60  extraversion =~ support1 6.262  0.134   0.102    0.097    0.097
## 113       stress =~ support2 5.597  0.124   0.120    0.115    0.115
## 182       extra3 ~~ support4 5.111  0.068   0.068    0.111    0.111
## 74   neuroticism =~   extra4 4.923 -0.093  -0.083   -0.077   -0.077
## 137       extra1 ~~   extra3 4.633 -0.060  -0.060   -0.122   -0.122
## 129    wellbeing =~ support1 4.555  0.106   0.138    0.130    0.130
## 56  extraversion =~   neuro1 4.471 -0.099  -0.076   -0.072   -0.072
# Residuals
residuals_sem <- residuals(sem_fit, type = "cor")
print("Correlation residuals (should be small):")
## [1] "Correlation residuals (should be small):"
if(is.matrix(residuals_sem$cor) && is.numeric(residuals_sem$cor)) {
  print(round(residuals_sem$cor[1:5, 1:5], 3))
} else {
  print("Residual correlation matrix:")
  print(residuals_sem$cor[1:5, 1:5])
}
## [1] "Residual correlation matrix:"
## NULL

2.18 Visualization of the SEM Model

# Plot the SEM model with standardized coefficients
semPaths(sem_fit, what = "std", whatLabels = "std", style = "lisrel", 
         layout = "tree2", rotation = 2, curve = 1, 
         sizeLat = 8, sizeMan = 4, edge.label.cex = 0.8)
title("Structural Equation Model: Personality, Support, Stress, and Well-being")

# Alternative visualization with path coefficients
semPaths(sem_fit, what = "par", whatLabels = "par", style = "lisrel", 
         layout = "tree2", rotation = 2, curve = 1,
         sizeLat = 8, sizeMan = 4, edge.label.cex = 0.8)
title("SEM Model with Parameter Estimates")

2.19 Effect Decomposition and Mediation Analysis

# Calculate direct, indirect, and total effects
effects <- parameterEstimates(sem_fit, standardized = TRUE)

# Extract structural parameters
structural_params <- effects[effects$op == "~", ]
print("Structural Parameters (Direct Effects):")
## [1] "Structural Parameters (Direct Effects):"
print(structural_params[, c("lhs", "rhs", "est", "se", "pvalue", "std.all")])
##               lhs            rhs    est    se pvalue std.all
## 21 social_support   extraversion  0.439 0.057      0   0.375
## 22 social_support    neuroticism -0.340 0.048      0  -0.339
## 23         stress   extraversion -0.334 0.058      0  -0.263
## 24         stress    neuroticism  0.503 0.051      0   0.463
## 25         stress social_support -0.344 0.054      0  -0.318
## 26      wellbeing   extraversion  0.281 0.053      0   0.165
## 27      wellbeing    neuroticism -0.381 0.057      0  -0.261
## 28      wellbeing social_support  0.442 0.045      0   0.304
## 29      wellbeing         stress -0.617 0.069      0  -0.460
# Calculate indirect effects using lavaan
indirect_model <- '
  # Measurement model
  extraversion =~ extra1 + extra2 + extra3 + extra4 + extra5
  neuroticism =~ neuro1 + neuro2 + neuro3 + neuro4
  social_support =~ support1 + support2 + support3 + support4
  stress =~ stress1 + stress2 + stress3
  wellbeing =~ wellbeing1 + wellbeing2 + wellbeing3 + wellbeing4
  
  # Structural model
  social_support ~ a1*extraversion + a2*neuroticism
  stress ~ b1*extraversion + b2*neuroticism + b3*social_support
  wellbeing ~ c1*extraversion + c2*neuroticism + c3*social_support + c4*stress
  
  # Indirect effects
  ext_well_via_support := a1 * c3
  ext_well_via_stress := b1 * c4
  ext_well_via_support_stress := a1 * b3 * c4
  
  neuro_well_via_support := a2 * c3
  neuro_well_via_stress := b2 * c4
  neuro_well_via_support_stress := a2 * b3 * c4
  
  support_well_via_stress := b3 * c4
  
  # Total indirect effects
  ext_well_total_indirect := ext_well_via_support + ext_well_via_stress + ext_well_via_support_stress
  neuro_well_total_indirect := neuro_well_via_support + neuro_well_via_stress + neuro_well_via_support_stress
'

# Fit model with indirect effects
indirect_fit <- sem(indirect_model, data = sem_data, estimator = "MLR")

# Extract indirect effects
indirect_effects <- parameterEstimates(indirect_fit, standardized = TRUE)
indirect_results <- indirect_effects[grep(":=", indirect_effects$op), ]
print("Indirect Effects:")
## [1] "Indirect Effects:"
print(indirect_results[, c("lhs", "est", "se", "pvalue", "std.all")])
##                              lhs    est    se pvalue std.all
## 56          ext_well_via_support  0.194 0.032      0   0.114
## 57           ext_well_via_stress  0.206 0.043      0   0.121
## 58   ext_well_via_support_stress  0.093 0.020      0   0.055
## 59        neuro_well_via_support -0.150 0.025      0  -0.103
## 60         neuro_well_via_stress -0.310 0.044      0  -0.213
## 61 neuro_well_via_support_stress -0.072 0.016      0  -0.050
## 62       support_well_via_stress  0.212 0.039      0   0.146
## 63       ext_well_total_indirect  0.493 0.056      0   0.290
## 64     neuro_well_total_indirect -0.533 0.055      0  -0.365

2.20 Bootstrap Confidence Intervals for Indirect Effects

# Bootstrap confidence intervals for more robust inference
# Note: Bootstrap requires ML estimator, not MLR
bootstrap_fit <- sem(indirect_model, data = sem_data, estimator = "ML", 
                     se = "bootstrap", bootstrap = 500)

# Bootstrap results for indirect effects
bootstrap_results <- parameterEstimates(bootstrap_fit, standardized = TRUE, 
                                        boot.ci.type = "perc")
bootstrap_indirect <- bootstrap_results[grep(":=", bootstrap_results$op), ]
print("Bootstrap Confidence Intervals for Indirect Effects:")
## [1] "Bootstrap Confidence Intervals for Indirect Effects:"
print(bootstrap_indirect[, c("lhs", "est", "se", "ci.lower", "ci.upper", "pvalue")])
##                              lhs    est    se ci.lower ci.upper pvalue
## 56          ext_well_via_support  0.194 0.031    0.132    0.259      0
## 57           ext_well_via_stress  0.206 0.044    0.129    0.297      0
## 58   ext_well_via_support_stress  0.093 0.020    0.057    0.135      0
## 59        neuro_well_via_support -0.150 0.025   -0.201   -0.103      0
## 60         neuro_well_via_stress -0.310 0.043   -0.402   -0.235      0
## 61 neuro_well_via_support_stress -0.072 0.017   -0.113   -0.044      0
## 62       support_well_via_stress  0.212 0.040    0.133    0.295      0
## 63       ext_well_total_indirect  0.493 0.056    0.392    0.622      0
## 64     neuro_well_total_indirect -0.533 0.056   -0.648   -0.430      0

2.21 Model Comparison: Alternative Models

# Test alternative model without direct paths from personality to well-being
alternative_model <- '
  # Measurement model
  extraversion =~ extra1 + extra2 + extra3 + extra4 + extra5
  neuroticism =~ neuro1 + neuro2 + neuro3 + neuro4
  social_support =~ support1 + support2 + support3 + support4
  stress =~ stress1 + stress2 + stress3
  wellbeing =~ wellbeing1 + wellbeing2 + wellbeing3 + wellbeing4
  
  # Structural model (mediation only)
  social_support ~ extraversion + neuroticism
  stress ~ extraversion + neuroticism + social_support
  wellbeing ~ social_support + stress
'

# Fit alternative model
alt_fit <- sem(alternative_model, data = sem_data, estimator = "MLR")

# Compare models
anova(alt_fit, sem_fit)
## 
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
## 
## lavaan->lavTestLRT():  
##    lavaan NOTE: The "Chisq" column contains standard test statistics, not the 
##    robust test that should be reported per model. A robust difference test is 
##    a function of two standard (not robust) statistics.
##          Df   AIC   BIC  Chisq Chisq diff Df diff Pr(>Chisq)    
## sem_fit 160 24187 24398 172.55                                  
## alt_fit 162 24240 24442 228.80     65.354       2  6.436e-15 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Compare fit indices
comparison <- data.frame(
  Model = c("Full Model", "Mediation Only"),
  AIC = c(AIC(sem_fit), AIC(alt_fit)),
  BIC = c(BIC(sem_fit), BIC(alt_fit)),
  CFI = c(fitMeasures(sem_fit, "cfi"), fitMeasures(alt_fit, "cfi")),
  RMSEA = c(fitMeasures(sem_fit, "rmsea"), fitMeasures(alt_fit, "rmsea"))
)
print(comparison)
##            Model      AIC      BIC       CFI      RMSEA
## 1     Full Model 24187.37 24398.10 0.9978995 0.01252712
## 2 Mediation Only 24239.61 24441.91 0.9888241 0.02871649

2.22 Power Analysis for SEM

# Power analysis using semTools
# Note: SSpower requires a population model specification

# Alternative approach: Use simulation-based power analysis
# First, extract the population parameters from our fitted model
pop_params <- parameterEstimates(sem_fit, standardized = FALSE)

# Define population model using the fitted parameters
pop_model <- '
  # Measurement model
  extraversion =~ 0.8*extra1 + 0.9*extra2 + 0.7*extra3 + 0.8*extra4 + 0.6*extra5
  neuroticism =~ 0.9*neuro1 + 0.8*neuro2 + 0.7*neuro3 + 0.8*neuro4
  social_support =~ 0.9*support1 + 0.8*support2 + 0.8*support3 + 0.7*support4
  stress =~ 0.9*stress1 + 0.8*stress2 + 0.8*stress3
  wellbeing =~ 0.9*wellbeing1 + 0.8*wellbeing2 + 0.8*wellbeing3 + 0.7*wellbeing4
  
  # Structural model (using approximate population values)
  social_support ~ 0.35*extraversion + -0.25*neuroticism
  stress ~ -0.25*extraversion + 0.45*neuroticism + -0.35*social_support
  wellbeing ~ 0.25*extraversion + -0.35*neuroticism + 0.45*social_support + -0.55*stress
'

# Simple power analysis using effect sizes
# Calculate power for key structural paths using approximate formulas
print("Power Analysis for Key Structural Paths:")
## [1] "Power Analysis for Key Structural Paths:"
print("========================================")
## [1] "========================================"
# Function to calculate approximate power for regression coefficients
calc_power <- function(effect_size, n, alpha = 0.05) {
  # Convert effect size to t-statistic
  t_stat <- effect_size * sqrt(n - 2) / sqrt(1 - effect_size^2)
  # Calculate power using t-distribution
  t_crit <- qt(1 - alpha/2, n - 2)
  power <- 1 - pt(t_crit, n - 2, ncp = abs(t_stat)) + pt(-t_crit, n - 2, ncp = abs(t_stat))
  return(power)
}

# Calculate power for standardized effects
n_current <- nrow(sem_data)

# Get standardized coefficients (effect sizes)
std_estimates <- standardizedSolution(sem_fit)
structural_std <- std_estimates[std_estimates$op == "~", ]
key_effects <- structural_std[c(1:7), c("lhs", "rhs", "est.std")]

for(i in seq_len(nrow(key_effects))) {
  effect <- key_effects[i, "est.std"]
  if(!is.na(effect) && abs(effect) > 0.001) {
    power <- calc_power(effect, n_current)
    cat(sprintf("%s -> %s: Effect = %.3f, Power = %.3f\n", 
                key_effects[i, "rhs"], key_effects[i, "lhs"], effect, power))
  }
}
## extraversion -> social_support: Effect = 0.375, Power = 1.000
## neuroticism -> social_support: Effect = -0.339, Power = 1.000
## extraversion -> stress: Effect = -0.263, Power = 1.000
## neuroticism -> stress: Effect = 0.463, Power = 1.000
## social_support -> stress: Effect = -0.318, Power = 1.000
## extraversion -> wellbeing: Effect = 0.165, Power = 0.962
## neuroticism -> wellbeing: Effect = -0.261, Power = 1.000
# Sample size calculation for 80% power
print("\nSample Size Requirements for 80% Power:")
## [1] "\nSample Size Requirements for 80% Power:"
print("======================================")
## [1] "======================================"
target_power <- 0.80
alpha <- 0.05

for(i in seq_len(nrow(key_effects))) {
  effect <- key_effects[i, "est.std"]
  if(!is.na(effect) && abs(effect) > 0.001) {
    # Approximate sample size for desired power
    # Using Cohen's formula for correlation/regression
    z_alpha <- qnorm(1 - alpha/2)
    z_beta <- qnorm(target_power)
    n_needed <- ((z_alpha + z_beta)^2 / effect^2) + 3
    
    cat(sprintf("%s -> %s: Effect = %.3f, N needed = %.0f\n", 
                key_effects[i, "rhs"], key_effects[i, "lhs"], effect, n_needed))
  }
}
## extraversion -> social_support: Effect = 0.375, N needed = 59
## neuroticism -> social_support: Effect = -0.339, N needed = 71
## extraversion -> stress: Effect = -0.263, N needed = 116
## neuroticism -> stress: Effect = 0.463, N needed = 40
## social_support -> stress: Effect = -0.318, N needed = 81
## extraversion -> wellbeing: Effect = 0.165, N needed = 291
## neuroticism -> wellbeing: Effect = -0.261, N needed = 118
# Model-based power assessment
print("\nModel Fit and Power Assessment:")
## [1] "\nModel Fit and Power Assessment:"
print("===============================")
## [1] "==============================="
cat(sprintf("Current sample size: %d\n", n_current))
## Current sample size: 500
cat(sprintf("Model degrees of freedom: %d\n", fitMeasures(sem_fit, "df")))
## Model degrees of freedom: 160
cat(sprintf("Chi-square p-value: %.3f\n", fitMeasures(sem_fit, "pvalue")))
## Chi-square p-value: 0.235
cat(sprintf("RMSEA: %.3f (90%% CI: %.3f - %.3f)\n", 
           fitMeasures(sem_fit, "rmsea"), 
           fitMeasures(sem_fit, "rmsea.ci.lower"), 
           fitMeasures(sem_fit, "rmsea.ci.upper")))
## RMSEA: 0.013 (90% CI: 0.000 - 0.025)
# Rule of thumb: 10-20 observations per parameter
n_params <- length(coef(sem_fit))
cat(sprintf("Number of free parameters: %d\n", n_params))
## Number of free parameters: 50
cat(sprintf("Observations per parameter: %.1f\n", n_current/n_params))
## Observations per parameter: 10.0
cat(sprintf("Recommended minimum sample size (10:1 rule): %d\n", n_params * 10))
## Recommended minimum sample size (10:1 rule): 500
cat(sprintf("Recommended optimal sample size (20:1 rule): %d\n", n_params * 20))
## Recommended optimal sample size (20:1 rule): 1000

2.23 Effect Sizes and Practical Significance

# R-squared for endogenous variables
r_squared <- inspect(sem_fit, "r2")
print("R-squared values for endogenous variables:")
## [1] "R-squared values for endogenous variables:"
print(r_squared)
##         extra1         extra2         extra3         extra4         extra5 
##          0.599          0.707          0.419          0.595          0.340 
##         neuro1         neuro2         neuro3         neuro4       support1 
##          0.721          0.527          0.431          0.579          0.716 
##       support2       support3       support4        stress1        stress2 
##          0.622          0.533          0.408          0.727          0.659 
##        stress3     wellbeing1     wellbeing2     wellbeing3     wellbeing4 
##          0.649          0.851          0.774          0.756          0.670 
## social_support         stress      wellbeing 
##          0.277          0.583          0.884
# Standardized coefficients (effect sizes) - already calculated in power analysis
print("Standardized coefficients (effect sizes):")
## [1] "Standardized coefficients (effect sizes):"
print(structural_std[, c("lhs", "rhs", "est.std", "pvalue")])
##               lhs            rhs est.std pvalue
## 21 social_support   extraversion   0.375      0
## 22 social_support    neuroticism  -0.339      0
## 23         stress   extraversion  -0.263      0
## 24         stress    neuroticism   0.463      0
## 25         stress social_support  -0.318      0
## 26      wellbeing   extraversion   0.165      0
## 27      wellbeing    neuroticism  -0.261      0
## 28      wellbeing social_support   0.304      0
## 29      wellbeing         stress  -0.460      0
# Cohen's guidelines for effect sizes:
# Small: 0.10, Medium: 0.30, Large: 0.50

effect_size_interpretation <- function(coef) {
  abs_coef <- abs(coef)
  if (abs_coef < 0.10) return("Negligible")
  else if (abs_coef < 0.30) return("Small")
  else if (abs_coef < 0.50) return("Medium")
  else return("Large")
}

structural_std$effect_size <- sapply(structural_std$est.std, effect_size_interpretation)
print("Effect size interpretations:")
## [1] "Effect size interpretations:"
print(structural_std[, c("lhs", "rhs", "est.std", "effect_size")])
##               lhs            rhs est.std effect_size
## 21 social_support   extraversion   0.375      Medium
## 22 social_support    neuroticism  -0.339      Medium
## 23         stress   extraversion  -0.263       Small
## 24         stress    neuroticism   0.463      Medium
## 25         stress social_support  -0.318      Medium
## 26      wellbeing   extraversion   0.165       Small
## 27      wellbeing    neuroticism  -0.261       Small
## 28      wellbeing social_support   0.304      Medium
## 29      wellbeing         stress  -0.460      Medium

2.24 Interpretation of Results

2.24.1 Measurement Model Quality

# Factor loadings
loadings <- parameterEstimates(sem_fit, standardized = TRUE)
factor_loadings <- loadings[loadings$op == "=~", ]
print("Standardized Factor Loadings:")
## [1] "Standardized Factor Loadings:"
print(factor_loadings[, c("lhs", "rhs", "std.all")])
##               lhs        rhs std.all
## 1    extraversion     extra1   0.774
## 2    extraversion     extra2   0.841
## 3    extraversion     extra3   0.647
## 4    extraversion     extra4   0.771
## 5    extraversion     extra5   0.583
## 6     neuroticism     neuro1   0.849
## 7     neuroticism     neuro2   0.726
## 8     neuroticism     neuro3   0.656
## 9     neuroticism     neuro4   0.761
## 10 social_support   support1   0.846
## 11 social_support   support2   0.789
## 12 social_support   support3   0.730
## 13 social_support   support4   0.639
## 14         stress    stress1   0.853
## 15         stress    stress2   0.812
## 16         stress    stress3   0.806
## 17      wellbeing wellbeing1   0.922
## 18      wellbeing wellbeing2   0.880
## 19      wellbeing wellbeing3   0.869
## 20      wellbeing wellbeing4   0.818
# Composite reliability
reliability <- semTools::reliability(sem_fit)
print("Composite Reliability:")
## [1] "Composite Reliability:"
print(reliability)
##        extraversion neuroticism social_support    stress wellbeing
## alpha     0.8448760   0.8349058      0.8376044 0.8632208 0.9265730
## omega     0.8488118   0.8391137      0.8422359 0.8638518 0.9282472
## omega2    0.8488118   0.8391137      0.8422359 0.8638518 0.9282472
## omega3    0.8484606   0.8396557      0.8405916 0.8638235 0.9292674
## avevar    0.5337054   0.5693380      0.5764996 0.6792991 0.7645405

All factor loadings are above 0.6, indicating good convergent validity. Composite reliability values are all above 0.7, indicating good internal consistency.

2.24.2 Structural Model Results

The structural model reveals several important findings:

  1. Social Support Predictors:
    • Extraversion positively predicts social support (β = 0.375)
    • Neuroticism negatively predicts social support (β = -0.339)
  2. Stress Predictors:
    • Social support reduces stress (β = -0.318)
    • Neuroticism increases stress (β = 0.463)
  3. Well-being Predictors:
    • Social support enhances well-being (β = 0.304)
    • Stress reduces well-being (β = -0.46)

2.24.3 Mediation Effects

The indirect effects analysis reveals significant mediation pathways, suggesting that personality traits influence well-being both directly and indirectly through social support and stress mechanisms.

2.25 Conclusions and Recommendations

2.25.1 Key Findings:

  1. Model Fit: The SEM model shows acceptable fit (CFI = 0.998, RMSEA = 0.013)

  2. Mediation Mechanisms: Social support and stress serve as important mediators between personality and well-being

  3. Effect Sizes: Most effects are in the small to medium range, which is typical for personality research

2.25.2 Methodological Considerations:

  1. Sample Size: Current sample (n=500) provides adequate power for most effects
  2. Model Complexity: The model balances parsimony with theoretical completeness
  3. Measurement Quality: All constructs show good psychometric properties

2.25.3 Practical Implications:

  1. Intervention Targets: Social support enhancement and stress reduction are viable intervention points
  2. Individual Differences: Personality traits provide important context for understanding well-being
  3. Mediation Pathways: Understanding indirect effects can inform more effective interventions

This SEM analysis demonstrates how complex theoretical models can be tested empirically, providing insights into the mechanisms underlying psychological well-being in behavioral science research.