Creating a custom palette
In the code chunk below, we define a custom color palette for your organization. This palette can be used in your visualizations to maintain brand consistency. We also define some functions to extract colors from the palette and create color scales for a wide variety of ggplot2
visualizations.
1 What this code provides
The purpose of this code is to create a custom color palette for your organization that is also easy to use. The code includes:
- A named vector of your organization’s color hex codes (that you can, and should, customize)
- A named list of palettes using your organization’s colors that are appropriate for sequential, divergent, and qualitative data that is either discrete or continuous
- A way to use a single palette for either discrete or continuous data — there’s no need to use different functions for different types of data
- Functions that you can use without any changes at all
2 How to use this code
The functions in this code are designed to make it easy to use your organization’s color palette in ggplot2
visualizations. You can use the functions scale_color_org()
and scale_fill_org()
to apply the color palette to your plots. These are the only functions that you need to use in your plots. The other functions are used internally to create the color scales.
In the following, we assume that you already have a ggplot2
plot object called my_plot
. You need to know the following about the my_plot
object before applying the color palette:
- the type of data you are coloring (discrete or continuous); this will determine whether the
discrete
argument is set toTRUE
orFALSE
- whether you are coloring the
color
or thefill
of the graph; this will determine whether you usescale_color_org()
orscale_fill_org()
- the type of palette you want to use (sequential, divergent, or categorical); this will determine which palette you use in the
palette
argument - only if you are using a divergent palette, you need to know the midpoint of the data; this will determine the
midpoint
argument
Calling scale_color_org()
or scale_fill_org()
works the same, so we only show one of the functions in each example.
2.1 Divergent/Discrete
This is an example of setting a color palette for a discrete variable that is diverging.
2.2 Divergent/Continuous
This is an example of setting a color palette for a continuous variable that is diverging. Note, the functions that we have defined are essentially helper functions for scale_*_grandientn()
functions; as such, any arguments that are not used within the scale_*_org()
functions can be passed directly to the scale_*_gradientn()
functions.
2.3 Sequential/Discrete
This is an example of setting a color palette for a discrete variable that is sequential.
2.4 Sequential/Continuous
This is an example of setting a color palette for a continuous variable that is sequential.
2.5 Categorical
This is an example of setting a color palette for a categorical variable.
3 Customizing this palette
Here’s the process for customizing this code for your organization:
- Copy the code below into your own
R
script file. - Save the file as
colorsorg.R
in your project directory (or wherever you put yourR
scripts). - Customize the
org_colors
vector with your organization’s color hex codes. - Re-define the palettes in the
org_palettes
list to use the colors you defined in theorg_colors
vector. You can use the same names as the ones in the example, or you can change them to something that makes more sense for your organization. - Load the
colorsorg.R
file into yourR
session (or into your ownR
script) using thesource()
function. - Call the
scale_color_org()
orscale_fill_org()
function in your plots to apply the color palette as you would in any otherggplot
chart. - When you are fully satisfied with any customization that you are going to do, then distribute this
colorsorg.R
file to your colleagues so they can use the same theme in their plots. This is just another way that you can make it easier for your analytical team’s customers to use your work.
4 The code itself
The following is the code that encapsulates all of the above functionality. You can copy and paste this into your own R
script file to use it.
# Define your organization's color palettes
#' Your organization's brand colors (hex codes)
#'
#' A named vector of your organization's color hex codes.
#'
#' @export
org_colors <- c(
`purple` = "#582c83",
`midnight purple` = "#201547",
`gray` = "#54585a",
`light blue` = "#aadeeb",
`red` = "#e3322b",
`yellow` = "#f2be1a",
`green` = "#669933",
`light gray` = "#f5f5f5",
`medium gray` = "#c0c0c0",
`royal blue` = "#4169E1"
)
#' Extract your organization's colors by name
#'
#' @param ... Character names (from the vector above) of the colors to extract
#' @return A character vector of hex codes
#' @export
org_cols <- function(...) {
cols <- c(...)
if (is.null(cols))
return (org_colors)
org_colors[cols]
}
#' Your organization's color palettes
#'
#' A list of named palettes using your organization's colors
#' @export
org_palettes <- list(
`main` = org_cols("purple", "gray", "light blue", "red", "yellow", "green"),
`cool` = org_cols("green", "light blue", "gray"),
`gray` = org_cols("gray", "light gray", "medium gray"),
`divergent1` = org_cols("red", "light gray", "purple"),
`divergent2` = org_cols("royal blue", "light gray", "midnight purple"),
`divergent3` = org_cols("red", "light gray", "green"),
`sequential1` = org_cols("light blue", "royal blue"),
`sequential2` = org_cols("light gray", "midnight purple"),
`sequential3` = org_cols("light gray", "purple"),
`evaluate` = org_cols("red", "yellow", "green")
)
#' Interpolated color palette from your organization's palette
#'
#' @param palette Name of palette to use
#' @param reverse Logical. Should the palette be reversed?
#' @param ... Additional arguments passed to colorRampPalette()
#' @return A function to generate interpolated colors
#' @export
org_pal <- function(palette = "main",
reverse = FALSE, ...) {
pal <- org_palettes[[palette]]
if (reverse) pal <- rev(pal)
colorRampPalette(pal, ...)
}
#' Color scale using your organization's color palettes
#'
#' @param palette Palette name
#' @param discrete Is the scale discrete?
#' @param reverse Reverse the palette?
#' @param midpoint Optional midpoint value for diverging scales
#' @param ... Other arguments to scale functions
#' @export
scale_color_org <- function(palette = "main",
discrete = TRUE,
reverse = FALSE,
midpoint = NULL, ...) {
pal <- org_pal(palette = palette, reverse = reverse)
if (discrete) {
ggplot2::discrete_scale(aesthetic = "colour", palette = pal, ...)
} else {
colors <- pal(256)
args <- list(...)
if (!is.null(midpoint) && length(org_palettes[[palette]]) == 3) {
lims <- if ("limits" %in% names(args)) args$limits else range(c(0, midpoint))
vals <- c(lims[1], midpoint, lims[2])
if (length(unique(vals)) < 3) {
warning("Color scale values are not unique. Consider adjusting limits or midpoint.")
}
ggplot2::scale_color_gradientn(
colours = colors,
values = scales::rescale(vals, from = range(vals)),
...
)
} else {
ggplot2::scale_color_gradientn(colours = colors, ...)
}
}
}
#' Fill scale using your organization's color palettes
#'
#' @inheritParams scale_color_org
#' @export
scale_fill_org <- function(palette = "main",
discrete = TRUE,
reverse = FALSE,
midpoint = NULL, ...) {
pal <- org_pal(palette = palette, reverse = reverse)
if (discrete) {
ggplot2::discrete_scale(aesthetic = "fill", palette = pal, ...)
} else {
colors <- pal(256)
args <- list(...)
if (!is.null(midpoint) && length(org_palettes[[palette]]) == 3) {
lims <- if ("limits" %in% names(args)) args$limits else range(c(0, midpoint))
vals <- c(lims[1], midpoint, lims[2])
if (length(unique(vals)) < 3) {
warning("Color scale values are not unique. Consider adjusting limits or midpoint.")
}
ggplot2::scale_color_gradientn(
colours = colors,
values = scales::rescale(vals, from = range(vals)),
...
)
} else {
ggplot2::scale_fill_gradientn(colours = colors, ...)
}
}
}
#' Display all your organization's palettes visually
#'
#' Shows contents of all named palettes as horizontal bars.
#'
#' @export
show_org_palettes <- function() {
n <- length(org_palettes)
max_cols <- max(sapply(org_palettes, length))
palette_names <- names(org_palettes)
# Set larger left margin (bottom, left, top, right)
old_par <- graphics::par(mar = c(1, 6, 3, 1))
graphics::plot(
NULL, xlim = c(0, max_cols), ylim = c(0, n),
type = "n", axes = FALSE, xlab = "", ylab = "",
main = "Color Palettes"
)
for (i in seq_along(org_palettes)) {
pal <- org_palettes[[i]]
for (j in seq_along(pal)) {
graphics::rect(j - 1, n - i, j, n - i + 0.8, col = pal[j], border = NA)
}
graphics::text(-0.2, n - i + 0.4, labels = palette_names[i], adj = 1, xpd = TRUE)
}
# Reset margin settings
graphics::par(old_par)
}