6

I am using R (3.0.1), and ggplot2 (0.9.3.1). I have data that contains both positive and negative values, and I want to obtain a geom_tile plot in which there are different color scale gradients for the positive and the negative numbers (e.g., positive numbers go from red to yellow, negative numbers from blue to cyan). I am able to approach what I want using scale_fill_gradientn (see below), but this results in one continuous gradient that includes "white" color at the midpoint (0). I need to avoid these midpoint colors, and therefore somehow need to "break" the gradients at zero. In other words, I need two continuous gradients (one for positive values, one for negative values) that have a discrete break at zero. Any help?

dat <- data.frame(Row = rep(x = LETTERS[1:5], times = 10)
 , Col = rep(x = LETTERS[1:10], each = 5)
 , Y = runif(n = 50, min = -1, max = 1))

p <- ggplot(data =  dat, aes(x = Row, y = Col)) + 
  geom_tile(aes(fill = Y)) +
  scale_fill_gradientn(colours=c("blue","cyan","white", "yellow","red"), values=rescale(c(-1,0,1)))
Niels Janssen
  • 149
  • 1
  • 3
  • 8
  • Exactly what I'd like to do as well... – marsl Sep 11 '13 at 16:28
  • Check out this thread: [link](http://stackoverflow.com/questions/16129876/ggplot2-multiple-scales-legends-per-aesthetic-revisited/17002610#17002610). There is an example of using two scale gradients in the same graph. – Niels Janssen Sep 11 '13 at 22:08

3 Answers3

12

I'm not sure what version of ggplot2 this was added, but there is now built in functionality for this:

p <- ggplot(data =  dat, aes(x = Row, y = Col)) + 
    geom_tile(aes(fill = Y)) +
    scale_fill_gradient2()
Keith
  • 121
  • 1
  • 2
  • thanks for pointing out the existence of that function, makes things easier! – Maxim.K Sep 24 '14 at 08:54
  • 1
    `scale_fill_gradient2` is not new; it have been in available since before version 1.0. For simple problems where a midpoint/zero color needs to be specified, it works well. However, it gives a smooth transition from high through the midpoint to the low. The original question was wanting not just for the midpoint to be white, but for there to be an effective break in the color scale between that used for positive and negative (so that a little bit positive was not just a little bit different than white). That is why I created an answer based on `scale_fill_gradientn`. – Brian Diggs May 13 '16 at 15:56
11

Make the range between the cyan and yellow very very small:

ggplot(data =  dat, aes(x = Row, y = Col)) + 
  geom_tile(aes(fill = Y)) +
  scale_fill_gradientn(colours=c("blue","cyan","white", "yellow","red"), 
    values=rescale(c(-1,0-.Machine$double.eps,0,0+.Machine$double.eps,1)))

enter image description here

The guide does not have a physical break in it, but the colors map as you described.

Brian Diggs
  • 57,757
  • 13
  • 166
  • 188
  • 1
    FYI to all other searchers: the answer by Keith below reflects the new ggplot2 function that does this automatically and cleanly – Will Apr 11 '16 at 15:49
  • @Will `scale_fill_gradient2` is not a new function; it has been there since the beginning. However, it does not quite fulfill the requirements of the question. – Brian Diggs May 13 '16 at 15:52
2

You can always pad the results by a little bit when graphing in order to avoid the white range entirely and exclusively show light yellow and light cyan for numbers right around 0:

dat$Y2 <- ifelse(dat$Y > 0, dat$Y + .25, ifelse(dat$Y < 0, dat$Y-.25,dat$Y))
thompsor
  • 149
  • 4
  • Thanks for the comment, but I do not understand how this solves the problem of having a continuous region across zero, while I want to have a discrete break at zero. – Niels Janssen Sep 11 '13 at 14:52
  • The boxes are only colored white when the Y values are within a certain range of 0 (I'm not sure on the exact range, but ±.25 works fine here). You can just move all of the points away from 0 by a small constant and you won't have any white boxes anymore. A box that was -.0001 will now be -.2501 and display as light cyan instead of white. – thompsor Sep 12 '13 at 02:39
  • Thanks for your help, indeed that works. Unfortunately, for my project I am not in a position to change the values as you suggest. – Niels Janssen Sep 12 '13 at 09:26
  • The answer by Brian Diggs is what I was trying to find a way to do but couldn't quite figure out. His solution is definitely preferable. – thompsor Sep 14 '13 at 21:29