Legend in missing facet

I often want to use the existence of a ‘missing’ facet to put in a legend, and always have to re-figure out how, so I’m writing it down here.

library(ggplot2)
library(patchwork)

Make a plot with a gap.

irisplot <- iris |> 
  ggplot(aes(x = Sepal.Length, y = Petal.Width, color = Species)) +
  geom_point() +
  facet_wrap('Species', nrow = 2)

irisplot

We want the legend in that lower right hole. The easiest is to just use theme(legend.position = c(x, y)) where x and y are in plot units.

irisplot + theme(legend.position = c(0.6, 0.2))
Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2
3.5.0.
ℹ Please use the `legend.position.inside` argument of `theme()` instead.

Other options that can give more flexibility, particularly across plots, are to get the legend grob and treat it as another plot. That can’t be plotted directly, but can with other plots. Because we can treat this as a plot, we can do some complex things with ggpubr and patchwork, including overlaying it into that spot or using it across figures or subsets of figures. Though here we just do something simple to demo.

ggpubr can use the grob directly

irislegend <- ggpubr::get_legend(irisplot)
ggpubr::ggarrange(irisplot + theme(legend.position = 'none'), irislegend)

patchwork needs to make it a ggplot

(irisplot + theme(legend.position = 'none')) + ggpubr::as_ggplot(irislegend)

With patchwork we can use inset_element if we want to overlay and not treat as external plot

(irisplot + theme(legend.position = 'none')) + 
  inset_element(ggpubr::as_ggplot(irislegend), left = 0.6, bottom = 0.2, right = 0.8, top = 0.4)