D.4 Using color packages

Most packages that primarily deal with visualizations — like ggplot2, lattice, or plotly — provide their own color palettes. To reach beyond the 657 predefined colors of R and the color palettes included in grDevices, the primary resource are packages that provide additional — and typically more specialized — color support.

There is a large number of R packages that provide dedicated color support (i.e., define colors and color scales, and corresponding functions) for all means and purposes. As a consequence, many users of R never define a new color palette, but use the color palettes provided by others. However, to choose nice colors, we have to know which options exist and how they can be chosen and compared. As choosing colors is not just an art, but also a matter of taste, we merely mention some personal preferences in this section.

D.4.1 RColorBrewer

The colors at http://colorbrewer2.org (Brewer, 2019) were primarily designed to color maps. Beyond cartography, the vibrant color palettes are widely used in the R community, thanks to the popular RColorBrewer package (Neuwirth, 2014) and their integration into the ggplot2 package (Wickham et al., 2020).

Actually, unless you happen to need very special colors, RColorBrewer provides nice and useful color palettes that are sufficient for most purposes.

library(RColorBrewer) # load package

The following command prints all color palettes included in RColorBrewer:

display.brewer.all()

D.4.2 viridis/viridisLite

The color scales of the viridis package (Garnier, 2018a) were specifically designed to be perceptually-uniform, both in regular form and when converted to black-and-white. Its color palettes viridis, magma, plasma, and inferno can also be perceived by readers with the most common form of color blindness, and the cividis scale is even suited for people with color vision deficiency.

library(viridis)  # load package

x <- y <- seq(-8*pi, 8*pi, len = 40)
r <- sqrt(outer(x^2, y^2, "+"))
filled.contour(cos(r^2) * exp(-r/(2*pi)), 
               axes = FALSE,
               color.palette = viridis,
               asp = 1)

x <- y <- seq(-6*pi, 6*pi, len = 36)
r <- sqrt(outer(x^2, y^2, "+"))
filled.contour(cos(r^2) * exp(-r/(2*pi)), 
               axes = FALSE,
               color.palette = inferno,
               asp = 1)

In recent versions of ggplot2, the viridis scales are integrated and can be used by specifying scale_color_viridis_c() and scale_color_viridis_c() (for continuous color scales) and scale_fill_viridis_d() and scale_fill_viridis_d() (for discrete color scales).

(See also Chapter 12.3 Using a Colorblind-Friendly Palette in the R Graphics Cookbook.)

The viridisLite package (Garnier, 2018b) is a simpler version of the viridis package and sufficient for most purposes:

library(viridisLite)  # load package

vir_10 <- viridis(n = 10)
seecol(vir_10, col_brd = "white", lwd_brd = 4, 
       title = "Example of a viridis color palette (n = 10)",
       pal_names = paste0("viridis_", 1:10))

The following functions each define a color scale of n colors:

n <- 20  # number of colors

# define 5 different color scales (n colors each):
v1 <- viridis(n)
v2 <- magma(n)
v3 <- inferno(n)
v4 <- plasma(n)
v5 <- cividis(n)

# See and compare color scales:
seecol(list(v1, v2, v3, v4, v5), 
       col_brd = "white", lwd_brd = 4, 
       title = "Various viridis color palettes (n = 20)",
       pal_names = c("v1: viridis", "v2: magma", "v3: inferno", "v4: plasma",  "v5: cividis"))

Check out ?viridisLite::viridis for additional information.

Another option for obtaining perceptually ordered and uniform color palettes are the Scientific colour maps by Fabio Crameri (F. Crameri, 2018). They are provided in many different formats — implemented by the scico package in R — friendly to people with color vision deficiency, and still readable in black-and-white print.

Finally, the dichromat package focuses on palettes for color-impaired viewers and allows simulating the effects of different types of color-blindness.

D.4.3 unikn

The unikn package (Neth & Gradwohl, 2021) implements the color schemes of the University of Konstanz and is used throughout this book.

Please note: As one of the authors of the unikn package, I cannot help thinking that it is indispensable for working with colors. However, many people appear to live quite happy and colorful lives without ever needing this package.

Besides providing a range of pre-defined color palettes, the package provides some color functions that are generally useful:

  1. The seecol() function allows a quick and easy inspections of color palettes. It has two distint modes, depending on the type of its first argument pal:

    • When called with a (single) color palette, it allows seeing (or printing the details of) a particular color palette;

    • When called with a list of (several) color palettes or a recognized keyword, it allows comparing multiple color palettes.

Here are examples for seeing a single color palette:

library(unikn)  # load package
seecol(pal = pal_unikn)  # see/print details of a particular color palette

and for comparing several color palettes:

seecol(pal = "all")  # compare all available color palettes

  1. The usecol() function makes creating new and modifying existing color palettes very easy:
mix_1 <- usecol(pal = c(Karpfenblau, "white", Peach), n = 15)   # define color palette from 3 colors
mix_2 <- usecol(pal = c(rev(pal_seeblau), "white", pal_pinky))  # combining 2 color palettes
mix_3 <- usecol(pal = c(rev(pal_bordeaux), "white", pal_petrol), n = 15)  # mix and extend color palettes

# Show and compare custom color palettes: 
seecol(list(mix_1, mix_2, mix_3),
       col_brd = "white", lwd_brd = 4, 
       title = "Comparing palettes mixed from unikn colors",
       pal_names = c("mix_1", "mix_2", "mix_3"))

The usecol() function also provides a wrapper for using and modifying (pre-defined or newly created) color palettes in graphs:

x <- y <- seq(-8*pi, 8*pi, len = 40)
r <- sqrt(outer(x^2, y^2, "+"))
filled.contour(cos(r^2) * exp(-r/(2*pi)), 
               axes = FALSE,
               col = usecol(mix_2, n = 20),
               asp = 1)

Even when not using the color palettes of the University of Konstanz, the usecol() and seecol() functions are often useful, as they also work with the color palettes provided by other packages. For instance, we can use the seecol() function to compare (and modify) sets of color palettes from different packages.

In the following example, we first create 12 color palettes from three packages: four color palettes from hcl.pals() of grDevices (R Core Team, 2021), four color palettes from RColorBrewer (Neuwirth, 2014), and four color palettes from yarrr (Phillips, 2017). We then uses the seecol() function to view and compare all 12 color palettes:

# (a) get some HCL palettes (assuming R version 3.6.0+):
# hcl.pals() shows the names of all HCL palettes 

hcl_1 <- hcl.colors(n = 10, palette = "Fall")
hcl_2 <- hcl.colors(n = 10, palette = "Geyser")
hcl_3 <- hcl.colors(n = 10, palette = "Berlin")
hcl_4 <- hcl.colors(n = 10, palette = "Zissou 1")


# (b) get some palettes from RColorBrewer: 
library(RColorBrewer)
# display.brewer.all()  # shows all Brewer palettes

brew_1 <- brewer.pal(n = 11, name = "BrBG")
brew_2 <- brewer.pal(n = 11, name = "PRGn")
brew_3 <- brewer.pal(n = 11, name = "Spectral")
brew_4 <- brewer.pal(n = 8, name = "Pastel1")


# (c) get some palettes from yarrr:
library(yarrr)
# yarrr::piratepal()  # shows all pirate palettes

yarrr_1 <- yarrr::piratepal(palette = "basel")
yarrr_2 <- yarrr::piratepal(palette = "appletv")
yarrr_3 <- yarrr::piratepal(palette = "espresso")
yarrr_4 <- yarrr::piratepal(palette = "info")


my_pals <- list(hcl_1, hcl_2, hcl_3, hcl_4, 
                brew_1, brew_2, brew_3, brew_4, 
                yarrr_1, yarrr_2, yarrr_3, yarrr_4)
my_names <- c("hcl_1", "hcl_2", "hcl_3", "hcl_4", 
              "brew_1", "brew_2", "brew_3", "brew_4", 
              "yarrr_1", "yarrr_2", "yarrr_3", "yarrr_4")

# Show and compare color palettes: 
library(unikn) 
seecol(pal = my_pals,
       col_brd = "white", lwd_brd = 2, 
       title = "Comparing HCL, RColorBrewer and yarrr color palettes",
       pal_names = my_names)

Similarly, we can use the usecol() function to mix and modify color palettes from various sources. Again, it makes sense to inspect the results with seecol():

# Mix, extend and modify some palettes (from other packages):
brew_mix <- usecol(c(rev(  brewer.pal(n = 4, name = "Reds")), 
                     "white", brewer.pal(n = 4, name = "Blues")), n = 13)
brew_ext <- usecol(brewer.pal(n = 11, name = "Spectral"), n = 12)

yarrr_mix <- usecol(c(piratepal("nemo"), piratepal("bugs")))
yarrr_mod <- usecol(c(piratepal("ipod")), n = 9)

my_pals <- list(brew_mix, brew_ext, 
                yarrr_mix, yarrr_mod)
my_names <- c("brew_mix", "brew_ext", 
              "yarrr_mix", "yarrr_mod") 

# Show and compare new color palettes: 
seecol(pal = my_pals,
       col_brd = "white", lwd_brd = 2, 
       title = "Using usecol() and seecol() to mix and modify palettes",
       pal_names = my_names)

Additionally, the unikn package provides two functions that help with defining new named color palettes or finding colors with specific names:

  1. The newpal() function makes it easy to define new named color palettes:
# (1) Source: <https://www.schemecolor.com/germany-flag-colors.php>
col_flag <- c("#000000", "#dd0000", "#ffce00")
names_de <- c("Black", "Electric red", "Tangerine yellow")

# (2) Source: <https://en.wikipedia.org/wiki/Flag_of_Germany#Design>
col_flag <- c("#000000", "#FF0000", "#FFCC00") 
names_de <- c("Jet black", "Traffic red", "Rapeseed yellow")

# Decision: Simplify names:  
simple_names <- c("de_black", "de_red", "de_gold")

# Define color palette:
flag_de  <- newpal(col = col_flag,
                   names = simple_names)

seecol(flag_de, 
       title = "Defining a flag_de color palette", 
       mar_note = "Colors based on <https://en.wikipedia.org/wiki/Flag_of_Germany>"  # credit source
       )

Note that different sources often provide different colors (and certainly different names) for the same color schemes. Thus, it always makes sense to double-check definitions and acknowledge one’s sources.

  1. The grepal() function addresses a common problem: We typically know that we want some shade of some color (e.g., orange), but do not know which corresponding colors exist in our environment. The grepal() function searches color names for a pattern:
grepal(pattern = "orange")  # returns colors() with "orange" in their name
#>  [1] "darkorange"  "darkorange1" "darkorange2" "darkorange3" "darkorange4"
#>  [6] "orange"      "orange1"     "orange2"     "orange3"     "orange4"    
#> [11] "orangered"   "orangered1"  "orangered2"  "orangered3"  "orangered4"

seecol(pal = grepal("orange"), 
       title = "See all colors() with 'orange' in their name")

By default, grepal(pattern, x) searches x = colors() (i.e., the 657 named colors provided by the grDevices package), but x can be set to other named color palettes (including data frames). As pattern can be a regular expression (see Appendix E now provides a primer on using regular expressions), we can easily answer more complex questions:

  • Which color names end on pink, purple, or violet? How do these colors look?

  • How many shades of gray or grey are there (in R)?

Both these questions can easily be answered by combining two calls to the grepal() and the seecol() functions:

  • Which color names end on pink, purple, or violet? How do these colors look?
cols_ppv <- grepal(pattern = "pink$|purple$|violet$")

seecol(pal = cols_ppv, 
       title = "See all colors() with names ending on 'pink', 'purple' or 'violet'")

  • How many shades of gray or grey are there (in R)?
length(grepal("gr(a|e)y"))   # shades of "gray" or "grey"
#> [1] 224
length(grepal("^gr(a|e)y"))  # shades starting with "gray" or "grey"
#> [1] 204
length(grepal("^gr(a|e)y$")) # shades starting and ending with "gray" or "grey"
#> [1] 2

Finally, combining grepal() with seecol() also allows comparing long color palettes containing certain keywords:

pal_r <- grepal("red")
pal_g <- grepal("green")
pal_b <- grepal("blue")

seecol(list(pal_r, pal_g, pal_b), 
       title = "See all colors() with 'red', 'green', 'blue' in their name")

  1. When using the pipe operator %>% from the magrittr package (Bache & Wickham, 2014), we can use multiple unikn functions to create and evaluate colorful chains of commands:
library(magrittr)

cols <- c("#FAAB18", "#1380A1","#990000", "#588300")

cols %>% 
  newpal(names = c("orangy", "bluish", "redish", "greeny")) %>%
  # usecol(n = 10) %>% 
  seecol(title = "My new custom color palette")

This concludes our overview of selected color packages in R. See Resources on color packages (in Section D.7.3) for links to many additional color packages.