<-
categorical_penguins ::penguins %>%
palmerpenguinsggplot() +
geom_point(aes(x = bill_length_mm, y = flipper_length_mm,
colour = sex, size = body_mass_g),
show.legend = F) +
labs(title = "Palmer Penguins",
subtitle = "Look at them go!",
x = "Bill length",
y = "Flipper length") +
theme_minimal()
categorical_penguins
How to make data viz that matches your organisation’s branding
Picking a colour scheme that ties in with a client’s brand or that evokes the subject matter behind the data is what allows datavisualisations to contribute to, rather than detract from, the main story the client is seeking to tell. This post covers both the design steps and the coding steps involved in creating bespoke colour schemes.
“Ooh look, a ggplot()
plot!”
In creating polished reports and visualisations for a client, that is never the reaction we’re hoping for. Sure, the “I didn’t know you could do that in R!” reaction is always fun, but what we want above all is for the plots and tables to blend in seamlessly with the rest of the client’s aesthetic. Picking a colour scheme that ties in with a client’s brand or that evokes the subject matter behind the data is what allows the plot to contribute to, rather than detract from, the main story the client is seeking to tell.
This post explores how we go about choosing an appropriate colour scheme and how to create scale_fill/colour_discrete/continous()
to align all plots with that colour scheme, using the example of a recent R for the Rest of Us project I worked on for Colibri Consulting.1
One of the first things we do when working with a client is ask if they have a branding guide. Something like this. Some of the clients do, but most don’t. How, then, do we take what we have and make plots that match the look-and-feel of the organization?
Step 1: Decide what colours to use
In the case of this project, our client had a logo with two colours
Jody O’Connor, head of Colibri, was able to give us the hex codes for those colours, which was handy, but there are plenty of online tools to help you pick out the hex colours in an image or even in a website. My personal go-to is Color Picker Online which does what it says on the tin: it allows you to pick out different colours from an image or a website, and provides you with their hex codes.
So, back to Colibri Consulting. Our two starting colours were: - a deep purple: #43d293
- a bright green: #530577
Step 2: See what those colours look like when used for dataviz
There are plenty of datasets around that can be used to create plots to get a feel for the colour schemes we’ll be using. The penguins
set from {palmerpenguins}
is one of my favorites: it contains both continuous data (e.g. flipper lengths) and categorical data (e.g. species), along with some NAs. And, because of the way the flipper and bill lengths group together into clusters, it’s a great way of seeing how the colours highlight the different clusters of points.
Let’s put together a basic plot that we can then apply our colour schemes to:
I had an inkling this might happen, but when I created a plot using the colours from the client’s logo, the problem became clear: the green “popped” way more than the purple, meaning that whichever group ended up being green in our data viz would be highlighted to the detriment of the purple group.
+
categorical_penguins scale_colour_manual(values = c("#43d293", "#530577"))
This wasn’t the effect we were going for, so some tweaking was required.
My first step was to blend together the two colours, to see if we could get a “muted” green that tied in nicely with the original colours. I created the {monochromer}
package for exactly this purpose:
::generate_palette("#530577", modification = "blend",
monochromeRblend_colour = "#43d293",
n_colours = 6, view_palette = T)
[1] "#530577" "#50257B" "#4D467F" "#4B6784" "#488888" "#46A98D"
The last colour in this palette is the green with a touch of purple. After some experimenting, it was still a bit too bright, so, in discussion with the client, we decided to go for a purple that was one step towards the green, and green that was one further step towards the purple as the two main colours to base the palettes on.
Here’s the penguin plot again with those two colours. It looks much more balanced!
+
categorical_penguins scale_colour_manual(values = c("#50257B", "#488888"))
Step 3: Check the colours work for people with different types of colour vision
Creating plots that work for as many readers as possible is important. Claire D. McWhite and Claus O. Wilke have made it easy for us #rstats users to check this with {colorblindr}
, a package that simulates how plots look to readers with different colour perception deficiencies.
::cvd_grid(
colorblindr
+
categorical_penguins scale_colour_manual(values = c("#50257B", "#488888"))
)
The two main colours seem to come across well, but the NAs were getting lost in all but one of the plots. To rectify that, I created an NA colour which was a faded version of the middle colour between our two main colours:
<- c(monochromeR::generate_palette("#50257B",
middle_col modification = "blend",
blend_colour = "#488888",
n_colours = 4), "#488888")[3]
<- monochromeR::generate_palette(middle_col,
NA_col modification = "go_both_ways",
n_colours = 5)[2]
+
categorical_penguins scale_colour_manual(values = c("#50257B", "#488888"), na.value = NA_col)
That made the NAs blend more into the background. Let’s check it again with {colorblindr}:
::cvd_grid(
colorblindr
+
categorical_penguins scale_colour_manual(values = c("#50257B", "#488888"), na.value = NA_col)
)
The are a different colour to the two main colours in all four of the plots - mission accomplished!
Step 4: Create colour/fill scales
For this project, we’ll be creating a lot of plots! We have plots some comparing two groups, some requiring more colours, and some using continuous data. We don’t want to be adding those colours manually every time! Instead, let’s create a few palettes we can call upon. First we need to get our anchors at either end of the palettes. We already have our “Purple-to-Green” anchor colours, but we also needed an all-purple palette and an all-green palette, so we needed a light purple and a light green to fade to:
# Purple extremes
::generate_palette("#50257B",
monochromeRmodification = "go_lighter",
n_colours = 2, view_palette = T)
[1] "#50257B" "#DCD3E4"
# Green extremes
::generate_palette("#488888",
monochromeRmodification = "go_lighter",
n_colours = 2, view_palette = T)
[1] "#488888" "#DAE7E7"
Next, we use those values to create palettes that we call upon within scale_colour/fill
functions. Note the NA_col
applied to na_values
from earlier in this post.
<- function(palette = "Main",
colibri_pal reverse = FALSE,
...) {
<- list(
colibri_palettes "Main" = c("#50257B", "#488888"),
"Purple" = c("#50257B", "#DCD3E4"),
"Green" = c("#488888", "#DAE7E7")
)
<- colibri_palettes[[palette]]
pal
if (reverse)
<- rev(pal)
pal
::colorRampPalette(pal, ...)
grDevices
}
# Discrete scales
<-
scale_colour_colibri_discrete function(palette = "Main",
reverse = FALSE,
...) {<- colibri_pal(palette = palette, reverse = reverse)
pal
::discrete_scale("colour", pal,
ggplot2na.value = NA_col,
palette = pal, ...)
}
<-
scale_fill_colibri_discrete function(palette = "Main",
reverse = FALSE,
...) {<- colibri_pal(palette = palette, reverse = reverse)
pal
::discrete_scale("colour", pal,
ggplot2na.value = NA_col,
palette = pal, ...)
}
# Continuous scales
<-
scale_colour_colibri_continuous function(palette = "Main",
reverse = FALSE,
...) {<- colibri_pal(palette = palette, reverse = reverse)
pal
::scale_colour_gradientn(colours = pal(256),
ggplot2na.value = NA_col,
...)
}
<-
scale_fill_colibri_continuous function(palette = "Main",
reverse = FALSE,
...) {<- colibri_pal(palette = palette, reverse = reverse)
pal
::scale_colour_gradientn(colours = pal(256),
ggplot2na.value = NA_col,
...)
}
Step 5: Align the text and grid lines with the colour scheme for a more polished look
The final touch is to make the rest of the colours we see in each plot line up nicely with the colour scheme: the grid lines and the text. For this, I used the “middle” colour again as a basis, creating a “light text” colour, a “light text” colour (the same as the NAs - the colour we want to blend into the background), and a “light text” colour.
<- NA_col
light_text_col
<- monochromeR::generate_palette(middle_col,
dark_text_col modification = "go_both_ways",
n_colours = 5)[4]
<- monochromeR::generate_palette(middle_col,
light_col modification = "go_both_ways",
n_colours = 5)[1]
We can then use these to modify theme_minimal()
:
+
categorical_penguins scale_fill_colibri_discrete("Main") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_line(colour = light_col),
text = element_text(colour = light_text_col),
plot.subtitle = element_text(size = 14, colour = light_text_col),
axis.ticks = element_blank(),
axis.text = element_text(colour = light_text_col),
plot.title = element_text(colour = dark_text_col, size = 20))
Bringing it all together
Here are some plots with continuous colour scales, making use of all the steps above.
+
continuous_penguins scale_colour_colibri_continuous()
+
continuous_penguins scale_colour_colibri_continuous("Green")
+
continuous_penguins scale_colour_colibri_continuous("Purple", reverse = T)
We now have custom colour palettes that allow us to make plots throughout the report that will reflect Colibri branding. A little setup in the beginning of a project sets us up for success.
There are other tricks we use to move further away from the ggplot()
defaults with fonts, margins, line heights, etc. But there’s only so much we can cover in one post! We’ll get to those another day.
In the meantime, happy plotting!
Footnotes
This blog post is adapted from one I wrote for the R for the Rest of Us blog↩︎