Exploring colour blind and colour weak plotting options in ggplot2

On my ‘over breakfast’ Twitter scroll I cam across this interesting series of blogposts on the Chartable blog (hosted by Datawrapper) that discusses how we can make our plots and visuals more friendly for colour blind and colour weak readers. They raised some interesting points and options to use and I thought it would be a ‘constructive’ way to spend my Friday trying to implement these ideas in R, specifically using ggplot2.

In the second blogpost they have a long discussion about different ways to make your figures more inclusive (I highly recommend reading the full article - it has some awesome visuals as well) and along with the standard recommendation to avoid green and trying to go with complimentary colours such as blue and orange some other points they raised that I thought were interesting include:

  • Playing with the lightness of your colours
    • Even if you end up using non-ideal colours you can use darker and lighter shades to offset the similarity
  • Using fewer colours
    • Maybe only use colour to highlight differences or pull out the main points of the figure
  • Play with shapes and patterns
    • This can include using symbols (such as ticks and crosses) to emphasise a point or show different groups in scatterplots, or use patterns for line plots or even for fill colours on geom-type plots

So at first glance most of these options seem very do-able and easy to integrate into the ggplot2 workflow, with the exception of specifying patterns for fills of geom objects (think barplots) but I came across the ggpattern package which solves that problem with what seems to be a huge degree of customisability - see the package website here. At time of writing this package isn’t available on CRAN yet but can be downloaded and installed via GitHub.

With that out of the way let’s get to the actual plotting. Also I thought this would be a good opportunity to play with the penguins dataset that was recently released as an iris alternative.

Playing with lightness

This is theoretically a very simple example to execute as we can just specify our colour choices to be of varying shades. For this example I decided to plot body mass vs. flipper length for the different penguin species as well as for males and females. I used [Adobe Color][https://color.adobe.com/create/color-wheel] to pick my colours but that just personal preference.

library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(palmerpenguins)

#import data
penguins <- penguins %>%
  #removing NAs for ease
  na.omit()

#specify colours manually
AdeMale <- "#1C1DAD"
AdeFem <- "#A0A0FA"
ChiMale <- "#AD400B"
ChiFem <- "#FA9966"
GenMale <- "#445748"
GenFem <- "#6AD674"

#create plot
P1 <- ggplot() +
  geom_point(data = penguins,
             aes(x = body_mass_g,
                 y = flipper_length_mm,
                 #make new grouping variable with the species sex combos
                 colour = paste(penguins$species,
                                penguins$sex)),
             #for simplicity removing legend
             show.legend = FALSE) +
  #set colours
  scale_colour_manual(values = c(AdeMale,
                                 AdeFem,
                                 ChiMale,
                                 ChiFem,
                                 GenMale,
                                 GenFem)) +
  theme_bw()

So we can see that using the different shades of the same colour for the different sexes works really well - although arguably there is some issue distinguishing between the species so lets add some shapes…

Adding shapes

P2 <- ggplot() +
  geom_point(data = penguins,
             aes(x = body_mass_g,
                 y = flipper_length_mm,
                 #make new grouping variable with the species sex combos
                 colour = paste(penguins$species,
                                penguins$sex),
                 #specify grouping var for shape
                 shape = species),
             #for simplicity removing legend
             show.legend = FALSE) +
  #set colours
  scale_colour_manual(values = c(AdeMale,
                                 AdeFem,
                                 ChiMale,
                                 ChiFem,
                                 GenMale,
                                 GenFem)) +
  theme_bw()

Nice! As we can see just adding one line to our code can make all the difference.

Playing with patterns

This is where it gets fun - time to play with a new package. If we want to call patterns for our geoms instead of using the standard geom_* function from ggplot2 we will be using the geom_*_pattern function from the ggpattern package. But first we can have a look at our original colour palette using a standard boxplot - let’s stick to flipper length.

Here we can see how the different shades pop out really nicely - arguably we can still distinguish between the Chinstraps and the Gentoos but I think we can play with the aesthetics a bit more and make the plot pretty - not just functional!

#To download package from GitHub
#remotes::install_github("coolbutuseless/ggpattern")
library(ggpattern)

#Summarised the dataset for plotting

P4 <- ggplot() +
  geom_col_pattern(data = penguins_summary,
                   aes(y = flipper_length_mm,
                       x = species,
                       group = sex,
                       #make new grouping variable with the species sex combos
                       fill = paste(penguins_summary$species,
                                    penguins_summary$sex),
                       #specify pattern type
                       pattern = sex),
                   #specify the fill for the pattern
                   pattern_fill = "black",
                   #specify box colour
                   colour  = 'black',
                   #for simplicity removing legend
                   show.legend = FALSE,
                   position = "dodge") +
  #set colours
  scale_fill_manual(values = c(AdeMale,
                               AdeFem,
                               ChiMale,
                               ChiFem,
                               GenMale,
                               GenFem)) +
  theme_bw()

Neat! We can see that the patterns add a new ‘level’ to our plots and if you look at the syntax of the ggpatterns functions we see that it mirrors that of ggplot - it just has a few more _pattern thrown in! This package allows a large degree of freedom with regards to what and how you want your patterns to look like. You can even import images or make your own patterns. Combine this with the somewhat intuitive syntax (if you’re comfortable with ggplot) it is something I can see myself trying to use more often when making figures.

🐾 Tanya

Footnote

If you want a better look at all of the code feel free to have a peek on GitHub

Tanya Strydom
Tanya Strydom
PhD Student

Self-diagnosed theoretical ecologist, code switcher (both spoken and programmatic), artistic alter-ego, and peruser of warm beverages.

Related