Skip to contents

Fit a two-stage path analysis (2S-PA) model.

Usage

tspa_mx_model(
  mx_model,
  data,
  mat_ld,
  mat_ev,
  mat_int = NULL,
  fs_lv_names = NULL,
  ...
)

Arguments

mx_model

A model object of class OpenMx::MxRAMModel, created by OpenMx::mxModel() or the umx package. This is the structural model part.

data

A data frame containing factor scores.

mat_ld

A \(p \times p\) matrix indicating the loadings of the factor scores on the latent variables. The ith row indicate loadings of the ith factor score variable on the latent variables. This could be one of the following:

  • A matrix created by OpenMx::mxMatrix() with loading values.

  • A named numeric matrix, with rownames and column names matching the factor score and latent variables.

  • A named character matrix, with rownames and column names matching the factor score and latent variables, and the character values indicating the variable names in data for the corresponding loadings.

mat_ev

Similar to mat_ld but for the error variance-covariance matrix of the factor scores.

mat_int

Similar to mat_ld but for the measurement intercept matrix of the factor scores.

fs_lv_names

A named character vector where each element is the name of a factor score variable, and the names of the vector are the corresponding name of the latent variables.

...

Additional arguments passed to OpenMx::mxModel().

Value

An object of class OpenMx::MxModel. Note that the model has not been run.

Examples

library(mirt)
#> Loading required package: stats4
#> Loading required package: lattice
library(umx)
#> Loading required package: OpenMx
#> To take full advantage of multiple cores, use:
#>   mxOption(key='Number of Threads', value=parallel::detectCores()) #now
#>   Sys.setenv(OMP_NUM_THREADS=parallel::detectCores()) #before library(OpenMx)
#> For an overview type '?umx'
#> 
#> Attaching package: ‘umx’
#> The following object is masked from ‘package:stats’:
#> 
#>     loadings
library(OpenMx)
# Simulate data with mirt
set.seed(1324)
num_obs <- 100
# Simulate theta
eta <- MASS::mvrnorm(num_obs, mu = c(0, 0), Sigma = diag(c(1, 1 - 0.5^2)),
                     empirical = TRUE)
th1 <- eta[, 1]
th2 <- -1 + 0.5 * th1 + eta[, 2]
# items and response data
a1 <- matrix(1, 10)
d1 <- matrix(rnorm(10))
a2 <- matrix(runif(10, min = 0.5, max = 1.5))
d2 <- matrix(rnorm(10))
dat1 <- mirt::simdata(a = a1, d = d1,
                      N = num_obs, itemtype = "2PL", Theta = th1)
dat2 <- mirt::simdata(a = a2, d = d2, N = num_obs,
                      itemtype = "2PL", Theta = th2)
# Factor scores
mod1 <- mirt(dat1, model = 1, itemtype = "Rasch", verbose = FALSE)
mod2 <- mirt(dat2, model = 1, itemtype = "2PL", verbose = FALSE)
fs1 <- fscores(mod1, full.scores.SE = TRUE)
fs2 <- fscores(mod2, full.scores.SE = TRUE)
# Combine factor scores and standard errors into data set
fs_dat <- as.data.frame(cbind(fs1, fs2))
names(fs_dat) <- c("fs1", "se_fs1", "fs2", "se_fs2")
# Compute reliability and error variances
fs_dat <- within(fs_dat, expr = {
  rel_fs1 <- 1 - se_fs1^2
  rel_fs2 <- 1 - se_fs2^2
  ev_fs1 <- se_fs1^2 * (1 - se_fs1^2)
  ev_fs2 <- se_fs2^2 * (1 - se_fs2^2)
})
# OpenMx model (from umx so that lavaan syntax can be used)
fsreg_umx <- umxLav2RAM(
  "
    fs2 ~ fs1
    fs2 + fs1 ~ 1
  ",
  printTab = FALSE)
#> 
#> ?plot.MxModel options: std, means, digits, strip_zero, file, splines=T/F/ortho,..., min=, max =, same = , fixed, resid= 'circle|line|none'
# Prepare loading and error covariance matrices
cross_load <- matrix(c("rel_fs2", NA, NA, "rel_fs1"), nrow = 2) |>
  `dimnames<-`(rep(list(c("fs2", "fs1")), 2))
err_cov <- matrix(c("ev_fs2", NA, NA, "ev_fs1"), nrow = 2) |>
  `dimnames<-`(rep(list(c("fs2", "fs1")), 2))
# Create 2S-PA model (with definition variables)
tspa_mx <-
  tspa_mx_model(fsreg_umx,
    data = fs_dat,
    mat_ld = cross_load,
    mat_ev = err_cov
  )
# Run OpenMx
tspa_mx_fit <- mxRun(tspa_mx)
#> Running 2SPAD with 5 parameters
# Summarize the results
summary(tspa_mx_fit)
#> Summary of 2SPAD 
#>  
#> free parameters:
#>           name matrix row col     Estimate Std.Error A
#> 1   fs1_to_fs2   m1.A fs2 fs1  0.526025404 0.2351855  
#> 2 fs2_with_fs2   m1.S fs2 fs2  0.857316415 0.2253235  
#> 3 fs1_with_fs1   m1.S fs1 fs1  0.541308628 0.1472492  
#> 4   one_to_fs2   m1.M   1 fs2 -0.003189929 0.1234779  
#> 5   one_to_fs1   m1.M   1 fs1  0.003287301 0.1002638  
#> 
#> Model Statistics: 
#>                |  Parameters  |  Degrees of Freedom  |  Fit (-2lnL units)
#>        Model:              5                    395              453.4113
#>    Saturated:             NA                     NA                    NA
#> Independence:             NA                     NA                    NA
#> Number of observations/statistics: 100/400
#> 
#> Information Criteria: 
#>       |  df Penalty  |  Parameters Penalty  |  Sample-Size Adjusted
#> AIC:      -336.5887               463.4113                 464.0496
#> BIC:     -1365.6310               476.4371                 460.6459
#> CFI: NA 
#> TLI: 1   (also known as NNFI) 
#> RMSEA:  0  [95% CI (NA, NA)]
#> Prob(RMSEA <= 0.05): NA
#> To get additional fit indices, see help(mxRefModels)
#> timestamp: 2024-09-06 04:15:37 
#> Wall clock time: 0.152458 secs 
#> optimizer:  SLSQP 
#> OpenMx version number: 2.21.12 
#> Need help?  See help(mxSummary) 
#>