Add vertical spacing to ggplot legend [duplicate] - r

This is a follow-up on https://stackoverflow.com/questions/32275113
The problem is to tweak the legend elements to increase the space between legend keys without simultaneously extending the legend keys themselves. The solution is likely to be in tweaking the correct legend theme option.
Desired result: more vertical space between the legend key text labels, but without stretching the legend key lines.
d <- data.frame(x = mtcars$mpg, y = 0.10)
vlines <- rbind(aggregate(d[1], d[2], mean),
aggregate(d[1], d[2], median))
vlines$stat <- rep(c("mean", "median"), each = nrow(vlines)/2)
library("ggplot2")
ggplot(data = d, aes(x = x, y = ..density..)) +
geom_histogram(fill = "lightblue", color = "black") +
geom_vline(data = vlines, mapping = aes(xintercept = x, colour = stat),
show.legend = TRUE) +
theme(legend.direction = "vertical",
legend.position = "right",
# legend.key = element_rect(size = 2),
legend.key.size = unit(3, "cm"),
# legend.key.width = unit(2, "cm"),
# legend.key.height = unit(1, "cm")
)
Increasing legend.key.size, as suggested in answers to the linked question (see above), has the undesired side effect of increasing the vertical lines as well.
Edit Based on PoGibas's clever workaround, here's a screenshot of the desired result, included here to make sure the purpose is clear:
Following PoGibas, I used: shape = 73, legend.key.height = unit(2, "cm") and size = 6 inside the color guide.

One solution is to replace lines with points (requires additional geom layer):
Create plot with invisible points (size = 0 and rectangle shape shape = 15).
p <- ggplot(d, aes(x, ..density..)) +
geom_histogram(fill = "lightblue", color = "black") +
geom_vline(data = vlines, mapping = aes(xintercept = x, colour = stat)) +
geom_point(data = vlines, aes(0, 0, colour = stat), size = 0, shape = 15)
Add legend theme to:
Mask background color in legend (legend.key = element_rect(fill = "white"))
Create large legend (legend.key.height = unit(3, "cm"))
Remove lines (linetype = 0) and make large points (size = 5)
Code:
p +
theme(legend.direction = "vertical",
legend.position = "right",
legend.key = element_rect(fill = "white"),
legend.key.height = unit(3, "cm")) +
guides(color = guide_legend(override.aes = list(linetype = 0, size = 5)))
PS.:
This is not a perfect solution as there's a gap between legend label and boxes.
If you want lines instead of rectangles use shape = 73

Related

Change the vertical spacing of one legend in ggplot?

I'm plotting two sets of data in ggplot2 with code like the following, which leads to me having two legends (ignore the ugly plot, this is just an example)
x <- ggplot(mtcars)+
theme_bw() +
theme(legend.position=c(0.8, 0.8), legend.direction="horizontal",
legend.key.size=unit(0.008, "cm"), legend.title=element_blank(),
legend.margin=margin(), legend.spacing = unit(0.04, "cm")) +
guides(colour = guide_legend(override.aes = list(size=6)), shape= guide_legend(override.aes = list(size=5))) +
geom_point(aes(x=mpg, y=cyl, colour=cyl))+
geom_point(aes(x=mpg, y = hp, shape=as.factor(carb)))
print(x)
The issue is that for me, the black shapes in the bottom are vertically too close together, I would like the two rows of black shapes to have more vertical space between them. I tried to use legend.spacing.y but it did not help at all, it only changed the space between the two individual legends (for cyl and carb). I would like to know if there's some theme command that would let me do something like legend.spacing(legend=carb, unit(0.1, "cm")) so it specifically acts on the carb legend.
Thanks!
You can use the keyheight argument in guide_legend
ggplot(mtcars) +
theme_bw() +
theme(
legend.position = c(0.8, 0.8),
legend.direction = "horizontal",
legend.key.size = unit(0.008, "cm"),
legend.title = element_blank(),
legend.margin = margin(),
legend.spacing = unit(0.04, "cm")
) +
guides(colour = guide_legend(override.aes = list(size = 6)),
shape = guide_legend(override.aes = list(size = 5), keyheight = 2)) +
geom_point(aes(x = mpg, y = cyl, colour = cyl)) +
geom_point(aes(x = mpg, y = hp, shape = as.factor(carb)))

R ggplot 2 legend border issue

I know this is minor, but it's for a publication and will drive me crazy. The bottom of the P0688 box is like 1-2 pixels thinner than the rest. I don't want to make the borders thicker because then it doesn't match the rest of the bar chart.
plot<- ggplot(tukey_letters, aes(x = variable, y = value.x,
fill = L1)) +
theme(panel.background=element_rect(fill="#ffffff", color
="#000000"), panel.grid.major=element_blank(),
panel.grid.minor=element_blank()) +
geom_bar(stat = "identity", position=position_dodge(),color="black")+ scale_fill_manual(values=c("#FFFFFF", "#999999"))+ guides(fill=guide_legend(title="Genotype", title.position = "left")) +
geom_errorbar(aes(ymin=value.x-se, ymax=value.x+se), width=.1,size=.5,position=position_dodge(0.9), color="black")+
theme(
axis.title = element_text(size =12, face="bold"),
axis.text = element_text(angle=30, vjust=0.5,hjust=0.6,size=8,face="bold", color="#000000"),
axis.ticks = element_line(size = rel(1)),
axis.ticks.length = unit(0.3, "cm"),
legend.position = c(0.2, 0.9)
)+
labs(
x="Treatment",
y="ARI1"
)+
#facet_wrap(~L1)+ ## You can use face_wrap function only if you need it+
geom_text(data =tukey_letters,
aes(x=xpos, y=ymax+offset_asterisk,label=groups),
size = 4,position=position_dodge(0.9) , vjust=-0.5
)
Thanks in advance. Let me know if there is anything else needed to help solve this.
This is due to the filling behaviour of the legend keys. It's a known issue, see this GitHub thread https://github.com/tidyverse/ggplot2/issues/2844. There is also a fix suggested on this site, let me show this here.
library(tidyverse)
ggplot(mtcars) +
aes(fill=factor(cyl), x=cyl) +
geom_bar(color = 'black') +
guides(fill=guide_legend(title.position = "left")) +
theme(legend.key = element_rect(color="white") +
legend.position = c(0.2, 0.7))
Enlarged, the legend will look now like this:
Now let's do the fix.
draw_key_polygon3 <- function(data, params, size) {
lwd <- min(data$size, min(size) / 4)
grid::rectGrob(
width = grid::unit(0.7, "npc"),
height = grid::unit(0.7, "npc"),
gp = grid::gpar(
col = data$colour,
fill = alpha(data$fill, data$alpha),
lty = data$linetype,
lwd = lwd * .pt,
linejoin = "mitre"
))
}
GeomBar$draw_key = draw_key_polygon3
ggplot(mtcars) +
aes(fill=factor(cyl), x=cyl) +
geom_bar(color = 'black') +
guides(fill=guide_legend(title.position = "left")) +
theme(legend.key = element_rect(color="white", fill = 'white'),
legend.position = c(0.2, 0.7))
But what is actually going on here? Let's see!
ggplot(mtcars) +
aes(fill=factor(cyl), x=cyl) +
geom_bar(color = 'black') +
guides(fill=guide_legend(title.position = "left")) +
theme(legend.position = c(0.2, 0.7),
legend.key = element_rect(color="black", fill = 'white'))
The legend has two borders! One for the legend glyph, the other for the key. You draw the border for the key with the call to theme, and the border around the glyph is created with your color argument in geom_bar
Created on 2020-04-04 by the reprex package (v0.3.0)

Spacing between ggplot legend key and labels using treemapify

I have a treemap that I've made using ggplot and treemapify. It's fine, but the legend is weirdly squashed - the labels are right up to the edges of the keys/symbols/icons. This doesn't happen with other plots so maybe it is a treemapify issue?
When I've searched for answers, most are about changing spacing between labels, for e.g., but that is not what I'm trying to do. I've found this one answer to what seems to be my question. And I guess I could add spaces to the labels! But it feels like there must be a better solution, especially because the labels are my column names and don't need spaces elsewhere, because for some reason the legend spacing is only an issue with this plot.
I've also tried theme(legend.text.align = 0.2), for e.g., but that clearly isn't right because it's about alignment, not margins or padding.
library(ggplot2)
library(treemapify)
tree_data <- as.data.frame(matrix(0, ncol = 0, nrow = 12))
tree_data$colour <- as.character(c("Red", "Red", "Blue", "Green"))
tree_data$shade <- as.character(c("Ruby", "Merlot", "Ink", "Olive",
"Garnet", "Wine", "Royal", "Emerald",
"Brick", "Berry", "Navy", "Apple"))
tree_data$freq <- sample(100, size = nrow(tree_data), replace = TRUE)
treeMapPlot <- ggplot(tree_data, aes(area = freq, fill = colour, label = shade,
subgroup = colour)) +
geom_treemap(color = "gray20") +
geom_treemap_subgroup_border() +
geom_treemap_text(colour = "white", place = "topleft", reflow = T, padding.x = grid::unit(1.5, "mm"),
padding.y = grid::unit(2, "mm"), size = 20) +
theme(plot.title = element_text(hjust = 0.5, size = 16),
legend.title = element_blank())
treeMapPlot
(Apologies for the colour mismatching!)
You can manually add a margin between the legend symbols and the text by specifying legend.spacing.x within theme. How about this:
treeMapPlot <- ggplot(tree_data, aes(area = freq, fill = colour, label = shade,
subgroup = colour)) +
geom_treemap(color = "gray20") +
geom_treemap_subgroup_border() +
geom_treemap_text(colour = "white", place = "topleft", reflow = T, padding.x = grid::unit(1.5, "mm"),
padding.y = grid::unit(2, "mm"), size = 20) +
theme(plot.title = element_text(hjust = 0.5, size = 16),
legend.title = element_blank(),
legend.spacing.x = unit(0.2, 'cm'))
treeMapPlot

ggplot2: Adjust legend symbols in overlayed plot

I need to create a plot, in which a histogram gets overlayed by a density. Here is my result so far using some example data:
library("ggplot2")
set.seed(1234)
a <- round(rnorm(10000, 5, 5), 0)
b <- rnorm(10000, 5, 7)
df <- data.frame(a, b)
ggplot(df) +
geom_histogram(aes(x = a, y = ..density.., col = "histogram", linetype = "histogram"), fill = "blue") +
stat_density(aes(x = b, y = ..density.., col = "density", linetype = "density"), geom = "line") +
scale_color_manual(values = c("red", "white"),
breaks = c("density", "histogram")) +
scale_linetype_manual(values = c("solid", "solid")) +
theme(legend.title = element_blank(),
legend.position = c(.75, .75),
legend.text = element_text(size = 15))
Unfortunately I can not figure out how I can change the symbols in the legend properly. The first symbol should be a relatively thick red line and the second symbol should be a blue box without the white line in the middle.
Based on some internet research, I tried to change different things in scale_linetype_manual and further I tried to use override.aes, but I could not figure out how I would have to use it in this specific case.
EDIT - Here is the best solution based on the very helpful answers below.
ggplot(df) +
geom_histogram(aes(x = a, y = ..density.., linetype = "histogram"),
fill = "blue",
# I added the following 2 lines to keep the white colour arround the histogram.
col = "white") +
scale_linetype_manual(values = c("solid", "solid")) +
stat_density(aes(x = b, y = ..density.., linetype = "density"),
geom = "line", color = "red") +
theme(legend.title = element_blank(),
legend.position = c(.75, .75),
legend.text = element_text(size = 15),
legend.key = element_blank()) +
guides(linetype = guide_legend(override.aes = list(linetype = c(1, 0),
fill = c("white", "blue"),
size = c(1.5, 1.5))))
As you thought, most of the work can be done via override.aes for linetype.
Note I removed color from the aes of both layers to avoid some trouble I was having with the legend box outline. Doing this also avoids the need for the scale_*_* function calls. To set the color of the density line I used color outside of aes.
In override.aes I set the linetype to be solid or blank, the fill to be either white or blue, and the size to be 2 or 0 for the density box and histogram box, respectively.
ggplot(df) +
geom_histogram(aes(x = a, y = ..density.., linetype = "histogram"), fill = "blue") +
stat_density(aes(x = b, y = ..density.., linetype = "density"), geom = "line", color = "red") +
theme(legend.title = element_blank(),
legend.position = c(.75, .75),
legend.text = element_text(size = 15),
legend.key = element_blank()) +
guides(linetype = guide_legend(override.aes = list(linetype = c(1, 0),
fill = c("white", "blue"),
size = c(2, 0))))
The fill and colour aesthetics are labelled by histogram and density respectively, and their values set using scale_*_manual. Doing so maps directly to the desired legend without needing any overrides.
ggplot(df) +
geom_histogram(aes(x = a, y = ..density.., fill = "histogram")) +
stat_density(aes(x = b, y = ..density.., colour="density"), geom = "line") +
scale_fill_manual(values = c("blue")) +
scale_colour_manual(values = c("red")) +
labs(fill="", colour="") +
theme(legend.title = element_blank(),
legend.position = c(.75, .75),
legend.box.just = "left",
legend.background = element_rect(fill=NULL),
legend.key = element_rect(fill=NULL),
legend.text = element_text(size = 15))

Is there a way to change the spacing between legend items in ggplot2?

Is there a way to change the spacing between legend items in ggplot2? I currently have
legend.position ="top"
which automatically produces a horizontal legend. However, the spacing of the items is very close together and I am wondering how to space them farther apart.
ggplot2 v3.0.0 released in July 2018 has working options to modify legend.spacing.x, legend.spacing.y and legend.text.
Update Dec 2021 - to make legend.spacing.y work, you will need to set byrow = TRUE in the corresponding guide_legend. See also this thread. Example below.
Example: Increase horizontal spacing between legend keys
library(ggplot2)
ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
geom_bar() +
coord_flip() +
scale_fill_brewer("Cyl", palette = "Dark2") +
theme_minimal(base_size = 14) +
theme(legend.position = 'top',
legend.spacing.x = unit(1.0, 'cm'))
Note: If you only want to expand the spacing to the right of the legend text, use stringr::str_pad()
Example: Increase vertical spacing (mind byrow = TRUE)
library(ggplot2)
ggplot(mtcars, aes(y = factor(cyl), fill = factor(cyl))) +
geom_bar() +
theme(legend.spacing.y = unit(1.0, 'cm')) +
## important additional element
guides(fill = guide_legend(byrow = TRUE))
Example: Move the legend key labels to the bottom and increase vertical spacing
ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
geom_bar() +
coord_flip() +
scale_fill_brewer("Cyl", palette = "Dark2") +
theme_minimal(base_size = 14) +
theme(legend.position = 'top',
legend.spacing.x = unit(1.0, 'cm'),
legend.text = element_text(margin = margin(t = 10))) +
guides(fill = guide_legend(title = "Cyl",
label.position = "bottom",
title.position = "left", title.vjust = 1))
Example: for scale_fill_xxx & guide_colorbar
ggplot(mtcars, aes(mpg, wt)) +
geom_point(aes(fill = hp), pch = I(21), size = 5)+
scale_fill_viridis_c(guide = FALSE) +
theme_classic(base_size = 14) +
theme(legend.position = 'top',
legend.spacing.x = unit(0.5, 'cm'),
legend.text = element_text(margin = margin(t = 10))) +
guides(fill = guide_colorbar(title = "HP",
label.position = "bottom",
title.position = "left", title.vjust = 1,
# draw border around the legend
frame.colour = "black",
barwidth = 15,
barheight = 1.5))
The below is obsolete, but is left for curious people.
For vertical legends, settinglegend.key.size only increases the size of the legend keys, not the vertical space between them
ggplot(mtcars) +
aes(x = cyl, fill = factor(cyl)) +
geom_bar() +
scale_fill_brewer("Cyl", palette = "Dark2") +
theme_minimal(base_size = 14) +
theme(legend.key.size = unit(1, "cm"))
In order to increase the distance between legend keys, modification of the legend-draw.r function is needed. See this issue for more info
# function to increase vertical spacing between legend keys
# #clauswilke
draw_key_polygon3 <- function(data, params, size) {
lwd <- min(data$size, min(size) / 4)
grid::rectGrob(
width = grid::unit(0.6, "npc"),
height = grid::unit(0.6, "npc"),
gp = grid::gpar(
col = data$colour,
fill = alpha(data$fill, data$alpha),
lty = data$linetype,
lwd = lwd * .pt,
linejoin = "mitre"
))
}
### this step is not needed anymore per tjebo's comment below
### see also: https://ggplot2.tidyverse.org/reference/draw_key.html
# register new key drawing function,
# the effect is global & persistent throughout the R session
# GeomBar$draw_key = draw_key_polygon3
ggplot(mtcars) +
aes(x = cyl, fill = factor(cyl)) +
geom_bar(key_glyph = "polygon3") +
scale_fill_brewer("Cyl", palette = "Dark2") +
theme_minimal(base_size = 14) +
theme(legend.key = element_rect(color = NA, fill = NA),
legend.key.size = unit(1.5, "cm")) +
theme(legend.title.align = 0.5)
I think the best option is to use guide_legend within guides:
p + guides(fill=guide_legend(
keywidth=0.1,
keyheight=0.1,
default.unit="inch")
)
Note the use of default.unit , no need to load grid package.
A simple fix that I use to add space in horizontal legends, simply add spaces in the labels (see extract below):
scale_fill_manual(values=c("red","blue","white"),
labels=c("Label of category 1 ",
"Label of category 2 ",
"Label of category 3"))
To add spacing between entries in a legend, adjust the margins of the theme element legend.text.
To add 30pt of space to the right of each legend label (may be useful for a horizontal legend):
p + theme(legend.text = element_text(
margin = margin(r = 30, unit = "pt")))
To add 30pt of space to the left of each legend label (may be useful for a vertical legend):
p + theme(legend.text = element_text(
margin = margin(l = 30, unit = "pt")))
for a ggplot2 object p. The keywords are legend.text and margin.
[Note about edit: When this answer was first posted, there was a bug. The bug has now been fixed]
Now that opts is deprecated in ggplot2 package, function theme should be used instead:
library(grid) # for unit()
... + theme(legend.key.height=unit(3,"line"))
... + theme(legend.key.width=unit(3,"line"))
Looks like the best approach (in 2018) is to use legend.key.size under the theme object. (e.g., see here).
#Set-up:
library(ggplot2)
library(gridExtra)
gp <- ggplot(data = mtcars, aes(mpg, cyl, colour = factor(cyl))) +
geom_point()
This is real easy if you are using theme_bw():
gpbw <- gp + theme_bw()
#Change spacing size:
g1bw <- gpbw + theme(legend.key.size = unit(0, 'lines'))
g2bw <- gpbw + theme(legend.key.size = unit(1.5, 'lines'))
g3bw <- gpbw + theme(legend.key.size = unit(3, 'lines'))
grid.arrange(g1bw,g2bw,g3bw,nrow=3)
However, this doesn't work quite so well otherwise (e.g., if you need the grey background on your legend symbol):
g1 <- gp + theme(legend.key.size = unit(0, 'lines'))
g2 <- gp + theme(legend.key.size = unit(1.5, 'lines'))
g3 <- gp + theme(legend.key.size = unit(3, 'lines'))
grid.arrange(g1,g2,g3,nrow=3)
#Notice that the legend symbol squares get bigger (that's what legend.key.size does).
#Let's [indirectly] "control" that, too:
gp2 <- g3
g4 <- gp2 + theme(legend.key = element_rect(size = 1))
g5 <- gp2 + theme(legend.key = element_rect(size = 3))
g6 <- gp2 + theme(legend.key = element_rect(size = 10))
grid.arrange(g4,g5,g6,nrow=3) #see picture below, left
Notice that white squares begin blocking legend title (and eventually the graph itself if we kept increasing the value).
#This shows you why:
gt <- gp2 + theme(legend.key = element_rect(size = 10,color = 'yellow' ))
I haven't quite found a work-around for fixing the above problem...
Let me know in the comments if you have an idea, and I'll update accordingly!
I wonder if there is some way to re-layer things using $layers...
From Koshke's work on ggplot2 and his blog (Koshke's blog)
... + theme(legend.key.height=unit(3,"line")) # Change 3 to X
... + theme(legend.key.width=unit(3,"line")) # Change 3 to X
Type theme_get() in the console to see other editable legend attributes.
Use any of these
legend.spacing = unit(1,"cm")
legend.spacing.x = unit(1,"cm")
legend.spacing.y = unit(1,"cm")

Resources