How to select the first n values of a scale using ggplot's scale_discrete, instead of the beginning, mid and end points?

Viewed 114

I am in the process of creating my own colour palette mainly using this blogpost.

This leads me to use ggplot's function discrete_scale(). The problem I'm running into is that I have a 9 point discrete colour scale (all really different colours, see below*). However, when I ask ggplot to draw a scale for a factor with 3 levels, it selects the first, middle and last points of the colour values. Instead what I want is to select the first 3. How do I go about this?

  • The palette I'm using is: c(#00AD2E, #29C29E, #2B8DAB, #00A1DB, #0C41ED, #8B00F5, #C724F5, #EF4E8E, #F63131, #F66223, #FA85A6, #F5910F, #F0BC00, #D6901F, #8D7F6D, #8F8F8F, #73B823, #FF4D6A, #B1652F, #1DAA76)
1 Answers

The issue is that in blogpost you linked the author makes use of colorRampPalette to return an interpolated color palette for any number of colors. The first color will always be the first element and the last one the last element of the color vector you passed as input to colorRampPalette. And in case of three colors you get the mid element too.

In general the palette argument of ggplot2::discrete_scale should be a function which takes one argument n and returns n colors.

One option to achieve your desired result may look like so where besides the palette function my_pal I added a custom scale_color_my function:

Note: My simple function does not do any checks and throws no warnings e.g. in case you demand more colors than provided by the color palette. Anyway, if you want to go further I would suggest to have a look a the source code of e.g. ggplot::scale_color_brewer and scales::brewer_pal.

my_pal <- function(n) {
  cols <- c(
    "#00AD2E", "#29C29E", "#2B8DAB", "#00A1DB", "#0C41ED", "#8B00F5",
    "#C724F5", "#EF4E8E", "#F63131", "#F66223", "#FA85A6", "#F5910F", 
    "#F0BC00", "#D6901F", "#8D7F6D", "#8F8F8F", "#73B823", "#FF4D6A",
    "#B1652F", "#1DAA76"
  )
  cols[seq_len(n)]
}

scale_colour_my <- function() {
  discrete_scale("colour", "mypal", my_pal)
}
scale_color_my <- scale_colour_my

library(ggplot2)

d <- data.frame(
  x = 1:12,
  y = 1:12,
  color4 = LETTERS[1:4],
  color12 = LETTERS[1:12]
)

base <- ggplot(d, aes(x, y)) +
  geom_point() +
  scale_color_my()

base + 
  aes(color = color4)

  
base + 
  aes(color = color12)

EDIT If you want to account for multiple palettes you could follow the source code of scale_color_brewer:

my_pal <- function(palette = "my1") {
  cols <- c(
    "#00AD2E", "#29C29E", "#2B8DAB", "#00A1DB", "#0C41ED", "#8B00F5",
    "#C724F5", "#EF4E8E", "#F63131", "#F66223", "#FA85A6", "#F5910F", 
    "#F0BC00", "#D6901F", "#8D7F6D", "#8F8F8F", "#73B823", "#FF4D6A",
    "#B1652F", "#1DAA76"
  )
  pal <- if (palette == "my1") {
    cols  
  } else if (palette == "my2") {
    rev(cols)
  } else {
    cols
  }
  function(n) {
    pal[seq_len(n)]
  }
}

scale_colour_my <- function(..., palette = "my1") {
  discrete_scale("colour", "mypal", my_pal(palette = palette))
}
scale_color_my <- scale_colour_my

library(ggplot2)

d <- data.frame(
  x = 1:12,
  y = 1:12,
  color4 = LETTERS[1:4],
  color12 = LETTERS[1:12]
)

base <- ggplot(d, aes(x, y)) +
  geom_point() + 
  aes(color = color4)

base +
  scale_color_my()

base +
  scale_color_my(palette = "my2")

Related