How to make a color scale with a sharp transition in ggplot2

I am trying to create a color scale with a sharp transition of color to one point. I am currently doing the following:

test <- data.frame(x = c(1:20), y = seq(0.01, 0.2, by = 0.01)) cutoff <- 0.10 ggplot(data = test, aes(x = as.factor(x), y = y, fill = log(y), width = 1, binwidth = 0)) + geom_bar(stat = "identity") + scale_fill_gradientn(colours = c("red", "red", "yellow", "green"), values = rescale(log(c(0.01, cutoff - 0.0000000000000001, cutoff, 0.2))), breaks = c(log(cutoff)), label = c(cutoff)) 

He produces the plots that I want. But the position of the gap in the color bar somehow changes depending on the cutoff. Sometimes lower than the value, sometimes higher, sometimes on the line. Here are a few graphs with different cuts (0.05, 0.06, 0.1):

cut off at 0.05cut off at 0.06cut off at 0.10

What am I doing wrong? Or, conversely, is there a better way to create such a color scale?

+5
source share
2 answers

I think it's a little difficult to make an exact discrete cutoff point in a continuous color scale using scale_fill_gradientn . A quick alternative is to use scale_fill_gradient , set the clipping using limits and set the color of the values ​​to “outside the limits” using na.value .

Here is a slightly simpler example than in your question:

 # some data df <- data.frame(x = factor(1:10), y = 1, z = 1:10) # a cutoff point lo <- 4 ggplot(df, aes(x = x, y = y, fill = z)) + geom_bar(stat = "identity") + scale_fill_gradient(low = "yellow", high = "green", limits = c(lo, max(df$z)), na.value = "red") 

enter image description here

As you can see, values ​​below the cut-out point will not be displayed in the legend, but you might think that in any case you can add a big chunk of the red waste of the “bandwidth of the legends”. Instead, you can simply add a verbal description of the red bars in the title of the picture.


You can also distinguish between values ​​below the low point and above the high point. For example, set the values ​​“too low” to blue and “too high” to red. Here I use findInterval to distinguish between low, medium and high values.

 # some data set.seed(2) df <- data.frame(x = factor(1:10), y = 1, z = sample(1:10)) # lower and upper limits lo <- 3 hi <- 8 # create a grouping variable based on the the break points df$grp <- findInterval(df$z, c(lo, hi), rightmost.closed = TRUE) ggplot(df, aes(x = x, y = y, fill = z)) + geom_bar(stat = "identity") + scale_fill_gradient(low = "yellow", high = "green", limits = c(lo, hi), na.value = "red") + geom_bar(data = df[df$grp == 0, ], fill = "blue", stat = "identity") 

enter image description here

0
source

If you are still interested in solving this problem, you can add guide = guide_colourbar(nbin = <some arbitrarily large number>) to scale_fill_gradientn() . This increases the number of cells used by the color image legend, which makes the transition clearer.

 # illustration using nbin = 1000, & weighted colours below the cutoff plot.cutoff <- function(cutoff){ p <- ggplot(data = test, aes(x = as.factor(x), y = y, fill = log(y))) + geom_col(width = 1) + scale_fill_gradientn(colours = c("red4", "red", "yellow", "green"), values = scales::rescale(log(c(0.01, cutoff - 0.0000000000000001, cutoff, 0.2))), breaks = c(log(cutoff)), label = c(cutoff), guide = guide_colourbar(nbin = 1000)) return(p) } cowplot::plot_grid(plot.cutoff(0.05), plot.cutoff(0.06), plot.cutoff(0.08), plot.cutoff(0.1), ncol = 2) 

illustration

(If you find the above image is not sharp enough at very high resolution, you can also set raster = FALSE in guide_colourbar() , which disables interpolation and draws rectangles instead.)

0
source

All Articles