5

I'm using this code to plot a map of temperature change in North America:

ggplot(maps, aes(y=Latitude, x=Longitude, z=variable)) +
  ggtitle(title)+
  scale_fill_brewer(palette = "Spectral")+
  geom_contour_filled(breaks = c(-Inf,-2., -1.5, -1., -0.5, 0, 0.5, 1, 1.5, 2, 3, 4, 5, 7, 9, 11,Inf))+
  xlim(-146,-44)+
  ylim(35,90)+
  theme(plot.title = element_text(hjust = 0.5))

And I want to change the color palette for better visualization. Currently, it writes scale_fill_brewer(palette = "Spectral"), and I'm going to change it into a custom palette

colors <- c(rgb(10,40,100,max=255),rgb(51,102,217,max=255),rgb(105,138,236,max=255),rgb(151,180,250,max=255),rgb(204,217,255,max=255),rgb(255,245,204,max=255),rgb(255,224,153,max=255),rgb(255,203,102,max=255),rgb(255,180,51,max=255),rgb(255,140,51,max=255),rgb(255,85,0,max=255),rgb(230,40,30,max=255),rgb(191,0,0,max=255),rgb(140,0,0,max=255),rgb(108,0,0,max=255),rgb(110,0,70,max=255))

Using scale_fill_brewer(palette = colors)will end with error, I also tried palette(colors), and it also doesn't work.

How can I custom a palette that the argument can recognize?

tjebo
  • 21,977
  • 7
  • 58
  • 94
Lambert Ye
  • 177
  • 2
  • 6

1 Answers1

1

If I understand your question correctly, you want to create a custom scale_... function. In short, this is dipping somewhat deeper into the source code.

I will show one approach which will simply modify the existing functions in the packages RColorBrewer, scales, and of course ggplot2.

  1. The core is to modify RcolorBrewer::brewer.pal, which I guess is bascially what you want. Seeing the code, it selects from a list of colors the right one, depending on your "n". And this is what you need to manually create. I just copied the colors from the palette "YlOrBr". Something to consider: Brewer palettes are not random, they have been tested and created for being recognizable for printing, copying and colorblindness, so I am not sure if it is very clever to create your own palette. Have a look at https://colorbrewer2.org/ to find suitable palettes.
  2. modify the underlying color selector scales:::brewer_pal
  3. modify the scale_fill/scale_color function

I've boiled down the functions to the cores, so they won't make the usual checks and are less flexible. You can modify the original functions to get this functionality back.

library(ggplot2)

mybrewerpal <- function(n, name) {# modified RcolorBrewer::brewer.pal
## the first call to switch would not be necessary in this example,
## but I leave it in order to make the underlying structure in brewer.pal clearer
  switch(name, mypal = switch(n - 2, rgb(c(255, 254, 217), c(247, 196, 95), c(188, 79, 14), maxColorValue = 255),
    rgb(c(255, 254, 254, 204), c(255, 217, 153, 76), c(212, 142, 41, 2), maxColorValue = 255),
    rgb(c(255, 254, 254, 217, 153), c(255, 217, 153, 95, 52), c(212, 142, 41, 14, 4), maxColorValue = 255),
    rgb(c(255, 254, 254, 254, 217, 153), c(255, 227, 196, 153, 95, 52), c(212, 145, 79, 41, 14, 4), maxColorValue = 255),
    rgb(c(255, 254, 254, 254, 236, 204, 140), c(255, 227, 196, 153, 112, 76, 45), c(212, 145, 79, 41, 20, 2, 4), maxColorValue = 255),
    rgb(c(255, 255, 254, 254, 254, 236, 204, 140), c(255, 247, 227, 196, 153, 112, 76, 45), c(229, 188, 145, 79, 41, 20, 2, 4), maxColorValue = 255),
    rgb(c(255, 255, 254, 254, 254, 236, 204, 153, 102), c(255, 247, 227, 196, 153, 112, 76, 52, 37), c(229, 188, 145, 79, 41, 20, 2, 4, 6), maxColorValue = 255)
  ))
}

brewer_pal2 <- # modified from scales:::brewer_pal
  function() { # stripped down all arguments, just to show the core
    function(n) {
      mybrewerpal(n, "mypal") ##modified, usually this is selected by a function 
      ## with type and name as arguments, selecting a palette from a list called scales:::brewer
    }
  }

scale_fill_custom <- ### modified from scale_fill_brewer, removed some arguments
  function (..., aesthetics = "fill") {
    discrete_scale(aesthetics, "custom", brewer_pal2(), ...) ## give a new name to the
    ## scale, it will create a new Scale object.  
  }

p <- 
  ggplot(mtcars, aes(x = mpg, y = disp)) +
  scale_fill_custom()

p + geom_point(shape = 21, aes(fill = as.factor(cyl))) 

p + geom_point(shape = 21, aes(fill = as.factor(carb))) 

tjebo
  • 21,977
  • 7
  • 58
  • 94
  • Genius, Thank you so much! – Lambert Ye Jun 19 '20 at 19:41
  • Interestingly I saw on the day you posted this ggplot2 3.3.2 came out. from 3.3.2 and forward has `options` to change colors see the help doc here: https://ggplot2.tidyverse.org/news/#ggplot2-332 – Mike Jul 19 '22 at 20:37
  • 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). - from the change log – Mike Jul 19 '22 at 20:38