simsem / semTools

Useful tools for structural equation modeling
74 stars 35 forks source link

fix bug with `nullRMSEA` requiring data in global environment #133

Closed bkeller2 closed 5 months ago

bkeller2 commented 5 months ago

This fixes a bug where nullRMSEA (i.e., used in moreFitIndices) relies on the data set in the global environment. If the data set does not exist there, currently, there is a nondescriptive error message. For example, if the model is generated within a function currently, there is an error:

# Minimal Working Example
library(semTools)
library(lavaan)
fit_model <- function() {
    set.seed(98123)
    # Generate Data
    f <- rnorm(100)
    mydata <- data.frame(
        x1 = rnorm(100, f, 1),
        x2 = rnorm(100, f, 1),
        x3 = rnorm(100, f, 1)
    )
    # Fit Model and return
    cfa("f  =~ x1 + x2 + x3", data = mydata)
}

fit <- fit_model()
moreFitIndices(fit) # This throws errors
# Error in eval(call, parent.frame()) : object 'mydata' not found

Another example is if the moreFitIndices is called even within the function:

# Minimal Working Example
library(semTools)
library(lavaan)
fit_model <- function() {
    set.seed(98123)
    # Generate Data
    f <- rnorm(100)
    mydata <- data.frame(
        x1 = rnorm(100, f, 1),
        x2 = rnorm(100, f, 1),
        x3 = rnorm(100, f, 1)
    )
    # Fit Model
    fit <- cfa("f  =~ x1 + x2 + x3", data = mydata)
    # Return fit indices
    moreFitIndices(fit) # Errors
    # Error in eval(call, parent.frame()) : object 'mydata' not found
}

fit_ind <- fit_model()

This issue can be problematic when trying to use the function in something like a simulation. The proposed fix forces nullRMSEA to use the data set stored in the lavaan object.

TDJorgensen commented 5 months ago

Hi Brian, Thanks for noticing this!
I'm not sure if you are aware that the object@Data slot is not a data.frame or matrix. It is an internally defined S4-class lavData object that contains tons of information about the analyzed data, including a copy of the data in the @X slot. Although not documented in ?lavaan, I believe lavaan() does accept this in place of a data.frame or matrix. I think the end result is equivalent, but to make sure (and save time by allowing lavaan() to skip the process of creating a new @Data slot), I just made it more explicit by using the dedicated slotData= argument to fix this.

bkeller2 commented 5 months ago

Awesome! Thanks