1

I want to create my own default color scheme/template for ggplot2 (think corporate colors) which is used automatically if the user loads the package.

The system works if all colors are used. If however only a subset of the colors are used, ggplot2, by default, selects the colors from the beginning. Is there a way to select the colors by another function? (E.g., when selecting two colors from the color list, select the first and the last. For three colors select the first, the middle, and the last, etc)

This should be done automatically, so that the user doesn't need to specify the colors herself.

To give an example of what I did so far:

library(ggplot2)
library(dplyr)

# in .onAttach as this is used within a package
my_colors <- c("red", "orange", "yellow", "green", "blue", "violet")

assign("scale_color_discrete", function(..., values = my_colors) scale_color_manual(..., values = as.character(values)), globalenv())
assign("scale_colour_discrete", function(..., values = my_colors) scale_colour_manual(..., values = as.character(values)), globalenv())
assign("scale_fill_discrete", function(..., values = my_colors) scale_fill_manual(..., values = as.character(values)), globalenv())

df <- mtcars %>% 
  mutate(carb = as.factor(carb))


df %>% 
  ggplot(aes(x = wt, y = mpg, color = carb)) + 
  geom_point()

Good figure: all 6 colors are used (~the whole spectrum of my colors are used).

df %>% 
  filter(carb %in% c(2, 3, 4)) %>% 
  ggplot(aes(x = wt, y = mpg, color = carb)) +
  geom_point()

Bad figure: the first three colors are used. The goal here is to have colors 1, 3 (or 4), 6 selected by the scale_color_manual() function to "select" the whole spectrum.

Created on 2020-01-17 by the reprex package (v0.3.0)

Any ideas how to select the right colors?

David
  • 9,216
  • 4
  • 45
  • 78
  • This link might be be helpful! https://drsimonj.svbtle.com/creating-corporate-colour-palettes-for-ggplot2 – Noah Olsen Jan 17 '20 at 14:41
  • I mostly followed drsimonj's example, but he intrapolates the colors for continuous colors, in my case I have discrete colors but want to have a selection of colors that spans the whole range. – David Jan 17 '20 at 14:45
  • Maybe I'm misunderstanding. Any reason why subsetting (e.g. `values = pal[c(1, 3, 5)]`) wouldn't work? I'm not sure why you're assigning to the scales, rather than making a vector of colors to pass to the `values` argument. If you need specific values to always be matched to specific colors, just use a named vector – camille Jan 17 '20 at 15:06
  • I am not selecting the colors directly but pass the palette to scale_color_manual. If I am able to extract the number of colors the plot will have (from within the assignment of the scale-functions), I could do that. But I don't know if that is the way to go, nor how to do that. – David Jan 17 '20 at 15:09
  • related https://stackoverflow.com/questions/62462030/how-to-customize-a-color-palette-in-r-for-ggplot – tjebo Jan 05 '23 at 19:59

1 Answers1

3

I learned this by looking at the source code from RColorBrewer and ggthemes a while back. I suggest looking there as well. What you need to do is use discrete_scale. You also need to create a function that given a length n, returns the colors you want.

## Libraries
library(ggplot2)
library(dplyr)

# Your custom vector of colors
my_colors <- c("red", "orange", "yellow", "green", "blue", "violet")

# Function that returns a vector of n colors from your palette
# I just made this up... you can do what you want
custom_palette <- function(n) {

  pal_index <- seq_along(my_colors)

  if (n == 1) {
    pal_index = c(3)

  } else if (n == 2) {
    pal_index = c(1, 6)

  } else if (n == 3) {
    pal_index = c(1, 3, 6)

  } else if (n == 4) {
    pal_index = c(1, 3, 4, 6)

  } else if (n == 5) {
    pal_index = c(1, 2, 3, 4, 6)

  }

  my_colors[pal_index]

}

# Custom palette mapper
custom_pal <- function(type = "discrete", palette = custom_palette, direction = 1) {

  if (type == "discrete") {
    function(n) {
      pal <- palette(n)

      if (direction == -1) {
        pal <- rev(pal)
      }

      pal
    }
  } else if (type == "continuous") {

    pal <- palette()

    if (direction == -1) {
      pal <- rev(pal)
    }

    scales::gradient_n_pal(pal)
  }
}

# Function to implement using discrete_scale
# You need to do something similar for continuous
scale_color_custom <- function(..., direction = 1) {

  discrete_scale(aesthetics = "color", "my_colors", custom_pal("discrete", custom_palette, direction), ...)

}

# Assign as you did
# You do basically the same thing for "fill", but with the aesthetic changed in the function above
assign("scale_color_discrete", scale_color_custom, globalenv())
assign("scale_colour_discrete", scale_color_custom, globalenv())

df <- mtcars %>% 
  mutate(carb = as.factor(carb))

df %>% 
  filter(carb %in% c(2, 3, 4)) %>% 
  ggplot(aes(x = wt, y = mpg, color = carb)) +
  geom_point()

ggplot2 output