5
votes

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

4
Couldn't you put a length(unique(df$group)) into the brewer.pal() command? Like colorRampPalette(brewer.pal(length(unique(df$group)), "Dark2"))Felix
I have to second Felix's comment above. This method is feasible and straightforward. Also worth noting, however, that in these examples, the color is actually redundant information (groups are already discriminated based on the x aesthetic), which should typically be avoided in figures such as these. That might not be the case with your real data, but just an observation.bk18
I feel there must be a more robust way of doing this within ggplot. This would work well but would need changing if you ever updated the variable used for the grouping.Michael Harper
the length(unique(group)) would return error message of "object 'group' not found".Phil Wu

4 Answers

4
votes

I wasn't 100% happy with my last answer, so this one is done in a more ggplot fashion. Digging through the code for scales within the ggplot package, there is the discrete_scale function which can be used:

library(ggplot2)
ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  discrete_scale("fill", "manual", palette_Dark2)

This code will automatically extract the number of groups from the specified aesthetic, in this case "fill". There is no need to manually specify the number of groups, and seems fairly robust. For example, increasing to include 20 group:

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

ggplot(df, aes(x = group, y = value, fill = group)) + 
  geom_bar(stat = "identity") +
  discrete_scale("fill", "manual", palette_Dark2)

enter image description here

2
votes

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.

1
votes

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()
1
votes

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")