Jen
Jen

Reputation: 355

ggplot two geom points, combine both color scheme into one legend

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)

Upvotes: 1

Views: 431

Answers (1)

aosmith
aosmith

Reputation: 36076

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.

Upvotes: 2

Related Questions