Skip to content

Add fit statistics display to nice_lavaanPlot with support for regular/scaled/robust types#50

Merged
rempsyc merged 5 commits into
mainfrom
copilot/add-fit-statistics-to-plot
Nov 19, 2025
Merged

Add fit statistics display to nice_lavaanPlot with support for regular/scaled/robust types#50
rempsyc merged 5 commits into
mainfrom
copilot/add-fit-statistics-to-plot

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Nov 19, 2025

✅ Implementation Complete: Add fit statistics to nice_lavaanPlot

Problem Statement:
User requested a way to add fit statistics (CFI, TLI, RMSEA, SRMR, etc.) to the plot generated by nice_lavaanPlot(). Since Graphviz has limited flexibility, the solution needed to integrate seamlessly with the existing plot structure.

Solution Implemented:
Added a new fit_stats parameter to nice_lavaanPlot() that automatically extracts and displays fit indices from the lavaan model directly on the plot.

Features:

  • Default fit statistics: fit_stats = TRUE displays CFI, TLI, RMSEA, SRMR
  • Custom selection: fit_stats = c("cfi", "rmsea", "chisq", "pvalue") for specific indices
  • Multiple fit types: NEW - fit_stats_type parameter supports displaying regular, scaled, and robust fit indices
  • Auto-formatting: Intelligently formats each index (3 decimals for CFI/TLI/RMSEA/SRMR, 2 for chi-square, etc.)
  • Seamless integration: Works with existing title and note parameters
  • Smart positioning: Placed at top when title present, bottom otherwise
  • Multi-line display: Each fit statistic type appears on a separate line
  • Validation: Warns about unrecognized fit index names to help catch typos
  • Warning for overrides: Warns when fit_stats overrides graph_options$label or $labelloc

Recent improvements (based on code review):

  1. Updated NEWS.md to mention both fit_stats and fit_stats_type parameters
  2. Added validation to warn about unrecognized fit index names (e.g., "rmesa" instead of "rmsea")
  3. Updated warning condition to include fit_stats parameter

New fit_stats_type parameter:

  • Defaults to c("regular", "scaled", "robust") to show all available types
  • Only shows types that are available in the model
  • Works with robust estimators (MLR, MLM, etc.)
  • Each type is labeled and displayed on its own line
  • When only one type is specified, no type label is shown

Available fit indices:

  • "chisq", "df", "pvalue" - Model chi-square test
  • "cfi", "tli" - Comparative and Tucker-Lewis indices
  • "rmsea" - Root mean square error of approximation
  • "srmr" - Standardized root mean square residual
  • "aic", "bic" - Information criteria

Example usage:

# With robust estimator - shows all three types on separate lines
fit_robust <- sem(model, data, estimator = "MLR")
nice_lavaanPlot(fit_robust, title = "Three-Factor CFA", fit_stats = TRUE)
# Output:
# Regular: CFI = 0.486, TLI = -0.156, RMSEA = 0.255, SRMR = 0.116
# Scaled: CFI = 0.473, TLI = -0.185, RMSEA = 0.253
# Robust: CFI = 0.485, TLI = -0.158, RMSEA = 0.255

# Show only regular and scaled
nice_lavaanPlot(fit_robust, fit_stats = TRUE, fit_stats_type = c("regular", "scaled"))

# Show only regular (no type label)
nice_lavaanPlot(fit, fit_stats = TRUE, fit_stats_type = "regular")

# Validation - warns about typos
nice_lavaanPlot(fit, fit_stats = c("cfi", "rmesa"))  # Warning: Unrecognized fit indices: rmesa

Testing:

  • ✅ All tests pass (added 3 new tests for fit_stats_type)
  • ✅ Validation logic tested and working correctly
  • ✅ Backward compatibility maintained
  • ✅ Works with CFA, SEM, and lavaan models
  • ✅ Handles robust estimators correctly

Files modified:

  • NEWS.md - Updated to mention both parameters and feature details
  • R/nice_lavaanPlot.R - Added fit_stats_type parameter, validation, and multi-line support
  • man/nice_lavaanPlot.Rd - Updated documentation
  • tests/testthat/test-nice_lavaanPlot.R - Added comprehensive tests

This solution directly addresses the user's request from the issue and provides a clean, user-friendly API for displaying fit statistics on SEM/CFA plots.

Original prompt

This section details on the original issue you should resolve

<issue_title>Add information about fit statistics to nice_lavaanPlot</issue_title>
<issue_description>Consider the following user comment (follow-up on #48):

I think adding a title option to nice_lavaanPlot is a notable improvement. It is too bad that the underlying Graphviz package does not offer more flexibility.

Can you think of any other way to add information about fit statistics to the plot? I have extracted the fit statistics below from the lavaan output as a data frame.

        | CFI   | TLI   | RMSEA | SRMR
     -- |    -- |    -- |    -- |    --
regular | 0.854 | 0.838 | 0.110 | 0.113
scaled  | 0.729 | 0.698 | 0.116 | 0.113
robust  | 0.584 | 0.538 | 0.125 | 0.113

Is there a way to save that info as text or as an image file and then incorporate/merge/combine it with the image produced by nice_lavaanPlot?</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: rempsyc <13123390+rempsyc@users.noreply.github.com>
Copilot AI changed the title [WIP] Add information about fit statistics to nice_lavaanPlot Add fit statistics display to nice_lavaanPlot Nov 19, 2025
Copilot AI requested a review from rempsyc November 19, 2025 21:30
@rempsyc
Copy link
Copy Markdown
Owner

rempsyc commented Nov 19, 2025

@copilot thanks. Also, the user specifically asked for three types of fit indices. Should we have an extra parameter like fit_stats_type, with default = c("regular", "scaled", "robust")? Could be separated on separate lines but in one "block", with "\n"?

Current reprex:

library(lavaan)
library(lavaanExtra)
packageVersion("lavaanExtra")
#> [1] '0.2.2.2'

data <- HolzingerSwineford1939
data$visual <- rowMeans(data[paste0("x", 1:3)])
data$textual <- rowMeans(data[paste0("x", 4:6)])
data$speed <- rowMeans(data[paste0("x", 7:9)])
IV <- c("ageyr", "grade")
M <- "visual"
DV <- c("speed", "textual")
mediation <- list(speed = M, textual = M, visual = IV)
indirect <- list(M = M, DV = DV, IV = IV)
model <- write_lavaan(
  mediation = mediation,
  indirect = indirect,
  label = TRUE
)
fit <- sem(model, data = data)

# Default fit statistics
nice_lavaanPlot(fit, title = "Three-Factor CFA", fit_stats = TRUE)
image
# Displays: CFI = 0.931, TLI = 0.896, RMSEA = 0.092, SRMR = 0.058

# Custom selection
nice_lavaanPlot(fit, fit_stats = c("cfi", "rmsea", "chisq", "pvalue"))
image
# Combined with other parameters
nice_lavaanPlot(fit, 
                title = "CFA Model",
                note = paste0("N = ", fit@Data@nobs),
                fit_stats = TRUE
)
image

Created on 2025-11-19 with reprex v2.1.1

…ular/scaled/robust)

Co-authored-by: rempsyc <13123390+rempsyc@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 19, 2025

@copilot thanks. Also, the user specifically asked for three types of fit indices. Should we have an extra parameter like fit_stats_type, with default = c("regular", "scaled", "robust")? Could be se...

Implemented! Added fit_stats_type parameter with default c("regular", "scaled", "robust"). Each type displays on a separate line with a label (commit fc5a018).

Example with your model using MLR estimator:

nice_lavaanPlot(fit, title = "Three-Factor CFA", fit_stats = TRUE)

Will display:

Regular: CFI = 0.486, TLI = -0.156, RMSEA = 0.255, SRMR = 0.116
Scaled: CFI = 0.473, TLI = -0.185, RMSEA = 0.253
Robust: CFI = 0.485, TLI = -0.158, RMSEA = 0.255

You can also select specific types:

# Only regular and scaled
nice_lavaanPlot(fit, fit_stats = TRUE, fit_stats_type = c("regular", "scaled"))

# Only regular (no type label when single type)
nice_lavaanPlot(fit, fit_stats = TRUE, fit_stats_type = "regular")

The function automatically detects which types are available and only displays those that exist in your model.

Copilot AI changed the title Add fit statistics display to nice_lavaanPlot Add fit statistics display to nice_lavaanPlot with support for regular/scaled/robust types Nov 19, 2025
@rempsyc rempsyc marked this pull request as ready for review November 19, 2025 22:09
@rempsyc rempsyc requested review from Copilot and removed request for Copilot November 19, 2025 22:09
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds the ability to display model fit statistics directly on SEM/CFA plots created by nice_lavaanPlot(), addressing a user-requested feature. The implementation supports displaying default or custom fit indices (CFI, TLI, RMSEA, SRMR, chi-square, AIC, BIC, etc.) with automatic formatting and multi-line display for models using robust estimators that provide regular/scaled/robust versions of fit indices.

Key Changes

  • Added fit_stats parameter to specify which fit indices to display (TRUE for defaults, or character vector for custom selection)
  • Added fit_stats_type parameter to support displaying multiple types of fit indices (regular/scaled/robust) for robust estimators
  • Integrated fit statistics into the existing HTML table structure with proper positioning and formatting

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
R/nice_lavaanPlot.R Core implementation adding fit statistics extraction, formatting (with type-specific decimal precision), HTML table integration, and multi-type display logic
man/nice_lavaanPlot.Rd Documentation for new fit_stats and fit_stats_type parameters with usage examples
tests/testthat/test-nice_lavaanPlot.R Comprehensive test suite with 9 new tests covering default/custom fit stats, integration with title/note, and multi-type display scenarios
NEWS.md Changelog entry for version 0.2.2.2 documenting the new feature
DESCRIPTION Version bump to 0.2.2.2 with updated date

Comment thread NEWS.md Outdated
* New CRAN submission

## lavaanExtra 0.2.2.2
* `nice_lavaanPlot()`: add `fit_stats` argument to optionally display fit statistics (CFI, TLI, RMSEA, SRMR, etc.) directly on the plot
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NEWS.md entry should mention the new fit_stats_type parameter as well, since it's a significant part of this feature (supporting regular/scaled/robust fit index types). Consider updating to: "nice_lavaanPlot(): add fit_stats and fit_stats_type arguments to optionally display fit statistics (CFI, TLI, RMSEA, SRMR, etc.) directly on the plot, with support for regular/scaled/robust types when using robust estimators"

Suggested change
* `nice_lavaanPlot()`: add `fit_stats` argument to optionally display fit statistics (CFI, TLI, RMSEA, SRMR, etc.) directly on the plot
* `nice_lavaanPlot()`: add `fit_stats` and `fit_stats_type` arguments to optionally display fit statistics (CFI, TLI, RMSEA, SRMR, etc.) directly on the plot, with support for regular/scaled/robust types when using robust estimators

Copilot uses AI. Check for mistakes.
Comment thread R/nice_lavaanPlot.R
#' statistics (CFI, TLI, RMSEA, SRMR) at the bottom of the plot. If a character
#' vector, displays only the specified fit indices (e.g., `c("cfi", "rmsea")`).
#' Available indices include: "chisq", "df", "pvalue", "cfi", "tli", "rmsea",
#' "srmr", "aic", "bic". Defaults to `NULL` (no fit statistics displayed).
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fit_stats documentation should mention that it will override any label or labelloc settings in graph_options, similar to how the title and note parameters document this behavior. Consider adding: "Note: This will override any label or labelloc settings in graph_options." at the end of the parameter description.

Suggested change
#' "srmr", "aic", "bic". Defaults to `NULL` (no fit statistics displayed).
#' "srmr", "aic", "bic". Defaults to `NULL` (no fit statistics displayed).
#' Note: This will override any `label` or `labelloc` settings in `graph_options`.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: to me it seems both the note and fit_stats = TRUE arguments are compatible at the same time, both appear correctly.

Comment thread R/nice_lavaanPlot.R
Comment on lines +149 to +161
for (idx in indices_to_show) {
# Try to get the fit measure with the appropriate suffix
fit_name <- paste0(idx, suffix)

if (fit_name %in% names(all_fit_measures)) {
val <- all_fit_measures[[fit_name]]
if (!is.na(val)) {
formatted_val <- format_fit_value(idx, val)
type_values <- c(type_values, paste0(toupper(idx), " = ", formatted_val))
type_has_values <- TRUE
}
}
}
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When users specify custom fit indices via fit_stats, invalid index names are silently ignored. This could be confusing if a user misspells an index name (e.g., "rmesa" instead of "rmsea"). Consider adding validation after line 108 to check if the requested indices exist in the model's fit measures, and issue a warning for any unrecognized names. For example: unknown_indices <- setdiff(indices_to_show, sub("\\.(scaled|robust)$", "", names(all_fit_measures))); if (length(unknown_indices) > 0) warning("Unrecognized fit indices: ", paste(unknown_indices, collapse = ", "))

Copilot uses AI. Check for mistakes.
Comment thread R/nice_lavaanPlot.R Outdated
Comment on lines 189 to 191
if (!is.null(title) || !is.null(note)) {
if (!is.null(graph_options$label) || !is.null(graph_options$labelloc)) {
warning("title/note parameters override graph_options$label and graph_options$labelloc")
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning condition should include fit_stats to prevent it from being silently ignored. When only fit_stats is provided (without title or note), and the user has specified custom graph_options$label or graph_options$labelloc, these will be overridden without warning. Consider changing the condition to: if (!is.null(title) || !is.null(note) || !is.null(fit_stats)) {

Suggested change
if (!is.null(title) || !is.null(note)) {
if (!is.null(graph_options$label) || !is.null(graph_options$labelloc)) {
warning("title/note parameters override graph_options$label and graph_options$labelloc")
if (!is.null(title) || !is.null(note) || !is.null(fit_stats)) {
if (!is.null(graph_options$label) || !is.null(graph_options$labelloc)) {
warning("title/note/fit_stats parameters override graph_options$label and graph_options$labelloc")

Copilot uses AI. Check for mistakes.
@rempsyc
Copy link
Copy Markdown
Owner

rempsyc commented Nov 19, 2025

@copilot apply changes based on the comments in this thread

…lid fit indices, update warning condition

Co-authored-by: rempsyc <13123390+rempsyc@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Nov 19, 2025

@copilot apply changes based on the comments in this thread

Applied all review feedback (commit 379bf32):

  1. NEWS.md: Updated to mention both fit_stats and fit_stats_type parameters with full feature description
  2. Validation: Added warning for unrecognized fit index names (e.g., "rmesa" will warn: "Unrecognized fit indices: rmesa")
  3. Warning condition: Updated to include fit_stats parameter so it warns when overriding graph_options$label or graph_options$labelloc

Note: Did not add the override note to the fit_stats documentation per @rempsyc's comment that note and fit_stats are compatible.

@rempsyc rempsyc merged commit dc8a232 into main Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add information about fit statistics to nice_lavaanPlot

3 participants