What’s that saying? Choice is the enemy of happiness, or something like that. There are so many colour palettes out there, that saying tends to resonate when either choosing one, or creating a new one. So, I started to think about a way I could make this more organic, and came up with the idea for evoPalette.
evoPalette allows for the discovery and generation of new colour palettes for R. Taking user input it uses an evolutionary algorithm to spawn new palettes with the expectation the colours you like will be combined over time. This can be repeated as many times as needed until you find something that works for you.
There’s a lot to colour theory, no choice is trivial, and so leaving some of those choices largely to an algorithm will have varying success. This is trading best practice for fun and discovery but I’m okay with that!
The algorithm in a nutshell
Very briefly, evolutionary algorithms operate in 3 main steps:
- Selection: Each candidate has a measure of fitness and those that reach some threshold survive. In this case parents of the next generation are selected by the user and so fitness is essentially personal preference.
- Crossover: Children are spawned from a random combination of two parents genes. In this case colours represent a gene.
- Mutation: Mutation occurs in two ways 1) there is a chance of a completely new colour generated for a palette and 2) each colour varies locally around it’s initial value.
The process can continue for as long as needed.
You can install evoPalette from Github with the following command.
(It’s only on Git for the moment. We’ll see how it goes.)
To jump straight into it launch the Shiny app with
To generate the first set of palettes, click ‘evolve’. Using the default setting the initial palettes will be randomly drawn from a standard set saved in evoPalette and paletteer. Paletteer contains some 1700+ palettes from a number of packages.
Select one or more palettes that you like and think may work well together from the check box and click ‘evolve’. The next generation of palettes will be created.
With each generation you’ll notice how the palette is converging to a similar theme based on your selections, along with some variation of each individual colour and a chance of a completely new colour. The features that you liked about one palette have a chance to be combined with other colours and flow through to the next generation. Although, this isn’t completely ensured since there is still a stochastic element at play, but you may discover another combination that works well for you. The selected parent palettes are shown in the sidebar for reference.
Selecting only a single parent is convenient for generating minor variations to tweak the colours.
Select ‘Example: Fill aesthetic’ to see how the palette works in practice (similarly for ‘Example: Colour aesthetic’).
To save a palette, select the desired palette from the check box and click ‘save‘. A box will appear giving the opportunity to change the randomly generated palette name but, why would you want to!
The palette is now accessible from
palette_box() once the app is closed. Multiple palettes can be saved which are collected in the palette box, just save one at a time. The palette box is refreshed when starting a new session so remember to save it to disk.
palette_box() $instructive_customers  "#C7B6C8" "#74B6BA" "#7783C4" "#B56D4E" "#F28E47" "#FCE269"
To begin again, deselect all palettes, click ‘evolve’. New palettes are randomly selected beginning a new process.
A palette is viewed by using
The continuous scales are simply created from the two colours on the ends and one in the middle (room for improvement).
The user has some control over the algorithm by adjusting the mutation rate and variation allowed for each colour. Select the parameters from the drop down in the menu.
- Number of colours to generate for each palette. Initial starting palettes with more / fewer colours are coerced to have the desired amount. So, a standard palette with more or fewer than this amount will be slightly different.
- Number of palettes to generate at each evolution. The default is 9.
- Mutation rate – Each colour in the palette has a probability of random mutation equal to this value (default p = 0.05)
- Mutation variation – Each colour will vary slightly around it’s original value.
- Load existing palettes from the global environment. Object should be a list, ideally with names i.e. the same format as
- Are you feeling lucky? Select for generating palettes from completely random colours.
When you are happy with your selections, click ‘evolve’ and begin a new process.
The generated colour palettes are easily added to ggplots using the scale functions
library(ggplot2) mpg %>% ggplot(aes(x = displ, fill = class)) + geom_histogram() + scale_fill_evo("instructive_customers")
The first palette in the list is used if no name is given. The scales are also parameterised to reverse the palette and switch discrete / continuous.
Evolution isn’t perfect
Colour theory is pretty complex stuff so choosing a good palette isn’t easy, let alone evolving one. So, you’re going to have some hits and some misses. This is definitely more for fun seeing what you discover rather than finding the perfect palette. Having said that you could discover some gold!
There are best practices when choosing a palette for data visualisation depending on the context and what is to be shown. For example people tend to respond to certain colours representing high / low, hot / cold or good / bad, there is also colourblindness considerations. evoPalette won’t necessarily adhere to these ideals.
This is best for generating discrete colour palettes with 4-7 distinct colours. It will help you find colours which are to your liking and generate a palette which is unlikely to already exist. It’s fun to experiment and see what you come up with!
Some of the initial starting palettes I’ve created and are shown below. A few are created from evoPalette, some from a clustering process. The 90’s rock palettes were created from the album covers. Sorry for doubling up on Alice in Chains 😉.
g_evo <- readRDS(system.file("extdata/palettes.rds", package = "evoPalette")) map2(g_evo$palette, g_evo$name, ~show_palette(.x, title = .y)) %>% wrap_plots(ncol = 2)