knitr::opts_chunk$set(collapse = TRUE, comment = "#>", dev = "png", fig.width = 7, fig.height = 4, message = FALSE, warning = FALSE) options(width = 800) if (!requireNamespace("ggplot2", quietly = TRUE) || !requireNamespace("lme4", quietly = TRUE) || !requireNamespace("patchwork", quietly = TRUE) || !requireNamespace("marginaleffects", quietly = TRUE)) { knitr::opts_chunk$set(eval = FALSE) }
This vignette compares the ggeffects package with the marginaleffects package, which is another package that can be used to create predictions for mixed models. It shows how to reproduce the plots shown in the mixed models case study for population- and unit-level predictions
It is probably a good idea to read the introduction to predictions for mixed models first, to get familiar with the basics of the package regarding adjusted predictions for mixed models.
First, we fit the two example models.
library(lme4) # modelling library(ggeffects) # predictions library(marginaleffects) # predictions library(ggplot2) # plotting library(patchwork) # plot layout data(ChickWeight) model1 <- lmer( weight ~ 1 + Time + (1 + Time | Chick), data = ChickWeight ) model2 <- lmer( weight ~ 1 + Time + I(Time^2) + Diet + Time:Diet + I(Time^2):Diet + (1 + Time + I(Time^2) | Chick), data = ChickWeight )
The first two plots show unit-level predictions created with the marginaleffects package. As can be seen, predictions for each level of the random effects are included.
pred1 <- predictions(model1, newdata = datagrid( Chick = ChickWeight$Chick, Time = 0:21 ) ) p1 <- ggplot(pred1, aes(Time, estimate, level = Chick)) + geom_line() + labs(y = "Predicted weight", x = "Time", title = "Linear growth model") pred2 <- predictions(model2, newdata = datagrid( Chick = ChickWeight$Chick, Time = 0:21 ) ) p2 <- ggplot(pred2, aes(Time, estimate, level = Chick)) + geom_line() + labs(y = "Predicted weight", x = "Time", title = "Quadratic growth model") p1 + p2
ggeffects handles unit-level predictions slightly different (see this vignette) - each unit-level is considered as own "group", thus the plot would normally use colors and a color legend to distinguish between the unit-levels. In this example, the default color palette is too small to plot all unit-levels.
pr <- predict_response(model1, terms = c("Time", "Chick"), type = "random") plot(pr) #> Error in `palette()`: #> Insufficient values in manual scale. 50 needed but only 9 provided.
To reproduce the plots from the marginaleffects package, we need to modify our plot. We simply provide a vector with a sufficient amount of color values and hide the legend.
pr <- predict_response(model1, terms = c("Time", "Chick"), type = "random") p3 <- plot(pr, colors = rep("grey30", 50), show_ci = FALSE, show_legend = FALSE) pr <- predict_response(model2, terms = c("Time", "Chick"), type = "random") p4 <- plot(pr, colors = rep("grey30", 50), show_ci = FALSE, show_legend = FALSE) p3 + p4
Diet
This is the next plot shown in the marginaleffects case study. Unit-level predictions are stratified by Diet
.
pred <- predictions(model2) ggplot(pred, aes(Time, estimate, level = Chick)) + geom_line() + ylab("Predicted Weight") + facet_wrap(~Diet, labeller = label_both)
ggeffects by default creates a (theoretical) reference grid for all possible combinations in the data. That's why the following plot looks different than the one above. We see predictions for all unit-levels in each panel of Diet
.
pr <- predict_response(model2, terms = c("Time", "Chick", "Diet"), type = "random") plot(pr, colors = rep("grey30", 50), show_ci = FALSE, show_legend = FALSE)
To limit the plot to the unit-levels that are actually present in the data, we need to set limit_range = TRUE
.
pr <- predict_response(model2, terms = c("Time", "Chick", "Diet"), type = "random") plot( pr, colors = rep("grey30", 50), show_ci = FALSE, show_legend = FALSE, limit_range = TRUE )
The last example shows population-level predictions.
pred <- predictions( model2, newdata = datagrid( Chick = NA, Diet = 1:4, Time = 0:21 ), re.form = NA ) ggplot(pred, aes(x = Time, y = estimate, ymin = conf.low, ymax = conf.high)) + geom_ribbon(alpha = 0.1, fill = "red") + geom_line() + facet_wrap(~Diet, labeller = label_both) + labs(title = "Population-level trajectories")
This plot is rather simple to reproduce with ggeffects. We don't need to specify the type
argument, since type = "fixed"
is the default and returns population-level predictions.
pr <- predict_response(model2, terms = c("Time", "Diet")) plot(pr, grid = TRUE)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.