Center-Align Legend Title and Legend Keys in Ggplot2 for Long Legend Titles

Center-align legend title and legend keys in ggplot2 for long legend titles

Update Oct. 4, 2019:

A while back I wrote a fairly general function based on the original idea I posted here almost two years ago. The function is on github here but it's not part of any officially published package. It is defined as follows:

align_legend <- function(p, hjust = 0.5)
{
# extract legend
g <- cowplot::plot_to_gtable(p)
grobs <- g$grobs
legend_index <- which(sapply(grobs, function(x) x$name) == "guide-box")
legend <- grobs[[legend_index]]

# extract guides table
guides_index <- which(sapply(legend$grobs, function(x) x$name) == "layout")

# there can be multiple guides within one legend box
for (gi in guides_index) {
guides <- legend$grobs[[gi]]

# add extra column for spacing
# guides$width[5] is the extra spacing from the end of the legend text
# to the end of the legend title. If we instead distribute it by `hjust:(1-hjust)` on
# both sides, we get an aligned legend
spacing <- guides$width[5]
guides <- gtable::gtable_add_cols(guides, hjust*spacing, 1)
guides$widths[6] <- (1-hjust)*spacing
title_index <- guides$layout$name == "title"
guides$layout$l[title_index] <- 2

# reconstruct guides and write back
legend$grobs[[gi]] <- guides
}

# reconstruct legend and write back
g$grobs[[legend_index]] <- legend
g
}

The function is quite flexible and general. Here are a few examples of how it can be used:

library(ggplot2)
library(cowplot)
#>
#> ********************************************************
#> Note: As of version 1.0.0, cowplot does not change the
#> default ggplot2 theme anymore. To recover the previous
#> behavior, execute:
#> theme_set(theme_cowplot())
#> ********************************************************
library(colorspace)

# single legend
p <- ggplot(iris, aes(Sepal.Width, Sepal.Length, color = Petal.Width)) + geom_point()
ggdraw(align_legend(p)) # centered

ggdraw(align_legend(p, hjust = 1)) # right aligned

# multiple legends
p2 <- ggplot(mtcars, aes(disp, mpg, fill = hp, shape = factor(cyl), size = wt)) +
geom_point(color = "white") +
scale_shape_manual(values = c(23, 24, 21), name = "cylinders") +
scale_fill_continuous_sequential(palette = "Emrld", name = "power (hp)", breaks = c(100, 200, 300)) +
xlab("displacement (cu. in.)") +
ylab("fuel efficiency (mpg)") +
guides(
shape = guide_legend(override.aes = list(size = 4, fill = "#329D84")),
size = guide_legend(
override.aes = list(shape = 21, fill = "#329D84"),
title = "weight (1000 lbs)")
) +
theme_half_open() + background_grid()

# works but maybe not the expected result
ggdraw(align_legend(p2))

# more sensible layout
ggdraw(align_legend(p2 + theme(legend.position = "top", legend.direction = "vertical")))

Created on 2019-10-04 by the reprex package (v0.3.0)

Original answer:

I found a solution. It requires some digging into the grob tree, and it may not work if there are multiple legends, but otherwise this seems a reasonable solution until something better comes along.

library(ggplot2)
library(gtable)
library(grid)

p <- ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Petal.Width)) +
geom_point(size = 3) +
scale_color_distiller(palette = "YlGn", type = "seq", direction = -1,
name = "Long legend heading\nShould be centered") +
theme(legend.title.align = 0.5)

# extract legend
g <- ggplotGrob(p)
grobs <- g$grobs
legend_index <- which(sapply(grobs, function(x) x$name) == "guide-box")
legend <- grobs[[legend_index]]

# extract guides table
guides_index <- which(sapply(legend$grobs, function(x) x$name) == "layout")
guides <- legend$grobs[[guides_index]]

# add extra column for spacing
# guides$width[5] is the extra spacing from the end of the legend text
# to the end of the legend title. If we instead distribute it 50:50 on
# both sides, we get a centered legend
guides <- gtable_add_cols(guides, 0.5*guides$width[5], 1)
guides$widths[6] <- guides$widths[2]
title_index <- guides$layout$name == "title"
guides$layout$l[title_index] <- 2

# reconstruct legend and write back
legend$grobs[[guides_index]] <- guides
g$grobs[[legend_index]] <- legend

grid.newpage()
grid.draw(g)

Sample Image

How to align the legend box to the middle of legend title in ggplot2?

Borrow the solution from this workaround

library(ggplot2)
library(gtable)
library(grid)

long1 <- ggplot(df, aes(x = Flow, y = Coef, color = VeryLongLegendTitle)) +
xlab(NULL) + scale_x_continuous(limits = c(0.0, 1.0), breaks = c(0.25, 0.75)) +
geom_point(size = 2, alpha = 0.8) +
theme_bw(base_size = 14) +
theme(axis.text.x = element_text(angle = 0, vjust = 0.5))

# extract legend
g <- ggplotGrob(long1)
grobs <- g$grobs
legend_index <- which(sapply(grobs, function(x) x$name) == "guide-box")
legend <- grobs[[legend_index]]

# extract guides table
guides_index <- which(sapply(legend$grobs, function(x) x$name) == "layout")
guides <- legend$grobs[[guides_index]]

# add extra column for spacing
# guides$width[5] is the extra spacing from the end of the legend text
# to the end of the legend title. If we instead distribute it 50:50 on
# both sides, we get a centered legend
guides <- gtable_add_cols(guides, 0.5*guides$width[5], 1)
guides$widths[6] <- guides$widths[2]
title_index <- guides$layout$name == "title"
guides$layout$l[title_index] <- 2

# reconstruct legend and write back
legend$grobs[[guides_index]] <- guides
g$grobs[[legend_index]] <- legend

grid.newpage()
grid.draw(g)

Created on 2018-03-18 by the reprex package (v0.2.0).

how to align the legend title to the middle of legend box in ggplot2?

You need legend.title.align rather than legend.title:

p + theme(legend.title.align=0.5) 

Sample Image

Aligning the keys legend and labels to the right side of the legend box, in ggplot2

You can use guide_legend(label.position = ... to adjust the label position, labels etc.

ggplot(df, aes(x=age, y=y, fill = gender)) + 
geom_col(position = 'dodge') +
guides(fill = guide_legend(# title.hjust = 1, # adjust title if needed
label.position = "left",
label.hjust = 1)

Sample Image

For more info: https://ggplot2.tidyverse.org/reference/guide_legend.html

How to align the legend key and text in ggplot?

I hope this is what you wanted

library(ggplot2)

df = data.frame(
x = c(1:10, 1:10),
y = 1:20,
group = rep(c('male', 'female'), each = 10))

ggplot(df, aes(x=x, y=y, color = group)) +
geom_smooth() +
theme(legend.position = 'right') +
guides(color = guide_legend(title.position = "top",
# hjust = 0.5 centres the title horizontally
title.hjust = 0.5,
label.position = "left"))
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

Created on 2020-01-08 by the reprex package (v0.3.0)

Align legend text in ggplot

You need to specify legend.text.align in theme():

ggplot(meltdf, aes(x = date, y = value, colour =variable)) + 
geom_smooth() +
stat_smooth(method = "gam") +
scale_color_discrete(name="Pollutant",
labels = c(expression(O[3]),
expression(NO[2]),
expression(NO[x]),
expression(PM[2.5]))) +
theme(legend.text.align = 0)

Sample Image

Alternatively, try using bquote instead of expression, and default left alignment takes place. I don't know why just using expression changes the alignment to the right...

ggplot(meltdf, aes(x = date, y = value, colour =variable)) + 
geom_smooth() +
stat_smooth(method = "gam") +
scale_color_discrete(name="Pollutant",
labels = c(bquote(O[3]),
bquote(NO[2]),
bquote(NO[x]),
bquote(PM[2.5])))

centering legend title over bottom legend

Solution:

Per @stefan's advice, using fill=guide_coloursteps makes it possible to specify the legend title location. The solution and result I used is below

#1990
ggplot() +
geom_sf(data = tracts, mapping = aes(fill = pop.compare), show.legend = TRUE) +
scale_fill_stepsn(colours = c("#fde725", "#addc30", "#5ec962", "#28ae80","#21918c", "#2c728e", "#3b528b", "#472d7b", "black"),
limits = c(0,227),
breaks = c(0.25, 0.75, .9, 1.1, 1.25, 2, 3, 10),
values = scales::rescale(c(0.115, 0.5, 0.825, 1, 1.175, 1.5, 2.5, 6.5, 118.5),
from = c(0,227))) +
labs(fill = "Population Ratio") +
theme(plot.title = element_text(hjust = 0.5),
legend.position = "bottom",
legend.key.width = unit(1.5,"cm"),
legend.spacing = unit(0.25,"cm"),
legend.title = element_text(hjust = 0.5),
legend.justification = "center") +
guides(fill=guide_coloursteps(title.position="top")) +
coord_sf()

Sample Image

Other resources, as noted by @phalteman:

https://ggplot2.tidyverse.org/reference/guide_legend.html



Related Topics



Leave a reply



Submit