'How do I limit the range of the viridis colour scale?
I have two sets of data, which I want to present using a heat map with the viridis color scale. For the first data set, my values range from 0 to 1.2 and I can easily see the differences I want to see. However my second data set has some outliers, resulting in a range from 0 to 2. Now it's harder to see the differences in the interesting range between 0 and 1 and it's more diffucult to compare the two images directly. Is there a possibility to show the data from 0 to 1.2 using the viridis colour scale while showing the higher values in yellow ("highest" colour of the viridis scale)? Here is an example:
library(viridis)
#Create Data
DataSet1 <- expand.grid(x = 0:5, y = 0:5)
DataSet1$z <- runif(36, 0, 1.2)
DataSet2 <- expand.grid(x = 0:5, y = 0:5)
DataSet2$z <- runif(36, 0, 2)
#Plot Data
ggplot(DataSet1, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_viridis() +
geom_text(aes(label = round(z, 2)), size = 2)
DataSet1: Differences between 0.5 and 0.7 are easy to see
ggplot(DataSet2, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_viridis() +
geom_text(aes(label = round(z, 2)), size = 2)
DataSet2: Differences between 0.5 and 0.7 are diffucult to see
Solution 1:[1]
You can define an arbitrary rescaling function. Not sure this looks that great, would likely need some work with the legend, but in principle this mechanism allows you to map data values onto the scale in any way you want.
ggplot(DataSet2, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_viridis(rescaler = function(x, to = c(0, 1), from = NULL) {
ifelse(x<1.2,
scales::rescale(x,
to = to,
from = c(min(x, na.rm = TRUE), 1.2)),
1)}) +
geom_text(aes(label = round(z, 2)), size = 2)
Solution 2:[2]
EDIT 2022-05-03: The scale function is called scale_fill_viridis_c()
these days.
@ClausWilke's solution is better because it shows in the legend, but sometimes one just needs a quick solution without having to write too much specific code. This one also relies on the scales
package
ggplot(DataSet2, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_viridis_c(limits = c(0.2, 1), oob = scales::squish) +
geom_text(aes(label = round(z, 2)), size = 2)
Solution 3:[3]
Are you looking for something like this?
ggplot(DataSet2, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_gradient(low="green", high="red", limits=c(0, 1.2),
na.value = "yellow") +
geom_text(aes(label = round(z, 2)), size = 2)
Using the viridis colors, asper jazzurro recommendation.
ggplot(DataSet2, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_gradientn(colors = viridis_pal()(9), limits=c(0, 1.2),
na.value = "#FDE725FF") +
geom_text(aes(label = round(z, 2)), size = 2)
Solution 4:[4]
It's not necessarily an improvement, but you could do something like this to show the higher values in yellow:
DataSet2A <- DataSet2 %>% filter(z <= 1.2)
DataSet2B <- DataSet2 %>% filter(z > 1.2)
ggplot(DataSet2A, aes(x, y, fill = z)) +
geom_tile() +
scale_fill_viridis(begin = 0, end = .75) +
geom_text(aes(label = round(z, 2)), size = 2) +
geom_tile(data = DataSet2B, aes(x, y), fill = "yellow")
Maybe if you play around with the cutoff as well as the begin=
and end=
parameters in the scale, which control the portion of the viridis
scale that you're employing, you can achieve the result you want. (Note that you can only have one fill scale per plot, but you can set additional constant fills as I've done here with yellow.)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Claus Wilke |
Solution 2 | |
Solution 3 | |
Solution 4 | jtr13 |