Search code examples
rggplot2contourgrob

move ggplot2 contour from other facets to main


I have x,y,z data with categorical variables that facilitate a facet. I want to include contour lines from all but the first facet and discard the rest of the data. One way to visualize the process is to facet the data and mentally move the contours from the other facets to the first.

MWE:

library(ggplot2)
library(dplyr)

data(volcano)
nx <- 87; ny <- 61
vdat <- data_frame(w=0L, x=rep(seq_len(nx), ny), y=rep(seq_len(ny), each=nx), z=c(volcano))
vdat <- bind_rows(vdat,
                  mutate(vdat, w=1L, x=x+4, y=y+4, z=z-20),
                  mutate(vdat, w=2L, x=x+8, y=y+8, z=z-40))

ggplot(vdat, aes(x, y, fill=z)) +
  geom_tile() +
  facet_wrap(~ w, nrow=1) +
  geom_contour(aes(z=z), color='white', breaks=c(-Inf,110,Inf))

enter image description here

In each facet, I have:

  • facet 0: X,Y,Z for w==0L, contour for w==0L
  • facet 1: X,Y,Z for w==1L, contour for w==1L
  • facet 2: X,Y,Z for w==2L, contour for w==2L

What I'd like to have is a single pane, effectively:

  • X,Y,Z for w==0L, contour for all values of the w categorical

enter image description here

(Forgive my hasty GIMP skills. In the real data, the contours will likely not overlap, but I don't think that that would be a problem.)

The real data has different values (and gradients) of z for the same X,Y system, so the contour is otherwise compatible with the first facet. However, it's still "different", so I cannot mock-up the contours with the single w==0L data.

I imagine there might be a few ways to do this:

  • form the data "right" the first time, informing ggplot how to pull the contours but lay them on the single plot (e.g., using different data= for certain layers);
  • form the faceted plot, extract the contours from the other facets, apply them to the first, and discard the other facets (perhaps using grid and/or gtable); or perhaps
  • (mathematically calculate the contours myself and add them as independent lines; I was hoping to re-use ggplot2's efforts to avoid this ...).

Solution

  • It doesn't fit so neatly with the grammar of graphics, but you can just add a geom_contour call for each subset of data. A quick way is to add a list of such calls to the graph, which you can generate quickly by lapplying across the split data:

    ggplot(vdat[vdat$w == 0, ], aes(x, y, z = z, fill = z)) +
        geom_tile() +
        lapply(split(vdat, vdat$w), function(dat){
            geom_contour(data = dat, color = 'white', breaks = c(-Inf, 110, Inf))
        })
    

    You can even make a legend, if you need:

    ggplot(vdat[vdat$w == 0, ], aes(x, y, z = z, fill = z, color = factor(w))) +
        geom_raster() +
        lapply(split(vdat, vdat$w), function(dat){
            geom_contour(data = dat, breaks = c(-Inf, 110, Inf))
        })