Load in packages - lavaan is our “go-to” package for CFA and SEM!

library(tidyverse)  #all things data management
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.3     ✓ purrr   0.3.4
✓ tibble  3.0.6     ✓ dplyr   1.0.4
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
── Conflicts ─────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(haven) # importing data from other packages
library(lavaan) # all things SEM
This is lavaan 0.6-7
lavaan is BETA software! Please report any bugs.
library(semPlot) # cool path diagrams for SEM models

Return to SPSS Anxiety Questionnaire, from Field, Ch. 17

saq <- read_dta("saq.dta")
glimpse(saq)
Rows: 2,571
Columns: 23
$ Q01 <dbl+lbl> 2, 1, 2, 3, 2, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 3, 1, 2, 2, 2, 1, 3, 2, 1,…
$ Q02 <dbl+lbl> 1, 1, 3, 1, 1, 1, 3, 2, 3, 4, 1, 1, 1, 2, 2, 1, 2, 2, 3, 1, 2, 2, 1, 1,…
$ Q03 <dbl+lbl> 4, 4, 2, 1, 3, 3, 3, 3, 1, 4, 5, 3, 3, 1, 3, 2, 5, 3, 4, 1, 3, 2, 4, 2,…
$ Q04 <dbl+lbl> 2, 3, 2, 4, 2, 2, 2, 2, 4, 3, 2, 3, 4, 2, 4, 2, 2, 3, 2, 2, 2, 4, 4, 2,…
$ Q05 <dbl+lbl> 2, 2, 4, 3, 2, 4, 2, 2, 5, 2, 2, 4, 3, 2, 2, 2, 1, 3, 3, 3, 4, 4, 2, 2,…
$ Q06 <dbl+lbl> 2, 2, 1, 3, 3, 4, 2, 2, 3, 1, 1, 3, 2, 2, 2, 2, 1, 4, 1, 4, 1, 5, 2, 1,…
$ Q07 <dbl+lbl> 3, 2, 2, 4, 3, 4, 2, 2, 5, 2, 2, 3, 3, 3, 3, 2, 1, 3, 1, 4, 2, 5, 4, 1,…
$ Q08 <dbl+lbl> 1, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 1, 3, 2, 2, 2, 1, 2, 1, 1, 1, 2, 3, 2,…
$ Q09 <dbl+lbl> 1, 5, 2, 2, 4, 4, 3, 4, 3, 3, 5, 3, 2, 2, 2, 2, 4, 5, 5, 5, 1, 3, 4, 2,…
$ Q10 <dbl+lbl> 2, 2, 2, 4, 2, 3, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3, 1, 2, 2, 1, 1, 3, 2, 1,…
$ Q11 <dbl+lbl> 1, 2, 3, 2, 2, 2, 2, 2, 5, 2, 1, 2, 3, 2, 2, 2, 1, 3, 1, 2, 1, 3, 3, 2,…
$ Q12 <dbl+lbl> 2, 3, 3, 2, 3, 4, 2, 3, 5, 3, 3, 3, 4, 4, 3, 3, 2, 3, 3, 5, 4, 3, 3, 4,…
$ Q13 <dbl+lbl> 2, 1, 2, 2, 3, 3, 2, 2, 5, 2, 1, 2, 4, 2, 2, 2, 1, 3, 1, 2, 1, 4, 2, 3,…
$ Q14 <dbl+lbl> 2, 3, 4, 3, 2, 3, 2, 2, 5, 1, 2, 2, 4, 4, 3, 3, 1, 3, 2, 5, 1, 3, 4, 2,…
$ Q15 <dbl+lbl> 2, 4, 2, 3, 2, 5, 2, 3, 5, 2, 1, 3, 4, 4, 3, 2, 1, 4, 2, 5, 1, 2, 4, 4,…
$ Q16 <dbl+lbl> 3, 3, 3, 3, 2, 2, 2, 2, 5, 3, 2, 3, 4, 4, 4, 3, 2, 3, 3, 5, 2, 3, 4, 2,…
$ Q17 <dbl+lbl> 1, 2, 2, 2, 2, 3, 2, 2, 5, 2, 2, 2, 3, 2, 2, 2, 2, 2, 1, 2, 1, 4, 3, 2,…
$ Q18 <dbl+lbl> 2, 2, 3, 4, 3, 5, 2, 2, 5, 2, 2, 2, 3, 4, 3, 3, 1, 2, 1, 5, 1, 4, 3, 1,…
$ Q19 <dbl+lbl> 3, 3, 1, 2, 3, 1, 3, 4, 2, 3, 5, 3, 2, 1, 3, 2, 4, 2, 4, 1, 2, 2, 2, 1,…
$ Q20 <dbl+lbl> 2, 4, 4, 4, 4, 5, 2, 3, 5, 3, 3, 4, 4, 5, 4, 3, 2, 3, 2, 5, 4, 5, 3, 5,…
$ Q21 <dbl+lbl> 2, 4, 3, 4, 2, 3, 2, 2, 5, 2, 2, 3, 4, 5, 4, 2, 1, 3, 2, 5, 3, 5, 4, 2,…
$ Q22 <dbl+lbl> 2, 4, 2, 4, 4, 1, 4, 4, 3, 4, 5, 4, 3, 3, 4, 3, 4, 3, 4, 5, 2, 2, 3, 4,…
$ Q23 <dbl+lbl> 5, 2, 2, 3, 4, 4, 4, 4, 3, 4, 5, 4, 4, 1, 4, 4, 4, 4, 4, 5, 5, 4, 5, 5,…

Reverse Code Item 3, Like Last Time

saq.clean <-saq %>%
  mutate(.,
         Q03R = 6- Q03) %>%
  select(.,
         -Q03)

Double check that the reverse coding worked:

glimpse(saq.clean$Q03R)
 num [1:2571] 2 2 4 5 3 3 3 3 5 2 ...

Specifying, running, and interpreting a CFA for the “Computers” subscale

Note the single quotation mark at the start and end of the CFA model below. This is required for lavaan to correctly interpret and estimate your CFA model.

computers_cfa1  <- 'computers  =~ Q06 + Q07 + Q10 + Q12 + Q13 + Q14 + Q15 + Q18'
computers_cfa1_model <- cfa(computers_cfa1, data=saq.clean) 
summary(computers_cfa1_model)
lavaan 0.6-7 ended normally after 20 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         16
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                               253.847
  Degrees of freedom                                20
  P-value (Chi-square)                           0.000

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)
  computers =~                                        
    Q06               1.000                           
    Q07               1.017    0.035   29.399    0.000
    Q10               0.503    0.026   19.289    0.000
    Q12               0.772    0.028   27.243    0.000
    Q13               0.892    0.030   29.853    0.000
    Q14               0.878    0.031   28.235    0.000
    Q15               0.718    0.031   23.486    0.000
    Q18               1.061    0.034   31.504    0.000

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
   .Q06               0.706    0.023   31.044    0.000
   .Q07               0.643    0.021   30.381    0.000
   .Q10               0.629    0.018   34.501    0.000
   .Q12               0.511    0.016   31.897    0.000
   .Q13               0.460    0.015   29.963    0.000
   .Q14               0.572    0.018   31.280    0.000
   .Q15               0.734    0.022   33.485    0.000
   .Q18               0.487    0.017   27.960    0.000
    computers         0.552    0.031   17.693    0.000

By default, lavaan gives the latent variables its scale by fixing the loading of the first item to 1 (also known as the marker method). Other options are possible (for example, fixing the variance of the latent variable to 1). In order to free a parameter, put NA* in front of the parameter to be freed, to fix a parameter to 1, put 1* in front of the parameter to be fixed. For example, the syntax NA*q03 would free the loading of the first item because by default marker method fixes it to one, and f ~~ 1*f would fix the variance of the latent factor to one.

Visualize it with the semPaths function from the semPlot package

semPlot::semPaths(computers_cfa1_model, whatLabels = "est")

Same thing, but with standardized estimates…just re-run the summary function

summary(computers_cfa1_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 20 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         16
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                               253.847
  Degrees of freedom                                20
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                              6309.869
  Degrees of freedom                                28
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.963
  Tucker-Lewis Index (TLI)                       0.948

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -26155.590
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                               52343.180
  Bayesian (BIC)                             52436.812
  Sample-size adjusted Bayesian (BIC)        52385.976

Root Mean Square Error of Approximation:

  RMSEA                                          0.067
  90 Percent confidence interval - lower         0.060
  90 Percent confidence interval - upper         0.075
  P-value RMSEA <= 0.05                          0.000

Standardized Root Mean Square Residual:

  SRMR                                           0.031

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  computers =~                                                          
    Q06               1.000                               0.743    0.662
    Q07               1.017    0.035   29.399    0.000    0.756    0.686
    Q10               0.503    0.026   19.289    0.000    0.374    0.426
    Q12               0.772    0.028   27.243    0.000    0.574    0.626
    Q13               0.892    0.030   29.853    0.000    0.663    0.699
    Q14               0.878    0.031   28.235    0.000    0.652    0.653
    Q15               0.718    0.031   23.486    0.000    0.533    0.528
    Q18               1.061    0.034   31.504    0.000    0.789    0.749

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q06               0.706    0.023   31.044    0.000    0.706    0.561
   .Q07               0.643    0.021   30.381    0.000    0.643    0.530
   .Q10               0.629    0.018   34.501    0.000    0.629    0.818
   .Q12               0.511    0.016   31.897    0.000    0.511    0.608
   .Q13               0.460    0.015   29.963    0.000    0.460    0.512
   .Q14               0.572    0.018   31.280    0.000    0.572    0.574
   .Q15               0.734    0.022   33.485    0.000    0.734    0.721
   .Q18               0.487    0.017   27.960    0.000    0.487    0.439
    computers         0.552    0.031   17.693    0.000    1.000    1.000

Visualize the standardized estimates

semPlot::semPaths(computers_cfa1_model, whatLabels = "stand")

Should we add another item? We can test this!

computers_cfa2  <- ' computers  =~ Q06 + Q07 + Q10 + Q12 + Q13 + Q14 + Q15 + Q18 + Q23' 
computers_cfa2_model <- cfa(computers_cfa2, data=saq.clean) 
summary(computers_cfa2_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 20 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         18
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                               258.748
  Degrees of freedom                                27
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                              6334.827
  Degrees of freedom                                36
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.963
  Tucker-Lewis Index (TLI)                       0.951

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -29903.202
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                               59842.405
  Bayesian (BIC)                             59947.741
  Sample-size adjusted Bayesian (BIC)        59890.550

Root Mean Square Error of Approximation:

  RMSEA                                          0.058
  90 Percent confidence interval - lower         0.051
  90 Percent confidence interval - upper         0.064
  P-value RMSEA <= 0.05                          0.022

Standardized Root Mean Square Residual:

  SRMR                                           0.028

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  computers =~                                                          
    Q06               1.000                               0.743    0.662
    Q07               1.017    0.035   29.418    0.000    0.756    0.686
    Q10               0.503    0.026   19.310    0.000    0.374    0.426
    Q12               0.771    0.028   27.242    0.000    0.573    0.626
    Q13               0.891    0.030   29.855    0.000    0.662    0.699
    Q14               0.877    0.031   28.236    0.000    0.652    0.653
    Q15               0.718    0.031   23.502    0.000    0.533    0.529
    Q18               1.061    0.034   31.528    0.000    0.789    0.749
    Q23              -0.134    0.030   -4.463    0.000   -0.099   -0.095

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q06               0.706    0.023   31.047    0.000    0.706    0.561
   .Q07               0.643    0.021   30.385    0.000    0.643    0.530
   .Q10               0.629    0.018   34.500    0.000    0.629    0.818
   .Q12               0.511    0.016   31.910    0.000    0.511    0.609
   .Q13               0.460    0.015   29.984    0.000    0.460    0.512
   .Q14               0.572    0.018   31.295    0.000    0.572    0.574
   .Q15               0.734    0.022   33.486    0.000    0.734    0.721
   .Q18               0.487    0.017   27.962    0.000    0.487    0.439
   .Q23               1.079    0.030   35.798    0.000    1.079    0.991
    computers         0.552    0.031   17.702    0.000    1.000    1.000

Now, try a CFA for the “friends” subscale (with standardized estimates)

friends_cfa  <- ' friends  =~ Q02 + Q09 + Q19 + Q22 + Q23' 
friends_cfa_model <- cfa(friends_cfa, data=saq.clean) 
summary(friends_cfa_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 35 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         10
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                                44.608
  Degrees of freedom                                 5
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                               978.759
  Degrees of freedom                                10
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.959
  Tucker-Lewis Index (TLI)                       0.918

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -18417.898
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                               36855.796
  Bayesian (BIC)                             36914.317
  Sample-size adjusted Bayesian (BIC)        36882.544

Root Mean Square Error of Approximation:

  RMSEA                                          0.056
  90 Percent confidence interval - lower         0.041
  90 Percent confidence interval - upper         0.071
  P-value RMSEA <= 0.05                          0.247

Standardized Root Mean Square Residual:

  SRMR                                           0.027

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  friends =~                                                            
    Q02               1.000                               0.413    0.486
    Q09               1.749    0.124   14.094    0.000    0.723    0.572
    Q19               1.161    0.090   12.873    0.000    0.480    0.436
    Q22               1.263    0.092   13.700    0.000    0.522    0.501
    Q23               0.793    0.076   10.483    0.000    0.328    0.314

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q02               0.553    0.020   28.043    0.000    0.553    0.764
   .Q09               1.072    0.046   23.496    0.000    1.072    0.672
   .Q19               0.982    0.033   29.979    0.000    0.982    0.810
   .Q22               0.810    0.030   27.319    0.000    0.810    0.749
   .Q23               0.982    0.030   33.159    0.000    0.982    0.901
    friends           0.171    0.018    9.530    0.000    1.000    1.000

Visualize the friends CFA model

semPlot::semPaths(friends_cfa_model, whatLabels = "stand")

What about models with more than one factor? Combining computers, friends, and math

cfa_3factor <- ' computers  =~ Q06 + Q07 + Q10 + Q12 + Q13 + Q14 + Q15 + Q18
              friends =~ Q02 + Q09 + Q19 + Q22 + Q23
              math   =~ Q08 + Q11 + Q17'
cfa_3factor_model <- cfa(cfa_3factor, data=saq.clean)
summary(cfa_3factor_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 46 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         35
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                               843.108
  Degrees of freedom                               101
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                             11775.153
  Degrees of freedom                               120
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.936
  Tucker-Lewis Index (TLI)                       0.924

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -52550.485
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                              105170.970
  Bayesian (BIC)                            105375.792
  Sample-size adjusted Bayesian (BIC)       105264.587

Root Mean Square Error of Approximation:

  RMSEA                                          0.053
  90 Percent confidence interval - lower         0.050
  90 Percent confidence interval - upper         0.057
  P-value RMSEA <= 0.05                          0.043

Standardized Root Mean Square Residual:

  SRMR                                           0.041

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  computers =~                                                          
    Q06               1.000                               0.724    0.646
    Q07               1.043    0.036   29.112    0.000    0.755    0.685
    Q10               0.520    0.027   19.410    0.000    0.376    0.429
    Q12               0.798    0.029   27.218    0.000    0.578    0.631
    Q13               0.925    0.031   29.815    0.000    0.670    0.706
    Q14               0.900    0.032   27.991    0.000    0.652    0.653
    Q15               0.766    0.032   24.221    0.000    0.555    0.550
    Q18               1.073    0.035   30.836    0.000    0.777    0.738
  friends =~                                                            
    Q02               1.000                               0.401    0.471
    Q09               1.663    0.113   14.762    0.000    0.666    0.527
    Q19               1.385    0.096   14.486    0.000    0.555    0.504
    Q22               1.313    0.091   14.509    0.000    0.526    0.505
    Q23               0.758    0.074   10.292    0.000    0.304    0.291
  math =~                                                               
    Q08               1.000                               0.662    0.758
    Q11               1.073    0.029   36.459    0.000    0.710    0.806
    Q17               1.018    0.029   35.256    0.000    0.673    0.762

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  computers ~~                                                          
    friends          -0.149    0.011  -12.970    0.000   -0.513   -0.513
    math              0.309    0.016   19.667    0.000    0.646    0.646
  friends ~~                                                            
    math             -0.077    0.009   -9.020    0.000   -0.290   -0.290

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q06               0.734    0.023   31.931    0.000    0.734    0.583
   .Q07               0.644    0.021   30.982    0.000    0.644    0.530
   .Q10               0.627    0.018   34.622    0.000    0.627    0.816
   .Q12               0.505    0.016   32.227    0.000    0.505    0.602
   .Q13               0.450    0.015   30.369    0.000    0.450    0.501
   .Q14               0.572    0.018   31.777    0.000    0.572    0.574
   .Q15               0.711    0.021   33.484    0.000    0.711    0.698
   .Q18               0.505    0.017   29.255    0.000    0.505    0.455
   .Q02               0.564    0.019   29.854    0.000    0.564    0.778
   .Q09               1.151    0.042   27.695    0.000    1.151    0.722
   .Q19               0.905    0.032   28.670    0.000    0.905    0.746
   .Q22               0.806    0.028   28.598    0.000    0.806    0.745
   .Q23               0.997    0.029   33.929    0.000    0.997    0.915
   .Q08               0.323    0.013   25.763    0.000    0.323    0.425
   .Q11               0.272    0.012   22.036    0.000    0.272    0.350
   .Q17               0.327    0.013   25.510    0.000    0.327    0.419
    computers         0.525    0.030   17.272    0.000    1.000    1.000
    friends           0.160    0.016    9.763    0.000    1.000    1.000
    math              0.438    0.021   20.734    0.000    1.000    1.000

Visualize the three-factor CFA model

semPlot::semPaths(cfa_3factor_model, whatLabels = "stand")

What about combining the whole scale all together? Do all 23 items fit together?

cfa_all_1factor  <- 'anx  =~ Q01 + Q02 + Q03R + Q04 + Q05 + Q06 + Q07 + Q08 + Q09 + Q10 + Q11 + Q12 + Q13 + Q14 + Q15 + Q16 + Q17 + Q18 + Q19 + Q21 + Q22 + Q23'
cfa_all_1factor_model <- cfa(cfa_all_1factor, data=saq.clean) 
summary(cfa_all_1factor_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 34 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         44
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                              4075.552
  Degrees of freedom                               209
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                             18596.355
  Degrees of freedom                               231
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.789
  Tucker-Lewis Index (TLI)                       0.767

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -71849.166
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                              143786.333
  Bayesian (BIC)                            144043.823
  Sample-size adjusted Bayesian (BIC)       143904.023

Root Mean Square Error of Approximation:

  RMSEA                                          0.085
  90 Percent confidence interval - lower         0.083
  90 Percent confidence interval - upper         0.087
  P-value RMSEA <= 0.05                          0.000

Standardized Root Mean Square Residual:

  SRMR                                           0.062

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  anx =~                                                                
    Q01               1.000                               0.463    0.559
    Q02              -0.481    0.040  -12.152    0.000   -0.222   -0.261
    Q03R              1.361    0.057   23.768    0.000    0.630    0.586
    Q04               1.248    0.051   24.410    0.000    0.578    0.609
    Q05               1.093    0.050   21.942    0.000    0.506    0.525
    Q06               1.327    0.059   22.645    0.000    0.614    0.548
    Q07               1.590    0.061   25.952    0.000    0.736    0.668
    Q08               0.989    0.045   21.941    0.000    0.458    0.525
    Q09              -0.663    0.058  -11.356    0.000   -0.307   -0.243
    Q10               0.779    0.043   18.097    0.000    0.360    0.411
    Q11               1.182    0.048   24.754    0.000    0.547    0.622
    Q12               1.277    0.050   25.366    0.000    0.591    0.645
    Q13               1.347    0.052   25.686    0.000    0.623    0.657
    Q14               1.373    0.055   25.143    0.000    0.635    0.636
    Q15               1.226    0.053   23.083    0.000    0.568    0.562
    Q16               1.288    0.050   25.521    0.000    0.596    0.651
    Q17               1.187    0.048   24.751    0.000    0.549    0.622
    Q18               1.567    0.059   26.463    0.000    0.725    0.689
    Q19              -0.911    0.053  -17.056    0.000   -0.422   -0.383
    Q21               1.318    0.053   24.702    0.000    0.610    0.620
    Q22              -0.597    0.048  -12.327    0.000   -0.276   -0.265
    Q23              -0.276    0.047   -5.877    0.000   -0.128   -0.123

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q01               0.471    0.014   34.269    0.000    0.471    0.687
   .Q02               0.675    0.019   35.599    0.000    0.675    0.932
   .Q03R              0.758    0.022   34.030    0.000    0.758    0.657
   .Q04               0.566    0.017   33.798    0.000    0.566    0.629
   .Q05               0.674    0.020   34.529    0.000    0.674    0.725
   .Q06               0.881    0.026   34.360    0.000    0.881    0.700
   .Q07               0.673    0.020   33.044    0.000    0.673    0.554
   .Q08               0.552    0.016   34.530    0.000    0.552    0.725
   .Q09               1.501    0.042   35.635    0.000    1.501    0.941
   .Q10               0.639    0.018   35.146    0.000    0.639    0.831
   .Q11               0.475    0.014   33.657    0.000    0.475    0.614
   .Q12               0.490    0.015   33.370    0.000    0.490    0.584
   .Q13               0.511    0.015   33.199    0.000    0.511    0.568
   .Q14               0.593    0.018   33.480    0.000    0.593    0.595
   .Q15               0.696    0.020   34.241    0.000    0.696    0.684
   .Q16               0.483    0.015   33.290    0.000    0.483    0.576
   .Q17               0.479    0.014   33.658    0.000    0.479    0.614
   .Q18               0.583    0.018   32.704    0.000    0.583    0.526
   .Q19               1.035    0.029   35.255    0.000    1.035    0.853
   .Q21               0.597    0.018   33.679    0.000    0.597    0.616
   .Q22               1.006    0.028   35.590    0.000    1.006    0.930
   .Q23               1.073    0.030   35.801    0.000    1.073    0.985
    anx               0.214    0.014   14.817    0.000    1.000    1.000

Visualize the one-factor CFA model

semPlot::semPaths(cfa_all_1factor_model, whatLabels = "stand")

Opening Pandora’s Box!?! - using modification indices

Modification indices tell you how model fit would change if you added new parameters to the model. This is not the same as adding new items - rather, it usually means covarying items, or including items from one latent factor that also fit with a second factor. Since your CFA model should in most cases not be exploratory (i.e. you should know what parameters you want to include in the model before you begin), modification indices can be dangerous. Hence the Pandora’s Box reference! If you make the changes they suggest, you often run the risk of over-fitting this specific model to your data and reducing the generalizability of your results in other samples.

modificationIndices(computers_cfa1_model, sort.=TRUE, minimum.value = 3)

I recommend using modification indices mostly as another description of the places where your model is not fitting well, a bit like examining the residuals.

Same Model- with error covariance for q12 and q06

computers_cfa3  <- ' computers  =~ Q06 + Q07 + Q10 + Q12 + Q13 + Q14 + Q15 + Q18
Q06 ~~ Q12' 
computers_cfa3_model <- cfa(computers_cfa3, data=saq.clean) 
summary(computers_cfa3_model, fit.measures=TRUE, standardized=TRUE)
lavaan 0.6-7 ended normally after 20 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of free parameters                         17
                                                      
  Number of observations                          2571
                                                      
Model Test User Model:
                                                      
  Test statistic                               136.693
  Degrees of freedom                                19
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                              6309.869
  Degrees of freedom                                28
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.981
  Tucker-Lewis Index (TLI)                       0.972

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -26097.013
  Loglikelihood unrestricted model (H1)             NA
                                                      
  Akaike (AIC)                               52228.025
  Bayesian (BIC)                             52327.510
  Sample-size adjusted Bayesian (BIC)        52273.496

Root Mean Square Error of Approximation:

  RMSEA                                          0.049
  90 Percent confidence interval - lower         0.042
  90 Percent confidence interval - upper         0.057
  P-value RMSEA <= 0.05                          0.560

Standardized Root Mean Square Residual:

  SRMR                                           0.024

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Expected
  Information saturated (h1) model          Structured

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
  computers =~                                                          
    Q06               1.000                               0.776    0.691
    Q07               0.970    0.032   30.255    0.000    0.753    0.683
    Q10               0.479    0.025   19.533    0.000    0.371    0.423
    Q12               0.777    0.030   26.272    0.000    0.603    0.658
    Q13               0.850    0.028   30.736    0.000    0.660    0.696
    Q14               0.828    0.029   28.741    0.000    0.643    0.644
    Q15               0.681    0.029   23.840    0.000    0.528    0.523
    Q18               1.008    0.031   32.431    0.000    0.782    0.742

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
 .Q06 ~~                                                                
   .Q12              -0.146    0.013  -11.031    0.000   -0.146   -0.262

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
   .Q06               0.657    0.022   29.485    0.000    0.657    0.522
   .Q07               0.648    0.021   31.004    0.000    0.648    0.534
   .Q10               0.631    0.018   34.663    0.000    0.631    0.821
   .Q12               0.476    0.016   30.402    0.000    0.476    0.567
   .Q13               0.464    0.015   30.638    0.000    0.464    0.516
   .Q14               0.584    0.018   31.947    0.000    0.584    0.586
   .Q15               0.739    0.022   33.788    0.000    0.739    0.726
   .Q18               0.498    0.017   28.962    0.000    0.498    0.449
    computers         0.602    0.033   18.508    0.000    1.000    1.000

Visualize the model with the extra covariance between q12 and q06

semPlot::semPaths(computers_cfa3_model, whatLabels = "stand")

LS0tCnRpdGxlOiAiTW9kdWxlIDEwOiBJbnRybyB0byBDb25maXJtYXRvcnkgRmFjdG9yIEFuYWx5c2lzIChDRkEpIgphdXRob3I6ICJEci4gQnJvZGEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KIyBMb2FkIGluIHBhY2thZ2VzIC0gYGxhdmFhbmAgaXMgb3VyICJnby10byIgcGFja2FnZSBmb3IgQ0ZBIGFuZCBTRU0hCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICNhbGwgdGhpbmdzIGRhdGEgbWFuYWdlbWVudApsaWJyYXJ5KGhhdmVuKSAjIGltcG9ydGluZyBkYXRhIGZyb20gb3RoZXIgcGFja2FnZXMKbGlicmFyeShsYXZhYW4pICMgYWxsIHRoaW5ncyBTRU0KbGlicmFyeShzZW1QbG90KSAjIGNvb2wgcGF0aCBkaWFncmFtcyBmb3IgU0VNIG1vZGVscwpgYGAKCiMgUmV0dXJuIHRvIFNQU1MgQW54aWV0eSBRdWVzdGlvbm5haXJlLCBmcm9tIEZpZWxkLCBDaC4gMTcKYGBge3J9CnNhcSA8LSByZWFkX2R0YSgic2FxLmR0YSIpCmdsaW1wc2Uoc2FxKQpgYGAKCiMjIFJldmVyc2UgQ29kZSBJdGVtIDMsIExpa2UgTGFzdCBUaW1lCmBgYHtyfQpzYXEuY2xlYW4gPC1zYXEgJT4lCiAgbXV0YXRlKC4sCiAgICAgICAgIFEwM1IgPSA2LSBRMDMpICU+JQogIHNlbGVjdCguLAogICAgICAgICAtUTAzKQpgYGAKCiMjIERvdWJsZSBjaGVjayB0aGF0IHRoZSByZXZlcnNlIGNvZGluZyB3b3JrZWQ6CmBgYHtyfQpnbGltcHNlKHNhcS5jbGVhbiRRMDNSKQpgYGAKCiMgU3BlY2lmeWluZywgcnVubmluZywgYW5kIGludGVycHJldGluZyBhIENGQSBmb3IgdGhlICJDb21wdXRlcnMiIHN1YnNjYWxlCk5vdGUgdGhlIHNpbmdsZSBxdW90YXRpb24gbWFyayBhdCB0aGUgc3RhcnQgYW5kIGVuZCBvZiB0aGUgQ0ZBIG1vZGVsIGJlbG93LiBUaGlzIGlzIHJlcXVpcmVkIGZvciBgbGF2YWFuYCB0byBjb3JyZWN0bHkgaW50ZXJwcmV0IGFuZCBlc3RpbWF0ZSB5b3VyIENGQSBtb2RlbC4KYGBge3J9CmNvbXB1dGVyc19jZmExICA8LSAnY29tcHV0ZXJzICA9fiBRMDYgKyBRMDcgKyBRMTAgKyBRMTIgKyBRMTMgKyBRMTQgKyBRMTUgKyBRMTgnCmNvbXB1dGVyc19jZmExX21vZGVsIDwtIGNmYShjb21wdXRlcnNfY2ZhMSwgZGF0YT1zYXEuY2xlYW4pIApzdW1tYXJ5KGNvbXB1dGVyc19jZmExX21vZGVsKQpgYGAKCkJ5IGRlZmF1bHQsIGBsYXZhYW5gIGdpdmVzIHRoZSBsYXRlbnQgdmFyaWFibGVzIGl0cyBzY2FsZSBieSBmaXhpbmcgdGhlIGxvYWRpbmcgb2YgdGhlIGZpcnN0IGl0ZW0gdG8gMSAoYWxzbyBrbm93biBhcyB0aGUgbWFya2VyIG1ldGhvZCkuIE90aGVyIG9wdGlvbnMgYXJlIHBvc3NpYmxlIChmb3IgZXhhbXBsZSwgZml4aW5nIHRoZSB2YXJpYW5jZSBvZiB0aGUgbGF0ZW50IHZhcmlhYmxlIHRvIDEpLiBJbiBvcmRlciB0byBmcmVlIGEgcGFyYW1ldGVyLCBwdXQgYE5BKmAgaW4gZnJvbnQgb2YgdGhlIHBhcmFtZXRlciB0byBiZSBmcmVlZCwgdG8gZml4IGEgcGFyYW1ldGVyIHRvIDEsIHB1dCBgMSpgIGluIGZyb250IG9mIHRoZSBwYXJhbWV0ZXIgdG8gYmUgZml4ZWQuIEZvciBleGFtcGxlLCB0aGUgc3ludGF4IGBOQSpxMDNgIHdvdWxkIGZyZWUgdGhlIGxvYWRpbmcgb2YgdGhlIGZpcnN0IGl0ZW0gYmVjYXVzZSBieSBkZWZhdWx0IG1hcmtlciBtZXRob2QgZml4ZXMgaXQgdG8gb25lLCBhbmQgYGYgfn4gMSpmYCB3b3VsZCBmaXggdGhlIHZhcmlhbmNlIG9mIHRoZSBsYXRlbnQgZmFjdG9yIHRvIG9uZS4KCiMjIFZpc3VhbGl6ZSBpdCB3aXRoIHRoZSBgc2VtUGF0aHNgIGZ1bmN0aW9uIGZyb20gdGhlIGBzZW1QbG90YCBwYWNrYWdlCmBgYHtyfQpzZW1QbG90OjpzZW1QYXRocyhjb21wdXRlcnNfY2ZhMV9tb2RlbCwgd2hhdExhYmVscyA9ICJlc3QiKQpgYGAKCiMjIFNhbWUgdGhpbmcsIGJ1dCB3aXRoIHN0YW5kYXJkaXplZCBlc3RpbWF0ZXMuLi5qdXN0IHJlLXJ1biB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uCmBgYHtyfQpzdW1tYXJ5KGNvbXB1dGVyc19jZmExX21vZGVsLCBmaXQubWVhc3VyZXM9VFJVRSwgc3RhbmRhcmRpemVkPVRSVUUpCmBgYAoKIyMgVmlzdWFsaXplIHRoZSBzdGFuZGFyZGl6ZWQgZXN0aW1hdGVzCmBgYHtyfQpzZW1QbG90OjpzZW1QYXRocyhjb21wdXRlcnNfY2ZhMV9tb2RlbCwgd2hhdExhYmVscyA9ICJzdGFuZCIpCmBgYAoKIyMgU2hvdWxkIHdlIGFkZCBhbm90aGVyIGl0ZW0/IFdlIGNhbiB0ZXN0IHRoaXMhCmBgYHtyfQpjb21wdXRlcnNfY2ZhMiAgPC0gJyBjb21wdXRlcnMgID1+IFEwNiArIFEwNyArIFExMCArIFExMiArIFExMyArIFExNCArIFExNSArIFExOCArIFEyMycgCmNvbXB1dGVyc19jZmEyX21vZGVsIDwtIGNmYShjb21wdXRlcnNfY2ZhMiwgZGF0YT1zYXEuY2xlYW4pIApzdW1tYXJ5KGNvbXB1dGVyc19jZmEyX21vZGVsLCBmaXQubWVhc3VyZXM9VFJVRSwgc3RhbmRhcmRpemVkPVRSVUUpCmBgYAoKIyBOb3csIHRyeSBhIENGQSBmb3IgdGhlICJmcmllbmRzIiBzdWJzY2FsZSAod2l0aCBzdGFuZGFyZGl6ZWQgZXN0aW1hdGVzKQpgYGB7cn0KZnJpZW5kc19jZmEgIDwtICcgZnJpZW5kcyAgPX4gUTAyICsgUTA5ICsgUTE5ICsgUTIyICsgUTIzJyAKZnJpZW5kc19jZmFfbW9kZWwgPC0gY2ZhKGZyaWVuZHNfY2ZhLCBkYXRhPXNhcS5jbGVhbikgCnN1bW1hcnkoZnJpZW5kc19jZmFfbW9kZWwsIGZpdC5tZWFzdXJlcz1UUlVFLCBzdGFuZGFyZGl6ZWQ9VFJVRSkKYGBgCgojIyBWaXN1YWxpemUgdGhlIGZyaWVuZHMgQ0ZBIG1vZGVsCmBgYHtyfQpzZW1QbG90OjpzZW1QYXRocyhmcmllbmRzX2NmYV9tb2RlbCwgd2hhdExhYmVscyA9ICJzdGFuZCIpCmBgYAoKIyBXaGF0IGFib3V0IG1vZGVscyB3aXRoIG1vcmUgdGhhbiBvbmUgZmFjdG9yPyBDb21iaW5pbmcgY29tcHV0ZXJzLCBmcmllbmRzLCBhbmQgbWF0aApgYGB7cn0KY2ZhXzNmYWN0b3IgPC0gJyBjb21wdXRlcnMgID1+IFEwNiArIFEwNyArIFExMCArIFExMiArIFExMyArIFExNCArIFExNSArIFExOAogICAgICAgICAgICAgIGZyaWVuZHMgPX4gUTAyICsgUTA5ICsgUTE5ICsgUTIyICsgUTIzCiAgICAgICAgICAgICAgbWF0aCAgID1+IFEwOCArIFExMSArIFExNycKY2ZhXzNmYWN0b3JfbW9kZWwgPC0gY2ZhKGNmYV8zZmFjdG9yLCBkYXRhPXNhcS5jbGVhbikKc3VtbWFyeShjZmFfM2ZhY3Rvcl9tb2RlbCwgZml0Lm1lYXN1cmVzPVRSVUUsIHN0YW5kYXJkaXplZD1UUlVFKQpgYGAKCiMjIFZpc3VhbGl6ZSB0aGUgdGhyZWUtZmFjdG9yIENGQSBtb2RlbApgYGB7cn0Kc2VtUGxvdDo6c2VtUGF0aHMoY2ZhXzNmYWN0b3JfbW9kZWwsIHdoYXRMYWJlbHMgPSAic3RhbmQiKQpgYGAKCiMgV2hhdCBhYm91dCBjb21iaW5pbmcgdGhlIHdob2xlIHNjYWxlIGFsbCB0b2dldGhlcj8gRG8gYWxsIDIzIGl0ZW1zIGZpdCB0b2dldGhlcj8KYGBge3J9CmNmYV9hbGxfMWZhY3RvciAgPC0gJ2FueCAgPX4gUTAxICsgUTAyICsgUTAzUiArIFEwNCArIFEwNSArIFEwNiArIFEwNyArIFEwOCArIFEwOSArIFExMCArIFExMSArIFExMiArIFExMyArIFExNCArIFExNSArIFExNiArIFExNyArIFExOCArIFExOSArIFEyMSArIFEyMiArIFEyMycKY2ZhX2FsbF8xZmFjdG9yX21vZGVsIDwtIGNmYShjZmFfYWxsXzFmYWN0b3IsIGRhdGE9c2FxLmNsZWFuKSAKc3VtbWFyeShjZmFfYWxsXzFmYWN0b3JfbW9kZWwsIGZpdC5tZWFzdXJlcz1UUlVFLCBzdGFuZGFyZGl6ZWQ9VFJVRSkKYGBgCgojIyBWaXN1YWxpemUgdGhlIG9uZS1mYWN0b3IgQ0ZBIG1vZGVsCmBgYHtyfQpzZW1QbG90OjpzZW1QYXRocyhjZmFfYWxsXzFmYWN0b3JfbW9kZWwsIHdoYXRMYWJlbHMgPSAic3RhbmQiKQpgYGAKCiMgT3BlbmluZyBQYW5kb3JhJ3MgQm94IT8hIC0gdXNpbmcgbW9kaWZpY2F0aW9uIGluZGljZXMKTW9kaWZpY2F0aW9uIGluZGljZXMgdGVsbCB5b3UgaG93IG1vZGVsIGZpdCB3b3VsZCBjaGFuZ2UgaWYgeW91IGFkZGVkIG5ldyBwYXJhbWV0ZXJzIHRvIHRoZSBtb2RlbC4gVGhpcyBpcyBub3QgdGhlIHNhbWUgYXMgYWRkaW5nIG5ldyBpdGVtcyAtIHJhdGhlciwgaXQgdXN1YWxseSBtZWFucyBjb3ZhcnlpbmcgaXRlbXMsIG9yIGluY2x1ZGluZyBpdGVtcyBmcm9tIG9uZSBsYXRlbnQgZmFjdG9yIHRoYXQgYWxzbyBmaXQgd2l0aCBhIHNlY29uZCBmYWN0b3IuIFNpbmNlIHlvdXIgQ0ZBIG1vZGVsIHNob3VsZCBpbiBtb3N0IGNhc2VzIG5vdCBiZSBleHBsb3JhdG9yeSAoaS5lLiB5b3Ugc2hvdWxkIGtub3cgd2hhdCBwYXJhbWV0ZXJzIHlvdSB3YW50IHRvIGluY2x1ZGUgaW4gdGhlIG1vZGVsIGJlZm9yZSB5b3UgYmVnaW4pLCBtb2RpZmljYXRpb24gaW5kaWNlcyBjYW4gYmUgZGFuZ2Vyb3VzLiBIZW5jZSB0aGUgUGFuZG9yYSdzIEJveCByZWZlcmVuY2UhIElmIHlvdSBtYWtlIHRoZSBjaGFuZ2VzIHRoZXkgc3VnZ2VzdCwgeW91IG9mdGVuIHJ1biB0aGUgcmlzayBvZiBvdmVyLWZpdHRpbmcgdGhpcyBzcGVjaWZpYyBtb2RlbCB0byB5b3VyIGRhdGEgYW5kIHJlZHVjaW5nIHRoZSBnZW5lcmFsaXphYmlsaXR5IG9mIHlvdXIgcmVzdWx0cyBpbiBvdGhlciBzYW1wbGVzLgoKYGBge3J9Cm1vZGlmaWNhdGlvbkluZGljZXMoY29tcHV0ZXJzX2NmYTFfbW9kZWwsIHNvcnQuPVRSVUUsIG1pbmltdW0udmFsdWUgPSAzKQpgYGAKSSByZWNvbW1lbmQgdXNpbmcgbW9kaWZpY2F0aW9uIGluZGljZXMgbW9zdGx5IGFzIGFub3RoZXIgZGVzY3JpcHRpb24gb2YgdGhlIHBsYWNlcyB3aGVyZSB5b3VyIG1vZGVsIGlzIG5vdCBmaXR0aW5nIHdlbGwsIGEgYml0IGxpa2UgZXhhbWluaW5nIHRoZSByZXNpZHVhbHMuCgojIyBTYW1lIE1vZGVsLSB3aXRoIGVycm9yIGNvdmFyaWFuY2UgZm9yIHExMiBhbmQgcTA2CmBgYHtyfQpjb21wdXRlcnNfY2ZhMyAgPC0gJyBjb21wdXRlcnMgID1+IFEwNiArIFEwNyArIFExMCArIFExMiArIFExMyArIFExNCArIFExNSArIFExOApRMDYgfn4gUTEyJyAKY29tcHV0ZXJzX2NmYTNfbW9kZWwgPC0gY2ZhKGNvbXB1dGVyc19jZmEzLCBkYXRhPXNhcS5jbGVhbikgCnN1bW1hcnkoY29tcHV0ZXJzX2NmYTNfbW9kZWwsIGZpdC5tZWFzdXJlcz1UUlVFLCBzdGFuZGFyZGl6ZWQ9VFJVRSkKYGBgCgojIyBWaXN1YWxpemUgdGhlIG1vZGVsIHdpdGggdGhlIGV4dHJhIGNvdmFyaWFuY2UgYmV0d2VlbiBxMTIgYW5kIHEwNgpgYGB7cn0Kc2VtUGxvdDo6c2VtUGF0aHMoY29tcHV0ZXJzX2NmYTNfbW9kZWwsIHdoYXRMYWJlbHMgPSAic3RhbmQiKQpgYGAKCg==