What is a "good" palette for divergent colors in R? (or: can viridis and magma be combined together?)

Question:

I am interested in having a “good” divergent color pallette. One could obviously use just red, white, and blue:

img <- function(obj, nam) {
  image(1:length(obj), 1, as.matrix(1:length(obj)), col=obj, 
        main = nam, ylab = "", xaxt = "n", yaxt = "n",  bty = "n")
}
rwb <- colorRampPalette(colors = c("red", "white", "blue"))
img(rwb(100), "red-white-blue")

enter image description here

Since I recently fell in love with the viridis color palettes, I was hoping to combine viridis and magma to form such divergent colors (of course, color blind people would only see the absolute value of the color, but that is sometimes o.k.).

When I tried combining viridis and magma, I found that they don’t “end” (or “start”) at the same place, so I get something like this (I’m using R, but this would probably be the same for python users):

library(viridis)
img(c(rev(viridis(100, begin = 0)), magma(100, begin = 0)), "magma-viridis")

enter image description here

We can see that when close to zero, viridis is purple, while magma is black. I would like for both of them to start in (more or less) the same spot, so I tried using 0.3 as a starting point:

img(c(rev(viridis(100, begin = 0.3)), magma(100, begin = 0.3)), "-viridis-magma(0.3)")

enter image description here

This is indeed better, but I wonder if there is a better solution.

(I am also “tagging” python users, since viridis is originally from matplotlib, so someone using it may know of such a solution)

Thanks!

Asked By: Tal Galili

||

Answers:

Library RColorBrewer provides beautiful palettes for =<13 colors. For example, palette BrBG shows diverging colors from brown to green.

library(RColorBrewer)
display.brewer.pal(11, "BrBG")

enter image description here

Which can be expanded to a less informative palette by creating palettes to and from a mid-point color.

brbg <- brewer.pal(11, "BrBG")
cols <- c(colorRampPalette(c(brbg[1], brbg[6]))(51), 
    colorRampPalette(c(brbg[6], brbg[11]))(51)[-1])

enter image description here

Analogically, using your choice of viridis and magma palettes, you can try finding a similarity between them. This could be a point, where to join the palettes back to back.

select.col <- function(cols1, cols2){
    x <- col2rgb(cols1)
    y <- col2rgb(cols2)
    sim <- which.min(colSums(abs(x[,ncol(x)] - y)))
    message(paste("Your palette will be", sim, "colors shorter."))
    cols.x <- apply(x, 2, function(temp) rgb(t(temp)/255))
    cols.y <- apply(y[,sim:ncol(y)], 2, function(temp) rgb(t(temp)/255))
    return(c(cols.x,cols.y))
}

img(select.col(rev(viridis(100,0)),magma(100,0)), "")
# Your palette will be 16 colors shorter.

Palette

Answered By: nya

I find Kenneth Moreland’s proposal quite useful. It has now been implemented as cool_warm in heatmaply:

# install.packages("heatmaply")
img(heatmaply::cool_warm(500), "Cool-warm, (Moreland 2009)")

cool-warm colormap
This it how it looks like in action compared to an interpolated RColorBrewer "RdBu": comparison

Answered By: jan-glx

There have been some good and useful suggestions already but let me add a few remarks:

  1. The viridis and magma palettes are sequential palettes with multiple hues. Thus, along the scale you increase from very light colors to rather dark colors. Simultaneously the colorfulness is increased and the hue changes from yellow to blue (either via green or via red).
  2. Diverging palettes can be created by combining two sequential palettes. Typically, you join them at the light colors and then let them diverge to different dark colors.
  3. Usually, one uses single-hue sequential palettes that diverge from a neutral light gray to two different dark colors. One should pay attention though that the different “arms” of the palette are balanced with respect to luminance (light-dark) and chroma (colorfuness).

Therefore, combining magma and viridis does not work well. You could let them diverge from a similar yellowish color but you would diverge to similar blueish colors. Also with the changing hues it would just become more difficult to judge in which arm of the palette you are.

As mentioned by others, ColorBrewer.org provides good diverging palettes. Moreland’s approach is also useful. Yet another general solution is our diverging_hcl() function in the colorspace package. In the accompanying paper at https://arxiv.org/abs/1903.06490 (forthcoming in JSS) the construction principles are described and also how the general HCL-based strategy can approximate numerous palettes from ColorBrewer.org, CARTO, etc.
(Earlier references include our initial work in CSDA at http://dx.doi.org/10.1016/j.csda.2008.11.033 and further recommendations geared towards meteorology, but applicable beyond, in a BAMS paper at http://dx.doi.org/10.1175/BAMS-D-13-00155.1.)

The advantage of our solution in HCL space (hue-chroma-luminance) is that you can interpret the coordinates relatively easily. It does take some practice but isn’t as opaque as other solutions. Also we provide a GUI hclwizard() (see below) that helps understanding the importance of the different coordinates.

Most of the palettes in the question and the other answers can be matched rather closely by diverging_hcl() provided that the two hues (argument h), the maximum chroma (c), and minimal/maximal luminance (l) are chosen appropriately. Furthermore, one may have to tweak the power argument which controls how quickly chroma and luminance are increased, respectively. Typically, chroma is added rather quickly (power[1] < 1) whereas luminance is increased more slowly (power[2] > 1).

Moreland’s “cool-warm” palette for example uses a blue (h = 250) and red (h = 10) hue but with a relatively small luminance contrast(l = 37 vs. l = 88):

coolwarm_hcl <- colorspace::diverging_hcl(11,
  h = c(250, 10), c = 100, l = c(37, 88), power = c(0.7, 1.7))

which looks rather similar (see below) to:

coolwarm <- Rgnuplot:::GpdivergingColormap(seq(0, 1, length.out = 11),
  rgb1 = colorspace::sRGB( 0.230, 0.299, 0.754),
  rgb2 = colorspace::sRGB( 0.706, 0.016, 0.150),
  outColorspace = "sRGB")
coolwarm[coolwarm > 1] <- 1
coolwarm <- rgb(coolwarm[, 1], coolwarm[, 2], coolwarm[, 3])

In contrast, ColorBrewer.org’s BrBG palette a much higher luminance contrast (l = 20 vs. l = 95):

brbg <- rev(RColorBrewer::brewer.pal(11, "BrBG"))
brbg_hcl <- colorspace::diverging_hcl(11,
  h = c(180, 50), c = 80, l = c(20, 95), power = c(0.7, 1.3))

The resulting palettes are compared below with the HCL-based version below the original. You see that these are not identical but rather close. On the right-hand side I’ve also matched viridis and plasma with HCL-based palettes.

palettes

Whether you prefer the cool-warm or BrBG palette may depend on your personal taste but also – more importantly – what you want to bring out in your visualization. The low luminance contrast in cool-warm will be more useful if the sign of the deviation matters most. A high luminance contrast will be more useful if you want to bring out the size of the (extreme) deviations. More practical guidance is provided in the papers above.

The rest of the replication code for the figure above is:

viridis <- viridis::viridis(11)
viridis_hcl <- colorspace::sequential_hcl(11,
  h = c(300, 75), c = c(35, 95), l = c(15, 90), power = c(0.8, 1.2))

plasma <- viridis::plasma(11)
plasma_hcl <- colorspace::sequential_hcl(11,
  h = c(-100, 100), c = c(60, 100), l = c(15, 95), power = c(2, 0.9))

pal <- function(col, border = "transparent") {
  n <- length(col)
  plot(0, 0, type="n", xlim = c(0, 1), ylim = c(0, 1),
    axes = FALSE, xlab = "", ylab = "")
  rect(0:(n-1)/n, 0, 1:n/n, 1, col = col, border = border)
}

par(mar = rep(0, 4), mfrow = c(4, 2))
pal(coolwarm)
pal(viridis)
pal(coolwarm_hcl)
pal(viridis_hcl)
pal(brbg)
pal(plasma)
pal(brbg_hcl)
pal(plasma_hcl)

Update: These HCL-based approximations of colors from other tools (ColorBrewer.org, viridis, scico, CARTO, …) are now also available as named palettes in both the colorspace package and the hcl.colors() function from the basic grDevices package (starting from 3.6.0). Thus, you can now also say easily:

colorspace::sequential_hcl(11, "viridis")
grDevices::hcl.colors(11, "viridis")

Finally, you can explore our proposed colors interactively in a shiny app:
http://hclwizard.org:64230/hclwizard/. For users of R, you can also start the shiny app locally on your computer (which runs somewhat faster than from our server) or you can run a Tcl/Tk version of it (which is even faster):

colorspace::hclwizard(gui = "shiny")
colorspace::hclwizard(gui = "tcltk")

If you want to understand what the paths of the palettes look like in RGB and HCL coordinates, the colorspace::specplot() is useful. See for example colorspace::specplot(coolwarm).

Answered By: Achim Zeileis

The scico package (Palettes for R based on the Scientific Colour-Maps ) has several good diverging palettes that are perceptually uniform and colorblind safe (e.g., vik, roma, berlin).

Also available for Python, MatLab, GMT, QGIS, Plotly, Paraview, VisIt, Mathematica, Surfer, d3, etc. here

Paper: Crameri, F. (2018), Geodynamic diagnostics, scientific visualisation and StagLab 3.0, Geosci. Model Dev., 11, 2541-2562, doi:10.5194/gmd-11-2541-2018

Blog: The Rainbow Colour Map (repeatedly) considered harmful

# install.packages('scico')
# or
# install.packages("devtools")
# devtools::install_github("thomasp85/scico")
library(scico)
scico_palette_show(palettes = c("broc", "cork", "vik",
                                "lisbon", "tofino", "berlin",
                                "batlow", "roma"))

Döhmann et al. (2019), Strain Localization and Weakening Processes in Viscously Deforming Rocks, JGR, doi:10.1029/2018JB016917

Thieulot (2018), GHOST: Geoscientific Hollow Sphere Tessellation, Solid Earth, doi:10.5194/se-9-1169-2018

Another great package is cmocean. Its colormaps are available in R via the pals package or the oce package.

Paper: Thyng, K. M., Greene, C. A., Hetland, R. D., Zimmerle, H. M., & DiMarco, S. F. (2016). True colors of oceanography. Oceanography, 29(3), 10, http://dx.doi.org/10.5670/oceanog.2016.66.

Talk: PLOTCON 2016: Kristen Thyng, Custom Colormaps for Your Field.

### install.packages("devtools")    
### devtools::install_github("kwstat/pals")   
library(pals)   
pal.bands(ocean.balance, ocean.delta, ocean.curl, main = "cmocean")   

numerical simulation of the Texas and Louisiana shelf

Spatial Temporal Oceanographic Query System (STOQS)


Edit: add seven levels max colorblind-friendly palettes from the rcartocolor package

library(rcartocolor)
display_carto_all(type = 'diverging', colorblind_friendly = TRUE)

Answered By: Tung

Viridis now provides the cividis color ramp, which is basically a diverging color ramp. It’s also their recommended color ramp.
enter image description here

Answered By: filups21

Viridis 0.6.0 (introduced mid-2021) added 3 more colourmaps: mako, rocket and turbo. I’ve read enough of Tal’s questions to strongly suspect it will be hard to resist the temptation of combining Seaborn’s mako and rocket into a diverging scale – they’d be the two obvious choices from the viridis package to combine in that way – but I want to talk about turbo, whose makers claim works well as a diverging scale. Let’s borrow a picture from the vignette and wow! Isn’t turbo disturbingly … spectral? How anti-viridislike is that?!
https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html

Viridis colour scales

If I desaturate that image, we see turbo sticks out like a sore thumb as the only (as of Version 0.6.2) viridis package colourmap to have diverging lightness, though not perfectly symmetrical at very high and very low values.

Viridis colour scales desaturated

Everyone knows rainbows are bad, right? They’re not perceptually uniform, totally messed up for people with various forms of colour blindness, and look nonsensical when printed in greyscale. The viridis package vignette points out the obvious "kinks" in base R’s rainbow.colors where colour progression is not perceptually smooth, and uses the dichromat package to show how poorly the palette performs under simulated colour blindness.

R's default rainbow colourmap looks bad under simulated colour blindness and desaturation

In the paper where Nuñez, Anderton and Renslow introduced cividis, a version of the viridis colourmap available as an alternative in the viridis package (see top image) that’s optimised to be safer for colour blindness, they laid some justified heavy criticism on the widely-used spectral colourmap jet. https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0199239

jet vs viridis comparison in Nuñez, Anderton and Renslow

Comparison between different colormaps overlaid onto the test image by Kovesi and a nanoscale secondary ion mass spectrometry image. Colormaps are as follows: (a) perceptually uniform grayscale, (b) jet, (c) jet as it appears to someone with red-green colorblindness, and (d) viridis, the current gold standard colormap. Below each NanoSIMS image is a corresponding “colormap-data perceptual sensitivity” (CDPS) plot, which compares perceptual differences of the colormap to actual, underlying data differences. m is the slope of the fitted line and r2 is the coefficient of determination calculated using a simple linear regression. An example of how the data may be misinterpreted are evident in the bright yellow spots in (b) and (c), which appear to represent significantly higher values than the surrounding regions. However, in fact, the dark red (in b) and dark yellow (in c) actually represent the highest values. For someone who is red-green colorblind, this is made even more difficult to interpret due to the broad, bright band in the center of the colormap with values that are difficult to distinguish.

Clearly spectral colourmaps are a "here be dragons" business, especially so if you place a high value on accessibility. But there’s an interesting comment at the end of Nuñez, Anderton and Renslow’s paper: they don’t expect cividis to supplant viridis because optimising for colour blindness also has certain disadvantages. An immediate consequence of their aim is that cividis is less colourful than viridis: this means many people have an aesthetic preference for viridis, but also that people with normal vision are less able to use colour cues in cividis so their visual perception is less sensitive than when using viridis. This is a Watch This Space moment, as the researchers announced they hoped to introduce more colours into future versions of colourblind-friendly maps, but they aren’t totally confident this is a circle that can be squared. And it does raise the question, if you’re prepared to accept certain trade-offs, what might the perceptual benefits be of giving readers an even wider range of colours?

This is where turbo, billed as an "improved rainbow", comes in. It was developed in Google’s now defunct Daydream VR division to help produce false colour images of computer vision problems, particularly depth perception. This is a field where the aforementioned jet colourmap is ubiquitous, despite its well-known problems. Why use the dreaded rainbow? Well, try judging which of the spheres on the left lines up with which of the rings on the right in the following images, taken from the Google blog post which introduced turbo. https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html

Colourmaps for false colour depth perception: try to compare spheres on left with rings on right

In greyscale this is an almost impossible task: it’s very hard to compare or match shades of grey in different areas of an image, as is well known to anyone who’s encountered the checker shadow illusion. But in viridis or inferno, it’s not much better! The range of hues in jet and turbo allow for faster and easier comparison. But we can see an artificial banding effect due to the "kink" of intense yellow in jet we complained about before, whereas turbo has been designed to be much more perceptually smooth, both in lightness and hue change. The cyan/blue boundary is much improved too. This is also visible in the quantised versions of turbo and jet you might use for discrete data: its creators claim turbo can be quantised into up to 33 distinguishable colours.

Quantised versions of turbo (left) and jet (right) colourmaps

The ability to identify matching hues also helps when reading turbo values from a numerical scale: I find the colours at various important points on this scale to be pretty distinctive.

Numerical scale for turbo colourmap

The question asked for diverging colour scales, whereas in the use case of depth perception we see turbo being used as a sequential colour scale: one that ranges from low to high (or in this case, near to far). However, while its hues are sequential in the spectral sense, we’ve already seen the lightness isn’t. The lightness plots below help illustrate why turbo is considered a perceptual improvement over jet.

Lightness plots for turbo, jet, viridis and inferno colourmaps

The colourmaps viridis and, over a wider range so even more steeply, inferno both increase linearly in lightness. In contrast, both turbo and jet are lighter in the middle of their range and dark at the extremes. Sadly jet does this is an uneven way with obvious banding, but turbo rises then falls in lightness very smoothly and fairly symmetrically. This is the justification for using turbo for a diverging scale, albeit with caveats.

When used this way, zero is green, negative values are shades of blue, and positive values are shades of red. Note, however, that the negative minimum is darker than the positive maximum, so it is not truly balanced.

One place its creators used turbo as a diverging scale was in "difference images" that show the error, positive or negative, between ground truth and estimated depth. One thing I find hard about this is that I don’t get an intuitive sense from the colour scheme as to what’s "extreme positive" and what’s "extreme negative": the visual culture my brain is used to would posit "green for positive, red for negative" but that’s a poor choice for data visualisation due to red-green colour blindness being so common. Whatever colour scheme you pick, it’s important to provide a key!

Difference image using turbo colourmap

Perhaps unfortunately, turbo doesn’t vary linearly in lightness – it’s more like an inverted "U" than inverted "V". Opponents of rainbow colourmaps do pick up on this. Fabio Crameri, author of The Rainbow Colour Map (repeatedly) considered harmful, was one of the authors of a Nature Communications article on "The misuse of colour in science communication" which responded to turbo by calling it an example of "so-called ‘improved’ rainbow-like maps" which appears to meet the requirement of colours being in intuitive perceptual order, but "the perceptual uniformity requirement of a science-ready colour map is not met due to its non-uniform lightness spectra." The inverted "U" is not utterly without benefits, though. You may have noticed how inferno is better than viridis at showing extremely high or low data, partly because it varies in lightness more steeply (due to its wider range). As you can see from the lightness plots, turbo has even steeper slope at the extremes (at least on the left i.e. blue end – this is something turbo is asymmetric at), and combined with its more rapid hue changes this makes it easier to distinguish details there. In the following false colour image, the "extreme data" are the trees at high distance – the background is noticeably clearer with turbo than inferno.

Inferno vs Turbo false colour image

Do read the rest of the Google team’s blog to see how turbo performs under a colour blindness simulator. The upshot is that it does pretty well except for achromatopsia (the rare condition of total colour blindness) which any scale with diverging lightness will work badly for, since extreme low/high values are ambiguous.

Is turbo what I would use when I want a diverging scale? For many purposes I prefer some of the other suggestions in other answers, especially because I don’t have a strong intuitive sense as to which end of the rainbow naturally represents highs or lows. But turbo certainly has its advantages – particularly because it uses more hues that, for most viewers, grant them greater ability to distinguish small differences, identify areas on different parts of the page that are close in value, and (as a result of the latter) to meaningfully compare to a scale… all done without totally sacrificing the experience of most people with colour blindness. If you like the viridis package philosophy and you want a diverging colourmap whose brightness also diverges then turbo is an obvious choice. Just be aware that diverging brightness comes at the inevitable expense of meaningful greyscale printing and the perception of people with total colour blindness.

Some alternatives

I’m not the first person to answerer on this page to point out Crameri’s anti-rainbow stance. His website on scientific colourmaps is worth looking at: https://www.fabiocrameri.ch/colourmaps/

You’ve already had some suggestions for accessing Crameri’s preferred diverging scales, but another option is the khroma package. You can check this vignette to see Crameri’s diverging colourmaps broc, cork, vik, lisbon, tofino, berlin, roma, bam and vanimo. There are also sequential and multi-sequential scales available.

But khroma also offers a set of colour schemes by Paul Tol, based on this technical note. Have a look at the separate vignette for Tol’s schemes. There are a lot of schemes given for qualitative data (where sequential appearance is deliberately avoided) but a few diverging ones too: sunset, BuRd and PRGn are shown below.

Paul Tol diverging colour schemes sunset, BuRd and PRGn

These have diverging and rather symmetric lightness, judging from the desaturated image.

Desaturated Paul Tol diverging colour schemes sunset, BuRd and PRGn

Tol also criticises the use of rainbow schemes, but in case his warnings are not heeded does provide a discrete and smooth rainbow scheme that are reasonably safe for colour blindness. I have illustrated both below. I do prefer turbo to Tol’s continuous rainbow, the first 25% of which is an off-white blending into purple and the last 10% has red blending into brown. The vignette recommends to "start off-white instead of purple if the lowest data value occurs often; end at red instead of brown if the highest data value occurs often." I have provided desaturated versions below: Tol’s discrete rainbow varies irregularly in lightness, which isn’t good for either sequential or diverging scales, but the natural ordering of rainbow hues would also be a drawback for presenting qualitative (non-sequential) data. The smooth rainbow, with off-white purple section removed, does diverges in lightness, but noticeably more asymmetrically than turbo. Including the off-white section results in an unfortunate light-dark-light-dark pattern rather than a truly diverging scale.

Paul Tol discrete and continuous rainbow colour schemes

Paul Tol discrete and continuous rainbow colour schemes desaturated

Answered By: Silverfish
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.