AIC, erreur anova: les modèles ne sont pas tous ajustés au même nombre d'observations, les modèles ne sont pas tous ajustés à la même taille de jeu de données

9

J'ai des modèles comme celui-ci:

require(nlme)

set.seed(123)
n <- 100
k <- 5
cat <- as.factor(rep(1:k, n))
cat_i <- 1:k # intercept per kategorie
x <- rep(1:n, each = k)
sigma <- 0.2
alpha <- 0.001
y <- cat_i[cat] + alpha * x + rnorm(n*k, 0, sigma)
plot(x, y)

m1 <- lm(y ~ x)
summary(m1)

m2 <- lm(y ~ cat + x)
summary(m2)

m3 <- lme(y ~ x, random = ~ 1|cat, na.action = na.omit)
summary(m3)

J'essaie maintenant d'évaluer si l'effet aléatoire doit être présent dans le modèle. Je compare donc les modèles en utilisant AIC ou anova, et j'obtiens l'erreur suivante:

> AIC(m1, m2, m3)
   df       AIC
m1  3 1771.4696
m2  7 -209.1825
m3  4 -154.0245
Warning message:
In AIC.default(m1, m2, m3) :
  models are not all fitted to the same number of observations  
> anova(m2, m3)
Error in anova.lmlist(object, ...) : 
  models were not all fitted to the same size of dataset

Comme vous pouvez le voir, dans les deux cas, j'utilise le même ensemble de données. J'ai trouvé deux remèdes, mais je ne les considère pas satisfaisants:

  1. Ajout method = "ML"à l'appel lme () - je ne sais pas si c'est une bonne idée de changer la méthode.
  2. Utiliser à la lmer()place. Étonnamment, cela fonctionne, malgré le fait que lmer () utilise la méthode REML. Cependant, je n'aime pas cette solution car le lmer()ne montre pas les valeurs de p pour les coefficients - j'aime utiliser plus ancien à la lme()place.

Avez-vous une idée s'il s'agit d'un bug ou non et comment pouvons-nous contourner cela?

Curieuse
la source

Réponses:

6

Une recherche rapide montre que c'est possible (même si je dois admettre que je pensais que ce n'était pas le cas) et que ce n'est pas un bug ... juste un autre cas où les méthodes de R sont cachées et entraînent des choses qui semblent 'inattendues' », mais la foule RTFM dit:« C'est dans la documentation ». Quoi qu'il en soit ... votre solution consiste à utiliser anovale lmecomme premier argument et les lmmodèles comme deuxième (et troisième si vous voulez) argument (s). Si cela semble étrange, c'est parce que c'est un peu étrange. La raison en est que lorsque vous appelez anova, la anova.lmeméthode n'est appelée que si le premier argument est un lmeobjet. Sinon, il appelle anova.lm(qui à son tour appelle anova.lmlist; si vous creusez anova.lm, vous verrez pourquoi). Pour plus de détails sur la façon dont vous souhaitez appeleranovadans ce cas, récupérez l'aide pour anova.lme. Vous verrez que vous pouvez comparer d'autres modèles avec des lmemodèles, mais ils doivent être dans une position autre que le premier argument. Apparemment, il est également possible d'utiliser anovasur des modèles ajustés en utilisant la glsfonction sans trop se soucier de l'ordre des arguments du modèle. Mais je ne connais pas suffisamment les détails pour déterminer si c'est une bonne idée ou non, ou ce que cela implique exactement (cela semble probablement bien, mais votre appel). À partir de ce lien, la comparaison lmavec lmesemble être bien documentée et citée comme méthode, donc je me tromperais dans cette direction, si j'étais vous.

Bonne chance.

russellpierce
la source
1
Oh, et la réponse de user11852 en ce qui concerne AIC avec les addenda de Gavin, il n'y a pas de AIC.lme spécial ou quoi que ce soit pour résoudre ce problème et le tout commence à glisser au-delà de ma note de salaire
russellpierce
3

m2m3MLREMLykkX=0method="ML"

Cela dit, regardons sous le capot:

 methods(AIC)  
 getAnywhere('AIC.default')

 A single object matching AIC.default was found
 It was found in the following places
   registered S3 method for AIC from namespace stats
   namespace:stats with value

 function (object, ..., k = 2) 
 {
     ll <- if ("stats4" %in% loadedNamespaces()) 
         stats4:::logLik
     else logLik
     if (!missing(...)) {
         lls <- lapply(list(object, ...), ll)
         vals <- sapply(lls, function(el) {
             no <- attr(el, "nobs") #THIS IS THE ISSUE!
             c(as.numeric(el), attr(el, "df"), if (is.null(no)) NA_integer_ else no)
         })
         val <- data.frame(df = vals[2L, ], ll = vals[1L, ])
         nos <- na.omit(vals[3L, ])
         if (length(nos) && any(nos != nos[1L])) 
             warning("models are not all fitted to the same number of observations")
         val <- data.frame(df = val$df, AIC = -2 * val$ll + k * val$df)
             Call <- match.call()
             Call$k <- NULL
         row.names(val) <- as.character(Call[-1L])
         val
     }
     else {
         lls <- ll(object)
         -2 * as.numeric(lls) + k * attr(lls, "df")
     }     
 }

où dans votre cas, vous pouvez voir que:

  lls <- lapply(list(m2,m3), stats4::logLik)
  attr(lls[[1]], "nobs")
  #[1] 500
  attr(lls[[2]], "nobs")
  #[1] 498

et fait évidemment logLikquelque chose (peut-être?) inattendu ...? non, pas vraiment, si vous regardez la documentation logLik, ?logLikvous verrez il est dit explicitement:

 There may be other attributes depending on the method used: see
 the appropriate documentation.  One that is used by several
 methods is "nobs"’, the number of observations used in estimation
 (after the restrictions if REML = TRUE’)

ce qui nous ramène à notre point d'origine, vous devriez utiliser ML.

Pour utiliser un dicton courant dans CS: "Ce n'est pas un bug; c'est une (vraie) fonctionnalité!"

EDIT : (Juste pour répondre à votre commentaire :) Supposons que vous montiez un autre modèle en utilisant lmercette fois:

m3lmer <- lmer(y ~ x + 1|cat)

et vous procédez comme suit:

lls <- lapply(list(m2,m3, m3lmer), stats4::logLik)
attr(lls[[3]], "nobs")
#[1] 500
 attr(lls[[2]], "nobs")
#[1] 498

Ce qui semble être une nette différence entre les deux, mais ce n'est vraiment pas comme l'explique Gavin. Juste pour dire l'évidence:

 attr( logLik(lme(y ~ x, random = ~ 1|cat, na.action = na.omit, method="ML")),
 "nobs")
#[1] 500

Il y a une bonne raison pour laquelle cela se produit en termes de méthodologie, je pense. lmeessaie de donner un sens à la régression LME pour vous tandis que lmerlorsque vous faites des comparaisons de modèles, cela revient immédiatement à ses résultats ML. Je pense qu'il n'y a pas de bugs à ce sujet dans lmeet lmerjuste des raisons différentes derrière chaque paquet.

Voir aussi le commentaire de Gavin Simposon sur une explication plus perspicace de ce qui s'est passé anova()(la même chose se produit pratiquement avec AIC)

usεr11852
la source
"vous devriez utiliser ML" - mais comment pouvez-vous expliquer qu'il lmerutilise REML (voir le résumé du modèle) et fonctionne bien en AIC? Il y a donc deux possibilités: 1) le message d'erreur est * une fonctionnalité , pas un bug, et le fait que cela fonctionne lmerest un bug. Ou 2) le message d'erreur est un bug , pas une fonctionnalité.
Curieux
Voir l'article mis à jour (j'ai dû inclure du code). J'ai moi-même remarqué votre argument valable lors de la rédaction de votre réponse d'origine, mais à l'origine, j'ai choisi de la conserver, de sorte que la justification de ma réponse est strictement basée sur le calcul.
usεr11852
3
@Tomas lmer() n'utilise pas REML lorsque vous lui demandez de faire des comparaisons. IIRC, ils ont inclus du sucre fantaisie lmer()afin que vous n'ayez pas à réaménager le modèle avec MLjuste pour comparer les ajustements lorsque vous le souhaitez REMLsur les ajustements individuels pour obtenir les meilleures estimations des paramètres de variance. Regardez ?lmer, exécutez le premier exemple LME jusqu'à l' anova(fm1, fm2)appel inclus . Examinez les probabilités logarithmiques rapportées par anova()et celles rapportées précédemment dans la sortie imprimée. Le anova()obtient des estimations de ML pour vous.
Gavin Simpson
Bon point Gavin, j'oublie qu'il lmera les deux en même temps (il utilise PLS donc il ne fait que n'en estimer qu'un à la fois). J'ai oublié de vérifier ce que vous avez mentionné.
usεr11852
2
@rpierce: L'AIC signalé dans celui-cianova() est celui basé sur ML. L'AIC rapporté AIC()est juste celui basé sur REML.
usεr11852