umxCompare: Print a comparison table of two or more 'OpenMx::mxModel()'s,...

View source: R/umxCompare.R

umxCompareR Documentation

Print a comparison table of two or more OpenMx::mxModel()s, nicely formatted.

Description

umxCompare compares two or more OpenMx::mxModel()s. It has several nice features:

  1. Direct control of precision via digits=. p-values use APA style.

  2. Report = directs publication to your chosen format: markdown, html, latex.

  3. Columns arranged to make for easy visual comparison.

  4. report = 'inline', includes a summary sentence suitable for your report.

  5. report = "html" opens a web table in your browser to paste into a word processor.

Interpreting fit under WLS/DWLS and Genomic SEM

Hu & Bentler (1999) style cutoffs (e.g. CFI > .95, RMSEA < .06) were developed for maximum likelihood with continuous data. They do not transfer to WLS/DWLS (Shi et al., 2020), and still less to Genomic SEM (DWLS on LDSC genetic covariances with an estimated sampling covariance, often with bookkeeping numObs = 1).

  • Incremental indices (CFI, TLI, NFI, ...) compare the model to an independence baseline under the same weight matrix. That baseline behaves differently than under ML. Report CFI/TLI only descriptively; do not apply conventional cutoffs.

  • Absolute fit: SRMR (and residual matrices) is preferred. SRMR lives on a standardized residual metric and is less hostage to weight-matrix / N scaling. Rough guide: SRMR < 0.10 suggests acceptable residual structure (not a hard law).

  • RMSEA folds in chi-square scaling and N; it is often misleading for WLS and especially for GSEM. Do not use the ML cutoff of < .06.

  • Nested model building: prefer scaled chi-square difference tests and DeltaSRMR over DeltaCFI alone (see Details for what umxCompare computes).

Usage

umxCompare(
  base = NULL,
  comparison = NULL,
  all = TRUE,
  digits = 3,
  report = c("markdown", "html", "inline"),
  compareWeightedAIC = FALSE,
  silent = FALSE,
  file = "tmp.html",
  uncertainty = c("none", "SE", "MLR", "CI")
)

Arguments

base

The base OpenMx::mxModel() for comparison

comparison

The model (or list of models) which will be compared for fit with the base model (can be empty)

all

Whether to make all possible comparisons if there is more than one base model (defaults to T)

digits

rounding for p-values etc.

report

"markdown" (default), "inline" (a sentence suitable for inclusion in a paper), or "html". create a web table and open your default browser. (handy for getting tables into Word, and other text systems!)

compareWeightedAIC

Show the Wagenmakers AIC weighted comparison (default = FALSE)

silent

(don't print, just return the table as a dataframe (default = FALSE)

file

file to write html too if report = "html" (defaults to "tmp.html")

uncertainty

What type of parameter uncertainty to report: "SE" (standard ML standard errors), "MLR" (robust standard errors and robust fit), "CI" (profile likelihood confidence intervals), or "none" (none).

Details

What umxCompare does for WLS

  • Refuses mixed WLS vs ML comparisons (same engine required).

  • Continuous WLS with cached implied_jacobian:

    • Table Chi is the same Satorra-Bentler (2010) scaled omnibus as umxSummary() (F/c from output$fit), not OpenMx Browne residual output$chi (those two differ under DWLS). Saturated residual df reports Chi = 0, CFI = 1, RMSEA = 0.

    • Nested diffFit is SB-2010 scaled \Delta F = F_{nested} - F_{base} (requires both models at comparable minima of the same WLS objective: same moments, useWeight, and N). If F_{nested} < F_{base} (often multigroup optimization), diffFit is NA with a warning - not a negative chi-square with p = 1.

  • Genomic SEM (MxModelGSEM): nested difference on the GSEM DWLS chi-square scale (same discrepancy as the structural fit; not a substitute for ML LRT). If LDSC matrices were nearPD-smoothed, a fiduciary warning notes that difference tests may look artificially precise.

  • Table also reports SRMR / DeltaSRMR (preferred absolute residual summary) and CFI / DeltaCFI (descriptive only). AIC may appear but is not a primary GSEM decision rule under asymptotic / bookkeeping N.

Ordinal WLS is a separate track: robust CFI/TLI/RMSEA use Savalei (2021) catML corrections in umxSummary(); Hu-Bentler cutoffs can apply to those robust indices. That exception does not apply to continuous WLS or Genomic SEM.

Best practice: report estimates with SEs, SRMR (and residuals), nested SB/GSEM difference tests, and avoid single-number "good fit" claims from CFI or RMSEA.

Constraints and the EP column

EP is OpenMx's count of free parameters, summary(model)$estimatedParameters. Equality OpenMx::mxConstraint()s are not removed from that count. OpenMx adds their rows to observedStatistics, and degrees of freedom already include them. A common-pathway model that sets each factor with A + C + E = 1 can therefore show a larger EP than an independent-pathway model while \Delta df is 0. Both numbers are the OpenMx counts. When sum(summary(model)$constraints) is above 0, umxCompare adds one line for that model:

⁠Note: df for 'CP' reflects 3 constraints: 51 estimated parameters (48 effective after constraints).⁠

With silent = TRUE, the lines are attr(table, "constraintNotes"), one element per model. A comparison in which no model has an equality constraint does not set that attribute. Inequality constraints contribute 0 and do not produce a line.

References

https://github.com/tbates/umx

See Also

  • umxSummary(), umxRAM(),umxCompare()

Other Model Summary and Comparison: umx, umxEquate(), umxMI(), umxReduce(), umxSetParameters(), umxSummary(), xmu_compare_WLS(), xmu_robust_WLS_fit()

Examples

## Not run: 
require(umx)
data(demoOneFactor)
manifests = names(demoOneFactor)

m1 = umxRAM("One Factor", data = demoOneFactor, type = "cov",
	umxPath("G", to = manifests),
	umxPath(var = manifests),
	umxPath(var = "G", fixedAt = 1)
)

m2 = umxModify(m1, update = "G_to_x2", name = "drop_path_2_x2")
umxCompare(m1, m2)
umxCompare(m1, m2, report = "inline") # Add English-sentence descriptions
umxCompare(m1, m2, report = "html") # Open table in browser

# Two comparison models
m3 = umxModify(m2, update = "G_to_x3", name = "drop_path_2_x2_and_3")

umxCompare(m1, c(m2, m3))
umxCompare(m1, c(m2, m3), compareWeightedAIC = TRUE)
umxCompare(c(m1, m2), c(m2, m3), all = TRUE)

manifests = names(demoOneFactor)
m1 = umxRAM("WLS", data = demoOneFactor, type = "DWLS",
	umxPath("G", to = manifests),
	umxPath(var = manifests),
	umxPath(var = "G", fixedAt = 1)
)

m2 = umxModify(m1, update = "G_to_x2", name = "drop_path_2_x2")
umxCompare(m1, m2)
umxCompare(m1, m2, report = "inline") # Add English-sentence descriptions
umxCompare(m1, m2, report = "html") # Open table in browser

## End(Not run)

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