32
votes

I have written a function that returns a vector of color names:

custom.colors <- function(n) {
  palette <- c("dodgerblue1", "skyblue4", "chocolate1", "seagreen4",
               "bisque3", "red4", "purple4", "mediumpurple3",
               "maroon", "dodgerblue4", "skyblue2", "darkcyan",
               "darkslategray3", "lightgreen", "bisque",
               "palevioletred1", "black", "gray79", "lightsalmon4",
               "darkgoldenrod1")
  if (n > length(palette))
    warning('palette has duplicated colours')
  rep(palette, length.out=n)
}

I would like ggplot to use the above function to generate the palette by default. Maybe only for discrete scales. Using scale_manual() every time is too much of a drag. Is it possible?

4
this post should be useful.baptiste
Thanks. I'll keep that for later, but now I'm stuck with version 0.8.9.Ernest A

4 Answers

19
votes

To redefine the default color scale you can also just redefine the ggplot function:

ggplot <- function(...) ggplot2::ggplot(...) + scale_color_brewer(palette="Spectral")

The same works for the fill scale.

14
votes

17 days ago (2020-05-19) work by Carson Sievert has been merged into the development version of ggplot2 that allow to specify the default discrete color palette(s) with an option (ggplot2.discrete.fill & ggplot2.discrete.color):

options(ggplot2.discrete.color = c("red", "#af01ef"))

It also allows to specify several pallets of different size (with the smallest sufficient palette beieng used):

options(ggplot2.discrete.color = list(c("red", "#af01ef"), custom.colors(99)))

Unfortunately it does not take a palette function (like your custom.colors) but it does take a scale function (so you could build one as outlined in @ernestA's answer to produce the warning that you want).

From the NEWS:

Default discrete color scales are now configurable through the options() of ggplot2.discrete.colour and ggplot2.discrete.fill. When set to a character vector of colour codes (or list of character vectors) with sufficient length, these colours are used for the default scale. See help(scale_colour_discrete) for more details and examples (@cpsievert, #3833).

8
votes

@baptiste pointed me to a message board post that mentions the function set_default_scale which can be used to set a default palette. The following solution only works with old versions of ggplot2, though.

First we need a function that produces colour names or codes. I called mine magazine.colours:

magazine.colours <- function(n, set=NULL) {
  set <- match.arg(set, c('1', '2'))
  palette <- c("red4", "darkslategray3", "dodgerblue1", "darkcyan",
               "gray79", "black", "skyblue2", "dodgerblue4",
               "purple4", "maroon", "chocolate1", "bisque3", "bisque",
               "seagreen4", "lightgreen", "skyblue4", "mediumpurple3",
               "palevioletred1", "lightsalmon4", "darkgoldenrod1")
  if (set == 2)
    palette <- rev(palette)
  if (n > length(palette))
    warning('generated palette has duplicated colours')
  rep(palette, length.out=n)
}

(It accepts an optional set argument just to show that you are not restricted to a single palette.) Ok, now we create a "scale", which I called magazine. It's based on ggplot's brewer scale and the code is pretty ugly:

ScaleMagazine <- proto(ScaleColour, expr={
  objname <- 'magazine'
  new <- function(., name=NULL, set=NULL, na.colour='yellowgreen',
                  limits=NULL, breaks = NULL, labels=NULL,
                  formatter = identity, variable, legend = TRUE) {
    b_and_l <- check_breaks_and_labels(breaks, labels)
    .$proto(name=name, set=set, .input=variable, .output=variable,
            .labels = b_and_l$labels, breaks = b_and_l$breaks,
            limits= limits, formatter = formatter, legend = legend,
            na.colour = na.colour)
  }
  output_set <- function(.) {
    missing <- is.na(.$input_set())
    n <- sum(!missing)
    palette <- magazine.colours(n, .$set)
    missing_colour(palette, missing, .$na.colour)
  }
  max_levels <- function(.) Inf
})
scale_colour_magazine <- ScaleMagazine$build_accessor(list(variable = '"colour"'))
scale_fill_magazine <- ScaleMagazine$build_accessor(list(variable = '"fill"'))

The important thing here is to define output_set which is the function that ggplot calls to get the colour names/codes. Also, if you need extra arguments, those must be included in new and later can be accessed as .$argument_name. In the example above, output_set simply calls magazine.colours.

Now, check the new scale does actually work:

qplot(mpg, wt, data=mtcars, shape=21,
      colour=factor(carb), fill=factor(carb)) +
  scale_colour_magazine(set='1') +
  scale_fill_magazine(set='1')

To make it the default, simply use set_default_scale.

set_default_scale("colour", "discrete", "magazine") 
set_default_scale("fill", "discrete", "magazine") 

And that'll be it.

> qplot(mpg, wt, data=mtcars, colour=factor(carb), fill=factor(carb))

plot showing the new palette

6
votes

Simply assign a variable with the name of your desired scale:

scale_colour_discrete <- function(...)
  scale_colour_manual(..., values = c('dodgerblue1', *))

This works since ggplot will take its default scales from the global environment if possible, similarly to:

get('scale_colour_discrete', envir = globalenv())