¿Cómo pasar una lista con nombre al argumento de puntos (`...`) de una función (específicamente `anova`) en R? (alternativas a `do.call`)

Dec 15 2020

Tengo una lista de salida de modelos mixtos de los lme4::lmerque quiero pasar a los anovaque tienen el formulario anova(object, ...), así que lo hago

models_list <- list("lmm1" = lmm1, "lmm2" = lmm2, "lmm3" = lmm3, "lmm4" = lmm4, "lmm5" = lmm5)
do.call(anova, c(models_list[[1]], models_list[-1]))
Warning in anova.merMod(new("lmerMod", resp = new("lmerResp", .xData = <environment>),  :
  failed to find model names, assigning generic names

Obtengo el resultado, pero con los nombres genéricos señalados por la advertencia, por lo que el mismo resultado que si models_listno tuviera un nombre. Pregunté también en github (https://github.com/lme4/lme4/issues/612), pero al usar do.callparece que no podré resolver este problema. ¿Hay alguna otra manera?

Ejemplo reproducible

library(lme4)
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
fm2 <- lmer(Reaction ~ Days + (Days || Subject), sleepstudy)
anova(fm1,fm2)
refitting model(s) with ML (instead of REML)
Data: sleepstudy
Models:
fm2: Reaction ~ Days + ((1 | Subject) + (0 + Days | Subject))
fm1: Reaction ~ Days + (Days | Subject)
    npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)
fm2    5 1762.0 1778.0 -876.00   1752.0                     
fm1    6 1763.9 1783.1 -875.97   1751.9 0.0639  1     0.8004
# so I can see fm2 and fm2, to which model corresponds each line, but
models_list <- list("fm1" = fm1, "fm2" = fm2)
do.call(anova, c(lmaux[[1]], lmaux[-1]))
Warning in anova.merMod(new("lmerMod", resp = new("lmerResp", .xData = <environment>),  :
  failed to find model names, assigning generic names
refitting model(s) with ML (instead of REML)
Data: sleepstudy
Models:
MODEL2: Reaction ~ Days + ((1 | Subject) + (0 + Days | Subject))
MODEL1: Reaction ~ Days + (Days | Subject)
       npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)
MODEL2    5 1762.0 1778.0 -876.00   1752.0                     
MODEL1    6 1763.9 1783.1 -875.97   1751.9 0.0639  1     0.8004

por lo que los nombres de modelo fm1, fm2fueron reemplazados con MODEL2, MODEL1; esto es un problema si los nombres de los modelos se dan cambiando números (posiblemente no consecutivos).

He comprobado posibles preguntas de las cuales serían una especie de duplicado, como

  • ¿Cómo pasar una lista a una función en R?
  • Pasar lista parcial de argumentos a do.call ()
  • Cómo pasar un argumento adicional al argumento de la función de do.call en R

pero no he encontrado ninguna respuesta satisfactoria.

¡Gracias!

Respuestas

2 Roland Dec 15 2020 at 18:50

anova.merModutiliza una evaluación no estándar (NSE) para obtener los nombres de los modelos. Como suele ser el caso, NSE es más problemático de lo que vale. He aquí una solución:

eval(
  do.call(
    call, 
    c(list("anova"), 
      lapply(names(models_list), as.symbol)), 
    quote = TRUE), 
  models_list)

#refitting model(s) with ML (instead of REML)
#Data: sleepstudy
#Models:
#fm2: Reaction ~ Days + ((1 | Subject) + (0 + Days | Subject))
#fm1: Reaction ~ Days + (Days | Subject)
#    npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)
#fm2    5 1762.0 1778.0 -876.00   1752.0                     
#fm1    6 1763.9 1783.1 -875.97   1751.9 0.0639  1     0.8004

La solución crea una llamada. Eso se hace con la callfunción como de costumbre. Sin embargo, aquí necesitamos pasar los argumentos (entre comillas) como una lista usando do.call. Luego evaluamos esta llamada dentro de la lista de modelos.

Intentaría evitar tener esto en el código de producción porque es bastante complejo y, por lo tanto, difícil de mantener.