I am trying to combine multiple partial legends (i.e. for two different features of one plot) legends into one box. My real-life data is a plot made with geom_sf() with fill colors for polygons and a specific border highlighted made with geom_sf()with a line. Since the line is only one feature, it makes no sense to do a separate legend.
REPREX
data(iris)
ggplot(iris)+theme_classic()+
geom_point(aes(x=Petal.Length, y=Sepal.Length, color=Species, size=Sepal.Width))+
theme(legend.position=c(0.1,0.75),legend.background=element_rect(fill="white", color="black"), legend.spacing.y=unit(0,"cm"))
### Why doesn't ggplot draw a rectangle around the entire legend?
I managed to reduce the legend spacing, which already makes the plot cleaner, but ...
How can I forego the two separate legend boxes altogether and combine them in one box so that my plot looks cleaner?
I think you're looking for legend.box.background instead of legend.background:
ggplot(iris) +
theme_classic() +
geom_point(aes(x = Petal.Length, y = Sepal.Length,
color = Species, size = Sepal.Width)) +
theme(legend.position = c(0.1, 0.75),
legend.box.background = element_rect(fill = "white", color = "black"),
legend.spacing.y = unit(0,"cm"))
Related
Problem
Greetings, I'm trying to plot a map with a dark background using ggplot2 by setting the theme() parameter plot.background to e.g. element_rect(fill = "#000000") but switching from the cartesian coordinate system to a map projection using coord_map() introduces white margins which can only be removed by adjusting the size of the plot to the same ratio as the map area.
How can I remove this margin and apply the fill colour specified using the plot.background parameter to the whole plot regardless of its ratio?
MWE
nz <- map_data("nz")
ggplot(nz, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white", colour = "black") +
coord_map() +
theme_void() +
theme(plot.background = element_rect(fill = "#000000"))
Output
Expected output
Update
After about two hours of trying different approaches and wondering how R plotting and especially ggplot2 can be so incredibly clumsy, I finally found a solution here but it requires another library and additional steps to produce this conceptually elementary adjustment which should really be the default in the first place so there surely must be a native solution… right? 😓
There is a solution with the grid package, which is the package ggplot2 uses to draw the plots. First, I would make the small adjustment to your plotting code by also setting the plot background colour to black:
g <- ggplot(nz, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white", colour = "black") +
coord_map() +
theme_void() +
theme(plot.background = element_rect(fill = "#000000", colour = "#000000"))
Next we convert the g to a gtable and draw it with the grid package:
library(grid)
gt <- ggplotGrob(g)
grid.newpage()
# Draw a black rectangle
grid.draw(rectGrob(gp = gpar(fill = "#000000")))
grid.draw(gt)
The problem is that many coord-functions set a fixed aspect ratio for the plot, which will in turn affect other plot elements that are defined in absolute dimensions.
I have two data set with a different range of y values, the y range of one data set includes the other. I'm trying to make separate plots for each data set, but combine the two legends, while keeping the color scheme separate.
I'm using iris dataset below, and created iris2 as an example of the larger dataset.
iris2 <- iris
iris2$Sepal.Length <- iris$Sepal.Length*10
iris2 <- rbind(iris, iris2)
Then I plotted iris and iris2 separately like below.
g1 <- ggplot(iris, aes(x=Petal.Length, y=Petal.Width, fill=Sepal.Length)) +
geom_point(size=5, shape=21) +
scale_color_gradient(low="white",high="red", aesthetics="fill")
g2 <- ggplot(iris2, aes(x=Petal.Length, y=Petal.Width, fill=Sepal.Length)) +
geom_point(size=5, shape=21) +
scale_color_gradient(low="red",high="blue", aesthetics="fill")
What I'd like to do is to keep the color scheme for g1, so that if I create a new legend for g2, the legend will still show white as 0 and red as 8, then from red at 8 start changing colors until it becomes blue at 80. (So that I can ultimately just keep the legend for g2, as it would show color schemes for both g1 and g2 data)
I tried to do this by using scale_color_manual like below but can't use it because this is forcing discrete values into a continuous scale.
scale_color_manual(c(0,max(iris$Sepal.Length),max(iris2$Sepal.Length)), values = c("white", "red", "blue"), aesthetics = "fill")
I also considered changing fill=Sepal.Length to fill=as.factor(Sepal.Length) to deal with that problem, but that would require me to manually type in dozens of colors to be used. Any advice?
(Just a note: in this reproducible example, iris2 includes the same Sepal.Length values as iris so there would be no need to actually produce g1, but y values from my actual dataset do not overlap. That was just for me to create a quick reproducible example)
You could bind your datasets and use facets with scale_fill_gradientn() to make the color scale with the correct midpoint at the max of the first dataset (only relevant if you want a continuous color bar).
This approach involves using scales::rescale(), which is mentioned in the documentation for the values argument of scale_fill_gradientn().
That could look something like:
iris2 = iris
iris2$Sepal.Length = iris$Sepal.Length*10
iris2 = rbind(iris, iris2)
iris$name = "iris"
iris2$name = "iris2"
iris3 = rbind(iris, iris2)
library(ggplot2)
ggplot(iris3, aes(x = Petal.Length, y = Petal.Width, fill = Sepal.Length) ) +
geom_point(size = 5, shape = 21) +
scale_fill_gradientn(colors = c("white", "red", "blue"),
values = scales::rescale(c(min(iris$Sepal.Length),
max(iris$Sepal.Length),
max(iris2$Sepal.Length))) ) +
facet_wrap(~name) +
theme(strip.background = element_blank(),
strip.text = element_blank() )
You can change the breaks of the scale to make things more clear, as needed.
Even if you don't want to use facets, you could make a plot of the combined data and create the legend with scale_fill_gradientn() to use with your other plots. One nice way to extract legends and combine plots and legends is with package cowplot. See cowplot::get_legend().
For the legend size
You can increase the physical hight of the legend via legend.key.height in theme().
ggplot(iris3, aes(x = Petal.Length, y = Petal.Width, fill = Sepal.Length) ) +
geom_point(size = 5, shape = 21) +
scale_fill_gradientn(colors = c("white", "red", "blue"),
values = scales::rescale(c(min(iris$Sepal.Length),
max(iris$Sepal.Length),
max(iris2$Sepal.Length))) ) +
facet_wrap(~name) +
theme(strip.background = element_blank(),
strip.text = element_blank(),
legend.key.height = unit(1.5, "cm"))
If you don't have any 0 values you could also try working with the legend on the log scale, which can be done with trans in scale_fill_gradientn(), but it might take some fiddling to get things to look right.
Here is my reproducible code:
This is an example of what I want my actual figure to look like.
library(tidyverse)
p <- mtcars %>%
mutate(cyl = factor(cyl)) %>%
ggplot(aes(carb)) +
geom_bar(aes(fill = cyl)) +
scale_fill_manual(values = c("Red","Green","Blue"))
Resulting figure:
Problem:
What I want to change is in the legend. The boxes depicting the color of the bars on the histogram are too big and I want to reduce the size.
Attempted Solutions:
I have tried this code from another stackoverflow question and it does not work:
p <- p + guides(fill = guide_legend(override.aes = list(width = .5)))
In the reference stackoverflow question, another user suggested making a dummy geom_point variable and then using that legend as the legend and removing the fill legend. I would rather not have to do that if possible.
Thank you for the help.
Use legend.key.size (or legend.key.height and legend.key.width). E.g., add
theme(legend.key.size = unit(0.1, "cm"))
to your plot
Is it possible to specify distinct labels axes in each panel within ggplot?
For example:
ggplot(diamonds, aes(x = carat, y = price)) + geom_point() + facet_grid(~cut)
In this figure we have five panels, I would like to specify my own label for each of them. The default output is to produce one label for all the axes.
Is there a solution that doesn't involve using grid.arrange as is done here:
Modify x-axis labels in each facet
(I'm assuming you're referring to axis titles, not labels.)
Out of principle, no. The philosophy behind facets is that they share common aesthetic mappings. But we can trick ggplot to get what we want, for example:
ggplot(diamonds, aes(x = carat, y = price)) +
geom_point() +
facet_grid(~cut, switch = 'x') +
theme(axis.title.x = element_blank(),
strip.background = element_blank())
The trick is to switch the facet strips to the bottom of the plot. Then we turn off the strip background and the original x-axis title to create the appearance of separate axis titles.
(You may also want to change strip.text.x = element_text(size = ??) to the same size as the y-axis title. However, it seems to not be documented what the defualt size is for axis titles.)
I am trying to make a boxplot filled by a binary variable, with a facet grid. I also want to have jitter on top of the boxplots, but without getting them confused with the outliers. In order to fix this, I have added colour to the jitter, but by doing so, they meld in with the already coloured boxplots, as they are the same colour.
I really want to keep the colours the same, so is there a way to add borders to the jitter (or is there a different way to fix the outlier problem)?
Example code:
plot <- ggplot(mpg, aes(class, hwy))+
geom_boxplot(aes(fill = drv))+
geom_jitter(width = .3, aes(colour =drv))
# facet_grid(. ~some_binary_variable, scales="free")
You can use a filled plotting symbol (21:25, cf. ?pch) and then use a white border to differentiate the points:
ggplot(mpg, aes(class, hwy))+
geom_boxplot(aes(fill = drv))+
geom_jitter(width = .3, aes(fill = drv), shape = 21, color = "white")