Overview

The following is a description of how to generate your own color palette function in R.


Outline

  1. Choose Colors
  2. Create RGB Codes
  3. Generate Color Palette Function
  4. Modify Custom Color Palette Function
  5. Plot!

Step 0: Load Packages & Set Working Directory

list.of.packages <- c("rstudioapi","fBasics","grDevices")
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
library(rstudioapi)
library(fBasics)
library(grDevices)

Set working directory to source file location first, or choose different directory:

current_path <- getSourceEditorContext()$path
setwd(dirname(current_path))

Step 1: Choose Colors

One way to do this is to print the colors from existing palettes.

The fBasics package has quite a few good ones to choose from:

This website also gives some good information:

grDevices package

And so forth. A few motivating reasons for creating your own palette are that you can change the order of colors (i.e. swap the yellows and greens), add more colors to a palette, and/or create a palette with a set of colors that is not present in other available palettes.

Mostly these packages will give you a series of n colors in hex form:

pal <- topo.colors(n = 50)
pal
##  [1] "#4C00FFFF" "#3B00FFFF" "#2800FFFF" "#1600FFFF" "#0400FFFF"
##  [6] "#000DFFFF" "#001FFFFF" "#0032FFFF" "#0043FFFF" "#0055FFFF"
## [11] "#0068FFFF" "#007AFFFF" "#008BFFFF" "#009EFFFF" "#00AFFFFF"
## [16] "#00C1FFFF" "#00D3FFFF" "#00E5FFFF" "#00FF4DFF" "#00FF38FF"
## [21] "#00FF24FF" "#00FF0FFF" "#05FF00FF" "#1AFF00FF" "#2EFF00FF"
## [26] "#42FF00FF" "#57FF00FF" "#6BFF00FF" "#80FF00FF" "#94FF00FF"
## [31] "#A8FF00FF" "#BDFF00FF" "#D1FF00FF" "#E6FF00FF" "#FFFF00FF"
## [36] "#FFF90CFF" "#FFF318FF" "#FFED24FF" "#FFE930FF" "#FFE53BFF"
## [41] "#FFE247FF" "#FFDF53FF" "#FFDD5FFF" "#FFDC6BFF" "#FFDB77FF"
## [46] "#FFDB83FF" "#FFDB8FFF" "#FFDC9BFF" "#FFDEA7FF" "#FFE0B3FF"

which look like:

par(mar = rep(0, 4))
pie(rep(1, length(pal)), col = pal)


Step 2: Create RGB Codes

These need to be converted into RGB color vectors. One way to do this is by using the col2rgb() in the grDevices package. The trick is to then re-order the colors in the way that you want (if pulling from an existing palette), or to identify the colors for the first time that should go into your palette. If picking colors from scratch, the adobe color wheel website can be helpful, and it provides both hex codes and rgb numbers:

For the particular project we were working on, instead of having the colors go from royal blue > light blue > green > yellow > tan, we instead wanted the colors to be ordered as blue > light blue > yellow > green. To do this, first I converted the hex codes from above into r, g, b vectors:

r_orig <- col2rgb(pal)[1,]
r_orig
##  [1]  76  59  40  22   4   0   0   0   0   0   0   0   0   0   0   0   0
## [18]   0   0   0   0   0   5  26  46  66  87 107 128 148 168 189 209 230
## [35] 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
g_orig <- col2rgb(pal)[2]
g_orig
## [1] 0
b_orig <- col2rgb(pal)[3,]
b_orig
##  [1] 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
## [18] 255  77  56  36  15   0   0   0   0   0   0   0   0   0   0   0   0
## [35]   0  12  24  36  48  59  71  83  95 107 119 131 143 155 167 179

Then, I selectively chose the colors to keep (a reduced set), and re-ordered the numbers within each vector to be the intended blue > light blue > yellow > green. This took a bit of playing around with, so if anyone knows of a better way to do this, your suggestions are much appreciated!

r <- c(76, 0, 0, 0, 0, 255, 255, 255, 255, 230, 209, 
       189, 168, 148, 128, 107, 87, 66, 46, 26)

g <- c(0, 67, 158, 211, 229, 237, 243, 249, 255, 255, 
       255, 255, 255, 255, 255, 255, 255, 255, 255, 255)

b <- c(255, 255, 255, 255, 255, 36, 24, 12, 0, 0, 0, 0, 
       0, 0, 0, 0, 0, 0, 0, 0)

Step 3: Generate Color Palette Function

Next, we will generate an existing color palette function from the fBasics package.

divPalette
## function (n, name = c("BrBG", "PiYG", "PRGn", "PuOr", "RdBu", 
##     "RdGy", "RdYlBu", "RdYlGn", "Spectral")) 
## {
##     BrBG = rgb(c(84, 140, 191, 223, 246, 245, 199, 128, 53, 1, 
##         0), c(48, 81, 129, 194, 232, 245, 234, 205, 151, 102, 
##         60), c(5, 10, 45, 125, 195, 245, 229, 193, 143, 94, 48), 
##         maxColorValue = 255)
##     PiYG = rgb(c(142, 197, 222, 241, 253, 247, 230, 184, 127, 
##         77, 39), c(1, 27, 119, 182, 224, 247, 245, 225, 188, 
##         146, 100), c(82, 125, 174, 218, 239, 247, 208, 134, 65, 
##         33, 25), maxColorValue = 255)
##     PRGn = rgb(c(64, 118, 153, 194, 231, 247, 217, 166, 90, 27, 
##         0), c(0, 42, 112, 165, 212, 247, 240, 219, 174, 120, 
##         68), c(75, 131, 171, 207, 232, 247, 211, 160, 97, 55, 
##         27), maxColorValue = 255)
##     PuOr = rgb(c(127, 179, 224, 253, 254, 247, 216, 178, 128, 
##         84, 45), c(59, 88, 130, 184, 224, 247, 218, 171, 115, 
##         39, 0), c(8, 6, 20, 99, 182, 247, 235, 210, 172, 136, 
##         75), maxColorValue = 255)
##     RdBu = rgb(c(103, 178, 214, 244, 253, 247, 209, 146, 67, 
##         33, 5), c(0, 24, 96, 165, 219, 247, 229, 197, 147, 102, 
##         48), c(31, 43, 77, 130, 199, 247, 240, 222, 195, 172, 
##         97), maxColorValue = 255)
##     RdGy = rgb(c(103, 178, 214, 244, 253, 255, 224, 186, 135, 
##         77, 26), c(0, 24, 96, 165, 219, 255, 224, 186, 135, 77, 
##         26), c(31, 43, 77, 130, 199, 255, 224, 186, 135, 77, 
##         26), maxColorValue = 255)
##     RdYlBu = rgb(c(165, 215, 244, 253, 254, 255, 224, 171, 116, 
##         69, 49), c(0, 48, 109, 174, 224, 255, 243, 217, 173, 
##         117, 54), c(38, 39, 67, 97, 144, 191, 248, 233, 209, 
##         180, 149), maxColorValue = 255)
##     RdYlGn = rgb(c(165, 215, 244, 253, 254, 255, 217, 166, 102, 
##         26, 0), c(0, 48, 109, 174, 224, 255, 239, 217, 189, 152, 
##         104), c(38, 39, 67, 97, 139, 191, 139, 106, 99, 80, 55), 
##         maxColorValue = 255)
##     Spectral = rgb(c(158, 213, 244, 253, 254, 255, 230, 171, 
##         102, 50, 94), c(1, 62, 109, 174, 224, 255, 245, 221, 
##         194, 136, 79), c(66, 79, 67, 97, 139, 191, 152, 164, 
##         165, 189, 162), maxColorValue = 255)
##     name = match.arg(name)
##     orig = eval(parse(text = name))
##     rgb = t(col2rgb(orig))
##     temp = matrix(NA, ncol = 3, nrow = n)
##     x = seq(0, 1, , length(orig))
##     xg = seq(0, 1, , n)
##     for (k in 1:3) {
##         hold = spline(x, rgb[, k], n = n)$y
##         hold[hold < 0] = 0
##         hold[hold > 255] = 255
##         temp[, k] = round(hold)
##     }
##     palette = rgb(temp[, 1], temp[, 2], temp[, 3], maxColorValue = 255)
##     palette
## }
## <environment: namespace:fBasics>

We don’t actually need the whole function though, and can instead reduce down to this:

function (n, name = c("insert your palette name here")) 
{
    your.palette.name = rgb(r, g, b, maxColorValue = 255)
    name = match.arg(name)
    orig = eval(parse(text = name))
    rgb = t(col2rgb(orig))
    temp = matrix(NA, ncol = 3, nrow = n)
    x = seq(0, 1, , length(orig))
    xg = seq(0, 1, , n)
    for (k in 1:3) {
        hold = spline(x, rgb[, k], n = n)$y
        hold[hold < 0] = 0
        hold[hold > 255] = 255
        temp[, k] = round(hold)
    }
    palette = rgb(temp[, 1], temp[, 2], temp[, 3], maxColorValue = 255)
    palette
}
## function (n, name = c("insert your palette name here")) 
## {
##     your.palette.name = rgb(r, g, b, maxColorValue = 255)
##     name = match.arg(name)
##     orig = eval(parse(text = name))
##     rgb = t(col2rgb(orig))
##     temp = matrix(NA, ncol = 3, nrow = n)
##     x = seq(0, 1, , length(orig))
##     xg = seq(0, 1, , n)
##     for (k in 1:3) {
##         hold = spline(x, rgb[, k], n = n)$y
##         hold[hold < 0] = 0
##         hold[hold > 255] = 255
##         temp[, k] = round(hold)
##     }
##     palette = rgb(temp[, 1], temp[, 2], temp[, 3], maxColorValue = 255)
##     palette
## }

Step 4: Modify Custom Color Palette Function

To get your own palette up and running, you just need to swap in your own palette name where it says name = c(“insert your palette name here”), put that same palette name in place of ‘your.palette.name’. Make sure you have named your red, green, and blue vectors r, g, b, respectively, and then you should be good to go.

beach <- function (n, name = c("beach.colors")) 
{
    beach.colors = rgb(r,g,b,maxColorValue = 255)
    name = match.arg(name)
    orig = eval(parse(text = name))
    rgb = t(col2rgb(orig))
    temp = matrix(NA, ncol = 3, nrow = n)
    x = seq(0, 1, , length(orig))
    xg = seq(0, 1, , n)
    for (k in 1:3) {
        hold = spline(x, rgb[, k], n = n)$y
        hold[hold < 0] = 0
        hold[hold > 255] = 255
        temp[, k] = round(hold)
    }
    palette = rgb(temp[, 1], temp[, 2], temp[, 3], maxColorValue = 255)
    palette
}

Step 5: Plot!

See the new color palette:

pal2 <- beach(n=50)
par(mar = rep(0, 4))
pie(rep(1, length(pal2)), col = pal2)

With the custom color function in hand, you can now use the resulting color palette(s) in other work such as a color palette for a plot. The plot that motivated this tutorial is pictured below and featured in the following paper:

Ram, N., Benson, L., Brick, T. R., Conroy, D. E., & Pincus, A. L. (2017). Behavioral landscapes and earth mover’s distance: A new approach for studying individual differences in density distributions. Journal of Research in Personality, 69, 191-205.


Questions, Suggestions, Sharing

If you have questions or suggestions on material to add to this tutorial, please contact Libby at leb237@psu.edu. We are happy for this tutorial to be shared, but rather than sharing directly, please make sure to link to it’s page on the QuantDev website: https://quantdev.ssri.psu.edu/tutorials/generating-custom-color-palette-function-r

There are also many other cool tutorials given by the QuantDev group at: https://quantdev.ssri.psu.edu/tutorials/