Two-Level Confirmatory Factor Analysis

Motivating Example

Data from the present example were drawn from a large-scale study in which indicators of verbal ability were measured for 1,141 students nested within 163 teachers. We will use xxM to fit unrestricted CFA models at the student and teacher levels in an effort to explain the common covariance among these indicators at each level.

In this case, each indicator of reading ability varies across students and teachers, and performance on these indicators is correlated within each level. Estimating a common latent factor at each level of analysis allows us to parsimoniously explain the common variance/covariance among the indicators.

Path Diagram

alt 2lcfa.xxmModel

Scalar Representation

Student Submodel (Level-1)

The measurement model for each indicator of reading ability at the student level can be presented as:

\[ y_{pij}^1= \nu_p^1 + \lambda_p^1 \times \eta_{1ij}^1 + e_{pij}^1 \]

\[ y_{pij}^1\ = \nu_p^1 + \lambda_p^1 \times \eta_{ij}^1 +e_{pij}^1 \]

where, \( y_{pij} \) is \( p^{th} \) observed indicator for student\( i \), nested within teacher \( j \). The superscript 1 is for the student level.
\[ \eta_{pij}^1 \sim N(0,\psi_{1, 1}^{1,1}) \] \[ e_{pij}^1 \sim N(0,diag( \theta^{1,1} ) ) \] The student submodel has the following parameters:

  1. \( (p-1) \) factor-loadings \( (\lambda_p^{1,1}) \) with the first factor loading being fixed to 1.0 for scale identification.
  2. Residual variance for each of the p observed indicators \( (\theta_{p,p}) \)
  3. Single latent variance \( (\psi_{1,1}^{1,1}) \).
  4. Measurement intercepts for each of the \( p \) observed indicators \( (\nu_p) \).

Teacher Submodel (Level-2) AND across Levels

The teacher level has a single latent variable with zero mean and unknown residual variance \( (\psi_{1,1}^{2,2}) \). The superscript 2 is for the teacher level, and the generic teacher-level equation is nearly identical to the student equation:

\[ y_{pij}^1=\nu_p^1+\lambda_{p,1}^{1,2} \times \eta_{1j}^2+e_{pj}^1 \]

where, \( y_{pij} \) is \( p^{th} \) observed indicator for student \( i \), nested within teacher \( j \). It is important to note that the student level reading outcomes serve as indicators of the reading ability factor at the teacher level. As a result, the student level measurement intercepts and residual variances carry-over from the previous equation. Hence, we need only a single teacher level reading ability latent variable:

\[ \eta_{1j}^2 \sim N(0,\psi_{1,1}^{2,2} ) \]

Because we have already specified the mean structure and residual variances for the observed indicators at the student level, only two ‘types’ of parameter remain:

  1. \( (p-1) \) factor-loadings \( (\lambda_{p,1}^{1,2}) \) with the first factor-loading being fixed to 1.0 for scale identification.
  2. Single latent variance \( (\psi_{1,1}^{2,2}). \)

XXM Model Matrices

Student Submodel (Level-1)

Factor-loading matrix (Lambda)

\[
\Lambda_{pattern}^{1,1} =
\begin{bmatrix}
\ 0 \\
\ 1 \\
\ 1 \\
\ 1
\end{bmatrix}
\]
\[
\Lambda_{value}^{1,1} =
\begin{bmatrix}
\ 1.0 \\
\ 1.1 \\
\ 0.9 \\
\ 0.8
\end{bmatrix}
\]
\[
\Lambda_{lable}^{1,1} =
\begin{bmatrix}
\lambda_{1,1}^{1,1} \\
\lambda_{2,1}^{1,1} \\
\lambda_{3,1}^{1,1} \\
\lambda_{4,1}^{1,1}
\end{bmatrix}
\]

The first factor-loading is fixed to 1.0. Hence, we need to fix the first parameter in the pattern matrix. The value at which it being fixed is specified in the value matrix. In this case the first factor-loading is being fixed to a value of 1.0.

Observed Residual Covariance Matrix (Theta)

\[
\Theta_{pattern}^{1,1} =
\begin{bmatrix}
\ 1 & 0 & 0 & 0 \\
\ 0 & 1 & 0 & 0 \\
\ 0 & 0 & 1 & 0 \\
\ 0 & 0 & 0 & 1
\end{bmatrix}
\]
\[
\Theta_{value}^{1,1} =
\begin{bmatrix}
\ 1.1 & 0.0 & 0.0 & 0.0 \\
\ 0.0 & 2.1 & 0.0 & 0.0 \\
\ 0.0 & 0.0 & 1.3 & 0.0 \\
\ 0.0 & 0.0 & 0.0 & 1.5
\end{bmatrix}
\]

The residual covariance matrix is a diagonal matrix, meaning we are only estimating residual variances. Residual covariances are all fixed to 0.0. Again we use a pattern and a value matrix to fix all off-diagonal elements to 0.0

Latent Covariance Matrix (PSI)

\( \Psi_{pattern}^{1,1}=[1] \), \( \Psi_{value}^{1,1}=[1.1] \)

Observed Variablel Intercepts (nu)

\[
\nu_{pattern}^{1,1}=
\begin{bmatrix}
\ 1 \\
\ 1 \\
\ 1 \\
\ 1 \\
\end{bmatrix}
\]

,

\[
\nu_{value}^{1,1}=
\begin{bmatrix}
\ 1.1 \\
\ 2.1 \\
\ 1.3 \\
\ 7.1 \\
\end{bmatrix}
\]

Teacher submodel (Level-2)

Factor-loading matrix (Lambda)

\[
\lambda_{pattern}^{1,2}=
\begin{bmatrix}
\ 0 \\
\ 1 \\
\ 1 \\
\ 1
\end{bmatrix}
\]

,

\[
\lambda_{value}^{1,2}=
\begin{bmatrix}
\ 1.0 \\
\ 1.1 \\
\ .9 \\
\ .8
\end{bmatrix}
\]

,

\[
\lambda_{label}^{1,2}=
\begin{bmatrix}
\lambda_{1,1}^{1,2}\\
\lambda_{2,1}^{1,2}\\
\lambda_{1,2}^{3,1}\\
\lambda_{1,2}^{4,1}
\end{bmatrix}
\]

It is important to note that in the current specification \( \Lambda_{label}^{1,1} \) \( \Lambda_{label}^{1,2} \) suggesting that the factor-loadings at each level are to be uniquely estimated. Modifying these matrices so that elements corresponding to common indicators share the same label (e.g., \( \lambda_{1,1}^{2,1} \), \( \lambda_{2,1}^{1,2} \) -> \( \lambda _{2,1} \) ) places equality constraints on loadings to the same indicator at the student and teacher level. The next example (hcfa) illustrates this concept in greater detail.

Latent Covariance Matrix (PSI)

\( \Psi_{pattern}^{2,2}=[1] \), \( \Psi_{value}^{2,2}=[0.05] \)

Model Matrices Summary

The following table provides a complete summary of parameter matrices:

Type Matrix Pattern
level 1: \( \Theta \)
\[
\Theta^{1,1} =
\begin{bmatrix}
\theta_{1,1}^{1,1} & \\
\theta_{2,1}^{1,1} & \theta_{2,2}^{1,1} \\
\theta_{3,1}^{1,1} & \theta_{3,2}^{1,1} & \theta_{3,3}^{1,1} \\
\theta_{4,1}^{1,1} & \theta_{4,2}^{1,1} & \theta_{4,3}^{1,1} & \theta_{4,4}^{1,1} \\
\end{bmatrix}
\]
\[
\Theta^{1,1} =
\begin{bmatrix}
\ 1 \\
\ 0 & 1 \\
\ 0 & 0 & 1 \\
\ 0 & 0 & 0 & 1\\
\end{bmatrix}
\]
level 1: \( \nu \)
\[
\nu^{1} =
\begin{bmatrix}
\nu_1^1\\
\nu_2^1\\
\nu_3^1 \\
\nu_4^1
\end{bmatrix}
\]
\[
\nu^{1} =
\begin{bmatrix}
\ 1\\
\ 1\\
\ 1\\
\ 1
\end{bmatrix}
\]
level 1: \( \Lambda \)
\[
\Lambda^{1,1} =
\begin{bmatrix}
\lambda_{1,1}^{1,1}\\
\lambda_{2,1}^{1,1}\\
\lambda_{3,1}^{1,1}\\
\lambda_{4,1}^{1,1}
\end{bmatrix}
\]
\[
\Lambda^{1,1} =
\begin{bmatrix}
\ 0\\
\ 1\\
\ 1\\
\ 1
\end{bmatrix}
\]
level1: \( \Psi \)
\[
\Psi^{1,1} = [\psi_{1,1}^{1,1}] \]
\[
\Psi^{1,1} = [1] \]
Level 2 -> Level 1: \( \Lambda \)
\[
\Lambda^{1,2} =
\begin{bmatrix}
\lambda_{1,1}^{1,2}\\
\lambda_{2,1}^{1,2}\\
\lambda_{3,1}^{1,2}\\
\lambda_{4,1}^{1,2}
\end{bmatrix}
\]
\[
\Lambda^{1,2} =
\begin{bmatrix}
\ 0\\
\ 1\\
\ 1\\
\ 1
\end{bmatrix}
\]
level 2: \( \Psi \)
\[
\Psi^{2,2} = [\psi_{1,1}^{2,2}] \]
\[
\Psi^{2,2} =[1] \]

Code Listing

“xxM”

xxM

The complete listing of xxM code is as follows:

Load xxM and data

library(xxm)
data(mlcfa.xxm)

Construct R-matrices

For each parameter matrix, construct three related matrices:

  1. pattern matrix: A matrix indicating free or fixed parameters.
  2. value matrix: with start or fixed values for corresponding parameters.
  3. label matrix: with user friendly label for each parameter. label matrix is optional.
# Student: factor-loading matrix
ly11_pat <- matrix(c(0, 1, 1, 1), 4, 1)
ly11_val <- matrix(1, 4, 1)
# Student: factor-covariance matrix
ps1_pat <- matrix(1, 1, 1)
ps1_val <- matrix(0.1, 1, 1)
# Student: observed residual-covariance matrix
th1_pat <- diag(1, 4)
th1_val <- diag(3, 4)
# Student: 'grand-means' NU
nu1_pat <- matrix(1, 4, 1)
nu1_val <- matrix(46, 4, 1)
# Teacher model matrices Teacher: factor-covariance matrix
ps2_pat <- matrix(1, 1, 1)
ps2_val <- matrix(0.1, 1, 1)
# Teacher -> Student factor-loading matrix
ly12_pat <- matrix(c(0, 1, 1, 1), 4, 1)
ly12_val <- matrix(1, 4, 1)

Construct main model object

xxmModel() is used to declare level names. The function returns a model object that is passed as a parameter to subsequent stattements.

mlcfa <- xxmModel(levels = c("student", "teacher"))
## A new model was created.

Add submodels to the model objects

For each declared level xxmSubmodel() is invoked to add corresponding submodel to the model object. The function adds three pieces of information:
1. parents declares a list of parents of the current level.
2. variables declares names of observed dependent (ys), observed independent (xs) and latent variables (etas) for the level.
3. data R data object for the current level.

### Submodel: Student
mlcfa <- xxmSubmodel(model = mlcfa, level = "student", parents = c("teacher"), 
    ys = c("LCE", "PCE", "PVE", "VAE"), xs = , etas = c("Eta_y_Stu"), data = mlcfa.student)
## Submodel for level `student` was created.
### Submodel: Teacher
mlcfa <- xxmSubmodel(model = mlcfa, level = "teacher", parents = , ys = , xs = , 
    etas = c("Eta_y_Tea"), data = mlcfa.teacher)
## Submodel for level `teacher` was created.

Add Within-level parameter matrices for each submodel

For each declared level xxmWithinMatrix() is used to add within-level parameter matrices. For each parameter matrix, the function adds the three matrices constructed earlier:

  • pattern
  • value
  • label (optional)
## Student within matrices (lambda, psi, theta and nu)
mlcfa <- xxmWithinMatrix(model = mlcfa, level = "student", "lambda", pattern = ly11_pat, 
    value = ly11_val, )
## 
## 'lambda' matrix does not exist and will be added.
##  Added `lambda` matrix.
mlcfa <- xxmWithinMatrix(model = mlcfa, level = "student", "psi", pattern = ps1_pat, 
    value = ps1_val, )
## 
## 'psi' matrix does not exist and will be added.
##  Added `psi` matrix.
mlcfa <- xxmWithinMatrix(model = mlcfa, level = "student", "theta", pattern = th1_pat, 
    value = th1_val, )
## 
## 'theta' matrix does not exist and will be added.
##  Added `theta` matrix.
mlcfa <- xxmWithinMatrix(model = mlcfa, level = "student", "nu", pattern = nu1_pat, 
    value = nu1_val, )
## 
## 'nu' matrix does not exist and will be added.
##  Added `nu` matrix.

## Teacher within matrices (psi)
mlcfa <- xxmWithinMatrix(model = mlcfa, level = "teacher", type = "psi", pattern = ps2_pat, 
    value = ps2_val, )
## 
## 'psi' matrix does not exist and will be added.
##  Added `psi` matrix.

Add Across-level parameter matrices to the model

Pairs of levels that share parent-child relationship have regression relationships. xxmBetweenMatrix() is used to add corresponding parameter matrices connecting the two levels.

  • Level with the independent variable is the parent level.
  • Level with the dependent variable is the child level.

For each parameter matrix, the function adds the three matrices constructed earlier:

  • pattern
  • value
  • label (optional)
## Teacher->Student lambda matrix
mlcfa <- xxmBetweenMatrix(model = mlcfa, parent = "teacher", child = "student", 
    type = "lambda", pattern = ly12_pat, value = ly12_val, )
## 
## 'lambda' matrix does not exist and will be added.
##  Added `lambda` matrix.

Estimate model parameters

Estimation process is initiated by xxmRun(). If all goes well, a quick printed summary of results is produced.

mlcfa <- xxmRun(mlcfa)
## ------------------------------------------------------------------------------
## Estimating model parameters
## ------------------------------------------------------------------------------
##               18155.6340456669 
##               17848.9236998019 
##               17775.2193776222 
##               17682.9385002453 
##               17604.4848139105 
##               17569.8858177196 
##               17551.1630582955 
##               17519.5953537898 
##               17508.5965495110 
##               17228.1804189639 
##               16617.5751533529 
##               16434.9399773555 
##               15979.0020360986 
##               15926.6951435682 
##               15629.5097216901 
##               15258.9027853765 
##               15150.9237293020 
##               15049.2515407334 
##               15032.2294037386 
##               14993.1664731226 
##               14976.8940523985 
##               14952.0367050969 
##               14928.9036308383 
##               14921.3422825619 
##               14908.7244057729 
##               14898.2028537533 
##               14879.0566376817 
##               14842.0990721910 
##               14819.4203674741 
##               14796.2891679245 
##               14774.0127509451 
##               14752.9346737992 
##               14733.2833403156 
##               14715.3061114098 
##               14699.1786643516 
##               14678.3209367574 
##               14652.9468438648 
##               14635.2622473743 
##               14616.0367148974 
##               14603.9858747684 
##               14597.8997180790 
##               14589.0306383535 
##               14578.8169365110 
##               14575.7221876542 
##               14574.4780395974 
##               14573.5443861433 
##               14572.4127189161 
##               14571.2332112713 
##               14570.1204988572 
##               14568.0906702493 
##               14562.1801299261 
##               14551.7633329785 
##               14534.1666698798 
##               14506.6329224343 
##               14481.6351799192 
##               14470.5316591543 
##               14464.3211639262 
##               14463.4942027190 
##               14463.3617863764 
##               14463.3408019336 
##               14463.3336805695 
##               14463.3317878270 
##               14463.3308532770 
##               14463.3306313733 
##               14463.3306094635 
##               14463.3306072412 
##               14463.3306070588 
##               14463.3306070522 
## Model converged normally
## nParms: 16
## ------------------------------------------------------------------------------
## *
##  1:                             student_theta_1_1 ::      0.792 [     0.000,      0.000]
## 
##  2:                             student_theta_2_2 ::      1.209 [     0.000,      0.000]
## 
##  3:                             student_theta_3_3 ::      0.830 [     0.000,      0.000]
## 
##  4:                             student_theta_4_4 ::      0.556 [     0.000,      0.000]
## 
##  5:                               student_psi_1_1 ::      1.379 [     0.000,      0.000]
## 
##  6:                                student_nu_1_1 ::     46.073 [     0.000,      0.000]
## 
##  7:                                student_nu_2_1 ::     47.057 [     0.000,      0.000]
## 
##  8:                                student_nu_3_1 ::     46.477 [     0.000,      0.000]
## 
##  9:                                student_nu_4_1 ::     48.006 [     0.000,      0.000]
## 
## 10:                            student_lambda_2_1 ::      0.884 [     0.000,      0.000]
## 
## 11:                            student_lambda_3_1 ::      1.098 [     0.000,      0.000]
## 
## 12:                            student_lambda_4_1 ::      0.763 [     0.000,      0.000]
## 
## 13:                    student_teacher_lambda_2_1 ::      1.169 [     0.000,      0.000]
## 
## 14:                    student_teacher_lambda_3_1 ::      1.224 [     0.000,      0.000]
## 
## 15:                    student_teacher_lambda_4_1 ::      0.646 [     0.000,      0.000]
## 
## 16:                               teacher_psi_1_1 ::      0.565 [     0.000,      0.000]
## 
## ------------------------------------------------------------------------------

Estimate profile-likelihood confidence intervals

Once parameters are estimated, confidence inetrvals are estimated by invoking xxmCI() . Depending on the the number of observations and the complexity of the dependence structure xxmCI() may take very long. xxMCI() displays a table of parameter estimates and CIS.

View results

A summary of results may be retrived as an R list by a call to xxmSummary()

summary <- xxmSummary(mlcfa)
summary
## $fit
## $fit$deviance
## [1] 14463
## 
## $fit$nParameters
## [1] 16
## 
## $fit$nObservations
## [1] 4564
## 
## $fit$aic
## [1] 14495
## 
## $fit$bic
## [1] 14598
## 
## 
## $estimates
##      child  parent        to      from                      label estimate
## 1  student student       LCE       LCE          student_theta_1_1   0.7915
## 3  student student       PCE       PCE          student_theta_2_2   1.2093
## 6  student student       PVE       PVE          student_theta_3_3   0.8297
## 10 student student       VAE       VAE          student_theta_4_4   0.5558
## 11 student student Eta_y_Stu Eta_y_Stu            student_psi_1_1   1.3788
## 12 student student       LCE       One             student_nu_1_1  46.0726
## 13 student student       PCE       One             student_nu_2_1  47.0572
## 14 student student       PVE       One             student_nu_3_1  46.4774
## 15 student student       VAE       One             student_nu_4_1  48.0057
## 17 student student       PCE Eta_y_Stu         student_lambda_2_1   0.8838
## 18 student student       PVE Eta_y_Stu         student_lambda_3_1   1.0983
## 19 student student       VAE Eta_y_Stu         student_lambda_4_1   0.7628
## 21 student teacher       PCE Eta_y_Tea student_teacher_lambda_2_1   1.1687
## 22 student teacher       PVE Eta_y_Tea student_teacher_lambda_3_1   1.2239
## 23 student teacher       VAE Eta_y_Tea student_teacher_lambda_4_1   0.6455
## 24 teacher teacher Eta_y_Tea Eta_y_Tea            teacher_psi_1_1   0.5651
##        low    high
## 1   0.7020  0.8890
## 3   1.0952  1.3357
## 6   0.7286  0.9399
## 10  0.4953  0.6214
## 11  1.1885  1.5939
## 12 45.9221 46.2244
## 13 46.8880 47.2282
## 14 46.3003 46.6561
## 15 47.9010 48.1115
## 17  0.8007  0.9748
## 18  1.0163  1.1886
## 19  0.6991  0.8315
## 21  1.0029  1.3717
## 22  1.0769  1.4025
## 23  0.5473  0.7544
## 24  0.3745  0.8232

Free model object

xxM model object may hog a large amount of RAM outside of R’s memory. This memory will automatically be released, when R’s workspace is cleared by a call to rm(list=ls()) or at the end of the R session. Alternatively, xxmFree() may be called to release memory.

mlcfa <- xxmFree(mlcfa)

For the current dataset, the parameter estimates are:

alt 2lcfa.results