The package itsadug
(http://github.com/vr-vr/itsadug) includes several plot functions to visualize the estimates of Generalized Additive (Mixed) Models (GAMM) implemented using the package mgcv
(Wood 2006; 2011). This paper presents a short overview of:
the available plot functions in the packages mgcv
and itsadug
;
the functions to retrieve predicted values from a GAMM, and how these could be used to create custom plots;
the plot functions for inspection of the residuals.
The code below was used to fit a GAMM model m1
to the data set simdat
from the package itsadug
. The data set simdat
is simulated time series data with arbitrary predictors. We use the interaction between the predictors Time and Trial to illustrate the various functions that are available for visualizing nonlinear interactions.
library(itsadug)
## Loading required package: mgcv
## Loading required package: nlme
## This is mgcv 1.8-3. For overview type 'help("mgcv-package")'.
## Loaded package itsadug 0.8 (see 'help("itsadug")' ).
data(simdat)
# For illustration purposes, we build a GAMM model
# with a nonlinear interaction, two groups, and
# random wiggly smooths for Subjects:
m1 <- bam(Y ~ Group + te(Time, Trial, by=Group)
+ s(Time, Subject, bs='fs', m=1),
data=simdat)
The function gamtabs
converts the summary quickly in a Latex table or in a HTML table (specify type="html"
), which could be included in a knitr
file.
gamtabs(m1, caption="Summaty of m1", comment=FALSE, type='html')
The default way to plot interactions is to use mgcv
’s plot.gam
. This function visualizes the partial effects, see Figure 1.
par(mfrow=c(1,2))
plot(m1, select=1, rug=FALSE,
main='Group=Children', cex.axis=1.5, cex.lab=1.5)
plot(m1, select=2, rug=FALSE,
main='Group=Adults', cex.axis=1.5, cex.lab=1.5)
Figure 1. Partial effects surfaces te(Time,Trial):GroupChildren
and te(Time,Trial):GroupAdults
plotted with plot.gam
.
Advantage and disadvantages of plot.gam
:
Confidence bands are plotted, which are useful for seeing whether effects are significant.
Without colored background the contour plots are difficult to read. It is possible to plot a colored background using the argument scheme=2
, but only heat.colors
are possible.
The use of plot.gam
is limited to interactions with two (or less) continuous variables.
Alternatively one could use the function pvisgam
from the package itsadug
to visualize the partial effects. The function plots exactly the same surfaces as plot.gam
, but visualizes the surface slightly different.
par(mfrow=c(1,2))
# Note: specify zlim when comparing two plots
pvisgam(m1, view=c("Time", "Trial"), select=1,
main='Group=Children', labcex=.8,
zlim=c(-15,15), print.summary=FALSE)
pvisgam(m1, view=c("Time", "Trial"), select=2,
main='Group=Adults', labcex=.8,
zlim=c(-15,15), print.summary=FALSE)
Figure 2. Partial effects surfaces te(Time,Trial):GroupChildren
and te(Time,Trial):GroupAdults
plotted with pvisgam
.
Advantage and disadvantages of pvisgam
:
pvisgam
plots are easier to interpret than plot.gam
plots, because the background is colored.
This function can be used to plot more complex interactions, including more than two continuous predictors.
The surfaces might look different when being plot with different z-range (zlim
), which changes the colors and contour lines being plot.
Highly similar in appearance to the function vis.gam
(summarized in next section). Therefore, it is good practice to report which function and settings were used to generate the plot.
For visualizing the summed effects, rather than partial effects, mgcv
’s function vis.gam
could be used. The functions pvisgam
and fvisgam
are derived from vis.gam
, and therefore look very similar in style.
par(mfrow=c(1,2))
# Note: specify zlim when comparing two plots
vis.gam(m1, view=c("Time", "Trial"),
cond=list(Group='Children', Subject='a01'),
plot.type='contour', color='topo', main='Group=Children',
zlim=c(-8,10))
vis.gam(m1, view=c("Time", "Trial"),
cond=list(Group='Adults', Subject='a01'),
plot.type='contour', color='topo', main='Group=Adults',
zlim=c(-8,10))
Figure 3. Summed effects surfaces for Time and Trial plotted with vis.gam
.
Advantages and disadvantages of vis.gam
:
It shows the additive effect of the different nonlinear components, which facilitates interpretation;
This function can be used to plot more complex interactions, including more than two continuous predictors;
The surfaces might look different when being plot with different z-range (zlim
), which changes the colors and contour lines being plot;
vis.gam
requires a value for each predictor in the model, and also includes the random effects (e.g., effects for participants and items). This makes it difficult to generalize over the random effects.
Different settings for not viewed predictors may change the surface. Therefore, it is good practice to report which settings were used to generate the plot.
The function gradientLegend
in the package itsadug
can be used to add a color legend to the vis.gam
plot (automatically added by pvisgam
and fvisgam
).
The function fvisgam
in itsadug
is a variant of vis.gam
that allows to exclude random effects. Figure 4 shows similar patterns as the partial effects in Figures 1 and 2, but the values on the contour lines are different. This is caused by the intercept and other terms in the model that are added as constants to the surface. When the random effects would not be excluded (i.e., setting rm.ranef
to NULL
) the surfaces in Figure 4 would be the same as with vis.gam
(Figure 3).
## Summary:
## * Group : factor; set to the value(s): Adults.
## * Time : numeric predictor; with 30 values ranging from 0.000000 to 2000.000000.
## * Trial : numeric predictor; with 30 values ranging from -10.000000 to 10.000000.
## * Subject : factor; set to the value(s): a01.
Figure 4. Summed effects surfaces for Time
and Trial
plotted with fvisgam
. Random effects are zeroed out.
Unless the argument is set to , the function will print the values of all model terms that are being used to generate the predictions.
Advantages and disadvantages of fvisgam
:
It shows the additive effect of the different nonlinear components, which facilitates interpretation;
This function can be used to plot more complex interactions, including more than two continuous predictors;
The surfaces might look different when being plot with different z-range (zlim
), which changes the colors and contour lines being plot;
It allows to generalize over random effects.
It reports the values of the other model terms that are not in view, but might influence the plot.
Different settings for not viewed predictors may change the surface. Therefore, it is good practice to report which settings were used to generate the plot.
The default function for plotting a one dimensional smooth is plot.gam
. This function plots the partial effect of a particular one dimensional smooth in the model. The argument shift
could be used to raise or lower the smooth with the intercept or intercept adjustments, as shown in Figure 5.
Figure 5. Using plot.gam
for plotting a one dimensional smooth.
Advantages and disadvantages of plot.gam
:
Useful for inspection of the partial effects of nonlinear smooths.
The use of plot.gam
is limited to interactions with two (or less) continuous variables.
The function plot_smooth
from the package itsadug
does not plot the partial effects, but the summed effects. Thus, when plotting the smooth for one particular group, the intercept for that group is also included in the smooth. Optionally the random effects could be excluded by setting the argument rm.ranef
to FALSE
.
par(mfrow=c(1,2), cex=1.1)
# First plot the smooth for Adult participants in gray...
plot_smooth(m2, view="Time", cond=list(Group="Adults"), rug=FALSE,
ylim=c(-10,15), print.summary=FALSE,
main='m2: Time, Group=Adults')
# ... then add the smooth from which the random effects are excluded
plot_smooth(m2, view="Time", cond=list(Group="Adults"),
rug=FALSE, add=TRUE, col='red', rm.ranef=TRUE,
ylim=c(-10,15), print.summary=FALSE, xpd=TRUE)
# Add legend:
legend('bottomleft',
legend=c("incl. random","excl. random"),
col=c("black", "red"), lwd=2,
bty='n')
# Secondly, a smooth based on the tensor in m1:
plot_smooth(m1, view="Time", cond=list(Group="Adults"),
rug=FALSE, ylim=c(-10,15), print.summary=FALSE,
main='m1: Time, Group=Adults')
plot_smooth(m1, view="Time", cond=list(Group="Adults"),
rug=FALSE, add=TRUE, col='red', rm.ranef=TRUE,
ylim=c(-10,15), print.summary=FALSE, xpd=TRUE)
Figure 6. Left: Using plot_smooth for plotting a one dimensional smooth from model m2
. Right: Using plot_smooth for extracting a one dimensional smooth from an interaction surface in model m1
.
Figure 6 Left and Right shows the same plot, Left is based on model m2
, whereas Right is based on model m1
. Remember that model m1
included only interaction surfaces, but no one dimensional smooths. The function plot_smooth
can derive the estimates of a one dimensional smooth on the basis of the complex interactions. Although very similar, deriving the estimate from an interaction surface results in a different smooth, because it is based on a different model.
Advantages and disadvantages of plot_smooth
:
Useful for inspection of the summed effects of nonlinear smooths;
Allows to overlay the smooths of different conditions;
The function plot_smooth
is able to plot interactions with two (or less) continuous variables.
The function plot_parametric
plots the estimates for one or more grouping predictors as a dotplot, see Figure 7.
par(cex=1.1)
# First plot the Group estimates by setting the smooth terms on zero:
plot_parametric(m1, pred=list(Group=c("Adults", "Children")),
cond=list(Time=0, Trial=0), rm.ranef=TRUE,
print.summary=FALSE)
Figure 7. Estimates for each group when all other predictors are set to zero.
The differences in the Time by Trial interaction between the adult participants and the children could be modeled using a binary predictor resulting in a difference surface. Alternatively, one could use the function plot_diff2
of package itsadug
to calculate and plot the difference surface. The function plot_diff
is the one dimensional version of plot_diff2
.
par(mfrow=c(1,2))
plot_diff2(m1, view=c("Time","Trial"),
comp=list(Group=c("Adults", "Children")),
zlim=c(-5,7.5),
main='Difference Adults-Children',
print.summary=FALSE)
plot_diff(m1, view="Time",
comp=list(Group=c("Adults", "Children")),
main='Time difference Adults-Children')
Figure 8. Difference surface for Group
plotted with plot_diff2
.
Note that the random effects do not need to removed, because these are canceled out when the difference between the conditions is calculated.
Rather than using the functions plot.gam
, pvisgam
, vis.gam
, fvisgam
, or plot_smooth
the model predictions provide a way to make your own plots. The functions get_predictions
, get_random
, and get_difference
return predictions for given conditions. Using the same GAMM model, we provide an example of how to make a contour plot and smooths on the basis of predictions.
For a contour plot, one need to generate all combinations of x- and y-values. These can be specified in the argument cond
of the function get_predictions
of itsadug
. A matrix is created out of these values, and consequently plotted using the functions image and contour, as shown in Figure 9.
# Extract prediction from model
# Note that the random effects are canceled out by setting rm.ranef to TRUE
xval <- seq(0,2000, length=100)
yval <- seq(-10,10, length=50)
g1 <- get_predictions(m1, cond=list(Time=xval, Trial=yval, Group='Adults'),
rm.ranef=TRUE, print.summary=FALSE)
# Create plot matrix
g1 <- g1[order(g1$Time, g1$Trial),]
zval <- matrix(g1$fit, byrow=TRUE, nrow=100,ncol=50)
image(xval, yval, zval, col=topo.colors(100),
main='Group=Adults', xlab='Time', ylab='Trial')
contour(xval, yval, zval, labcex=.8, add=TRUE, col='red')
Figure 9. Using image()
and contour()
to plot the interaction surface.
To explain how the reader should interpret this surface, one could use two smooths that illustrate how Time influences the dependent variable at Trial 5 and Trial -5. I generated two data frames, one for Trial==5
and one for Trial==-5
:
# Extract prediction from model
# Note that the random effects are canceled out by setting rm.ranef to TRUE
xval <- seq(0,2000, length=100)
g2 <- get_predictions(m1, cond=list(Time=xval, Trial=5, Group='Adults'),
rm.ranef=TRUE, print.summary=FALSE)
g3 <- get_predictions(m1, cond=list(Time=xval, Trial=-5, Group='Adults'),
rm.ranef=TRUE, print.summary=FALSE)
head(g2)
## Group Time Trial Subject fit CI rm.ranef
## 1 Adults 0.00000 5 a01 -1.308769111 1.966704 s(Time,Subject)
## 2 Adults 20.20202 5 a01 -0.873584687 1.956358 s(Time,Subject)
## 3 Adults 40.40404 5 a01 -0.439061543 1.946922 s(Time,Subject)
## 4 Adults 60.60606 5 a01 -0.005860961 1.938340 s(Time,Subject)
## 5 Adults 80.80808 5 a01 0.425355779 1.930549 s(Time,Subject)
## 6 Adults 101.01010 5 a01 0.853927396 1.923474 s(Time,Subject)
In the leftmost of the following plots, the interaction surface is plotted again. Now two arrows are added to indicate which smooths are being plot in the rightmost panel:
par(mfrow=c(1,2))
image(xval, yval, zval, col=topo.colors(100),
main='Group=Adults', xlab='Time', ylab='Trial')
contour(xval, yval, zval, labcex=.8, add=TRUE, col='red')
# Add arrows for comparing two conditions
arrows(x0=0, x1=2200, y0=5, y1=5, code=2,
length=.1, angle=30, lwd=2, xpd=TRUE)
arrows(x0=0, x1=2200, y0=-5, y1=-5, code=2,
length=.1, angle=30, lwd=2, col='magenta', xpd=TRUE)
text(2100, c(5,-5), labels=c('A', 'B'),
col=c('black', 'magenta'), pos=3, cex=1.1, xpd=TRUE)
# Setup the plot region for the two smooths:
emptyPlot(range(g2$Time),
range(c(g2$fit+g2$CI, g2$fit-g2$CI,g3$fit+g3$CI, g3$fit-g3$CI)),
main='Trials 5 and -5', xlab='Time', ylab='Est. value of Y',
h0=0)
# add two smooths:
# Note: f is set to 1, because the SE are already multiplied by 1.96 to get 95%CI
plot_error(g2$Time, g2$fit, g2$CI,
shade=TRUE, f=1, xpd=TRUE)
plot_error(g3$Time, g3$fit, g3$CI,
col='magenta', shade=TRUE, f=1, xpd=TRUE)
# add text as legend:
text(1500,c(15,10), labels=c("A", "B"),
col=c('black', 'magenta'), font=2, adj=0)
Figure 10. Left: Interaction between Time and Trial for adult participants. Right: The effect of Time for Trial 5 (A) and Trial -5 (B) with 95% CI.
It might be useful to inspect the random effects estimates. The random smooths do not necessarily sum to zero, and might show drifts. By default, random effects are plotted by the function plot.gam
. To calculate the mean or median of the random effects, the function get_random
may be helpful.
# Extract the mean of the random smooths from model m1
g4 <- get_random(m1, fun='mean')
# Extract the median of the random smooths from model m1
g5 <- get_random(m1, fun='median')
par(mfrow=c(1,2))
plot(m1, select=3, ylim=c(-20,20))
title(main="Random smooths")
abline(h=0)
# Plot the mean random smooth:
itsadug::emptyPlot(range(g4[[1]]$Time), c(-10,10),
main='Mean and median of random smooths',
xlab='Time', ylab='Est. value of Y',
h0=0)
lines(g4[[1]]$Time, g4[[1]]$x, lwd=2, xpd=TRUE)
lines(g5[[1]]$Time, g5[[1]]$x, lwd=2, col='blue', xpd=TRUE)
Figure 11. Left: Random effects smooths. Right: The mean (black) and median (blue) of the random smooths.
The function check_resid
can be used to inspect the residual error of the model. It checks for the distribution of the model and the the autocorrelation in the residuals. For GAMM models without AR1 model, only the standard residuals are presented. However, it will reflect both the standard and the corrected residuals in models with an AR1 model included, because the function makes use of resid_gam
. resid_gam
can be used to retrieve the corrected residuals rather than the normal residuals.
# Note: as no AR1 model was included, resid() is used instead of resid_gam()
check_resid(m1, split_by=list(Subject=simdat$Subject, Trial=simdat$Trial))
## No AR1 model included.
Figure 12. Top row: Test the distribution of the model residuals for normality. The residuals seem to follow a t-distribution rather than normal distribution. Bottom row: The autocorrelation in the residuals. The left panel shows the acf of all the residuals, treating them as a single time series. The right panel is based on the function acf_plot
, which averages over the time series.
To test the structure of the autocorrelation, the function acf_n_plots
presents \(N\) acf plots of individual participants or time series. These time series can be randomly selected, or provide an overview of the differences by selecting time series on the basis of the quantiles, as illustrated in Figure 13.
acf_n_plots(resid(m1),
split_by=list(Subject=simdat$Subject, Trial=simdat$Trial),
n=6,cex.lab=1.5, cex.axis=1.5, cex.main=2)
## Quantiles to be plotted:
## 0% 20% 40% 60% 80% 100%
## -0.31881507 -0.01289427 0.08513155 0.21909939 0.53231119 0.96714554
Figure 13. 6 ACF plots, averaged over time series.
The function acf_resid()
is a shortcut for plotting the ACF of model residuals. This function is also available for other regression models and lmer()
/ glmer()
models. Note that the split_pred
argument can take a vector with names of model terms that define how to split the data. If the argument n
is being used, acf_n_plots
is being called, otherwise acf_plot
.
acf_resid(m1, split_pred=c("Subject", "Trial"))
Figure 14. ACF plot of model residuals.
This summary was created on .
packageVersion("mgcv")
## [1] '1.8.3'
packageVersion("itsadug")
## [1] '0.8'
Use the following command for the citation information in BibTex format:
citation("itsadug")
##
## van Rij J, Wieling M, Baayen R and van Rijn H (2015). "itsadug:
## Interpreting Time Series, Autocorrelated Data Using GAMMs." R
## package version 0.8.
##
## A BibTeX entry for LaTeX users is
##
## @Misc{,
## title = {{itsadug}: Interpreting Time Series, Autocorrelated Data Using GAMMs},
## author = {Jacolien {van Rij} and Martijn Wieling and R. Harald Baayen and Hedderik {van Rijn}},
## year = {2015},
## note = {R package version 0.8},
## }
Wood, Simon N. 2006. Generalized Additive Models: An Introduction with R. Chapman; Hall/CRC.
———. 2011. “Fast Stable Restricted Maximum Likelihood and Marginal Likelihood Estimation of Semiparametric Generalized Linear Models.” Journal of the Royal Statistical Society (B) 73 (1): 3–36.