Modification Indices in umx

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 6,
  fig.height = 5
)
library(umx)
library(OpenMx)
umx_set_auto_plot(FALSE)

Introduction

Modification indices (MIs) flag single parameters whose addition or removal would most improve model fit (if freed and the model re-estimated). They are useful for exploring where a misspecified model struggles, but they are not a substitute for theory-driven model comparison.

umxMI() wraps [OpenMx::mxMI()] and prints the largest indices (by default those above $\chi^2_1(0.01) = 6.63$). By default full = FALSE (only the tested parameter moves — fast). Set full = TRUE to allow all free parameters to re-adjust (slower).

This vignette uses the classic Holzinger & Swineford (1939) data (HS.ability.data in OpenMx): fit a deliberately poor one-factor model (the worst of the usual teaching sequence), run one umxMI() call, then show a better three-factor alternative with umxCompare() (no second MI pass).

Data

data("HS.ability.data", package = "OpenMx")

manifests = c("visual", "cubes", "flags", "paragrap", "sentence", "wordm",
  "addition", "counting", "straight")

# Scale for stable optimization (recommended for this dataset in umx)
df = umx_scale(HS.ability.data)

One-factor model (worst fit → one MI call)

m1 = umxRAM("Holzinger_1f", data = df,
  umxPath("g", to = manifests),
  umxPath(v.m. = manifests),
  umxPath(v1m0 = "g")
)
umxSummary(m1, refModels = FALSE)

Fit is poor (as expected for a single factor on these data). What does umxMI() suggest?

umxMI(m1)

Large indices often point to within-domain residual covariances (e.g. flags_with_visual) or cross-domain loadings that a single factor cannot represent. That pattern hints at multiple abilities rather than one g.

Three-factor alternative (no second MI)

Holzinger and Swineford's data are usually modeled with three correlated abilities:

model3f = "
  spatial =~ visual + cubes + flags
  verbal  =~ paragrap + sentence + wordm
  speed   =~ addition + counting + straight
"
m3 = umxRAM(model3f, data = df, name = "Holzinger_3f")
umxSummary(m3, refModels = FALSE)
umxCompare(m3, m1)

The three-factor model fits substantially better. Remaining residual associations are typically smaller; run umxMI(m3) locally if you want that table.

Bifactor / multi-group (optional, local)

# Bifactor + umxMI, or multi-group one-factor by sex
modelBifactor = "
  g =~ visual + cubes + flags + paragrap + sentence + wordm + addition + counting + straight
  spatial =~ visual + cubes + flags
  verbal  =~ paragrap + sentence + wordm
  speed   =~ addition + counting + straight
  spatial ~~ 0*verbal + 0*speed + 0*g
  verbal  ~~ 0*speed + 0*g
  speed   ~~ 0*g
"
mBio = umxRAM(modelBifactor, data = df, name = "Holzinger_bifactor")
umxMI(mBio)

mMG = umxRAM("Holzinger_1f_MG", data = df, group = "Gender",
  umxPath("g", to = manifests),
  umxPath(v.m. = manifests),
  umxPath(v1m0 = "g")
)
umxMI(mMG)

Summary

| Model | Role | |:-----------------------|:--------------------------------------------------| | One-factor | Baseline misspecification; one umxMI() call | | Three-factor | Theory-driven improvement via umxCompare() | | Bifactor / multi-group | Optional local demos |

Take-home: Use umxMI() to diagnose where a model misfits; prefer umxCompare() on theory-driven competing models for inference.

References

Holzinger, K. J., & Swineford, F. (1939). A study in factor analysis: The stability of a bi-factor solution. Supplementary Educational Monographs, 48. University of Chicago Press.



Try the umx package in your browser

Any scripts or data that you put into this service are public.

umx documentation built on Oct. 11, 2026, 5:07 p.m.