Search code examples
rplotggplot2r-grid

Align discrete and continuous axes with ggplot2 and grid


I'm attempting to display a grid figure of summarized weekly data of several variables. The two components of this graph that are most pertinent are a distributional summary graph (so box plot or violin plot) of the values that a certain variables took over a given week and a cumulative count graph of an integer variable accumulating over weeks (so a step plot). I would like to plot these two graphs in on an aligned x-axis using grid. I'll be using ggplot2 to make the individual graphs, because I've got a crush on Hadley Wickham (j/k, ggplot is just really, really nice).

The problem is that geom_boxplot only takes factors for x-axis and the geom_step only takes continuous data for the x-axis. These don't necessarily align even if you force similar x-limits with coord_cartesian or scale_x_....

I've cobbled together a hack using geom_rect that will work for this specific application, but that will be a pain to adapt if, for example, I have some other factor that results in multiple boxes for a single week.

The obligatory reproducible:

library(ggplot2)
library(grid)

var1 <- data.frame(val = rnorm(300),
                   week = c(rep(25, 100), 
                        rep(26, 100), 
                        rep(27, 100))
                  )

var2 <- data.frame(cumul = cumsum(c(0, rpois(2, 15))),
                   week = c(25, 26, 27)
                  )


g1   <- ggplot(var1, aes(x = factor(week), y = val)) + 
  geom_boxplot()

g2   <- ggplot(var2, aes(x = week, y = cumul)) + 
  geom_step() + scale_x_continuous(breaks = 25:27)

grid.newpage()
grid.draw(rbind(ggplotGrob(g1),
                ggplotGrob(g2),
                size = "last"))

Example of non-aligned continuous and discrete axes

And the kludge:

library(dplyr)

chiggity_check <- var1 %>% 
  group_by(week) %>% 
  summarise(week.avg = mean(val),
            week.25  = quantile(val)[2],
            week.75  = quantile(val)[4],
            week.05  = quantile(val)[1],
            week.95  = quantile(val)[5])

riggity_rect <- ggplot(chiggity_check) +
  geom_rect(aes(xmin = week - 0.25, xmax = week + 0.25,
                ymin = week.25,
                ymax = week.75)) +
  geom_segment(aes(x = week - 0.25, xend = week + 0.25,
                   y = week.avg, yend=week.avg),
               color = "white") +
  geom_segment(aes(x = week, xend = week ,
                   y = week.25, yend=week.05)) +
  geom_segment(aes(x = week, xend = week ,
                   y = week.75, yend=week.95)) +
  coord_cartesian(c(24.5,27.5)) +
  scale_x_continuous(breaks = 25:27)

grid.newpage()
grid.draw(rbind(ggplotGrob(riggity_rect),
                ggplotGrob(g2 + coord_cartesian(c(24.5,27.5))),
                size = "last"))

Example of kludged together grid graph

So the question(s) is/are: is there a way to force geom_boxplot to a continuous axis or geom_step to a factor axis? Or is there some other implementation, perhaps stat_summary that will be a bit more flexible so that I can align axes and also potentially easily add in things like grouping color variables?


Solution

  • One approach is to plot the two charts on an x-axis set up with factor(week), but in the g2 plot (the step plot) do so in geom_blank() so that the scale is set up. Then in geom_step(), plot on a numeric scale: as.numeric(factor(week))

    library(ggplot2)
    library(grid)
    
    # Your data
    var1 <- data.frame(val = rnorm(300),
                       week = c(rep(25, 100), 
                            rep(26, 100), 
                            rep(27, 100))
                      )
    
    var2 <- data.frame(cumul = cumsum(c(0, rpois(2, 15))),
                       week = c(25, 26, 27)
                      )
    
    # Your g1
    g1   <- ggplot(var1, aes(x = factor(week), y = val)) + 
      geom_boxplot()
    
    # Modified g2
    g2   <- ggplot(var2) + geom_blank(aes(x = factor(week), y = cumul)) +
    geom_step(aes(x = as.numeric(as.factor(week)), y = cumul)) 
    
    grid.newpage()
    grid.draw(gridExtra::rbind.gtable(ggplotGrob(g1),
                    ggplotGrob(g2),
                    size = "last"))
    

    enter image description here