library(haven)
library(lavaan)
library(tidySEM)SEM with latent variables
From parceled SEM to full latent-variable SEM; disattenuation
Learning objectives
By the end of this tutorial you will be able to:
- explain why modelling latent variables is preferable to modelling manifest scores
- build a SEM that combines a measurement model (
=~) with a structural model (~) - count degrees of freedom for a SEM by hand
- compare path analysis estimates (manifest variables) with SEM estimates (latent variables) and understand disattenuation
- fit a full latent-variable SEM with three factors and a mediation structure
Setup
Setup code (you have seen this before)
Why latent variables?
In Tutorial 09 we fitted a path model with manifest variables — mean scores computed from the items. For example, pa was the mean of pa1, pa2, pa3. This is simple, but it has a cost: the mean score contains measurement error.
Measurement error has two consequences for regression:
Attenuation. The regression coefficient of a variable measured with error is biased toward zero. If
pais an imperfect measure of the true positive-affect construct, the path frompatocreatwill underestimate the true effect.Bias in other paths. When multiple predictors are measured with different amounts of error, the relative sizes of their coefficients are distorted — the more reliable predictor looks relatively stronger.
Modelling the latent variable behind the items removes these biases. The latent variable is the error-free part of the construct; the residual variances of the individual items absorb the measurement error. This is the central advantage of SEM over path analysis with manifest variables.
The trade-off
The cost of modelling latent variables is model complexity. A latent factor with three indicators adds parameters (loadings, residual variances) and the model must be identified. In return, you get:
- Unbiased estimates of the structural paths
- A testable measurement model — you can check whether the items load well on their factors
- More degrees of freedom, which makes the overall model testable
From path analysis to SEM: parceled SEM
Our first SEM uses a parceled job-control factor (from Tutorial 08) alongside manifest pa and creat. This is a half-way house: one latent variable, two manifest variables. It is a common practical choice when you have many items for one construct but only three items for the others.
Building the parcels
ps$p1 <- (ps$con9 + ps$con10) / 2
ps$p2 <- (ps$con1 + ps$con8) / 2
ps$p3 <- (ps$con3 + ps$con6) / 2
ps$pa <- (ps$pa1 + ps$pa2 + ps$pa3) / 3
ps$creat <- (ps$creat1 + ps$creat2 + ps$creat3) / 3The model
model_sem_parcel <- '
# Measurement model for job control (latent)
con =~ p1 + p2 + p3
# Structural model
creat ~ pa + con
pa ~ con
'The # lines are comments inside the model string — lavaan ignores them. We use comments to separate the measurement and structural parts of the model, which makes the syntax easier to read as models grow.
We fit with sem():
fit_sem_parcel <- sem(model_sem_parcel, data = ps, estimator = "MLR")
summary(fit_sem_parcel, standardized = TRUE, fit.measures = TRUE, rsquare = TRUE)## lavaan 0.6-21 ended normally after 22 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of model parameters 11
##
## Used Total
## Number of observations 332 343
##
## Model Test User Model:
## Standard Scaled
## Test Statistic 0.965 0.927
## Degrees of freedom 4 4
## P-value (Chi-square) 0.915 0.921
## Scaling correction factor 1.041
## Yuan-Bentler correction (Mplus variant)
##
## Model Test Baseline Model:
##
## Test statistic 402.002 378.803
## Degrees of freedom 10 10
## P-value 0.000 0.000
## Scaling correction factor 1.061
##
## User Model versus Baseline Model:
##
## Comparative Fit Index (CFI) 1.000 1.000
## Tucker-Lewis Index (TLI) 1.019 1.021
##
## Robust Comparative Fit Index (CFI) 1.000
## Robust Tucker-Lewis Index (TLI) 1.020
##
## Loglikelihood and Information Criteria:
##
## Loglikelihood user model (H0) -1888.462 -1888.462
## Scaling correction factor 0.912
## for the MLR correction
## Loglikelihood unrestricted model (H1) -1887.979 -1887.979
## Scaling correction factor 0.946
## for the MLR correction
##
## Akaike (AIC) 3798.923 3798.923
## Bayesian (BIC) 3840.780 3840.780
## Sample-size adjusted Bayesian (SABIC) 3805.887 3805.887
##
## Root Mean Square Error of Approximation:
##
## RMSEA 0.000 0.000
## 90 Percent confidence interval - lower 0.000 0.000
## 90 Percent confidence interval - upper 0.031 0.026
## P-value H_0: RMSEA <= 0.050 0.979 0.983
## P-value H_0: RMSEA >= 0.080 0.002 0.002
##
## Robust RMSEA 0.000
## 90 Percent confidence interval - lower 0.000
## 90 Percent confidence interval - upper 0.029
## P-value H_0: Robust RMSEA <= 0.050 0.980
## P-value H_0: Robust RMSEA >= 0.080 0.002
##
## Standardized Root Mean Square Residual:
##
## SRMR 0.008 0.008
##
## 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
## con =~
## p1 1.000 0.687 0.772
## p2 0.811 0.067 12.179 0.000 0.557 0.797
## p3 0.899 0.072 12.483 0.000 0.618 0.794
##
## Regressions:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## creat ~
## pa 0.187 0.043 4.335 0.000 0.187 0.237
## con -0.099 0.074 -1.343 0.179 -0.068 -0.080
## pa ~
## con 0.244 0.092 2.642 0.008 0.168 0.155
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .p1 0.321 0.038 8.430 0.000 0.321 0.405
## .p2 0.178 0.022 8.251 0.000 0.178 0.365
## .p3 0.224 0.029 7.711 0.000 0.224 0.370
## .creat 0.689 0.047 14.712 0.000 0.689 0.943
## .pa 1.146 0.073 15.757 0.000 1.146 0.976
## con 0.472 0.062 7.603 0.000 1.000 1.000
##
## R-Square:
## Estimate
## p1 0.595
## p2 0.635
## p3 0.630
## creat 0.057
## pa 0.024
Counting degrees of freedom
With \(q = 5\) observed variables (p1, p2, p3, pa, creat), the covariance matrix has \(5 \times 6 / 2 = 15\) non-redundant elements.
Free parameters:
- 2 loadings on
con(first fixed to 1 by marker-variable method) - 2 regression paths in the
createquation (pa,con) - 1 regression path in the
paequation (con) - 3 residual variances of the parcels
- 1 residual variance of
pa - 1 residual variance of
creat - 1 latent variance of
con
That is \(t = 11\). So \(\text{df} = 15 - 11 = 4\).
Reading the output
The output now has both a Latent variables section (the con =~ p1 + p2 + p3 loadings) and a Regressions section (the structural paths). Compare the structural paths to those from the path analysis in Tutorial 09:
- The path from
contopashould be slightly larger in the SEM than in the path analysis — disattenuation removes the bias fromconbeing measured with error. - The path from
patocreatshould be similar —pais still a manifest variable in this model. - The path from
contocreat(direct effect) may also change.
When a predictor is measured with error, its regression coefficient is biased toward zero. Modelling the predictor as a latent variable (with the measurement error absorbed into the residual variances of its indicators) removes this bias. The latent-variable coefficient is typically larger in absolute value than the manifest-variable coefficient. This is called disattenuation — the coefficient has been “corrected for attenuation” due to measurement error.
Full latent-variable SEM
Now we make the full jump: all three constructs are latent variables, each measured by its own indicators.
The model
model_sem_full <- '
# Measurement model
con =~ p1 + p2 + p3
pa =~ pa1 + pa2 + pa3
creat =~ creat1 + creat2 + creat3
# Structural model
creat ~ pa + con
pa ~ con
'We parcel only the job-control items (into p1, p2, p3) because there are six of them and we want a simpler measurement model. Positive affect and creativity are modelled at the item level with three indicators each.
Fitting the model
fit_sem_full <- sem(model_sem_full, data = ps, estimator = "MLR")
summary(fit_sem_full, standardized = TRUE, fit.measures = TRUE, rsquare = TRUE)## lavaan 0.6-21 ended normally after 28 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of model parameters 21
##
## Used Total
## Number of observations 332 343
##
## Model Test User Model:
## Standard Scaled
## Test Statistic 25.528 24.788
## Degrees of freedom 24 24
## P-value (Chi-square) 0.378 0.417
## Scaling correction factor 1.030
## Yuan-Bentler correction (Mplus variant)
##
## Model Test Baseline Model:
##
## Test statistic 1289.618 1206.185
## Degrees of freedom 36 36
## P-value 0.000 0.000
## Scaling correction factor 1.069
##
## User Model versus Baseline Model:
##
## Comparative Fit Index (CFI) 0.999 0.999
## Tucker-Lewis Index (TLI) 0.998 0.999
##
## Robust Comparative Fit Index (CFI) 0.999
## Robust Tucker-Lewis Index (TLI) 0.999
##
## Loglikelihood and Information Criteria:
##
## Loglikelihood user model (H0) -3551.266 -3551.266
## Scaling correction factor 0.974
## for the MLR correction
## Loglikelihood unrestricted model (H1) -3538.503 -3538.503
## Scaling correction factor 1.004
## for the MLR correction
##
## Akaike (AIC) 7144.533 7144.533
## Bayesian (BIC) 7224.441 7224.441
## Sample-size adjusted Bayesian (SABIC) 7157.828 7157.828
##
## Root Mean Square Error of Approximation:
##
## RMSEA 0.014 0.010
## 90 Percent confidence interval - lower 0.000 0.000
## 90 Percent confidence interval - upper 0.047 0.046
## P-value H_0: RMSEA <= 0.050 0.965 0.974
## P-value H_0: RMSEA >= 0.080 0.000 0.000
##
## Robust RMSEA 0.010
## 90 Percent confidence interval - lower 0.000
## 90 Percent confidence interval - upper 0.047
## P-value H_0: Robust RMSEA <= 0.050 0.969
## P-value H_0: Robust RMSEA >= 0.080 0.000
##
## Standardized Root Mean Square Residual:
##
## SRMR 0.028 0.028
##
## 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
## con =~
## p1 1.000 0.687 0.772
## p2 0.810 0.067 12.160 0.000 0.557 0.797
## p3 0.898 0.072 12.473 0.000 0.617 0.794
## pa =~
## pa1 1.000 0.956 0.806
## pa2 1.046 0.075 13.952 0.000 1.000 0.780
## pa3 1.090 0.076 14.379 0.000 1.042 0.843
## creat =~
## creat1 1.000 0.745 0.753
## creat2 1.047 0.077 13.540 0.000 0.780 0.818
## creat3 1.127 0.080 14.086 0.000 0.840 0.855
##
## Regressions:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## creat ~
## pa 0.205 0.054 3.789 0.000 0.264 0.264
## con -0.102 0.069 -1.465 0.143 -0.094 -0.094
## pa ~
## con 0.227 0.091 2.496 0.013 0.163 0.163
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .p1 0.320 0.038 8.412 0.000 0.320 0.404
## .p2 0.178 0.022 8.248 0.000 0.178 0.364
## .p3 0.224 0.029 7.734 0.000 0.224 0.370
## .pa1 0.492 0.077 6.359 0.000 0.492 0.350
## .pa2 0.645 0.073 8.851 0.000 0.645 0.392
## .pa3 0.444 0.065 6.881 0.000 0.444 0.290
## .creat1 0.423 0.042 9.979 0.000 0.423 0.432
## .creat2 0.300 0.037 8.162 0.000 0.300 0.330
## .creat3 0.259 0.039 6.578 0.000 0.259 0.268
## con 0.472 0.062 7.597 0.000 1.000 1.000
## .pa 0.890 0.105 8.485 0.000 0.973 0.973
## .creat 0.517 0.065 7.914 0.000 0.930 0.930
##
## R-Square:
## Estimate
## p1 0.596
## p2 0.636
## p3 0.630
## pa1 0.650
## pa2 0.608
## pa3 0.710
## creat1 0.568
## creat2 0.670
## creat3 0.732
## pa 0.027
## creat 0.070
Counting degrees of freedom
With \(q = 9\) observed variables (p1, p2, p3, pa1, pa2, pa3, creat1, creat2, creat3), the covariance matrix has \(9 \times 10 / 2 = 45\) non-redundant elements.
Free parameters:
- 2 loadings on
con+ 2 loadings onpa+ 2 loadings oncreat= 6 loadings - 2 regression paths in
createquation + 1 inpaequation = 3 structural paths - 3 parcel residual variances + 3 pa residual variances + 3 creat residual variances = 9 indicator residual variances
- 1 latent variance of
con+ 1 residual variance ofpa+ 1 residual variance ofcreat= 3 latent (residual) variances
That is \(t = 6 + 3 + 9 + 3 = 21\). So \(\text{df} = 45 - 21 = 24\).
Fit indices
fitMeasures(fit_sem_full, c("chisq", "df", "pvalue",
"cfi", "tli",
"rmsea", "rmsea.ci.lower", "rmsea.ci.upper",
"srmr"))## chisq df pvalue cfi tli
## 25.528 24.000 0.378 0.999 0.998
## rmsea rmsea.ci.lower rmsea.ci.upper srmr
## 0.014 0.000 0.047 0.028
The model has 24 degrees of freedom — plenty of information to test it. If the fit indices meet the Hu and Bentler cutoffs, the model’s structure is consistent with the data.
Comparing path analysis, parceled SEM, and full SEM
Let us compare the key structural paths across the three models:
# Path analysis (manifest variables) — from Tutorial 09
ps$con_m <- (ps$con1 + ps$con3 + ps$con6 + ps$con8 + ps$con9 + ps$con10) / 6
ps$pa_m <- (ps$pa1 + ps$pa2 + ps$pa3) / 3
ps$cr_m <- (ps$creat1 + ps$creat2 + ps$creat3) / 3
model_path_m <- '
cr_m ~ pa_m + con_m
pa_m ~ con_m
'
fit_path <- sem(model_path_m, data = ps, estimator = "MLR")
# Extract standardised structural paths from each model
get_paths <- function(fit) {
pe <- parameterEstimates(fit, standardized = TRUE)
pe[pe$op == "~", c("lhs", "rhs", "std.all")]
}
paths_path <- get_paths(fit_path)
paths_parcel <- get_paths(fit_sem_parcel)
paths_full <- get_paths(fit_sem_full)
paths_path## lhs rhs std.all
## 1 cr_m pa_m 0.235
## 2 cr_m con_m -0.073
## 3 pa_m con_m 0.144
paths_parcel## lhs rhs std.all
## 4 creat pa 0.237
## 5 creat con -0.080
## 6 pa con 0.155
paths_full## lhs rhs std.all
## 10 creat pa 0.264
## 11 creat con -0.094
## 12 pa con 0.163
The comparison shows:
- The path from
contopais larger in the SEM models than in the path analysis — disattenuation corrects for the measurement error in job control. - The path from
patocreatis larger in the full SEM than in the parceled SEM or path analysis — disattenuation corrects for the measurement error in positive affect. - The direct path from
contocreatmay change in either direction.
This is the core argument for SEM over path analysis with manifest scores: the structural paths are unbiased estimates of the true relationships among the constructs, not the attenuated estimates you get from imperfectly-measured scores.
Drawing the full SEM
tidySEM::graph_sem(fit_sem_full, std = TRUE)
The diagram shows both layers of the model:
- Measurement layer: arrows from each latent factor (circle) to its indicators (squares)
- Structural layer: arrows among the latent factors —
con → pa,con → creat,pa → creat
Mathematical foundation (optional)
A full SEM combines a measurement model with a structural model. In matrix form:
Measurement model:
\[ \mathbf{y} = \mathbf{\Lambda} \mathbf{\eta} + \mathbf{\varepsilon} \]
This is the same as the CFA matrix form from Tutorial 06 — observed variables (\(\mathbf{y}\)) are linear functions of latent variables (\(\mathbf{\eta}\)) plus measurement error (\(\mathbf{\varepsilon}\)).
Structural model:
\[ \mathbf{\eta} = \mathbf{B} \mathbf{\eta} + \mathbf{\zeta} \tag{1}\]
Here \(\mathbf{B}\) is a matrix of structural coefficients (the regression paths among latent variables) and \(\mathbf{\zeta}\) is a vector of disturbances — the residual variances of the endogenous latent variables.
The model-implied covariance matrix combines both:
\[ \hat{\Sigma}(\theta) = \mathbf{\Lambda} (\mathbf{I} - \mathbf{B})^{-1} \mathbf{\Psi} (\mathbf{I} - \mathbf{B})^{-T} \mathbf{\Lambda}' + \mathbf{\Theta} \tag{2}\]
You do not need to manipulate these matrices to use lavaan. They are included here so you recognise the notation when you encounter it in SEM papers. For the rest of this series we use the simplified lowercase Greek symbols (\(\lambda\), \(\beta\), \(\psi\), \(\theta\), \(\zeta\)).
Common errors
lavaan WARNING: model has not converged in full SEM
A full SEM with many indicators and structural paths is more likely to have convergence problems than a simple CFA. Check for Heywood cases (negative variances), check that every factor has at least three indicators, and consider parceling some of the indicators to simplify the measurement model.
Very different estimates between path analysis and SEM
If the SEM estimates are dramatically different from the path analysis estimates, check whether the measurement model fits well on its own (fit a CFA first). A poorly-fitting measurement model can distort the structural estimates.
Negative residual variance for a latent dependent variable
If pa or creat has a negative disturbance variance (\(\zeta\)), the structural model may be misspecified. Consider whether the structural paths are theoretically justified, and whether there are omitted predictors that should be included.
Exercises
Fit the parceled SEM from the tutorial (job control as a latent factor measured by p1, p2, p3;
paandcreatas manifest mean scores). Compare the standardised path fromcontopato the same path from the path analysis in Tutorial 09. Is the SEM estimate larger? Why?Fit the full latent-variable SEM (all three constructs as latent factors). Write down the standardised structural paths and the \(R^2\) for
creat. How much of the variance in creativity is explained by the model?Count the degrees of freedom for the full SEM by hand (using the t-rule). Confirm that your count matches the df reported by
fitMeasures().Compare the parceled SEM and the full SEM using
lavTestLRT(). Which model fits better? Is the more complex full SEM justified, or does the simpler parceled model fit as well?
Exercise 1
fitMeasures(fit_sem_parcel, c("chisq", "df", "pvalue", "cfi", "tli",
"rmsea", "srmr"))## chisq df pvalue cfi tli rmsea srmr
## 0.965 4.000 0.915 1.000 1.019 0.000 0.008
get_paths(fit_path)## lhs rhs std.all
## 1 cr_m pa_m 0.235
## 2 cr_m con_m -0.073
## 3 pa_m con_m 0.144
get_paths(fit_sem_parcel)## lhs rhs std.all
## 4 creat pa 0.237
## 5 creat con -0.080
## 6 pa con 0.155
The con → pa path from the parceled SEM should be larger than the same path from the path analysis. This is disattenuation — the latent con factor is free of measurement error, so its coefficient is not biased toward zero.
Exercise 2
summary(fit_sem_full, standardized = TRUE, rsquare = TRUE)## lavaan 0.6-21 ended normally after 28 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of model parameters 21
##
## Used Total
## Number of observations 332 343
##
## Model Test User Model:
## Standard Scaled
## Test Statistic 25.528 24.788
## Degrees of freedom 24 24
## P-value (Chi-square) 0.378 0.417
## Scaling correction factor 1.030
## Yuan-Bentler correction (Mplus variant)
##
## 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
## con =~
## p1 1.000 0.687 0.772
## p2 0.810 0.067 12.160 0.000 0.557 0.797
## p3 0.898 0.072 12.473 0.000 0.617 0.794
## pa =~
## pa1 1.000 0.956 0.806
## pa2 1.046 0.075 13.952 0.000 1.000 0.780
## pa3 1.090 0.076 14.379 0.000 1.042 0.843
## creat =~
## creat1 1.000 0.745 0.753
## creat2 1.047 0.077 13.540 0.000 0.780 0.818
## creat3 1.127 0.080 14.086 0.000 0.840 0.855
##
## Regressions:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## creat ~
## pa 0.205 0.054 3.789 0.000 0.264 0.264
## con -0.102 0.069 -1.465 0.143 -0.094 -0.094
## pa ~
## con 0.227 0.091 2.496 0.013 0.163 0.163
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .p1 0.320 0.038 8.412 0.000 0.320 0.404
## .p2 0.178 0.022 8.248 0.000 0.178 0.364
## .p3 0.224 0.029 7.734 0.000 0.224 0.370
## .pa1 0.492 0.077 6.359 0.000 0.492 0.350
## .pa2 0.645 0.073 8.851 0.000 0.645 0.392
## .pa3 0.444 0.065 6.881 0.000 0.444 0.290
## .creat1 0.423 0.042 9.979 0.000 0.423 0.432
## .creat2 0.300 0.037 8.162 0.000 0.300 0.330
## .creat3 0.259 0.039 6.578 0.000 0.259 0.268
## con 0.472 0.062 7.597 0.000 1.000 1.000
## .pa 0.890 0.105 8.485 0.000 0.973 0.973
## .creat 0.517 0.065 7.914 0.000 0.930 0.930
##
## R-Square:
## Estimate
## p1 0.596
## p2 0.636
## p3 0.630
## pa1 0.650
## pa2 0.608
## pa3 0.710
## creat1 0.568
## creat2 0.670
## creat3 0.732
## pa 0.027
## creat 0.070
The standardised structural paths appear in the Regressions section. The \(R^2\) for creat appears in the R-Square section — it tells you what proportion of the variance in the creativity latent factor is explained by pa and con.
Exercise 3
With \(q = 9\) observed variables, the covariance matrix has \(9 \times 10 / 2 = 45\) elements. Free parameters: 6 loadings + 3 structural paths + 9 indicator residual variances + 3 latent (residual) variances = 21. So \(\text{df} = 45 - 21 = 24\). Confirm with:
fitMeasures(fit_sem_full, "df")## df
## 24
Exercise 4
lavTestLRT(fit_sem_parcel, fit_sem_full)##
## 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 RMSEA Df diff Pr(>Chisq)
## fit_sem_parcel 4 3798.9 3840.8 0.9648
## fit_sem_full 24 7144.5 7224.4 25.5278 23.903 0.024577 20 0.2467
A significant result means the full SEM fits significantly better than the parceled SEM — the additional latent variables (pa and creat) improve the model. A non-significant result means the simpler parceled model fits as well. Note that the two models are not strictly nested (the parceled model uses manifest pa and creat, while the full model uses latent pa and creat), so the likelihood-ratio test is approximate. Compare the AIC/BIC values as well for a non-nested comparison.
Summary & next tutorial
You have moved from path analysis with manifest variables to full structural equation modelling with latent variables. You have seen how disattenuation changes the structural path estimates, how to count degrees of freedom for a SEM, and how to draw the full model with tidySEM. The model we fitted — con → pa → creat with a direct path con → creat — is the structure of a mediation model. In the next two tutorials we develop the theory of mediation (Tutorial 11) and then fit it in lavaan with bootstrap confidence intervals (Tutorial 12).
Further reading
- Kelloway (2015) — chapters 4 and 5 cover SEM with latent variables and the comparison to path analysis.
- Beaujean (2014) — a lavaan-based walkthrough of full SEM, including the matrix form.
- Bollen (1989) — the canonical reference for the mathematical foundations of SEM.
- Hoyle (2012) — the Handbook of Structural Equation Modeling, comprehensive reference for every aspect of SEM.