Reputation: 63
I'm trying to modify the scale of a heatmap with geom_raster
such that the color change occurs over a subset of the data and anything outside that subset doesn't get any more or less colorful.
library(tidyverse)
library(viridis)
library(reshape2)
q <- letters[1:5]
w <- rev(letters)[1:5]
x1 <- rnorm(5, 0, 1)
x2 <- rnorm(5, 0, 1)
x3 <- rnorm(5, 0, 1)
x4 <- rnorm(5, 0, 1)
x5 <- rnorm(5, 10, 1)
test.df <- melt(data.frame(q,w,x1,x2,x3,x4,x5))
ggplot(test.df, aes(q,variable,fill=value))+geom_raster()+scale_fill_viridis()
If you run that, you get this heatmap:
.
The top row is "hogging" some of the color range. Since the vast majority of the data is between -2 and 2, I'd like to make it so that the color scale change occurs over that range, and anything outside of it is just yellow or purple. To me, anything over 2 should just be "very high" and anything under -2 should be "very low", but the numbers between -2 and 2 are what I want to see.
I don't think cut
is what I want because then I need to supply a bunch of colors, and I don't want to remove any data or change any data over some value to some maximum or anything like that. Setting limits in the scale_viridis
command helps but removes data outside the limits.
Upvotes: 3
Views: 3321
Reputation: 1580
If the values only exceed the scale at one end (looks to be the case with your data, you have values greater than 2, but not less than -2) you can change values that exceed your maximum to NA
in your dataframe and then use the na.value =
option in scale_fill_viridis()
to make all of the NA values a certain color.
#change values greater than 2 to NA
test.df$value <- ifelse(test.df$value <= 2, test.df$value, NA)
ggplot(test.df, aes(q, variable, fill = value)) +
geom_raster() +
scale_fill_viridis(na.value = "yellow", #make NAs (values > 2) yellow
limits = c(-2,2), #define limits of scale
labels = c(as.character(-2:1), ">= 2"), breaks = -2:2)
This solution is less flexible than the other answers, as it won't work if you have values that exceed your range on the high and low end of the scale, but it's an easy, concise way to do this if you only have outliers on one end of the scale.
Upvotes: 3
Reputation: 2864
You can use scale_fill_gradientn
to have full control on your color gradient. The code below will assign different colors across [-2, 2]
but will keep the edge colors consistent beyond these values. Please try to set seed in your questions using set.seed
for reproducibility.
ggplot(test.df, aes(q, variable, fill = value)) +
geom_raster() +
scale_fill_gradientn(
colours = c("blue", "blue", "red", "red"),
values = c(-999, -2, 2, 999)
)
Simply increase the number of colors and values to extend your color gradient.
Upvotes: 2
Reputation: 16871
You can use a function such as dplyr::case_when
to truncate the values at your endpoints. Then if you want, you can set labels on the legend accordingly. Note that I typed the labels manually in order to do "<= -2"
and ">= 2"
; not sure if this would suffice, or if you may need something more dynamic.
library(tidyverse)
library(viridis)
library(reshape2)
set.seed(1234)
q <- letters[1:5]
w <- rev(letters)[1:5]
x1 <- rnorm(5, 0, 1)
x2 <- rnorm(5, 0, 1)
x3 <- rnorm(5, 0, 1)
x4 <- rnorm(5, 0, 1)
x5 <- rnorm(5, 10, 1)
test.df <- melt(data.frame(q,w,x1,x2,x3,x4,x5)) %>%
mutate(val_trimmed = case_when(
value > 2 ~ 2,
value < -2 ~ -2,
T ~ value
))
ggplot(test.df, aes(x = q, y = variable, fill = val_trimmed)) +
geom_raster() +
scale_fill_viridis(labels = c("<= -2", "-1", "0", "1", ">= 2"), breaks = -2:2) +
labs(caption = "Note: values truncated above 2 and below -2")
Created on 2018-08-13 by the reprex package (v0.2.0).
Upvotes: 3