R – ggplot: How to create a discrete color palette that fits the data automatically

ggplot2r

I have a customized function that I use for all my plots instead of the plain ggplot:

my_ggplot <- function(...){
    ggplot(...) +
    theme_bw() +
    scale_colour_manual(values=get_palette(20)) +
    theme(axis.text.x=element_text(size=15),
          axis.text.y=element_text(size=15),
          axis.title.y=element_text(vjust=-.5, size=15, face="bold"),
          axis.title.x=element_text(vjust=-.5, size=15, face="bold"),
          plot.title=element_text(vjust=1, size=18, face="bold"),
          legend.title=element_text(size=15, face="bold"),
          plot.margin=unit(c(2,1,2,2), "lines")) # T R B L
}

Now I can do something like:

my_ggplot(molten_df, aes(timepoint,count,group=gene_symbol)) +
    geom_line(aes(color=gene_symbol), lwd=1.5)

This gives me decent label font sizes and my own color scale. Unfortunately I have to manually encode the palette size (20) to my get_palette function, which returns a different palette depending on the number I pass it.

My question: is there a way to infer the number of levels that the color aesthetic will have so my_ggplot doesn't crash when I need to plot 21 colors? I guess this is the way ggplot does it, but I can't seem to find the relevant function in their source code.

Answered by @user946850
This is what I ended up doing:

my_ggplot <- function(...){
    ggplot(...) +
    theme_bw() +
    discrete_scale("colour", "my_scale", dyn_palette()) +
    theme(axis.text.x=element_text(size=15),
          axis.text.y=element_text(size=15),
          axis.title.y=element_text(vjust=-.5, size=15, face="bold"),
          axis.title.x=element_text(vjust=-.5, size=15, face="bold"),
          plot.title=element_text(vjust=1, size=18, face="bold"),
          legend.title=element_text(size=15, face="bold"),
          plot.margin=unit(c(2,1,2,2), "lines")) # T R B L
}

dyn_palette <- function(){
  function(n){
    get_palette(n)
  }
}

Best Answer

I'm afraid scale_colour_manual isn't the way to go here. Let's take a look behind the scenes: How does it work for, say, the Brewer scales?

If you look at the definition of scale_colour_brewer, you will see that it is just a call to

discrete_scale("colour", "brewer", brewer_pal(type, palette), ...)

Now let's take a look at brewer_pal (in the scales package):

> brewer_pal
function (type = "seq", palette = 1) 
{
    pal <- pal_name(palette, type)
    function(n) {
        brewer.pal(n, pal)[seq_len(n)]
    }
}

Interesting, right? A function that returns a (bound) function with one argument n -- that's where the number of levels is going to be passed when the plot is created. In turn, brewer.pal (in RColorBrewer) just returns a list of colors:

> brewer.pal(3, 'YlOrRd')
[1] "#FFEDA0" "#FEB24C" "#F03B20"

You should be able to achieve just the same with your custom palette by following this pattern.

Related Topic