simsem / semTools

Useful tools for structural equation modeling
75 stars 36 forks source link

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

Closed bkeller2 closed 10 months ago

bkeller2 commented 10 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 10 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 10 months ago

Awesome! Thanks