How does lattice package in R create gridlines automatically?

71 Views Asked by At

In the context of linear mixed effects model, it is extremely useful to plot residuals vs. fitted values to check homoscedasticity (of residuals). When I am using lme4::plot.merMod to plot such a figure, if I understand correctly, lme4 uses lattice to produce the figure. For example, if we regress hp on mpg with carb (from tidyverse::mtcars) as random effects*, we get a following plot:

enter image description here

I don't know, however, how lattice comes up with grey-color horizontal and vertical gridlines and if they represent any statistical aspect about model fit. If lattice just divides the x- and y-axis in four equal parts with these gridlines, how can I achieve such equally-spaced gridlines that consider x- and y-axis range in a base R plot?

Code to reproduce the figure:

library(lme4)
library(tidyverse)
model <- lme4::lmer(data = mtcars, formula = mpg ~ hp + (1 | gear))
plot(model, aspect = 1)

*I am not concerned about correct model specification at the moment.

2

There are 2 best solutions below

0
medium-dimensional On BEST ANSWER

In base R, this can be done by using graphics::grid (as suggested by Prof. Bolker):

library(lme4)
library(tidyverse)
library(broom.mixed)

model <- lme4::lmer(data = mtcars, formula = mpg ~ hp + (1 | gear))
data <- broom.mixed::augment(model)

par(pty='s')    # to keep aspect ratio 1
plot(data$.fixed, data$.resid)
graphics::grid(nx = 4, lty = 1)

Or by directly using:

plot(data$.fixed, data$.resid, panel.first = grid(4, lty = 1))

enter image description here

1
medium-dimensional On

The gridlines are produced by the call to grid::grid.segments inside lattice::panel.grid (see lattice::panel.grid).

When lme4::plot.merMod is called, it calls lattice::panel.grid, which in turn calls grid::grid.segments(h = 3, v = 3, ...). This results in partitioning the viewport in four equal parts - horizontally as well as vertically - by three horizontal and three vertical lines.

I provide a figure below, drawn using grid, that might confirm this:

library(lme4)
library(tidyverse)
library(broom.mixed)

model <- lme4::lmer(data = mtcars, formula = mpg ~ hp + (1 | gear))
data <- broom.mixed::augment(model)

v <- 3    # to reproduce default call to panel.grid
grid.newpage()
vp <- viewport(width=unit(3, "inches"), height=unit(3, "inches"))
pushViewport(vp)
pushViewport(dataViewport(data$.fitted, data$.resid))
grid.rect()
grid.xaxis()
grid.yaxis()
grid.text("LU", x=unit(0, "npc"), y=unit(1, "npc"), gp=gpar(col="red"))
grid.text("LB", x=unit(0,"npc"), y=unit(0, "npc"), gp=gpar(col="red"))
grid.text("1", x=unit(0.25,"npc"), y=unit(0, "npc"), gp=gpar(col="blue"))
grid.text("2", x=unit(0.5,"npc"), y=unit(0, "npc"), gp=gpar(col="darkgreen"))
grid.text("3", x=unit(0.75,"npc"), y=unit(0, "npc"), gp=gpar(col="darkred"))
grid.points(x=data$.fitted, y=data$.resid)
grid.segments(x0 = 1:v / (v+1),
              x1 = 1:v / (v+1),
              gp = gpar(col = "pink", lty = 2, lwd = 2),
              default.units = "npc")
grid.segments(y0 = 1:v / (v+1),
              y1 = 1:v / (v+1),
              gp = gpar(col = "orange", lty = 3, lwd = 2),
              default.units = "npc")
pushViewport(vp)

The red colored texts LB and LU show left-bottom and left-upper corners of the viewport. The colored numbers 1, 2, and 3 overlapping on x-axis are placed at 0.25 npc, 0.5 npc and 0.75 npc away from LB i.e. the origin of the viewport, respectively. This is because npc* means:

Normalised Parent Coordinates. Treats the bottom-left corner of the current viewport as the location (0, 0) and the top-right corner as (1, 1).

We see that the call (in the above code) to grid.segments plots lines that overlap with these colored numbers.

enter image description here


*See Table 1 in grid Graphics.