medium-dimensional
medium-dimensional

Reputation: 2253

How can I draw lines behind the scatterplot generated by car::qqPlot?

car::qqPlot plots

... empirical quantiles of a variable, or of studentized residuals from a linear model, against theoretical quantiles of a comparison distribution. Includes options not available in the qqnorm function.

By default, qqPlot generates a grid that overlays on associated scatter plot. panel.first argument is not accessible via qqPlot. Using plot.new, I could plot a grid behind the points, but I have been unable to plot a grid with lines at specific positions: the line are positioned with respect to the first data point.

# fit model
set.seed(123)
x <- rnorm(200)
y <- rnorm(200)

model <- lm(y ~ x)

plot.new()
abline(v = seq(0, 1, 0.5), lwd = 2)    # This doesn't work as expected.
par(new=TRUE)
car::qqPlot(resid(model), id = FALSE, grid = FALSE, envelope = FALSE, line = "none",
            col = 2)
grid(nx = 3, col = "green")    # Plots grid in front of the points

enter image description here

Is there a way I could plot lines at specified positions relative to the data in the plot generated by car::qqPlot(..., grid = FALSE)?

Upvotes: 0

Views: 47

Answers (2)

r2evans
r2evans

Reputation: 160447

Looking at the source for car:::qqPlot.default, we see that the canvas is formed with

    plot(z, ord.x, type = "n", xlab = xlab, ylab = ylab, main = main, 
        las = las, ylim = ylim)

so we can create an empty plot with the variables as formed within the function.

I'll use your resid(model) object initially, though inside car:::qqPlot.default it's the x.

    ylim <- range(resid(model), na.rm = TRUE)
    index <- seq(along = resid(model))
    good <- !is.na(x)
    ord <- order(x[good])
    ord.x <- x[good][ord]
    n <- length(ord.x)
    P <- ppoints(n)
    z <- qnorm(P) # based on 'distribution'

With this, we can do this:

plot(z, ord.x, ylim = ylim, type = "n", xaxt = "n", yaxt = "n", ann = FALSE, frame.plot = FALSE)
abline(v = seq(min(z), max(z), length.out = 3), lwd = 2)
grid(nx = 3, col = "green")    # Plots grid in front of the points
par(new=TRUE)
car::qqPlot(resid(model), id = FALSE, grid = FALSE, envelope = FALSE, line = "none",
            col = 2)

qqPlot with grid under the points

The extra arguments to plot are to ensure that nothing is plotted (no points, no frame, no titla, no axis labels/ticks, etc). While minor, this ensures that nothing is double-plotted when you again call qqPlot. For most displays this looks fine, but PDFs will have both sets of elements, and if there is anything at all different then they may appear overlaid or otherwise not quite right.

I assumed that you used seq(0, 1, 0.5) in order to counteract the fact that the coordinates on the first plot were not the same, so you aimed at what the default ranges (0,1 and 0,1). If you really want them at the real 0/0.5/1 in the plotted range, then go back to the original abline:

# ...
abline(v = seq(0, 1, 0.5), lwd = 2)
# ...

enter image description here

Upvotes: 1

Allan Cameron
Allan Cameron

Reputation: 173858

Draw the plot first with pch = NA, so that the axis values are all set up appropriately but no points are drawn. Then draw your ablines (and grid if you wish), then set new = TRUE and plot the actual qq values:

library(car)
#> Loading required package: carData

# fit model
set.seed(123)
x <- rnorm(200)
y <- rnorm(200)

model <- lm(y ~ x)

car::qqPlot(resid(model), id = FALSE, grid = FALSE, envelope = FALSE, 
            line = "none", col = 2, pch = NA)
abline(v = seq(0, 1, 0.5), lwd = 2) 
par(new = TRUE)
car::qqPlot(resid(model), id = FALSE, grid = FALSE, envelope = FALSE, 
            line = "none", col = 2)

enter image description here

Your vertical lines are now at x = 0, x = 0.5 and x = 1, as desired.

Created on 2023-08-04 with reprex v2.0.2

Upvotes: 1

Related Questions