How to ignore axis titles when arranging and tagging multiple ggplots

95 Views Asked by At

I would like to create a 3x4 grid of ggplots. To avoid repeating axis titles, I am only labeling the axes of some plots (see highlights in the first figure). However, my choice to label only some axes is causing the plots to be (1) unequally spaced and (2) improperly tagged.

(fit2.yield1+fit2.yield2+fit2.yield3+
 fit2.FallWeed1+fit2.FallWeed2+plot_spacer()+
 fit2.WheatDens1+plot_spacer()+plot_spacer()+
 fit2.SumWeed1+plot_spacer()+plot_spacer())+
  plot_annotation(tag_levels = 'a')+
  plot_layout(guides = "collect",ncol = 3) &
  theme(legend.position = 'right',plot.tag.position =  "topleft")

Image of my flawed output

Image of my flawed output

I would like to ignore the axes titles when combining plots to reduce the white area between plots and properly tag the figure. Here is an example of the desired output that I made with Photoshop.

Image of my preferred output

Image of my preferred output

I have tried to combine my plots using ggarrange() and patchwork(), and both have similar issues. I've also modified the plot.margin() of each component with no luck.

2

There are 2 best solutions below

2
On

This is tricky, especially without a reproducible example. I suspect the only way to do this in patchwork is to turn clipping off and draw the axis titles with custom annotations.

Let's make a similar plot to yours with the built in iris dataset:

library(ggplot2)
library(patchwork)

p1 <- ggplot(iris, aes(Sepal.Width, Petal.Length, col = Species)) + 
  geom_point() +
  theme(axis.title.y = element_text(angle = 0))

p2 <- p1 + theme(axis.title.x = element_blank())
p3 <- p1 + theme(axis.title.y = element_blank())
p4 <- p3 + theme(axis.title.x = element_blank())

p2 + p4 + p3 + 
  p2 + p3 + plot_spacer() +
  p2 + plot_spacer() + plot_spacer() +
  p1 + plot_spacer() + plot_spacer() +
  plot_annotation(tag_levels = 'a') +
  plot_layout(guides = "collect", ncol = 3) &
  theme(legend.position = 'right', plot.tag.position =  "topleft")

enter image description here

You can see that we have the same issue with the tag positions and row spacing caused by the variable presence of the axis titles.

The solution is to firstly remove the axis titles from all your plots:

p1 <- p1 + theme(axis.title.y = element_blank(), axis.title.x = element_blank())
p2 <- p2 + theme(axis.title.y = element_blank())
p3 <- p3 + theme(axis.title.x = element_blank())

Now we add custom annotations for the x and y axis, ensuring that we switch off clipping in each panel:

p1 <- p1 + coord_cartesian(clip = 'off') +
  annotation_custom(grid::textGrob('Sepal.Width', 0.5, -0.3)) +
  annotation_custom(grid::textGrob('Petal.Length', -0.5, 0.5)) 
p2 <- p2 + coord_cartesian(clip = 'off') +
  annotation_custom(grid::textGrob('Petal.Length', -0.5, 0.5))
p3 <- p3 + coord_cartesian(clip = 'off') +
  annotation_custom(grid::textGrob('Sepal.Width', 0.5, -0.3))

Finally, we make our patchwork, with an added margin to make room for the new text labels:

p2 + p4 + p3 + 
  p2 + p3 + plot_spacer() +
  p2 + plot_spacer() + plot_spacer() +
  p1 + plot_spacer() + plot_spacer() +
  plot_annotation(tag_levels = 'a', 
                  theme = theme(plot.margin = margin(20, 10, 50, 80))) +
  plot_layout(guides = "collect", ncol = 3) &
  theme(legend.position = 'right',
        plot.tag.position =  'topleft')

enter image description here

Note that the tag positions are all consistent in their relationship to the panels, the spacing between all the rows is constant, and the x axis labels reach down below the level of the top of the panel in the next row, as desired.

0
On

Here is as possible working approach using a minimal reproducible example based on mtcars with only 3 panels. Basic idea is to get rid of the x axis titles in columns > 1 and instead to add them manually as separate "plots" aka textGrobs. Doing so will allows to remove the "vertical" gaps added by patchwork when aligning your charts. To fix your issue with the tags and y axis title you could add them manually using annotation_custom:

library(ggplot2)
library(patchwork)

p1 <- p2 <- p3 <-
  ggplot(mtcars, aes(hp, mpg, color = factor(cyl))) +
  geom_point() +
  labs(y = "Longer Title\nwith mutlt line") +
  labs(x = "Longer Title\nwith mutlt line") +
  theme(
    axis.title.y.left = element_text(angle = 0, vjust = .5)
  )

p1 <- p1 + labs(x = NULL)
p2 <- p2 + labs(y = NULL, x = NULL)


x_axis_title_grob <- grid::textGrob(
  label = "Longer Title\nwith mutlt line",
  vjust = 1, y = unit(1, "npc"), x = .4,
  gp = grid::gpar(
    col = "grey30",
    fontsize = 11,
    lineheight = .9
  )
)

p_list <- list(p1, p2, p3) |>
  setNames(letters[1:3]) |>
  purrr::imap(\(x, y) {
    x +
      annotation_custom(grid::textGrob(
        label = y,
        x = unit(0, "npc") - unit(15, "pt"),
        y = unit(1, "npc") + unit(10, "pt"),
        vjust = 0, hjust = 1,
        gp = grid::gpar(fontface = "bold")
      )) +
      coord_cartesian(clip = "off") +
      theme(plot.margin = margin(5.5 + 15, 5.5, 5.5, 5.5, "pt"))
  })

names(p_list) <- c("p1", "p2", "p3")

list(
  p_list$p1, p_list$p2,
  p_list$p3, x_axis_title_grob
) |>
  wrap_plots(
    guides = "collect",
    ncol = 2
  ) &
  theme(
    legend.position = "right",
    plot.tag.position = "topleft"
  )

ggsave("patch.png", width = 20, height = 15, units = "cm")

enter image description here