amices / mice

Multivariate Imputation by Chained Equations
https://amices.org/mice/
GNU General Public License v2.0
424 stars 106 forks source link

pool() on lavaan fit objects gives error: coef() not available on S4 object #615

Closed Gootjes closed 2 months ago

Gootjes commented 6 months ago

Before submitting the issue

Describe the bug The error happens in get.dfcom on this line https://github.com/amices/mice/blob/c924a7473ea2a95a551ca54479557877defb6423/R/get.df.R#L24
The package lavaan implements coef as an S4 function (compare lavaan::coef to stats::coef), however, this function is not available at this point, so the regular coef function is called on the lavaan fit object, leading to an error.

To Reproduce

library(lavaan)
#> This is lavaan 0.6-15
#> lavaan is FREE software! Please report any bugs.
library(mice)
#> 
#> Attaching package: 'mice'
#> The following object is masked from 'package:stats':
#> 
#>     filter
#> The following objects are masked from 'package:base':
#> 
#>     cbind, rbind
pool(list(sem("mpg ~ cyl", data = mtcars[1:10,]), sem("mpg ~ cyl", data = mtcars[1:20,])))
#> Error in object$coefficients: $ operator not defined for this S4 class

Created on 2024-01-14 with reprex v2.0.2

Expected behavior That coef() works.

A solution would be to make get.dfcom a S3method, such that get.dfcom.default is the current function, and custom functions are implemented for lavaan and coxph (and in the future others?)

hanneoberman commented 6 months ago

I believe this is not a bug in mice but a problem with the lavaan fit object not being processed as expected by broom::tidy().

library(lavaan)
#> This is lavaan 0.6-15
#> lavaan is FREE software! Please report any bugs.
library(mice)
#> 
#> Attaching package: 'mice'
#> The following object is masked from 'package:stats':
#> 
#>     filter
#> The following objects are masked from 'package:base':
#> 
#>     cbind, rbind
sem("hgt ~ age", data = boys)
#> lavaan 0.6.15 ended normally after 1 iteration
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         2
#> 
#>                                                   Used       Total
#>   Number of observations                           728         748
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                                 0.000
#>   Degrees of freedom                                 0
imp <- mice(boys, print = FALSE)
fit <- with(imp, sem("hgt ~ age"))
pool(fit)
#> Warning in get.dfcom(object, dfcom): Infinite sample size assumed.
#> Warning: The `exponentiate` argument is not supported in the `tidy()` method
#> for `lavaan` objects and will be ignored.
#> Error in `purrr::keep()`:
#> ℹ In index: 1.
#> ℹ With name: estimate.
#> Caused by error in `lavaan::parameterEstimates()`:
#> ! unused arguments (effects = "fixed", parametric = TRUE, exponentiate = FALSE)
#> Backtrace:
#>      ▆
#>   1. ├─mice::pool(fit)
#>   2. │ └─mice:::pool.fitlist(...)
#>   3. │   ├─base::summary(fitlist, type = "tidy", exponentiate = FALSE)
#>   4. │   └─mice:::summary.mira(fitlist, type = "tidy", exponentiate = FALSE)
#>   5. │     ├─... %>% bind_rows()
#>   6. │     └─base::lapply(...)
#>   7. │       ├─generics (local) FUN(X[[i]], ...)
#>   8. │       └─broom:::tidy.lavaan(X[[i]], ...)
#>   9. │         └─... %>% as_tibble()
#>  10. ├─dplyr::bind_rows(.)
#>  11. │ └─rlang::list2(...)
#>  12. ├─tibble::as_tibble(.)
#>  13. ├─dplyr::select(...)
#>  14. ├─broom:::rename2(...)
#>  15. │ └─purrr::keep(dots, ~quo_name(.x) %in% colnames(.data))
#>  16. │   └─purrr:::where_if(.x, .p, ...)
#>  17. │     └─purrr:::map_(.x, .p, ..., .type = "logical", .purrr_error_call = .purrr_error_call)
#>  18. │       ├─purrr:::with_indexed_errors(...)
#>  19. │       │ └─base::withCallingHandlers(...)
#>  20. │       ├─purrr:::call_with_cleanup(...)
#>  21. │       └─purrr (local) .f(.x[[i]], ...)
#>  22. │         └─broom (local) .fn(...)
#>  23. │           ├─quo_name(.x) %in% colnames(.data)
#>  24. │           └─base::colnames(.data)
#>  25. │             └─base::is.data.frame(x)
#>  26. ├─dplyr::mutate(., term = paste(lhs, op, rhs))
#>  27. ├─tibble::rownames_to_column(.)
#>  28. ├─tibble::as_tibble(.)
#>  29. └─base::.handleSimpleError(...)
#>  30.   └─purrr (local) h(simpleError(msg, call))
#>  31.     └─cli::cli_abort(...)
#>  32.       └─rlang::abort(...)
rlang::last_trace()
#> Error: Can't show last error because no error was recorded yet

Created on 2024-01-15 with reprex v2.0.2

Gootjes commented 6 months ago

To clarify, my reprex does not reproduce the error on your machine?

That bug you found in your reprex is reported here: https://github.com/amices/mice/issues/614

I believe you are not running into the bug I am reporting because the sem models aren't fitted because the data argument isn't provided to lavaan::sem by mice:::with.mids. The calls on the non-fitted models never reach the offending line of code.

print(fit)
# (...)
#> lavaan 0.6.17 did not run (perhaps do.fit = FALSE)?

New reprex that reproduces the bug:

library(lavaan)
#> This is lavaan 0.6-17
#> lavaan is FREE software! Please report any bugs.
library(mice)
#> 
#> Attaching package: 'mice'
#> The following object is masked from 'package:stats':
#> 
#>     filter
#> The following objects are masked from 'package:base':
#> 
#>     cbind, rbind
imp <- mice(boys, print = FALSE)
fit <- as.mira(lapply(1:5, function(i) sem("hgt ~ age", data = complete(imp, i))))
pool(fit)
#> Error in object$coefficients: $ operator not defined for this S4 class

Created on 2024-01-15 with reprex v2.0.2