Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make ggplot automatically extract the number of groups for the palette

Tags:

r

ggplot2

When using ggplot on objects with large number of groups (e.g. n > 14), most palettes are not able to cope. The function colorRampPalette is able to extend the range, but to embed it in the scale_fill_manual, you would need to know the number of unique groups.

Is there a way to automatically extract the number of colours required from ggplot directly? I have provided an example below, and to make the palette work, I have had to add the number of groups (14) as an argument to scale_fill_manual(values = palette_Dark2(14)).

Plot using scale_fill_brewer

df <- data.frame(group = paste0("Category", 1:14),
             value = 1:28)

library(ggplot2)
ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  scale_fill_brewer(palette = "Set1")

Warning message: In RColorBrewer::brewer.pal(n, pal) : n too large, allowed maximum for palette Set1 is 9 Returning the palette you asked for with that many colors

enter image description here

Plot using custom palette

Here I have specified a new colour palette before the plot, which is a colour ramp with 14 steps.

library(RColorBrewer)
palette_Dark2 <- colorRampPalette(brewer.pal(14, "Dark2"))

I then use this in the plot as follows:

ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  scale_fill_manual(values = palette_Dark2(14))

enter image description here

Update:

I tried to use length(unique(group)) to extract the number of unique groups but obtained an error message

Error in unique(group) : object 'group' not found

In the attempt of using length(unique(df$group)), the error message is:

Error: Insufficient values in manual scale. 14 needed but only 1 provided. In addition: Warning message: In brewer.pal(length(unique(df$group)), "Dark2") :n too large, allowed maximum for palette Dark2 is 8 Returning the palette you asked for with that many colors

like image 769
Phil Wu Avatar asked Mar 05 '18 15:03

Phil Wu


3 Answers

This is an option, adapting the suggestion from Felix above. First, the palette is made before the plot, with the number of groups extracted from the dataframe:

palette_Dark2 <- colorRampPalette(brewer.pal(14, "Dark2"))
pal <- palette_Dark2(length(unique(df$group)))

ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  scale_fill_manual(values = pal)

It still is not perfect, as the palette has to be specified before and requires that the group is selected before the plot. I am hoping that there is a more elegant way that ggplot could just pick this out automatically.

Note: I presume the code is a minimal working example, but the colour specification is actually redundant information in this example, as it provides nothing more than the x axis already displays.

like image 120
Michael Harper Avatar answered Oct 08 '22 11:10

Michael Harper


You could do this inside ggplot2 by using scale_fill_identity and a left_join to get the column of fill colors mapped to the levels of group. This solution seems hacky and inelegant to me, but it is a way to create a color palette within ggplot2 that has the right number of colors.

ggplot(df, aes(x = group, y = value, 
               fill=left_join(data.frame(group), 
                              data.frame(group=unique(group),
                                         fill=palette_Dark2(length(unique(group)))))$fill)) + 
  geom_bar(stat = "identity") +
  scale_fill_identity()
like image 40
eipi10 Avatar answered Oct 08 '22 11:10

eipi10


Another possibility is to simply put the entire creation of the palette in scale_fill_manual

ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  scale_fill_manual(values = colorRampPalette(brewer.pal(8, "Dark2"))(length(unique(df$group))))

If you do this repeatedly and want to avoid typing of 'df' and 'group' twice, just wrap it in a function, and use aes_string:

f <- function(df, x, y, group){
  ggplot(df, aes_string(x = x, y = y, fill = group)) + 
    geom_bar(stat = "identity") +
    scale_fill_manual(values = colorRampPalette(brewer.pal(8, "Dark2"))(length(unique(df[[group]]))))
}

f(df, "x", "y", "group")
like image 42
Henrik Avatar answered Oct 08 '22 11:10

Henrik