Search code examples
rggplot2r-grid

Add axes to grid of ggplots


I have a grid composed of several ggplots and want to add an x axis, where axis ticks and annotations are added between the plots. I could not came up with a better solution than to create a custom plot for the axis and adding it below with arrangeGrob. But they do not align with the plots (I draw arrows where the numbers should be). Also there is a large white space below which I don't want.

I will also need an analogue for the y-axis.

library(ggplot2)
library(gridExtra)
library(ggpubr)
library(grid)

# Create a grid with several ggplots
p <- 
  ggplot(mtcars, aes(wt, mpg)) + 
  geom_point() + 
  theme_transparent() +
  theme(plot.background = element_rect(color = "black"))

main.plot <- arrangeGrob(p, p, p, p, p, p, p, p, ncol = 4, nrow = 2)
# grid.draw(main.plot)

# Now add an x axis to the main plot
x.breaks <- c(0, 1, 2.5, 8, 10)
p.axis <- ggplot() + 
  ylim(-0.1, 0) +
  xlim(1, length(x.breaks)) +
  ggpubr::theme_transparent()

for (i in seq_along(x.breaks)) {
  p.axis <- p.axis +
    geom_text(aes_(x = i, y = -0.01, label = as.character(x.breaks[i])), color = "red")
}
# p.axis

final.plot <- arrangeGrob(main.plot, p.axis, nrow = 2)
grid.draw(final.plot)

enter image description here

Any help appreciated.


Solution

  • Note: In the code below, I assume each plot in your grid has equal width / height, & used equally spaced label positions. If that's not the case, you'll have to adjust the positions yourself.

    Adding x-axis to main.plot:

    library(gtable)
    
    # create additional row below main plot
    # height may vary, depending on your actual plot dimensions
    main.plot.x <- gtable_add_rows(main.plot, heights = unit(20, "points"))
    
    # optional: check results to verify position of the new row
    dev.off(); gtable_show_layout(main.plot.x)
    
    # create x-axis labels as a text grob
    x.axis.grob <- textGrob(label = x.breaks,
                            x = unit(seq(0, 1, length.out = length(x.breaks)), "npc"),
                            y = unit(0.75, "npc"),
                            just = "top")
    
    # insert text grob
    main.plot.x <- gtable_add_grob(main.plot.x,
                                   x.axis.grob,
                                   t = nrow(main.plot.x), 
                                   l = 1, 
                                   r = ncol(main.plot.x),
                                   clip = "off")
    
    # check results
    dev.off(); grid.draw(main.plot.x)
    

    with x-axis labels added

    You can do the same for the y-axis:

    # create additional col
    main.plot.xy <- gtable_add_cols(main.plot.x, widths = unit(20, "points"), pos = 0)
    
    # create y-axis labels as a text grob
    y.breaks <- c("a", "b", "c") # placeholder, since this wasn't specified in the question
    y.axis.grob <- textGrob(label = y.breaks,
                            x = unit(0.75, "npc"),
                            y = unit(seq(0, 1, length.out = length(y.breaks)), "npc"),
                            just = "right")
    
    # add text grob into main plot's gtable
    main.plot.xy <- gtable_add_grob(main.plot.xy,
                                   y.axis.grob,
                                   t = 1, 
                                   l = 1, 
                                   b = nrow(main.plot.xy) - 1,
                                   clip = "off")
    
    # check results
    dev.off(); grid.draw(main.plot.xy)
    

    with y-axis labels

    (Note that the above order of x-axis followed by y-axis should not be switched blindly. If you are adding rows / columns, it's good habit to use gtable_show_layout() frequently to check the latest gtable object dimensions, & ensure that you are inserting new grobs into the right cells.)

    Finally, let's add some buffer on all sides, so that the labels & plot borders don't get cut off:

    final.plot <- gtable_add_padding(main.plot.xy,
                                     padding = unit(20, "points"))
    
    dev.off(); grid.draw(final.plot)
    

    with buffer