Skip to content

Instantly share code, notes, and snippets.

@geotheory
Created October 20, 2020 22:34
Show Gist options
  • Save geotheory/8e82242712f5e1466538b925b4490e40 to your computer and use it in GitHub Desktop.
Save geotheory/8e82242712f5e1466538b925b4490e40 to your computer and use it in GitHub Desktop.
#'
#' Distinctive colours for qualitative data visualisation
#'
#' Depends: cluster, TSP
#'
distinct_hues = function(n, H = c(0,360), C = c(30,80), L = c(15,90), alpha = NULL, sample_size = 250,
metric = c("euclidean", "manhattan"), ordered = FALSE, seed = NULL, ...){
if(!is.null(seed)) set.seed(seed)
ran_cols = data.frame(lapply(list(h=H,c=C,l=L), function(x) sample(x[1]:x[2], sample_size, replace=TRUE)))
pm = data.frame(cluster::pam(ran_cols, n, metric=metric)$medoids) # clustering
cols = hcl(pm$h, pm$c, pm$l, alpha = alpha)
if(!ordered) return(cols)
pm_norm = data.frame(lapply(pm, scales::rescale_max)) # normalise
dm = 1/dist(pm_norm, diag = FALSE, upper = FALSE) # inverse dist matrix
tsp = as.numeric(TSP::solve_TSP(TSP::TSP(dm), ...)) # travelling salesman
cols[tsp]
}
#' Parameters:
#' n - number of colours to generate
#' H - hue [0,360] - e.g. grDevices::hcl(h=H, c=C, l=L, alpha=alpha)
#' C - chroma [upper bound for chroma depends on hue and luminance]
#' L - luminance [0,100]
#' alpha - transparency [0,1]
#' sample_size - function clusters a large random sample of this many colours
#' metric - method applied by cluster::pam
#' ordered - apply a TSP method to order output colours to maximise adjacent contrasts
#' seed - for stable colours. Partly stochastic process if NULL
#' ... - arguments to pass to TSP::solve_TSP()
## Example usage:
# plot(1:n, col = distinct_hues(n), pch = 16, cex=5)
# plot(1:n, col = distinct_hues(n, L=c(5,90)), pch = 16, cex=5)
# plot(1:n, col = distinct_hues(n, ordered=T), pch = 16, cex=5)
# plot(1:n, col = distinct_hues(n, ordered=T, seed=42), pch = 16, cex=5) # stable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment