web-dev-qa-db-fra.com

Tracez les effets aléatoires de lmer (package lme4) en utilisant qqmath ou dotplot: comment le rendre élégant?

La fonction qqmath crée de superbes tracés d'effets aléatoires à l'aide de la sortie du package lmer. Autrement dit, qqmath est excellent pour tracer les intersections à partir d'un modèle hiérarchique avec leurs erreurs autour de l'estimation ponctuelle. Un exemple des fonctions lmer et qqmath est présenté ci-dessous en utilisant les données intégrées dans le package lme4 appelé Dyestuff. Le code produira le modèle hiérarchique et un tracé de Nice en utilisant la fonction ggmath.

library("lme4")
data(package = "lme4")

# Dyestuff 
# a balanced one-way classiï¬cation of Yield 
# from samples produced from six Batches

summary(Dyestuff)             

# Batch is an example of a random effect
# Fit 1-way random effects linear model
fit1 <- lmer(Yield ~ 1 + (1|Batch), Dyestuff) 
summary(fit1)
coef(fit1) #intercept for each level in Batch 

# qqplot of the random effects with their variances
qqmath(ranef(fit1, postVar = TRUE), strip = FALSE)$Batch

La dernière ligne de code produit un tracé vraiment agréable de chaque interception avec l'erreur autour de chaque estimation. Mais le formatage de la fonction qqmath semble être très difficile, et j'ai eu du mal à formater l'intrigue. J'ai trouvé quelques questions auxquelles je ne peux pas répondre, et que je pense que d'autres pourraient également bénéficier s'ils utilisent la combinaison lmer/qqmath:

  1. Existe-t-il un moyen de prendre la fonction qqmath ci-dessus et d'ajouter quelques options, telles que rendre certains points vides ou remplis, ou des couleurs différentes pour différents points? Par exemple, pouvez-vous remplir les points pour A, B et C de la variable Lot, mais le reste des points est alors vide?
  2. Est-il possible d'ajouter des étiquettes d'axe pour chaque point (peut-être le long de l'axe y supérieur ou droit, par exemple)?
  3. Mes données ont plus de 45 interceptions, il est donc possible d'ajouter un espacement entre les étiquettes pour qu'elles ne se heurtent pas? PRINCIPALEMENT, je suis intéressé à distinguer/étiqueter entre les points sur le graphique, ce qui semble être lourd/impossible dans la fonction ggmath.

Jusqu'à présent, l'ajout d'une option supplémentaire dans la fonction qqmath produit des erreurs où je n'obtiendrais pas d'erreurs s'il s'agissait d'un tracé standard, donc je suis à perte.

De plus, si vous pensez qu'il existe un meilleur package/fonction pour tracer les interceptions à partir de la sortie lmer, j'aimerais l'entendre! (par exemple, pouvez-vous faire les points 1-3 en utilisant dotplot?)

EDIT: Je suis également ouvert à un pointplot alternatif s'il peut être raisonnablement formaté. J'aime juste l'apparence d'un complot ggmath, donc je commence par une question à ce sujet.

34
Captain Murphy

Une possibilité consiste à utiliser la bibliothèque ggplot2 pour dessiner un graphique similaire, puis vous pouvez ajuster l'apparence de votre tracé.

Tout d'abord, l'objet ranef est enregistré sous randoms. Ensuite, les variances des interceptions sont enregistrées dans l'objet qq.

randoms<-ranef(fit1, postVar = TRUE)
qq <- attr(ranef(fit1, postVar = TRUE)[[1]], "postVar")

Objet Rand.interc contient juste des interceptions aléatoires avec des noms de niveau.

Rand.interc<-randoms$Batch

Tous les objets sont placés dans un seul bloc de données. Pour les intervalles d'erreur sd.interc est calculé comme la racine carrée de la variance 2 fois.

df<-data.frame(Intercepts=randoms$Batch[,1],
              sd.interc=2*sqrt(qq[,,1:length(qq)]),
              lev.names=rownames(Rand.interc))

Si vous avez besoin que les interceptions soient ordonnées dans le tracé selon la valeur, alors lev.names doit être réorganisé. Cette ligne peut être ignorée si les interceptions doivent être classées par nom de niveau.

df$lev.names<-factor(df$lev.names,levels=df$lev.names[order(df$Intercepts)])

Ce code produit un tracé. Maintenant, les points différeront de shape selon les niveaux de facteur.

library(ggplot2)
p <- ggplot(df,aes(lev.names,Intercepts,shape=lev.names))

#Added horizontal line at y=0, error bars to points and points with size two
p <- p + geom_hline(yintercept=0) +geom_errorbar(aes(ymin=Intercepts-sd.interc, ymax=Intercepts+sd.interc), width=0,color="black") + geom_point(aes(size=2)) 

#Removed legends and with scale_shape_manual point shapes set to 1 and 16
p <- p + guides(size=FALSE,shape=FALSE) + scale_shape_manual(values=c(1,1,1,16,16,16))

#Changed appearance of plot (black and white theme) and x and y axis labels
p <- p + theme_bw() + xlab("Levels") + ylab("")

#Final adjustments of plot
p <- p + theme(axis.text.x=element_text(size=rel(1.2)),
               axis.title.x=element_text(size=rel(1.3)),
               axis.text.y=element_text(size=rel(1.2)),
               panel.grid.minor=element_blank(),
               panel.grid.major.x=element_blank())

#To put levels on y axis you just need to use coord_flip()
p <- p+ coord_flip()
print(p)

enter image description here

39
Didzis Elferts

La réponse de Didzis est excellente! Pour résumer un peu, je l'ai mis dans sa propre fonction qui se comporte un peu comme qqmath.ranef.mer() et dotplot.ranef.mer(). En plus de la réponse de Didzis, il gère également des modèles avec plusieurs effets aléatoires corrélés (comme qqmath() et dotplot() do). Comparaison avec qqmath():

require(lme4)                            ## for lmer(), sleepstudy
require(lattice)                         ## for dotplot()
fit <- lmer(Reaction ~ Days + (Days|Subject), sleepstudy)
ggCaterpillar(ranef(fit, condVar=TRUE))  ## using ggplot2
qqmath(ranef(fit, condVar=TRUE))         ## for comparison

enter image description here

Comparaison avec dotplot():

ggCaterpillar(ranef(fit, condVar=TRUE), QQ=FALSE)
dotplot(ranef(fit, condVar=TRUE))

enter image description here

Parfois, il peut être utile d'avoir différentes échelles pour les effets aléatoires - quelque chose que dotplot() applique. Quand j'ai essayé de détendre cela, j'ai dû changer la facette (voir ceci réponse ).

ggCaterpillar(ranef(fit, condVar=TRUE), QQ=FALSE, likeDotplot=FALSE)

enter image description here

## re = object of class ranef.mer
ggCaterpillar <- function(re, QQ=TRUE, likeDotplot=TRUE) {
    require(ggplot2)
    f <- function(x) {
        pv   <- attr(x, "postVar")
        cols <- 1:(dim(pv)[1])
        se   <- unlist(lapply(cols, function(i) sqrt(pv[i, i, ])))
        ord  <- unlist(lapply(x, order)) + rep((0:(ncol(x) - 1)) * nrow(x), each=nrow(x))
        pDf  <- data.frame(y=unlist(x)[ord],
                           ci=1.96*se[ord],
                           nQQ=rep(qnorm(ppoints(nrow(x))), ncol(x)),
                           ID=factor(rep(rownames(x), ncol(x))[ord], levels=rownames(x)[ord]),
                           ind=gl(ncol(x), nrow(x), labels=names(x)))

        if(QQ) {  ## normal QQ-plot
            p <- ggplot(pDf, aes(nQQ, y))
            p <- p + facet_wrap(~ ind, scales="free")
            p <- p + xlab("Standard normal quantiles") + ylab("Random effect quantiles")
        } else {  ## caterpillar dotplot
            p <- ggplot(pDf, aes(ID, y)) + coord_flip()
            if(likeDotplot) {  ## imitate dotplot() -> same scales for random effects
                p <- p + facet_wrap(~ ind)
            } else {           ## different scales for random effects
                p <- p + facet_grid(ind ~ ., scales="free_y")
            }
            p <- p + xlab("Levels") + ylab("Random effects")
        }

        p <- p + theme(legend.position="none")
        p <- p + geom_hline(yintercept=0)
        p <- p + geom_errorbar(aes(ymin=y-ci, ymax=y+ci), width=0, colour="black")
        p <- p + geom_point(aes(size=1.2), colour="blue") 
        return(p)
    }

    lapply(re, f)
}
40
caracal

Une autre façon de procéder consiste à extraire des valeurs simulées de la distribution de chacun des effets aléatoires et à les tracer. En utilisant le package merTools, il est possible d'obtenir facilement les simulations à partir d'un objet lmer ou glmer, et de les tracer.

library(lme4); library(merTools)       ## for lmer(), sleepstudy
fit <- lmer(Reaction ~ Days + (Days|Subject), sleepstudy)
randoms <- REsim(fit, n.sims = 500)

randoms est maintenant un objet qui ressemble à:

head(randoms)
groupFctr groupID        term       mean     median       sd
1   Subject     308 (Intercept)   3.083375   2.214805 14.79050
2   Subject     309 (Intercept) -39.382557 -38.607697 12.68987
3   Subject     310 (Intercept) -37.314979 -38.107747 12.53729
4   Subject     330 (Intercept)  22.234687  21.048882 11.51082
5   Subject     331 (Intercept)  21.418040  21.122913 13.17926
6   Subject     332 (Intercept)  11.371621  12.238580 12.65172

Il fournit le nom du facteur de regroupement, le niveau du facteur pour lequel nous obtenons une estimation, le terme dans le modèle et la moyenne, la médiane et l'écart type des valeurs simulées. Nous pouvons l'utiliser pour générer un tracé de chenille similaire à ceux ci-dessus:

plotREsim(randoms)

Ce qui produit:

A caterpillar plot of random effects

Une caractéristique intéressante est que les valeurs qui ont un intervalle de confiance qui ne chevauche pas zéro sont surlignées en noir. Vous pouvez modifier la largeur de l'intervalle en utilisant le paramètre level sur plotREsim en élargissant ou en rétrécissant les intervalles de confiance en fonction de vos besoins.

15
jknowles