The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

MIMIC models with vbmimic: regularizing measurement and structure

A MIMIC (multiple-indicators multiple-causes) model adds a structural part to the factor model: observed covariates \(X\) predict the factors, which in turn produce the item responses \(Y\). vbmimic() implements the extended MIMIC model of Jin & Chen (2025, Multivariate Behavioral Research), whose contribution is to regularize both parts: spike-and-slab priors select unspecified entries of the measurement design Q_A (items on factors, J x K) and of the structural design Q_B (factors on covariates, K x P). Both matrices use the same -1/0/1 codes as vbfa().

With the default settings, vbmimic() reproduces the published estimator exactly.

Simulating MIMIC data

sim_lvm() (absorbed from the LAWBL package) generates the data. Supply the structural coefficients as a matrix — a sparse design where each factor is predicted by its own covariates is both realistic and estimable:

B <- matrix(0, 3, 9)
for (k in 1:3) B[k, (k * 3 - 2):(k * 3)] <- .3   # 3 covariates per factor

sim <- sim_lvm(N = 500, K = 3, J = 18, P = 9, b = B, phx = 0, rseed = 1)
Y <- sim$dat[, 1:18]        # items first ...
X <- sim$dat[, 19:27]       # ... covariates last

(A scalar b would make every covariate predict every factor, which quickly leaves no disturbance variance — sim_lvm() stops with an informative error if the design is infeasible.)

Anchor at least one part

One modelling caution, learned the empirical way: with both Q_A and Q_B fully exploratory the model converges cleanly but the solution is rotationally ambiguous — factors can merge or swap. Anchoring either part resolves it. Here we anchor two items per factor in the measurement part and leave the whole structural part to the data:

## Q_A is an AZ (anchor-zero) design: each anchor is specified (1) on its own
## factor and fixed to zero on the other two
Q_A <- matrix(-1L, 18, 3)
for (k in 1:3) {
  a <- which(rep(1:3, each = 6) == k)[1:2]
  Q_A[a, ] <- 0L
  Q_A[a, k] <- 1L
}
Q_B <- matrix(-1L, 3, 9)     # structural selection is the question

AZ (anchor-zero) and AO (anchor-only) are the two anchor conventions vbpm’s documentation uses throughout: AO codes each anchor’s intended cell 1 and leaves its cells on the other factors -1, while AZ additionally fixes those cells to 0. AZ is the stronger claim, and it is what anchors the measurement part here; vignette("bifactor") compares the two.

Fit and read

fit <- vbmimic(Y, X, Q_A, Q_B)
fit
#> vbmimic: regularized VB MIMIC model
#>   18 items, 3 factors, 9 covariates, N = 500
#>   converged in 19 iterations (0.19 secs)
#>   ELBO: NA (not available for this model) 
#>   active unspecified loadings    (PIP >= 0.5): 12 of 36
#>   active structural coefficients (PIP >= 0.5): 11 of 27
#> 
#> Components: model, call, nobs, nitem, nfactor, converged, A, B, pi_A, pi_B, Q_A, Q_B, eta, Phi, Sig, U, V, rho, theta, A_var, B_var, iter, flag, time, ELBO, objective, objective_type, standardize, preprocess, path
#> Access them with $ as usual; see ?vbpm_fit.

The structural question — which covariates predict which factors? — is answered by B and its posterior inclusion probabilities pi_B:

round(fit$B, 2)
#>      [,1]  [,2]  [,3]  [,4]  [,5]  [,6] [,7]  [,8]  [,9]
#> [1,] 0.25  0.30  0.32  0.00  0.01  0.00 0.02  0.00 -0.01
#> [2,] 0.00  0.01 -0.18  0.39  0.34  0.30 0.02 -0.01  0.00
#> [3,] 0.03 -0.02  0.01 -0.14 -0.01 -0.01 0.38  0.30  0.30
round(fit$pi_B, 2)
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 1.00 1.00 1.00 0.03 0.03 0.03 0.04 0.03 0.03
#> [2,] 0.03 0.03 1.00 1.00 1.00 1.00 0.04 0.03 0.03
#> [3,] 0.05 0.04 0.03 1.00 0.03 0.04 1.00 1.00 1.00

Compare PIP-based selection with the generating design:

selected <- fit$pi_B >= .5
table(truth = B != 0, selected = selected)
#>        selected
#> truth   FALSE TRUE
#>   FALSE    16    2
#>   TRUE      0    9

Read that table both ways. All nine generating coefficients are selected — no false negatives — but so are two of the eighteen true zeros, and not marginally: both sit at PIP 1.00 in the matrix above (B[2, 3] and B[3, 4], each a factor picking up a covariate from a neighbouring block). Exploratory structural selection at this N is therefore not free of false positives, and a PIP at the ceiling is not on its own evidence that a coefficient is real. Magnitude is the useful second filter here: the two false positives are estimated at -0.18 and -0.14, below every one of the nine true coefficients, whose smallest estimate is 0.25 against a generating value of 0.30.

The measurement side reads exactly as in vbfa():

round(fit$A, 2)[1:6, ]
#>      [,1]  [,2]  [,3]
#> [1,] 0.64  0.00  0.00
#> [2,] 0.63  0.00  0.00
#> [3,] 0.62 -0.02 -0.01
#> [4,] 0.62  0.00  0.01
#> [5,] 0.59  0.01 -0.01
#> [6,] 0.71  0.01 -0.03
round(fit$Phi, 2)            # factor correlations, from the disturbances
#>       [,1]  [,2]  [,3]
#> [1,]  1.00 -0.05 -0.03
#> [2,] -0.05  1.00 -0.01
#> [3,] -0.03 -0.01  1.00

Missing responses

vbmimic() accepts NA in Y; a handful of missing item responses is enough to demonstrate it:

Ym <- Y
Ym[cbind(1:20, rep(1:4, each = 5))] <- NA
sum(is.na(Ym))
#> [1] 20

fitm <- vbmimic(Ym, X, Q_A, Q_B)
fitm$preprocess$n_missing
#> [1] 20

## structural and measurement recovery are essentially unaffected
max(abs(fitm$B - fit$B))
#> [1] 0.002613604
max(abs(fitm$A - fit$A))
#> [1] 0.005325698

The residual covariance here is diagonal (vbmimic() has no LD branch), so a missing response is replaced in-loop by its conditional mean eta_i A', with the conditional variance 1 / V_j carried into the residual sum of squares — the same conditional-moment logic as vbfa()’s diagonal case, simplified because there is no cross-item residual covariance to condition on. As with vbfa(), this in-loop treatment is valid under missing at random (MAR) (Chen, 2021).

X must be complete. Covariates are conditioned on, not modelled, so there is no distribution to impute them from:

Xm <- X; Xm[1, 1] <- NA
vbmimic(Y, Xm, Q_A, Q_B)
#> Error: X contains NA. vbmimic() does not support missing covariates: X is conditioned on, not modelled. Impute or drop them beforehand.

Options worth knowing

References

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.