C2 Fit Statistics

Vignette 2: C2 Fit Statistic

The C2 fit statistic is the latest approach to evaluating absolute model fit with latent variable models.

  1. Generate ordinal data
  2. Compute C2 statistic
  3. Plot observed probabilities vs model-implied probabilties

Generate data with only ordinal items. Use 5 items and 1 timepoint with N=500 to keep things simple and because C2 run-times are very long.

Specify Details of Data Generation

  1. Specify the item.types (note that this implicitly specifies the number of items)
  2. Specify how many categories in the items
  3. If it’s a categorical/count item, specify number of categories - if it’s continuous (e.g., Normal or Beta distribution), just set to NA
  4. Specify the Q-matrix
library(CLCM)
N <- 500
number.timepoints <- 1
item.type <- rep('Ordinal', 5) 
sim.categories.j <- c(4, 2, 2, 2, 4) 
lc.prop <- list('Time_1' = c(0.5, 0.5)) 
Q <- matrix(1, nrow = length(item.type), ncol = 1, 
            dimnames = list(paste0('Item_', 1:length(item.type)), NULL))
Q
#>        [,1]
#> Item_1    1
#> Item_2    1
#> Item_3    1
#> Item_4    1
#> Item_5    1

This Q-matrix indicates a single attribute/factor (K=1) will be generated, creating 2 latent classes. Note: the simulation function has a default of 2 latent classes - we’ve just re-created the default Q-matrix.

In summary, 5 ordinal items will be generated, 2 of which have 4 categories, and 3 of which have 2 categories. The latent class proportions will be divided evenly, 50/50, across both latent classes. Only cross-sectional data will be generated. Let’s keep things simple because the C2 statistic is computationally burdensome to compute.

Simulate Item Responses

set.seed(03062021)
sim.dat <- simulate_clcm(N = N, Q = Q, number.timepoints = number.timepoints, 
                         item.type = item.type, 
                         categories.j = sim.categories.j, 
                         lc.prop = lc.prop)
                      

Estimate CLCM

Estimate the model:

mod <- clcm(dat = sim.dat $dat, 
            item.type = sim.dat $item.type, 
            item.names = sim.dat $item.names, 
            max.diff = 0.001, 
            Q = sim.dat$Q)   
#> iteration: 1  max diff in item parameter estimates: 0.986824
#> iteration: 2  max diff in item parameter estimates: 0.287047
#> iteration: 3  max diff in item parameter estimates: 0.164523
#> iteration: 4  max diff in item parameter estimates: 0.095628
#> iteration: 5  max diff in item parameter estimates: 0.056996
#> iteration: 6  max diff in item parameter estimates: 0.035052
#> iteration: 7  max diff in item parameter estimates: 0.022075
#> iteration: 8  max diff in item parameter estimates: 0.014072
#> iteration: 9  max diff in item parameter estimates: 0.008969
#> iteration: 10  max diff in item parameter estimates: 0.006492
#> iteration: 11  max diff in item parameter estimates: 0.004711
#> iteration: 12  max diff in item parameter estimates: 0.003381
#> iteration: 13  max diff in item parameter estimates: 0.002301
#> iteration: 14  max diff in item parameter estimates: 0.001612
#> iteration: 15  max diff in item parameter estimates: 0.001268
#> iteration: 16  max diff in item parameter estimates: 0.001385
#> iteration: 17  max diff in item parameter estimates: 0.000864

Model Fit

After fitting the model, let’s proceed to look at absolute fit statistics. The C2 statistic is slow to compute because of the numerical jacobian that is computed.

mod.fit <- C2_clcm(mod) 
#> C2 = 5.038  P-Value = 0.411  RMSEA = 0.045

As you’d expect, the model-fit statistic indicates that the model fits the data.

Plots

Plot Model Implied Probabilities vs Observed Probabilities

p.mod <- mod.fit$p.mod
p.obs <- mod.fit$p.obs
p.range <- range(p.mod, p.obs)

#
plot(p.mod, ylim = p.range, xlab = paste0(nrow(p.mod), ' model-implied probabilities'), 
     ylab = 'Probability', main = 'Model-Implied Probabilities')

#
plot(p.obs, ylim = p.range, xlab = paste0(nrow(p.obs), ' observed probabilities'), 
     ylab = 'Probability', main = 'Observed Probabilities')

#
plot(x = p.obs, y = p.mod, main = paste0(nrow(p.mod), ' Probabilities'),
     ylim = p.range, xlim = p.range, 
     xlab = 'Observed Probabilities', ylab = 'Model-Implied Probabilities')
abline(a = 0, b = 1)

```