Search code examples
rggplot2colorsvisualizationcontour

Use a custom colour scheme for ggplot contour plot


I want to create a contour plot using ggplot in R. Using reproducible examples, here is a picture of what I would like to get as my result:

desired plot

As seen, I want a divergent colour scheme with the lowest values dark red, the highest values dark blue, and medium values near white.

Here is the standard code for getting what I want:

ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
  geom_contour_filled()

default plot

I do not like the default colour scheme. So, I want to change the colour scheme. The standard way to do this seems to be:

ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
  geom_contour_filled() +
  scale_fill_brewer(type = 'div', palette = 'Spectral')

standard non-default colour palette

Unfortunately, colours available through scale_fill_brewer do not match want I want. (There are complicated reasons for this, but I want to keep things focused. I want a custom palette.)

So, to set up a custom palette, here is the best I've been able to do so far:

ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
  geom_contour_filled() +
  scale_fill_manual(values = rev(hcl.colors(8, 'Blue-Red 2')))

desired plot

So, that is the code that gives me exactly what I want. However, there is one BIG problem: in creating my custom colour palette with hcl.colors(8, 'Blue-Red 2') , I hardcoded the fact that there are exactly 8 different colours in the palette. I know this because I have tried different things and I know that for this dataset, geom_contour_filled creates exactly 8 contour categories. But I want to write my code flexibly (specifically, in a function) that will use different datasets for which geom_contour_filled will create different numbers of contour categories.

Note that if I use a palette that does not have enough different colours (e.g., 7), then the plot is buggy:

ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
  geom_contour_filled() +
  scale_fill_manual(values = rev(hcl.colors(7, 'Blue-Red 2')))

Error in `palette()`:
! Insufficient values in manual scale. 8 needed but only 7 provided.
Run `rlang::last_trace()` to see where the error occurred.

And if I try to use more than enough colours (e.g, 20), then the divergent colour scheme fails (I can no longer distinguish above-average from below-average values):

ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
  geom_contour_filled() +
  scale_fill_manual(values = rev(hcl.colors(20, 'Blue-Red 2')))

too many palette colours

So, how can I create a contour plot in ggplot and use a custom colour palette that might have a dynamic number of different colours? The solution I am looking for must use ggplot (for reasons beyond which I explain in this post). I would prefer to minimize the number of other packages that I would use, but if I can solve this only with another supplementary package that plays nicely with ggplot, then that would also be helpful.


Solution

  • You can define your own special fill scale using discrete_scale with a palette function:

    ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
      geom_contour_filled() +
      discrete_scale('fill', 'RB2', palette = \(x) rev(hcl.colors(x, 'Blue-Red 2')))
    

    enter image description here

    So even if you have 20 bins, the same code will work:

    ggplot(faithfuld, aes(waiting, eruptions, z = density)) +
      geom_contour_filled(bins = 20) +
      discrete_scale('fill', 'RB2', palette = \(x) rev(hcl.colors(x, 'Blue-Red 2')))
    

    enter image description here