Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change ggplot bar chart fill colors

With this data:

df <- data.frame(value =c(20, 50, 90), 
                 group = c(1, 2,3))

I can get a bar chart:

df %>% ggplot(aes(x = group, y = value, fill = value)) +
  geom_col() +
  coord_flip()+ 
  scale_fill_viridis_c(option = "C") +
  theme(legend.position = "none")

enter image description here

But I would like to have the colors of those bars to vary according to their corresponding values in value.

I have managed to change them using geom_raster:

ggplot() + 
  geom_raster(aes(x = c(0:20), y = .9, fill = c(0:20)),  
              interpolate = TRUE) +
  geom_raster(aes(x = c(0:50), y = 2, fill = c(0:50)), 
              interpolate = TRUE) +
  geom_raster(aes(x = c(0:90), y = 3.1, fill = c(0:90)),               
              interpolate = TRUE) +
  scale_fill_viridis_c(option = "C") +
  theme(legend.position = "none")

enter image description here

This approach is not efficient when I have many groups in real data. Any suggestions to get it done more efficiently would be appreciated.

I found the accepted answer to a previous similar question, but "These numbers needs to be adjusted depending on the number of x values and range of y". I was looking for an approach that I do not have to adjust numbers based on data. David Gibson's answer fits my purpose.

like image 785
Zhiqiang Wang Avatar asked Oct 18 '25 11:10

Zhiqiang Wang


2 Answers

It does not look like this is supported natively in ggplot. I was able to get something close by adding additional rows, ranging from 0 to value) to the data. Then use geom_tile and separating the tiles by specifying width.

library(tidyverse)

df <- data.frame(value = c(20, 50, 90),
                 group = c(1, 2, 3))

df_expanded <- df %>%
  rowwise() %>%
  summarise(group = group,
            value = list(0:value)) %>%
  unnest(cols = value)

df_expanded %>%
  ggplot() +
  geom_tile(aes(
    x = group,
    y = value,
    fill = value,
    width = 0.9
  )) +
  coord_flip() +
  scale_fill_viridis_c(option = "C") +
  theme(legend.position = "none")

enter image description here

If this is too pixilated you can increase the number of rows generated by replacing list(0:value) with seq(0, value, by = 0.1).

enter image description here

like image 182
David Gibson Avatar answered Oct 20 '25 00:10

David Gibson


This is a real hack using ggforce. This package has a geom that can take color gradients but it is for a line segment. I've just increased the size to make the line segment look like a bar. I made all the bars the same length to get the correct gradient, then covered a portion of each bar over with the same color as the background color to make them appear to be the correct length. Had to hide the grid lines, however. :-)

df %>% 
    ggplot() + 
    geom_link(aes(x = 0, xend = max(value), y = group, yend = group, color = stat(index)), size = 30) +
    geom_link(aes(x = value, xend = max(value), y = group, yend = group), color = "grey", size = 31) +
    scale_color_viridis_c(option = "C") +
    theme(legend.position = "none", panel.background = element_rect(fill = "grey"), 
          panel.grid = element_blank()) +
    ylim(0.5, max(df$group)+0.5 )

enter image description here

like image 33
stomper Avatar answered Oct 20 '25 01:10

stomper