Reputation: 72
How do you work around space issues when the scales/legends do not fit vertically beside a plot? I would like to arrange them in a grid with n-rows and m-cols (similar to this question) and align each row and column by legend title. Currently, I use get_legend()
from ggpubr but fail to align those properly.
Mock example copied from ggnewscale
package:
library(ggplot2)
library(ggnewscale)
library(ggpubr) # To extract legends
library(patchwork)
library(cowplot)
# Equivalent to melt(volcano)
topography <- expand.grid(x = 1:nrow(volcano),
y = 1:ncol(volcano))
topography$z <- c(volcano)
# point measurements of something at a few locations
set.seed(42)
measurements <- data.frame(x = runif(30, 1, 80),
y = runif(30, 1, 60),
thing = rnorm(30))
ggp = ggplot(mapping = aes(x, y)) +
geom_contour(data = topography, aes(z = z, color = stat(level))) +
# Color scale for topography
scale_color_viridis_c(option = "D") +
# geoms below will use another color scale
new_scale_color() +
geom_point(data = measurements, size = 3, aes(color = thing)) +
# Color scale applied to geoms added after new_scale_color()
scale_color_viridis_c(option = "A") +
geom_point(data = measurements, aes(color = thing, size = exp(thing))) +
# Color scale applied to geoms added after new_scale_color()
scale_size_continuous()
ggl = get_legend(ggp)[[1]]
ggl = lapply(ggl, as_ggplot)
# How to align the legends with respect to title?
wrap_plots(ggl)
ggl[[3]] + ggl[[1]] + ggl[[2]]
plot_grid(ggl[[1]],
ggl[[2]],
ggl[[3]],
ncol = 3, align = "h", axis = "t")
Upvotes: 1
Views: 76
Reputation: 125373
After a closer look I don't see any easy option to achieve your desired result. The first issue is that for the default legend.box="vertical"
layout the legends are aligned to the left of the overall legend box but center aligned in the vertical direction, whereas with legend.box="horizontal"
they are aligned at the top but center aligned in the horizontal direction. The second issue is that depending on the layout the single legends get stretched in the horizontal or vertical direction. And from my understanding this can't be fixed using some clever choice of theme options.
The only option I have found to fix that is to manipulate the widths/heights so that legends in the same column/row of the grid have the same widths/heights.
As in my original answer I start with adding legend.box="horizontal"
to the main plot to arrange the legends in the horizontal direction which already makes the legends the same height and hence achieves the desired alignment at the top. However, when we have multiple rows we lose the alignment at the left because the widths of the legends differ. To fix that I have to re-set the widths of the grobs which make up the legend. In the example code below, where the first column of the grid contains legends of type colorbar´ this is easily achieved, i.e. simply use the
widths` for the widest legend which in the present use case is the colorbar in the first row.
Note: For debugging purposes I added a red border around each legend.
library(ggplot2)
library(patchwork)
ggp <- ggp +
theme(
legend.box = "horizontal",
legend.background = element_rect(color = "red")
)
ggl <- ggpubr::get_legend(ggp)
legends <- lapply(c(3, 5, 7), \(i) ggl[3, i])
# Make the second guide_colorbar the same width as the first
legends[[3]]$grobs[[1]]$widths <- legends[[1]]$grobs[[1]]$widths
legend <- legends |>
wrap_plots(nrow = 2)
list(
ggp + theme(legend.position = "none"),
legend
) |>
wrap_plots(nrow = 1, widths = c(2, 1))
Original answer
You can add legend.box="horizontal"
to the main plot to arrange the legends in the horizontal direction. This way all legends and titles are aligned at the top. Just a small change but this is sufficient to ensure that the legends and titles are aligned at the top when arranging them to your desired grid layout using wrap_plots
.
library(ggplot2)
library(ggnewscale)
library(patchwork)
ggp <- ggp +
theme(
legend.box = "horizontal"
)
ggl <- ggpubr::get_legend(ggp)
legend <- list(ggl[3, 3], ggl[3, 5], ggl[3, 7]) |>
wrap_plots(nrow = 2)
list(
ggp + theme(legend.position = "none"),
legend
) |>
wrap_plots(nrow = 1)
Upvotes: 0