Factor analysis

This page is under the contruction! More to come!

Structural Equation Modeling (SEM)

Fitting SEM with piecewiseSEM package

piecewiseSEM is the right package if you are thinking to fit a nested SEM models. piecewiseSEM can handles other classes of linear models like Generalized Linear Model, Mixed Effects Model, or Generalized Least Squares, which this function make piecewiseSEM differ from the lavaan package.

Here we will be using a dataset from Grace & Keeley (2006) to look get the effects of biotic and abiotic factors on plant species richness in shrublands.

Grace, J. B., & Keeley, J. E. (2006). A structural equation model analysis of postfire plant diversity in California shrublands. Ecological Applications, 16(2), 503–514. https://doi.org/10.1890/1051-0761(2006)016[0503:ASEMAO]2.0.CO;2

#here we will be using two packages piecewiseSEM and nlme
library(piecewiseSEM)
## 
##   This is piecewiseSEM version 2.3.0.
## 
## 
##   Questions or bugs can be addressed to <LefcheckJ@si.edu>.
#getting keeley dataset from piecewiseSEM package

data("keeley")
head(keeley)
##   distance elev  abiotic age   hetero firesev     cover rich
## 1 53.40900 1225 60.67103  40 0.757065    3.50 1.0387974   51
## 2 37.03745   60 40.94291  25 0.491340    4.05 0.4775924   31
## 3 53.69565  200 50.98805  15 0.844485    2.60 0.9489357   71
## 4 53.69565  200 61.15633  15 0.690847    2.90 1.1949002   64
## 5 51.95985  970 46.66807  23 0.545628    4.30 1.2981890   68
## 6 51.95985  970 39.82357  24 0.652895    4.00 1.1734866   34
summary(keeley)
##     distance          elev           abiotic           age       
##  Min.   :37.04   Min.   :  60.0   Min.   :32.59   Min.   : 3.00  
##  1st Qu.:39.46   1st Qu.: 202.5   1st Qu.:43.81   1st Qu.:15.00  
##  Median :51.77   Median : 400.0   Median :48.04   Median :25.00  
##  Mean   :49.23   Mean   : 424.7   Mean   :49.24   Mean   :25.57  
##  3rd Qu.:58.40   3rd Qu.: 630.0   3rd Qu.:54.90   3rd Qu.:35.00  
##  Max.   :60.72   Max.   :1225.0   Max.   :70.46   Max.   :60.00  
##      hetero          firesev          cover              rich      
##  Min.   :0.3842   Min.   :1.200   Min.   :0.05558   Min.   :15.00  
##  1st Qu.:0.6246   1st Qu.:3.700   1st Qu.:0.48769   1st Qu.:37.00  
##  Median :0.6843   Median :4.300   Median :0.63712   Median :50.00  
##  Mean   :0.6833   Mean   :4.565   Mean   :0.69123   Mean   :49.23  
##  3rd Qu.:0.7684   3rd Qu.:5.550   3rd Qu.:0.91468   3rd Qu.:62.00  
##  Max.   :0.8779   Max.   :9.200   Max.   :1.53541   Max.   :85.00
# fiting structural equation modeling using piecewiseSEM

library(nlme)
model1 = psem (
  gls (rich ~ elev + abiotic + age + firesev + cover, data = keeley),
  gls (abiotic ~ elev + age + firesev + cover, data = keeley),
  gls (cover ~ age + firesev + hetero, data = keeley),
  data = keeley
  )

summary (model1)
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |======================================================================| 100%
## 
## Structural Equation Model of model1 
## 
## Call:
##   rich ~ elev + abiotic + age + firesev + cover
##   abiotic ~ elev + age + firesev + cover
##   cover ~ age + firesev + hetero
## 
##     AIC
##  1397.714
## 
## ---
## Tests of directed separation:
## 
##           Independ.Claim Test.Type DF Crit.Value P.Value    
##       cover ~ elev + ...      coef 90     2.8319  0.0058  **
##      rich ~ hetero + ...      coef 90     4.6231  0.0000 ***
##   abiotic ~ hetero + ...      coef 90     1.9412  0.0556    
## 
## --
## Global goodness-of-fit:
## 
## Chi-Squared = 26.839 with P-value = 0 and on 3 degrees of freedom
## Fisher's C = 38.479 with P-value = 0 and on 6 degrees of freedom
## 
## ---
## Coefficients:
## 
##   Response Predictor Estimate Std.Error DF Crit.Value P.Value Std.Estimate    
##       rich      elev   0.0071    0.0056 90     1.2790  0.2044       0.1217    
##       rich   abiotic   0.7906    0.1830 90     4.3209  0.0000       0.4019 ***
##       rich       age  -0.1762    0.1213 90    -1.4523  0.1501      -0.1465    
##       rich   firesev  -1.2860    0.9461 90    -1.3593  0.1777      -0.1407    
##       rich     cover   6.6921    4.7544 90     1.4075  0.1630       0.1405    
##    abiotic      elev   0.0099    0.0031 90     3.1861  0.0020       0.3341  **
##    abiotic       age  -0.0732    0.0715 90    -1.0240  0.3087      -0.1198    
##    abiotic   firesev  -0.6564    0.5563 90    -1.1799  0.2413      -0.1412    
##    abiotic     cover  -1.3001    2.8151 90    -0.4618  0.6454      -0.0537    
##      cover       age  -0.0053    0.0026 90    -2.0164  0.0469      -0.2102   *
##      cover   firesev  -0.0677    0.0200 90    -3.3925  0.0010      -0.3526  **
##      cover    hetero  -0.5719    0.2570 90    -2.2249  0.0287      -0.2070   *
## 
##   Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05
## 
## ---
## Individual R-squared:
## 
##   Response method R.squared
##       rich   none      0.38
##    abiotic   none      0.15
##      cover   none      0.26
# fiting structural equation modeling using piecewiseSEM

model2 = psem (
  gls (rich ~ elev + abiotic + age + firesev + cover, data = keeley),
  gls (abiotic ~ elev + age + firesev + cover, data = keeley),
  gls (cover ~ age + firesev + elev, data = keeley),
  data = keeley
)

summary(model2)
## 
## Structural Equation Model of model2 
## 
## Call:
##   rich ~ elev + abiotic + age + firesev + cover
##   abiotic ~ elev + age + firesev + cover
##   cover ~ age + firesev + elev
## 
##     AIC
##  1413.477
## 
## ---
## Tests of directed separation:
## 
##  No independence claims present. Tests of directed separation not possible.
## 
## --
## Global goodness-of-fit:
## 
## Chi-Squared = 0 with P-value = 1 and on 0 degrees of freedom
## Fisher's C = NA with P-value = NA and on 0 degrees of freedom
## 
## ---
## Coefficients:
## 
##   Response Predictor Estimate Std.Error DF Crit.Value P.Value Std.Estimate    
##       rich      elev   0.0071    0.0056 90     1.2790  0.2044       0.1217    
##       rich   abiotic   0.7906    0.1830 90     4.3209  0.0000       0.4019 ***
##       rich       age  -0.1762    0.1213 90    -1.4523  0.1501      -0.1465    
##       rich   firesev  -1.2860    0.9461 90    -1.3593  0.1777      -0.1407    
##       rich     cover   6.6921    4.7544 90     1.4075  0.1630       0.1405    
##    abiotic      elev   0.0099    0.0031 90     3.1861  0.0020       0.3341  **
##    abiotic       age  -0.0732    0.0715 90    -1.0240  0.3087      -0.1198    
##    abiotic   firesev  -0.6564    0.5563 90    -1.1799  0.2413      -0.1412    
##    abiotic     cover  -1.3001    2.8151 90    -0.4618  0.6454      -0.0537    
##      cover       age  -0.0058    0.0027 90    -2.1674  0.0330      -0.2289   *
##      cover   firesev  -0.0594    0.0203 90    -2.9236  0.0044      -0.3095  **
##      cover      elev   0.0002    0.0001 90     2.1381  0.0353       0.2026   *
## 
##   Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05
## 
## ---
## Individual R-squared:
## 
##   Response method R.squared
##       rich   none      0.38
##    abiotic   none      0.15
##      cover   none      0.26

Fitting SEM with lavaan package

library(lavaan)
## This is lavaan 0.6-17
## lavaan is FREE software! Please report any bugs.
library(lavaanPlot)
library(piecewiseSEM)

data("keeley")
fit =  '
      rich ~ elev + abiotic + age + firesev + cover
      abiotic ~ elev + age + firesev + cover
      cover ~ age + firesev + elev'

lavan1 = sem (fit, data = keeley ) #estimator: ML
## Warning in lav_data_full(data = data, group = group, cluster = cluster, :
## lavaan WARNING: some observed variances are (at least) a factor 1000 times
## larger than others; use varTable(fit) to investigate
summary(lavan1, fit.measures = TRUE, standardized = TRUE) # Std.all: standardized regression coefficients
## lavaan 0.6.17 ended normally after 1 iteration
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        15
## 
##   Number of observations                            90
## 
## Model Test User Model:
##                                                       
##   Test statistic                                 0.000
##   Degrees of freedom                                 0
## 
## Model Test Baseline Model:
## 
##   Test statistic                                84.779
##   Degrees of freedom                                12
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    1.000
##   Tucker-Lewis Index (TLI)                       1.000
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)               -663.707
##   Loglikelihood unrestricted model (H1)       -663.707
##                                                       
##   Akaike (AIC)                                1357.414
##   Bayesian (BIC)                              1394.911
##   Sample-size adjusted Bayesian (SABIC)       1347.570
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.000
##   90 Percent confidence interval - lower         0.000
##   90 Percent confidence interval - upper         0.000
##   P-value H_0: RMSEA <= 0.050                       NA
##   P-value H_0: RMSEA >= 0.080                       NA
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.000
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Regressions:
##                    Estimate   Std.Err  z-value  P(>|z|)   Std.lv   Std.all
##   rich ~                                                                  
##     elev               0.007    0.005    1.324    0.186     0.007    0.122
##     abiotic            0.791    0.177    4.473    0.000     0.791    0.402
##     age               -0.176    0.117   -1.503    0.133    -0.176   -0.147
##     firesev           -1.286    0.914   -1.407    0.159    -1.286   -0.141
##     cover              6.692    4.593    1.457    0.145     6.692    0.141
##   abiotic ~                                                               
##     elev               0.010    0.003    3.279    0.001     0.010    0.334
##     age               -0.073    0.069   -1.054    0.292    -0.073   -0.120
##     firesev           -0.656    0.541   -1.214    0.225    -0.656   -0.141
##     cover             -1.300    2.736   -0.475    0.635    -1.300   -0.054
##   cover ~                                                                 
##     age               -0.006    0.003   -2.217    0.027    -0.006   -0.229
##     firesev           -0.059    0.020   -2.991    0.003    -0.059   -0.309
##     elev               0.000    0.000    2.187    0.029     0.000    0.203
## 
## Variances:
##                    Estimate   Std.Err  z-value  P(>|z|)   Std.lv   Std.all
##    .rich             139.572   20.806    6.708    0.000   139.572    0.619
##    .abiotic           49.636    7.399    6.708    0.000    49.636    0.851
##    .cover              0.074    0.011    6.708    0.000     0.074    0.740
fitMeasures(lavan1, c("cfi", "rmsea", "srmr", "GFI")) #cfi and GFI > 0.95; rmsea and srmr < 0.08
##   cfi rmsea  srmr   gfi 
##     1     0     0     1
modificationindices(lavan1, minimum.value = 5) #this is to check if your model need to be modifies
## [1] lhs      op       rhs      mi       epc      sepc.lv  sepc.all sepc.nox
## <0 rows> (or 0-length row.names)
lavaanPlot(model = lavan1, coefs = TRUE, stand = TRUE, 
           sig = 0.05) #standardized regression paths, showing only paths with p<= .05