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
discreteargument is set toTRUEorFALSE - whether you are coloring the
coloror thefillof 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
paletteargument - only if you are using a divergent palette, you need to know the midpoint of the data; this will determine the
midpointargument
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
Rscript file. - Save the file as
colorsorg.Rin your project directory (or wherever you put yourRscripts). - Customize the
org_colorsvector with your organization’s color hex codes. - Re-define the palettes in the
org_paletteslist to use the colors you defined in theorg_colorsvector. 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.Rfile into yourRsession (or into your ownRscript) 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 otherggplotchart. - When you are fully satisfied with any customization that you are going to do, then distribute this
colorsorg.Rfile 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)
}