4

In ggplot2/geom_tile, how to change fill color whice exceed the limits? As the image, Region_4/5 are out of limis(1,11) , so the fill color is default grey, how to change 'Region_4' to 'darkblue', 'Region_5' to 'black' . Thanks!

library(tidyverse)
library(RColorBrewer)
tile_data <- data.frame(category=letters[1:5],
                        region=paste0('region_',1:5),
                        sales=c(1,2,5,0.1,300))


tile_data %>% ggplot(aes(x=category,
                         y=region,
                         fill=sales))+
  geom_tile()+
  scale_fill_gradientn(limits=c(1,11),
                       colors=brewer.pal(12,'Spectral'))+
  theme_minimal()

enter image description here

tjebo
  • 21,977
  • 7
  • 58
  • 94
anderwyang
  • 1,801
  • 4
  • 18
  • 1
    Thanks for your replay . " so- you wanna two different colors for values “off limits” (limits of what)"--right, this is waht i want. There are the three cut-off "limits=c(1,11) limits=c(0,0.9) limits=c(100,400)" – anderwyang Nov 22 '21 at 08:26

2 Answers2

2

You can try scales::squish, define the limits, and put the out of bound (oob) values into the scalw:

p = tile_data %>% ggplot(aes(x=category,y=region,fill=sales))+ geom_tile()
p + scale_fill_gradientn(colors = brewer.pal(11,"Spectral"),
limit = c(1,11),oob=scales::squish)

enter image description here

StupidWolf
  • 45,075
  • 17
  • 40
  • 72
  • Thanks for your replay , this is a new useful knowledge for me . Is there way to define the fill colors? – anderwyang Nov 22 '21 at 11:44
  • what do you mean by define fill colors? You have already done it with `colors = brewer.pal(11,"Spectral")` can you be a bit more specific, – StupidWolf Nov 22 '21 at 12:08
  • I want to define the fill color 'Region_4' > 'darkblue', 'Region_5' > 'black' . Thanks! – anderwyang Nov 22 '21 at 12:10
  • hey that doesn't work. you are using a continuous color scheme. Region 4 has a value of 0.1 so it should be more red, and yes you can make region 5 black by doing `colors = c(brewer.pal(11,"Spectral"),"#000000")` – StupidWolf Nov 22 '21 at 12:16
  • Thanks for your replay. I will try to use 'colors = c(brewer.pal(11,"Spectral"),"#000000")' , this is new knowledge for me. – anderwyang Nov 23 '21 at 01:20
1

If you want to keep the gradient scale and have two additional discrete values for off limits above and below, I think the easiest way would be to have separate fill scales for "in-limit" and "off-limit" values. This can be done with separate calls to geom_tile on subsets of your data and with packages such as {ggnewscale}.

I think it then would make sense to place the discrete "off-limits" at the respective extremes of your gradient color bar. You need then three geom_tile calls and three scale_fill calls, and you will need to specify the guide order within each scale_fill call. You will then need to play around with the legend margins, but it's not a big problem to make it look OK.

library(tidyverse)
library(RColorBrewer)
tile_data <- data.frame(
  category = letters[1:5],
  region = paste0("region_", 1:5),
  sales = c(1, 2, 5, 0.1, 300)
)


ggplot(tile_data, aes(
  x = category,
  y = region,
  fill = sales
)) +
  geom_tile(data = filter(tile_data, sales <= 11 & sales >=1)) +
  scale_fill_gradientn(NULL,
    limits = c(1, 11),
    colors = brewer.pal(11, "Spectral"),
    guide = guide_colorbar(order = 2)
  ) +
  ggnewscale::new_scale_fill() +
  geom_tile(data = filter(tile_data, sales > 11), mapping = aes(fill = sales > 11)) +
  scale_fill_manual("Sales", values = "black", labels = "> 11", guide = guide_legend(order = 1)) +
  ggnewscale::new_scale_fill() +
  geom_tile(data = filter(tile_data, sales < 1), mapping = aes(fill = sales < 1)) +
  scale_fill_manual(NULL, values = "darkblue", labels = "< 1", guide = guide_legend(order = 3)) +
  theme_minimal() +
  theme(legend.spacing.y = unit(-6, "pt"), 
        legend.title = element_text(margin = margin(b = 10)))

Created on 2021-11-22 by the reprex package (v2.0.1)

tjebo
  • 21,977
  • 7
  • 58
  • 94