Designing functions to generate and display color palettes in R

Designing functions to generate and display color palettes in R

In this post, I will go over the methodology I used to design the color palettes and functions to display them that comprise the colorways package referred to in my previous blog post.

Identifying the Problem

When it comes to color, I definitely believe in being adventurous and having many options based on mood and context. I think visualizations are best when they take an artistic and sometimes unexpected approach to color. Though blending in is sometimes necessary (for publication, or serious work-related presentations, maybe), when I work on projects for myself, I like to push the envelope a bit. It’s an aspect of data analysis that I truly enjoy.

I’ve found that palettes from RColorBrewer or default palettes work fine for certain situations, like heat maps that need a spectrum of color. But where they break down for me is in the fairly common occurrence of graphing several distinct subgroups. This calls for palettes with colors that are different enough from each other that no two subgroups are perceived as more similar than the others. RColorBrewer has a few “qualitative palettes” for this purpose, but I find them to be a bit generic.

Of course, RColorBrewer isn’t the only package for color palettes out there. This website has compiled a ton of color palette packages in one place, with a handy tool to select palettes. The palettes can then be retrieved in R using the Paletteer package, which is incredibly useful for accessing color palettes from many sources in a standardized way. Now that I found it, I will certainly be saving this resource and using it in the future. Some examples of favorites I’ve found from this site:

The current crop of color packages still lack some features that in my opinion are essential to choosing a palette. As far as I know, they don’t come with easy to use, built in color shuffling, and they don’t come with dynamic color previews in charts or graphs.

So, I set out to create my own package with three distinct functionalities:

  1. Save my own palettes
  2. Display any palette (either native or from another package) in a variety of forms (basic palette, charts, graphs)
  3. Shuffle color palettes if desired and save the newly ordered list of colors

Saving my own palettes

This is just as easy as choosing colors and putting them in lists. The fun part of course is naming the palettes based on my own whimsy. Some examples:

krampus <- c("#0782A6","#A66507","#994846","#CDD845","#624FAF","#52735D","#BBAE92","#FED2E7","#FFE402")
ballpit <- c("#5C33FF","#FF8E07","#E2E442","#42E44F","#C67CF9","#F64EBC","#ACF64E" ,"#C11736","#00B6A0")
donut <- c("#FA88F1","#2DEC93","#8FE2FF","#FF882B","#D80D0D","#D0A321","#369830","#B681FF","#858585")

I decided to go with 9 colors in each palette for uniformity and to maximize novel color combinations when shuffling. I ordered the palettes intentionally with my favorite color combinations come at the beginning, so that when the palettes aren’t shuffled, optimal color combinations are preselected.


Paletteprint: view all palettes

The first thing I wanted to do was write a function that would display all the palettes at once. To do this, I had to first create a list of palettes and their names:

palettes <- list(dino, hive, rumpus, taffy, sleuth, martian, krampus, tulip, donut, donette, creme, farmhand, mayhem, ballpit, january, pair1)

names(palettes) <- c("dino", "hive", "rumpus", "taffy", "sleuth", "martian", "krampus", "tulip", "donut", "donette", "creme", "farmhand", "mayhem","ballpit","january","pair1")

Then I wrote a function using rectangles in base R graphing to make a chart:

paletteprint <- function() {
  bordercolor <- "black"
  x <- c(-8,27)
  y <- c(0,(length(palettes)*3))
  i <- (length(palettes)*3)
  n <- 1
  plot(1, type="n", xlab="", ylab="", xlim=x, ylim=y,axes=FALSE, frame.plot=FALSE)
  for (p in palettes) {
    rect(0,i,3,i-1, col = p[1], border = bordercolor, lwd = 2)
    rect(3,i,6,i-1, col = p[2], border = bordercolor, lwd = 2)
    rect(6,i,9,i-1, col = p[3], border = bordercolor, lwd = 2)
    rect(9,i,12,i-1, col = p[4], border = bordercolor, lwd = 2)
    rect(12,i,15,i-1, col = p[5], border = bordercolor, lwd = 2)
    rect(15,i,18,i-1, col = p[6], border = bordercolor, lwd = 2)
    rect(18,i,21,i-1, col = p[7], border = bordercolor, lwd = 2)
    rect(21,i,24,i-1, col = p[8], border = bordercolor, lwd = 2)
    rect(24,i,27,i-1, col = p[9], border = bordercolor, lwd = 2)
    text(x = -8, y = i-.5, # Coordinates
         label = names(palettes[n]), pos =4)
    i = i-3
    n= n+1
  }
}

The output when calling paletteprint() looks like this:


Colordisplay: view, shuffle and choose the number of colors in a palette

Next I wanted a function that would display the colors in a selected palette, display them in a straightforward way, allow me to choose how many colors I wanted to see, shuffle the colors if desired, and return a list of the colors displayed.

colordisplay <- function(palette, number = 9, bordercolor = "black", shuffle = "no") {

  if (shuffle == "yes"){
    shuff <- sample(seq(from = 1, to = length(palette), by = 1), size = length(palette), replace = FALSE)
  }

  else {
    shuff <- seq(1, length(palette), by=1)
  }

  if (number == 9) {
    names = c(palette[shuff[1]], palette[shuff[2]],palette[shuff[3]],palette[shuff[4]],palette[shuff[5]],palette[shuff[6]],palette[shuff[7]],palette[shuff[8]],palette[shuff[9]])
    title <- paste(names, collapse = ", ")
    x <- c(0,3)
    y <- c(7,10)
    plot(1, type="n", xlab="", ylab="", xlim=x, ylim=y,axes=FALSE, main = title, frame.plot=FALSE)
    rect(0,10,1,9, col = palette[shuff[1]], border = bordercolor, lwd = 4)
    rect(1,10,2,9, col = palette[shuff[2]], border = bordercolor, lwd = 4)
    rect(2,10,3,9, col = palette[shuff[3]], border = bordercolor, lwd = 4)
    rect(0,9,1,8, col = palette[shuff[4]], border = bordercolor, lwd = 4)
    rect(1,9,2,8, col = palette[shuff[5]], border = bordercolor, lwd = 4)
    rect(2,9,3,8, col = palette[shuff[6]], border = bordercolor, lwd = 4)
    rect(0,8,1,7, col = palette[shuff[7]], border = bordercolor, lwd = 4)
    rect(1,8,2,7, col = palette[shuff[8]], border = bordercolor, lwd = 4)
    rect(2,8,3,7, col = palette[shuff[9]], border = bordercolor, lwd = 4)

    return(title)
  }

  else if (number == 8) {
    names = c(palette[shuff[1]], palette[shuff[2]],palette[shuff[3]],palette[shuff[4]],palette[shuff[5]],palette[shuff[6]],palette[shuff[7]],palette[shuff[8]])
    title <- paste(names, collapse = ", ")
    x <- c(0,4)
    y <- c(8,10)
    plot(1, type="n", xlab="", ylab="", xlim=x, ylim=y,axes=FALSE, main=title, frame.plot=FALSE)
    rect(0,10,1,9, col = palette[shuff[1]], border = bordercolor, lwd = 4)
    rect(1,10,2,9, col = palette[shuff[2]], border = bordercolor, lwd = 4)
    rect(2,10,3,9, col = palette[shuff[3]], border = bordercolor, lwd = 4)
    rect(3,10,4,9, col = palette[shuff[4]], border = bordercolor, lwd = 4)
    rect(0,9,1,8, col = palette[shuff[5]], border = bordercolor, lwd = 4)
    rect(1,9,2,8, col = palette[shuff[6]], border = bordercolor, lwd = 4)
    rect(2,9,3,8, col = palette[shuff[7]], border = bordercolor, lwd = 4)
    rect(3,9,4,8, col = palette[shuff[8]], border = bordercolor, lwd = 4)

    return(title)
  }

# and so on until number == 2

Ok, yes, there might have been a better way to set up a loop that doesn’t require that I write a new if statement for every number of colors BUT I did want control of how the display looks for each number of colors chosen so honestly I don’t think it’s really that needlessly wordy.

If I call colordisplay, choose my palette and use all default parameters, the result will look like this (fully zoomed out):

colordisplay(january)

I will also get this as output:

[1] "#F1F07C, #BB7EEE, #98EAC8, #65859C, #8C2438, #ADA99D, #AD840C, #398726, #EC5570"

If I call colordisplay with shuffle on, I get a randomly shuffled output with the corresponding list of colors:

colordisplay(january, shuffle = "yes")
[1] "#AD840C, #98EAC8, #8C2438, #EC5570, #F1F07C, #65859C, #BB7EEE, #398726, #ADA99

Changing the number parameter between 2-8 colors will result in the following shapes:

You can also choose to display a palette from another package, or using paletteer:

colordisplay(paletteer_d("nationalparkcolors::DeathValley"), number = 6, shuffle = "yes")
[1] "#E79498FF, #73652DFF, #F7E790FF, #514289FF, #B23539FF, #FAB57CFF"

Colorbar: view a palette as a bar chart:

I wanted to write a function that would allow me to input a palette, the number of colors I want from that palette, and whether I want the colors to be shuffled, and output a sample bar chart. This is the main functionality that I feel current color packages lack. Of course, you can keep your own code for a sample bar chart and test colors manually, but this function has made it so much easier to quickly assess whether a set of colors will work for a visualization. I also think it’s a ton of fun to shuffle the colors again and again and see what comes up!

The code behind this function is pretty similar to what I wrote for colordisplay. Full code for all functions can be found in this github repo.

Let’s try colorbar with the tulip palette and default parameters:

colorbar(palette = tulip)

We can add up to 7 colors in the bar chart:

colorbar(palette = tulip, number = 7)

We can choose to stack the bars:

colorbar(palette = tulip, number = 7, stacked = "yes")

We can shuffle the colors:

colorbar(palette = tulip, number = 7, stacked = "yes", shuffle = "yes")

Like colordisplay, colorbar will always output the ordered list of colors for each chart:

[1] “#D7DDDE, #A25E5F, #FFC27E, #FFFBC7, #9B8054, #E0E38C, #E0C2F6”

Lastly, we can use palettes from other packages:

colorbar(palette = paletteer_d("wesanderson::IsleofDogs2"), number = 4)

Colorscatter: view a palette as a scatter plot:

Colorscatter is virtually the same function as colorbar, but instead of a bar chart, color scatter will display a scatter plot. The only difference in parameters between the two is that color scatter lacks the “stacked” feature, for obvious reasons.

Let’s try the default colorscatter function using the “ballpit” palette:

colorscatter(palette = ballpit)

Like colorbar, we can view up to 7 colors using colorscatter:

colorscatter(palette = ballpit, number = 7)

We can also shuffle colors:

colorscatter(palette = ballpit, number = 5, shuffle = "yes")

Colorscatter will also return an ordered list of colors each time it is run:

[1] “#C11736, #E2E442, #5C33FF, #00B6A0, #ACF64E”

And of course like colorbar, colorscatter will accept external palettes:

colorscatter(palette = paletteer_d("tvthemes::simpsons"), number = 5, shuffle = "yes")

In conclusion:

I’ve certainly only scratched the surface of what’s out there and what’s capable in the world of R color packages. I’m happy with the package I’ve put together but I can already think of some improvements or additional functionalities I’d like to add (for example, a parameter for switching the charts to dark backgrounds). For now I think this is a great start and I’m excited to do more research and make more updates!

For full code, visit the colorways package GitHub repo here