Skip to content

Feature request: discrete color scales that don't fail with too few colors #2428

Closed
@jnolis

Description

@jnolis

When you make a chart that uses many discrete colors, you never have to worry about the number of colors if you use the default colors.

require(ggplot2)
ggplot(data.frame(x=letters[1:26],y=runif(26)),aes(x=x,y=y,fill=x))+geom_col()

image

If you want to use your own colors (for instance, if you have company colors), it's easy to use a scale_manual to set the colors

company_colors <-c("#E50000", "#008A8A", "#AF0076", "#E56800", "#1717A0", "#E5AC00", "#00B700")

ggplot(data.frame(x=letters[1:7],y=runif(7)),aes(x=x,y=y,fill=x)) +
geom_col() +
scale_fill_manual(values=company_colors)

image

However, when you use this method you'll run into trouble if your number of needed colors is greater than the length of your color vector. This can be especially problematic because you generally want to just say "always use my company colors so I don't worry about it."

ggplot(data.frame(x=letters[1:26],y=runif(26)),aes(x=x,y=y,fill=x)) +
geom_col() +
scale_fill_manual(values=company_colors)
Error: Insufficient values in manual scale. 26 needed but only 7 provided.

One solution to this is to create a function that makes a desired length vector of colors based on your existing ones:

make_color_vector <- function(desired_length,base_colors){
  if(desired_length <= length(base_colors)){
    base_colors[1:n]
  } else {
    ramp_to_colors <- function(color,amount){
      if(amount > 1){
        current_color <- color
        return_colors <- c()
        for(a in 1:(amount)){
          return_colors <- c(return_colors,current_color)
          current_color <- grDevices::colorRampPalette(c(current_color,"#222222"))(4)[2]
        }
        return_colors
      } else {
        color
      }
    }
    iterations <- ceiling(desired_length/length(base_colors))
    as.vector(do.call(rbind,lapply(base_colors,function(x) ramp_to_colors(x,iterations))))[1:desired_length]
  }
}

ggplot(data.frame(x=letters[1:26],y=runif(26)),aes(x=x,y=y,fill=x)) +
geom_col() +
scale_fill_manual(values = make_color_vector(26, company_colors))

But the downside to this is you still need to keep track of how many colors you need.

This seems like something that could be added to ggplot2 without breaking anything! One solution would be to let scale_fill_manual take a function which takes an integer as an input and returned the desired number of colors (rather than just a vector of colors). This feels similar to how some of the labels and breaks parameters work for scales. You could include the "make_color_vector" function within ggplot2 itself, or leave other people to make their own. Improving this functionality seems like it would help other packages downstream (like ggthemr, which sets default themes but breaks if your custom theme doesn't have enough colors).

I'm happy to help write the code to do this, but unfortunately I'm struggling to figure out where in the ggplot2 code base I would make these changes.

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions