vincentarelbundock/modelsummary

Bug: Cannot reorder residual SD for mixed effects models

mccarthy-m-g opened this issue · 5 comments

The coef_map argument is unable to reorder the residual SD for mixed effects models; the residual SD is always forced to the bottom. Here's a reprex:

library(modelsummary)
library(lme4)

sleepstudy_fit <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)

# Bug: The residual SD is always forced to the bottom of the estimates section.
modelsummary(
  sleepstudy_fit,
  coef_map = c("SD (Observations)", "(Intercept)")
)
(1)
(Intercept) 251.405
(6.825)
SD (Observations) 25.592
# As a consequence, it's impossible to group all the residuals together in the
# table.
modelsummary(
  sleepstudy_fit,
  coef_map = c(
    "SD (Intercept Subject)",
    "SD (Days Subject)",
    "SD (Observations)",
    "Cor (Intercept~Days Subject)"
  )
)
(1)
SD (Intercept Subject) 24.741
SD (Days Subject) 5.922
Cor (Intercept~Days Subject) 0.066
SD (Observations) 25.592

Created on 2024-04-23 with reprex v2.0.2

Session info:

R version 4.2.2 (2022-10-31)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_CA.UTF-8/en_CA.UTF-8/en_CA.UTF-8/C/en_CA.UTF-8/en_CA.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] lme4_1.1-35.2      Matrix_1.6-4       modelsummary_2.0.0 devtools_2.4.5    
[5] usethis_2.1.6     

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.11         lattice_0.20-45     listenv_0.9.1       ps_1.7.5           
 [5] utf8_1.2.4          digest_0.6.33       mime_0.12           parallelly_1.36.0  
 [9] R6_2.5.1            backports_1.4.1     reprex_2.0.2        evaluate_0.23      
[13] pillar_1.9.0        rlang_1.1.2         rstudioapi_0.14     minqa_1.2.6        
[17] data.table_1.15.4   miniUI_0.1.1.1      performance_0.11.0  callr_3.7.3        
[21] nloptr_2.0.3        urlchecker_1.0.1    checkmate_2.3.1     effectsize_0.8.6   
[25] rmarkdown_2.25      splines_4.2.2       stringr_1.5.1       htmlwidgets_1.6.4  
[29] shiny_1.7.4         compiler_4.2.2      httpuv_1.6.7        xfun_0.43          
[33] pkgconfig_2.0.3     parameters_0.21.6   pkgbuild_1.4.3      clipr_0.8.0        
[37] globals_0.16.3      htmltools_0.5.7     insight_0.19.10     tibble_3.2.1       
[41] codetools_0.2-18    fansi_1.0.6         future_1.33.2       withr_3.0.0        
[45] later_1.3.0         tables_0.9.25       MASS_7.3-58.1       grid_4.2.2         
[49] nlme_3.1-160        xtable_1.8-4        lifecycle_1.0.4     magrittr_2.0.3     
[53] bayestestR_0.13.2   datawizard_0.10.0   future.apply_1.11.2 cli_3.6.2          
[57] stringi_1.8.3       cachem_1.0.8        fs_1.6.3            promises_1.2.0.1   
[61] remotes_2.4.2       ellipsis_0.3.2      generics_0.1.3      vctrs_0.6.5        
[65] boot_1.3-28         tools_4.2.2         tinytable_0.2.1     glue_1.6.2         
[69] purrr_1.0.2         processx_3.8.3      yaml_2.3.8          pkgload_1.3.4      
[73] parallel_4.2.2      fastmap_1.1.1       sessioninfo_1.2.2   memoise_2.0.1      
[77] knitr_1.45          profvis_0.3.7      

I'm new to using modelsummary so I'm still familiarizing myself with it, but I believe the issue here is that the residual SD and other variance components have different group identifiers, which prevents the desired reordering.

get_estimates(sleepstudy_fit)
                          term     estimate std.error conf.level   conf.low conf.high
1                  (Intercept) 251.40510485  6.824597       0.95 237.935457  264.8748
2                         Days  10.46728596  1.545790       0.95   7.416374   13.5182
3       SD (Intercept Subject)  24.74065800        NA       0.95         NA        NA
4            SD (Days Subject)   5.92213766        NA       0.95         NA        NA
5 Cor (Intercept~Days Subject)   0.06555124        NA       0.95         NA        NA
6            SD (Observations)  25.59179572        NA       0.95         NA        NA
  statistic df.error      p.value effect    group s.value
1 36.838090      174 4.372791e-84  fixed            276.9
2  6.771481      174 1.882719e-10  fixed             32.3
3        NA       NA           NA random  Subject      NA
4        NA       NA           NA random  Subject      NA
5        NA       NA           NA random  Subject      NA
6        NA       NA           NA random Residual      NA

Note that changing the shape argument to use different group identifiers will make the desired reordering possible (e.g., shape = term + effect + statistic ~ model). So I guess the issue at hand is whether or not this could be the default for mixed models, rather than the present default. Not exactly a bug, but still unexpected behaviour that appears bug-like at first.

Yep, I think you're diagnostic is correct. You can use group_map to change the order of groups.

That said, I do agree that this is counterintuitive. I pushed a commit to Github. If you install it an restart R, you’ll get this:

library(lme4)
library(modelsummary)
sleepstudy_fit <- lmer(Reaction ~ Days + I(Days^2) + (Days | Subject), sleepstudy)

modelsummary(
  sleepstudy_fit,
  coef_map = c("SD (Observations)", "(Intercept)", "Days")
)
SD (Observations) 25.534
(Intercept) 255.449
(7.514)
Days 7.434
(2.819)
Num.Obs. 180
R2 Marg. 0.280
R2 Cond. 0.800
AIC 1756.8
BIC 1779.2
ICC 0.7
RMSE 23.30

Thanks Vincent, great package by the way!

Glad you like it!