Add fit statistics display to nice_lavaanPlot with support for regular/scaled/robust types#50
Conversation
Co-authored-by: rempsyc <13123390+rempsyc@users.noreply.github.com>
|
@copilot thanks. Also, the user specifically asked for three types of fit indices. Should we have an extra parameter like 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)
# 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"))
# Combined with other parameters
nice_lavaanPlot(fit,
title = "CFA Model",
note = paste0("N = ", fit@Data@nobs),
fit_stats = TRUE
)
Created on 2025-11-19 with reprex v2.1.1 |
…ular/scaled/robust) Co-authored-by: rempsyc <13123390+rempsyc@users.noreply.github.com>
Implemented! Added Example with your model using MLR estimator: nice_lavaanPlot(fit, title = "Three-Factor CFA", fit_stats = TRUE)Will display: 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. |
There was a problem hiding this comment.
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_statsparameter to specify which fit indices to display (TRUE for defaults, or character vector for custom selection) - Added
fit_stats_typeparameter 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 |
| * 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 |
There was a problem hiding this comment.
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"
| * `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 |
| #' 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). |
There was a problem hiding this comment.
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.
| #' "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`. |
There was a problem hiding this comment.
Note: to me it seems both the note and fit_stats = TRUE arguments are compatible at the same time, both appear correctly.
| 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 | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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 = ", "))
| 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") |
There was a problem hiding this comment.
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)) {
| 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 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>
Applied all review feedback (commit 379bf32):
Note: Did not add the override note to the |



✅ 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_statsparameter tonice_lavaanPlot()that automatically extracts and displays fit indices from the lavaan model directly on the plot.Features:
fit_stats = TRUEdisplays CFI, TLI, RMSEA, SRMRfit_stats = c("cfi", "rmsea", "chisq", "pvalue")for specific indicesfit_stats_typeparameter supports displaying regular, scaled, and robust fit indicestitleandnoteparametersRecent improvements (based on code review):
fit_statsandfit_stats_typeparametersfit_statsparameterNew
fit_stats_typeparameter:c("regular", "scaled", "robust")to show all available typesAvailable 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 criteriaExample usage:
Testing:
Files modified:
NEWS.md- Updated to mention both parameters and feature detailsR/nice_lavaanPlot.R- Added fit_stats_type parameter, validation, and multi-line supportman/nice_lavaanPlot.Rd- Updated documentationtests/testthat/test-nice_lavaanPlot.R- Added comprehensive testsThis 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
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.