Tutorial PMC

This tutorial requires tidySEM version 0.2.11 or higher and OpenMx. Make sure both packages are installed and loaded.

library(tidySEM)
library(OpenMx)

Example Data

We simulate a dataset with two continuous indicators, and two latent classes.

set.seed(10)
n <- 200
# Set class-specific means
class_means <- c(rep(0, floor(0.3 * n)), rep(2, ceiling(0.7 * n)))

# Simulate continuous indicators
df <- rnorm(2 * n, mean = rep(class_means, 2))
df <- data.frame(matrix(df, nrow = n))
names(df) <- paste0("X", 1:2)

Next, we estimate 1-3 class solutions:

res <- mx_profiles(data = df, classes = 1:3)

Optionally, one can perform the bootstrapped likelihood ratio test. This takes a long time to run, so we conduct only 100 replications, rather than a more sensible number like 1000. To accelerate computations, we can use the future package for parallel computing (see ?plan to select the appropriate back-end for your system).

library(future)
plan(multisession)
res_blrt <- BLRT(res, replications = 100)
res_blrt
#> Bootstrapped Likelihood Ratio Test:
#> 
#>  null  alt    lr df blrt_p samples
#>  mix1 mix2 91.95  3   0.00      99
#>  mix2 mix3  3.41  3   0.44      96

This test confirms that the 2-class solution is significantly better than the 1-class solution - but the 3-class solution offers no further significant improvement.

Next, we use predictive model comparison. If all variables are continuous, the function pmc() uses the srmr() function to compare the standardized root mean squared (SRMR) difference between the correlation matrices of the real and model-implied data:

set.seed(1)
res_pmc <- pmc(res)
#> PMC model comparison using SRMR:
#> 
#>  comparison null  alt null_stat alt_stat    lb     ub sig
#>     dif_seq mix1 mix2     0.517    0.036 -0.64 -0.349   *
#>     dif_seq mix2 mix3     0.036    0.034 -0.07  0.088    
#>     dif_one mix1 mix2     0.517    0.036 -0.64 -0.349   *
#>     dif_one mix1 mix3     0.517    0.034 -0.64 -0.364   *

This test, too, confirms that the 2-class solution is significantly better than the 1-class solution - but the 3-class solution offers no further significant improvement.

If we conduct an LCA with ordinal indicators, the function uses a chi squared statistic instead of SRMR. To demonstrate this, first, we convert the data to ordinal. Note that real research data should not be polytomized like this. Then, we estimate a model for ordinal indicators using mx_lca(). We specify a custom function that references x and y, where both are of type data.frame, and we supply it as an argument to pmc():

# Convert the indicators to ordinal
df[] <- lapply(df, cut, breaks = 3, labels = FALSE)
df[] <- lapply(df, mxFactor, levels = 1:3)
res_cat <- mx_lca(df, classes = 1:3)
pmc(res_cat, reps = 20)
#> PMC model comparison using chi squared:
#> 
#>  comparison null  alt null_stat alt_stat    lb  ub sig
#>     dif_seq mix1 mix2      -143     -187 -74.0 -27   *
#>     dif_seq mix2 mix3      -187     -181  -3.3  18    
#>     dif_one mix1 mix2      -143     -187 -74.0 -27   *
#>     dif_one mix1 mix3      -143     -181 -61.6 -15   *

The argument FUN allows users to override these defaults, and specify any custom function to compare the observed data (x) and model-implied data (y).

References

Van Lissa, C. J., Garnier-Villarreal, M., & Anadria, D. (2023). Recommended Practices in Latent Class Analysis using the Open-Source R-Package tidySEM. Structural Equation Modeling. https://doi.org/10.1080/10705511.2023.2250920