Custom colour palettes for {ggplot2}

R-bloggers 2022-06-23

[This article was first published on The Jumping Rivers Blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Choosing which colours to use in a plot is an important design decision.A good choice of colour palette can highlight important aspects of yourdata, but a poor choice can make it impossible to interpret correctly.There are numerous colour palette R packages out there that are alreadycompatible with {ggplot2}. For example, the{RColorBrewer}or{viridis}packages are both widely used.

If you regularly make plots at work, it’s great to have them beconsistent with your company’s branding. Maybe you’re already doing thismanually with the scale_colour_manual() function in {ggplot2} but it’sgetting a bit tedious? Or maybe you just want your plots to look alittle bit prettier? This blog post will show you how to make a basiccolour palette that is compatible with {ggplot2}. It assumes you havesome experience with {ggplot2} – you know your geoms from youraesthetics.

Building a colour palette

To make a custom colour palette, there are three basic things you needto do:

  • Define your colours
  • Generate a palette from your list of colours
  • Create {ggplot2} functions to use your palette

Data comes in all shapes and sizes. It can often be difficult to know where to start. Whatever your problem, Jumping Rivers can help.


Defining your colours

The process of adding colours is probably the simplest part of creatingcolour palette functions. We need to create a named list where the namesare the names of our colour palettes. Each entry in the list is a vectorof the colours in that palette. Using lists (instead of data frames) isessential because it allows us to create colour palettes with differentnumbers of colours. It’s most common to define colours by their hexcodes, but we could also define colours by using their character namese.g. "blue", or their RGB values using the rgb() function. We’llstick to hex codes here.

cvi_colours = list(  cvi_purples = c("#381532", "#4b1b42", "#5d2252", "#702963",                 "#833074", "#953784", "#a83e95"),  my_favourite_colours = c("#702963", "#637029",    "#296370"))

Here, we’ve kept the example small, and created a list calledcvi_colours (short for corporate visual identity) with just two colourpalettes.

Generating a palette

We need to create a function that generates an actual colour palettefrom our simple list of colours. This function will take four argumentsto define:

  • the name of the colour palette we want to use,
  • the list of colour palettes we want to extract our choice from,
  • how many colours from it we want to use
  • whether we want a discrete or continuous colour palette
cvi_palettes = function(name, n, all_palettes = cvi_colours, type = c("discrete", "continuous")) {  palette = all_palettes[[name]]  if (missing(n)) {    n = length(palette)  }  type = match.arg(type)  out = switch(type,               continuous = grDevices::colorRampPalette(palette)(n),               discrete = palette[1:n]  )  structure(out, name = name, class = "palette")}

If a user doesn’t input the number of colours, be default we use all ofthe colours in the palette. For a discrete palette, we simply use thevector of colours from cvi_colours as our colour palette. However, forcontinuous colour palettes, we need to use the colorRampPalette()function from {grDevices} to interpolate the given colours onto aspectrum. The switch() function then changes the output based on thechosen type of palette.

We don’t just want to simply return a vector of colours from thecvi_palettes() function, we want to add additional attributes usingthe structure() function. The first additional attribute is the name,which we match to the name we gave the palette in cvi_colours. Thesecond additional attribute is a class which here we’ll callpalette. By assigning a class to the colour palette, this means we canuse S3 methods. S3 methods in R are a way of writing functions that dodifferent things for objects of different classes. S3 methods aren’treally the topic of this post, but this blogposthas a nice overview of them.

cvi_palettes("my_favourite_colours", type = "discrete")

Colour palette showing three vertical strips in pink, green, and blue with the word my favourite colours in the centre

Creating {ggplot2} functions

We need to define some functions so that {ggplot2} understands what todo with our colour palettes. We’ll start by defining a simple {ggplot2}plot that we’ll use to demonstrate our colour palettes later on.

library("ggplot2")df = data.frame(x = c("A", "B", "C"),                y = 1:3)g = ggplot(data = df,           mapping = aes(x = x, y = y)) +  theme_minimal() +  theme(legend.position = c(0.05, 0.95),        legend.justification = c(0, 1),        legend.title = element_blank(),         axis.title = element_blank())

Within {ggplot2}, there are two main ways to control the look of yourplot: (i) using scale_*() functions to control the aesthetics thathave been mapped to your data; or (ii) using themes. Themes control theaspects of your plot which do not depend on your data e.g. thebackground colour. In this blog post, we’ll focus on the scale_*()functions.

There are two aesthetics in {ggplot2} that involve colour: (i) colour,which changes the outline colour of a geom; and (ii) fill, whichchanges the inner colour of a geom. Note that not all geoms have bothfill and colour options e.g. geom_line() is only affected by thecolour aesthetic.

g + geom_col(aes(fill = x), colour = "black", size = 2) + ggtitle("Fill")g + geom_col(aes(colour = x), fill = "white", size = 2) + ggtitle("Colour")

Two bars charts side by side. On the left the bars are coloured in pink, green, and blue. On the right, the outline is coloured in pink, green, and blue

For each aesthetic, colour and fill, the function needs to be able tohandle both discrete and continuous colour palettes. We need to make twofunctions: one to handle a discrete variable, and one for continuousvariables. We’ll start by dealing with discrete variables. Here, we passour palette colours generated by cvi_palettes() as the valuesargument in the scale_colour_manual() function from {ggplot2}:

scale_colour_cvi_d = function(name) {  ggplot2::scale_colour_manual(values = cvi_palettes(name,                                                    type = "discrete"))}

The function to use our colour palettes to change the fill colour isalmost identical, we simply change the function name, and usescale_fill_manual() instead of scale_colour_manual().

scale_fill_cvi_d = function(name) {  ggplot2::scale_fill_manual(values = cvi_palettes(name,                                                    type = "discrete"))}

Now for continuous variables. Continuous scales are similar but we usethe scale_colour_gradientn() function instead of the manual scalefunctions. This creates an n-colour gradient scale. We set the coloursused in the gradient scale using the cvi_palettes() function wedefined earlier, and set the type as continuous.

scale_colour_cvi_c = function(name) {  ggplot2::scale_colour_gradientn(colours = cvi_palettes(name = name,                                                       type = "continuous"))}

The scale_colour_gradientn() function already has the aestheticargument set as colour by default, so we don’t need to worry aboutchanging that. Again, the fill version of the function is analogous:change the name of the function, and use scale_fill_gradientn()instead of scale_colour_gradientn().

scale_fill_cvi_c = function(name) {  ggplot2::scale_fill_gradientn(colours = cvi_palettes(name = name,                                                     type = "continuous"))}

To ensure that the scale_colour_*() functions work with either theBritish or American spelling of colour, we can simply set one equal tothe other:

scale_color_cvi_d = scale_colour_cvi_dscale_color_cvi_c = scale_colour_cvi_c

Testing our colour palettes

Now that we have all the functions we need, we can call them in the sameway we would with any scale_*() function in {ggplot2}:

g +  geom_point(aes(colour = y), size = 3) +  scale_colour_cvi_c("cvi_purples")g +  geom_col(aes(fill = x), size = 3) +  scale_fill_cvi_d("my_favourite_colours")

Two charts side by side. On the left the points are coloured in gradients of purple. On the right, the bars in the bar chart are coloured in pink, green, and blue.

Extending the functionality of your colour palettes

The colour palette functions we’ve defined here are fairly basic, and wemay want to add some additional functionality to them.

Printing colour palettes

Users will often want to view the colours in a palette on their screenbefore they go the effort of implementing it. Although thistechnically isn’t necessary to make your colour palette work, it’sextremely useful to anyone using it. Earlier, we defined the paletteclass, so we could create a function print.palette() whichautomatically prints a plot of any object with the class palette.

Turn your colour palettes into an R package

If you have a collection of functions that work together and you need touse them in multiple projects, it’s best (at least in the long run) toturn them into an R package. All of the colour palettes I’ve used in mywork have been part of an R package. Making your palette functions intoa package also makes it easier to share them with other people (superhelpful if the colour palettes are for work).

If you’ve never made an R package before, check out our previous blogpost on Writing a Personal RPackage to helpyou get started.

Discrete vs continuous palettes

Some of the colour palettes you define might work better for continuousvariables, and some may work better for discrete variables. At themoment, any colour palette can be used for either discrete or continuousvariables at the user’s discretion. You may want to restrict whichpalettes are used with which type of palette, or at least provide awarning message to a user.

For example using the my_favourite_colours palette doesn’t look verynice when we interpolate the colours for a continuous palette.

cvi_palettes("my_favourite_colours", type = "continuous", n = 20)

Colour palette showing three vertical strips of pink, green, and blue blended together in a gradient with the word my favourite colours in the centre

Order the colours

By default colours are returned in the order you define them in thelist. For continuous colour palettes this works quite well, as itensures colours go from light to dark, or vice versa. However, fordiscrete palettes we may want to rearrange the colours to ensure greatercontrast between colours displayed next to each other. Thecvi_palettes() function could be edited to return colours in adifferent order if a discrete palette is chosen.

Similarly, many colour palettes include a "direction" argument whichreverses the order in which the colours in the palette are usede.g. going from light to dark instead of dark to light.

Checking for colourblind palettes

It’s a good idea to check if your colour palettes are colourblindfriendly, especially for discrete palettes. Sequential colour palettesusually have a better chance of being colourblind friendly. DavidNicholsprovides a tool for seeing what your palettes may look like to peoplewho are colourblind. If you choose to include colour palettes that arenot colourblind friendly, it may be useful to include a warning forusers.

Final thoughts

By now, you should have the tools to create your own simple colourpalette functions for using with {ggplot2}. Most of the functionsdescribed are based on those used in the{MetBrewer} and{wesanderson} colour palettes.If you want to see examples of some of these extensions implemented in alarger colour palette package, check out the source code for thosepackages on GitHub.


Jumping Rivers Logo

For updates and revisions to this article, see the original post

To leave a comment for the author, please follow the link and comment on their blog: The Jumping Rivers Blog.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Continue reading: Custom colour palettes for {ggplot2}