0%

How to implement two color scales in ggplot2

As indicated in the title, this article will discuss how to solve this problem in ggplot2.

I encounter this question when I want to construct two different color ranges to col aesthetic, such as geom_line and geom_text. Sometimes I may choose another way to visualize the data to avoid this situation, but I really want to know how to solve it if I have to use this color strategy.

From my Google search. I have found the best solution and it must be thanks to Elio Campitelli’s contribution, the author of the ggnewscale package. He demonstrates how to implement two color scales and explain what the principle is. You can refer to this article, Multiple color (and fill) scales with ggplot2.

Here I just show how it works. Firstly we prepare the dummy data.

library(tidyverse)
library(ggnewscale)

set.seed(123)
data <- tibble(
  id = rep(1:5, each = 4),
  day = sample(5:20, 20, replace = TRUE),
  linecol = str_c("col", id),
  day2 = day + 2,
  label = rep(c("Group1", "Group2"), each = 10)
)

And then I’d like to draw a line plot with labels around it. The line colors are determined by the linecol variable, while label colors are by label group. Let's look at the error example that doesn’t work with no surprise.

data %>% ggplot(aes(x = day, y = id)) +
  geom_line(aes(col = linecol)) +
  scale_color_manual(values = c("red", "orange", "yellow", "green", "blue")) +
  geom_text(aes(label = label, col = label)) +
  scale_color_manual(values = c("blue", "orange"), guide = NULL)
 
# Error
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
Error in `palette()`:
! Insufficient values in manual scale. 7 needed but only 2 provided.

In order to solve it, you just need to add one line code as mentioned in that reference article. structure(ggplot2::standardise_aes_names("colour"), class = "new_aes") or the function new_scale_color() wrapped in ggnewscale package. If you want to use scale_fill_*, replacing "colour" to "fill" is fine.

So the final code without any error is shown below.

data %>% ggplot(aes(x = day, y = id)) +
  geom_line(aes(col = linecol)) +
  scale_color_manual(values = c("red", "orange", "yellow", "green", "blue")) +
  structure(ggplot2::standardise_aes_names("colour"), class = "new_aes") +
  # new_scale_color() +
  geom_text(aes(label = label, col = label)) +
  scale_color_manual(values = c("blue", "orange"), guide = NULL)
ggnewscale_example

There is no doubt that this solution is not very common and formal. And I really hope it can be merged into ggplot2 big family so that I only need to import one package. Ah!