Consider the following plot:
library(ggplot2)
p <- ggplot(diamonds,
aes(x = carat, fill = cut)) +
geom_density(position = "stack") +
facet_wrap(~ color)
The facet_wrap
function wraps a sequence of faceted panels into a roughly rectangular display of nrow
rows and ncol
columns. However, depending on the data, the actual number of panels is often a few panels short of nrow * ncol
, which leaves a chunk of wasted space in the plot.
If the plot includes legend(s), the situation is exacerbated, because now we have even more wasted space due to the legend, whether it's on the right (default legend position), or one of the other three directions.
To save space, I would like to shift the legend(s) into the space created by unfilled facets.
The following works as a space-saving measure, but the legend is anchored to a corner of the plot area, with potentially a lot of space left on one side, creating an imbalanced look:
p +
theme(legend.position = c(1, 0),
legend.justification = c(1, 0))
Shifting a legend towards the centre of the blank space area by manually adjusting the legend.position
/legend.justification
values is a matter of trial and error, and difficult to scale if one has many faceted plots to work on.
In summary, I want a method that:
- Shifts the legend(s) of a faceted plot into the space created due to empty facets.
- Results in a reasonably nice-looking plot.
- Is easily automated to handle many plots.
This is a recurring use case for me, and I've decided to post it along with my working solution here in case anyone else finds it useful. I haven't seen this scenario asked/answered elsewhere on Stack Overflow. If anyone has, please leave a comment and I'll be happy to answer there instead or have this marked as a duplicate, as the case may be.
The following is an extension to an answer I wrote for a previous question about utilising the space from empty facet panels, but I think it's sufficiently different to warrant its own space.
Essentially, I wrote a function that takes a ggplot/grob object converted by
ggplotGrob()
, converts it to grob if it isn't one, and digs into the underlying grobs to move the legend grob into the cells that correspond to the empty space.Function:
Result:
Nicer looking result if we take advantage of the empty space's direction to arrange the legend horizontally:
Some other examples:
Nice Q&A!
I found something similar at this link. So, I thought that it would have been a nice addition to your function.
More precisely the function
reposition_legend()
fromlemon
seems to be quite what you needed, except that it doesn't look for the empty spaces.I took inspiration from your function to find the names of the empty panels that are passed to
reposition_legend()
with thepanel
arg.Example data and libraries:
Of course, I removed all the checks (
if
cases, which should be the same) just to concentrate on the important stuff.Note that this might still need some tweaking, I just thought it was something worth to be shared.
At the moment the behaviour seems OK, and the function is a few lines shorter.
Other cases.
First example:
Second example:
Third example:
Complete function:
I think
lemon::reposition_legend()
identified by @RLave is the most elegant solution. However, it does hinge on knowing the names of empty facets. I wanted to share a succinct way of finding these, thus proposing yet another version ofshift_legend()
: