Search code examples
rggplot2cowplot

Marginal plots using axis_canvas in cowplot: How to insert gap between main panel and marginal plots


The following came up in a comment to this post: When making marginal plots with the axis_canvas() function in cowplot, how can we create a gap between the main plot and the marginal plot?

Example code:

require(cowplot)

pmain <- ggplot(data = mpg, aes(x = cty, y = hwy, color = factor(cyl))) + 
  geom_point() + 
  xlab("City driving (miles/gallon)") +
  ylab("Highway driving (miles/gallon)") +
  theme_minimal()

xbox <- axis_canvas(pmain, axis = "x", coord_flip = TRUE) + 
  geom_boxplot(data = mpg, aes(y = cty, x = factor(cyl), color = factor(cyl))) + 
  scale_x_discrete() + coord_flip()
ybox <- axis_canvas(pmain, axis = "y") + 
  geom_boxplot(data = mpg, aes(y = hwy, x = factor(cyl), color = factor(cyl))) +
  scale_x_discrete()

p1 <- insert_xaxis_grob(pmain, xbox, grid::unit(0.6, "in"), position = "top")
p2 <- insert_yaxis_grob(p1, ybox, grid::unit(0.6, "in"), position = "right")
ggdraw(p2)

enter image description here

As we can see in this example, the marginal boxplots directly touch the main plot panel. The goal is to generate some gap. How can this be done?


Solution

  • I see two options:

    Insert empty plot

    We can apply the insert_xaxis_grob() / insert_yaxis_grob() functions iteratively to insert multiple grobs, one of which can be empty. In this way, we can insert a specified amount of space on either side of the marginal plots. Here I'm showing how to do this on the inside, to generate a gap between the main panel and the marginal plots:

    # pmain, xbox, ybox are defined as in the question
    pnull <- ggdraw() # generate empty plot
    
    p1 <- insert_xaxis_grob(
            insert_xaxis_grob(pmain, xbox, grid::unit(0.6, "in"), position = "top"),
            pnull, grid::unit(0.2, "in"), position = "top")
    
    p2 <- insert_yaxis_grob(
            insert_yaxis_grob(p1, ybox, grid::unit(0.6, "in"), position = "right"),
            pnull, grid::unit(0.2, "in"), position = "right")
    ggdraw(p2)
    

    enter image description here

    Create gap in the marginal plots

    Alternatively, since the marginal plots are drawn with ggplot2, we can just specify axis limits that generate space in the appropriate location. I.e., instead of xbox and ybox in the original code, we define xbox2 and ybox2 via:

    xbox2 <- axis_canvas(pmain, axis = "x", coord_flip = TRUE) + 
      geom_boxplot(data = mpg, aes(y = cty, x = as.numeric(factor(cyl)), color = factor(cyl))) + 
      scale_x_continuous(limits = c(-2, 4.5)) + coord_flip()
    ybox2 <- axis_canvas(pmain, axis = "y") + 
      geom_boxplot(data = mpg, aes(y = hwy, x = as.numeric(factor(cyl)), color = factor(cyl))) +
      scale_x_continuous(limits = c(-2, 4.5))
    
    p1 <- insert_xaxis_grob(pmain, xbox2, grid::unit(0.8, "in"), position = "top")
    p2 <- insert_yaxis_grob(p1, ybox2, grid::unit(0.8, "in"), position = "right")
    ggdraw(p2)
    

    enter image description here

    To understand what is happening here, let's compare xbox and xbox2 side by side:

    plot_grid(xbox + panel_border("black"), 
              xbox2 + panel_border("black"), nrow = 1, scale = 0.9)
    

    enter image description here

    We see how xbox2 (on the right) has extra space at the bottom, which was created by starting the axis at -2, even though the first box plot is located at position 1. More information on how to choose the axis ranges for these marginal plots can be found here.