Change the vertical spacing of one legend in ggplot? - r

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)))

Related

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)

Plot legend to move left ggplot

I like to move the legend a bit further on the left But I am not getting how to do it.
Secondly, I also want to reduce the space between axis label and legend too
Can you please suggest something
the code is given below that I am using
The image of the graph is in the link below
"tooltip"
ggplot(Q6_m, aes( choice,temp,fill=Answer ))+
geom_bar(position = position_stack(reverse = TRUE), stat="identity") +
coord_flip() +
xlab("") +
ylab("Number of responses") +
scale_fill_brewer(type = "div") +
theme(axis.text=element_text(size=8),
axis.title=element_text(size=8,face="bold"), legend.title = element_blank(),
legend.text=element_text(size=7)) +
ggtitle("Q6:Rate your ability to perform the following procedures WITHOUT attending assistance?")+
theme(plot.title = element_text(color = "black", size = 7.5, face = "bold", hjust = 1))+
facet_wrap(~gender,scales = "free_x")+
theme(legend.position="bottom", legend.direction = "horizontal",legend.key.size = unit(0.5,"line")
)
You should use legend.justification to get the legend on the left side of the plot and legend.margin to reduce the space between axis labels and the legend:
ggplot(iris, aes(x = Species, y = Sepal.Length, fill = Species))+
geom_boxplot()+
theme(legend.position = "bottom",
legend.justification = c(0,1),
legend.margin = margin(t = -15, r = 0, b = 0, l = 0, unit = "pt"))
Does it answer your question ?

Change line type of border

I would like to change the line type of the panel border from solid line to dashed line in my graph. I tried to use panel.border and panel_border in theme, but R keep telling me it cannot find these two functions in theme.
I think this will be very difficult to do with theme options. Even when the panel spacing is 0, I'm pretty sure it's using element_rect to draw the panel borders. Getting the plot you want with theme modifications would require modifying 1 or 2 lines of each panel border with awareness of whether the facet is a left, right, or central facet.
To get around this we can use hline and vline instead. Hacky, but it works:
hline_df = expand.grid(visit = unique(df$visit), yint = c(-Inf, Inf))
vline_df = expand.grid(visit = unique(df$visit), xint = c(-Inf, Inf)) %>%
mutate(no_dash = !(
(xint == Inf & visit == tail(levels(visit), 1)) |
(xint == -Inf & visit == head(levels(visit), 1))
))
ggplot(df, aes(x=avisit, y=mean, group=Type, color=Type, shape=Type)) +
scale_y_continuous(breaks=seq(0,18,2), limits=c(0, 18)) +
geom_point(position=pd, cex=2) +
xlab("") +
ylab("Mean") +
scale_colour_manual(values=c("blue", "red")) +
scale_shape_manual(values=c("triangle", "circle")) +
coord_cartesian(ylim = c(0, 18)) +
facet_grid(.~factor(visit), scales = "free_x", space ="free_x",switch = "both") +
geom_hline(data = hline_df, aes(yintercept = yint)) +
geom_vline(data = vline_df, aes(xintercept = xint, linetype = no_dash), show.legend = FALSE) +
theme_bw() +
theme(axis.text.y = element_text(margin = margin(r = 0)),
panel.spacing = unit(0, "mm"),
strip.background = element_blank(),
legend.title=element_blank(),
strip.placement = "outside",
legend.background = element_rect(color="black", fill="white", size=0.5, linetype="solid"),
legend.direction = "horizontal",
panel.grid.minor = element_line(colour="white", linetype="dashed"),
panel.grid.major = element_line(colour = "white",linetype="dashed"),
panel.border = element_blank())
element_rect takes a linetype argument. It might draw the axis line on top of the border or vice versa, so you can modify axis.line too. This theme should give you what you want and will be a little more elegant:
ggplot() + theme(axis.line = element_line(linetype = 3), panel.border = element_rect(linetype =3)

Make legend invisible but keep figure dimensions and margins the same

I have made a plot with a legend.
Using an image editing program I made the legend invisible (but otherwise the figure has the same dimensions)
Is it possible to do this in ggplot2? I want to have a 2x2 panel of diagrams in a document but only one legend.
Using this as an example,
library(ggplot2)
p <- ggplot(mtcars, aes(x = disp, y = hp, color = factor(cyl))) +
geom_point() +
geom_line()
The following seems to work:
p + theme(
legend.text = element_text(color = "white"),
legend.title = element_text(color = "white"),
legend.key = element_rect(fill = "white")
) +
scale_color_discrete(
guide = guide_legend(override.aes = list(color = "white"))
)
Notice that the dimension of the gray plot area did not change.
Making the elements just white could cause problems, i.e. in cases of continuous scales or so. One may makes the scales and text elements just invisible.
p <- ggplot(mtcars, aes(x = disp, y = hp, lty = factor(gear))) +
geom_point(aes(color = cyl)) +
geom_line()
Gives a normal plot with legend:
Now make it really "invisible" by setting alpha = 0 in override.aes = list() within the guide = guide_legend() argument for each of the scales and color = "transparent" for the text elements of the legend:
p + scale_color_continuous(guide = guide_legend(override.aes = list(alpha = 0) ) )+
scale_linetype(guide = guide_legend(override.aes = list(alpha = 0) ) )+
theme(legend.title = element_text(color = "transparent"),
legend.text = element_text(color = "transparent"))

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