botloggy
botloggy

Reputation: 403

Heatmap with multiple conditions and custom ranges

I want to build custom heatmap with color ranges based on multiple conditions.

I found a similar post that focuses on Heatmap with conditions. However, it does not address my question of having dedicated color scales for different conditions.

library(ggplot2)
library(reshape2) 

tt <- data.frame(C1=c(0.4,.5,.5, 0, .8,.8),
             C2=c(.5,.6,.7, 0.6, .7,.8), 
             C3=c(.8,.7,.9, 0, .8,.7), 
             C4=c(.8,.1,.6, 0.9, .5,.9),
             C5=c(0.4,.6,.6, 0.5, .8,.8),
             C6=c(0.8,.7,.5, 0.1, .8,.8), 
             C7=c(0.8,.6,.4, 0, .8,.8))

row.names(tt) <- paste("F", 1:6, sep='')
tt <- as.matrix(tt)

tt_melt <- melt(tt) 
colnames(tt_melt) <- c('rows', 'columns', 'performance')

tt_melt$cut <- cut(tt_melt$performance, 
               breaks=c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0),
               labels=as.character(c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)),
               include.lowest=TRUE)

perf_cols <- c(colorRampPalette(c("red", "green"))( 9 ))
perf_text_cols <- c(rep("white", 9))

gg <- ggplot(data=tt_melt, aes(x=columns, y=rows, fill=cut))
gg <- gg + geom_tile()
gg <- gg + geom_text(aes(label=performance, color=cut)) 
gg <- gg + labs(title = '')
gg <- gg + coord_equal()
gg <- gg + scale_colour_manual(values = perf_text_cols)
gg <- gg + scale_fill_manual(values=perf_cols)
gg <- gg + theme_minimal(base_size = 12, base_family = "")
gg <- gg + theme(legend.position="none")
gg

The heatmap from the above code looks like this:

enter image description here

However, I want the heatmap to have multiple color gradients based on the given conditions.

For ex, the conditions are below:

If the values are < 0.4 -> Green

If the values are > 0.5 -> Red

Values that lie between 0.4 and 0.5 then -> Grey

These colors would be having their own gradient scale. Higher the value in a condition darker the shade of that color.

Upvotes: 2

Views: 1005

Answers (2)

tjebo
tjebo

Reputation: 23797

If your aim is not a real color hue gradient but just to make the color "darker", you could add a layer of grey or even black tiles with changing alpha. This needs to be relative to your midpoint though. I think the approach with cuts is good, although I would suggest to make the cuts bigger.

In the end of the day, this is sort of reproducing a diverging color scale and I am not sure if user mindlessgreens solution is not much more straight forward - really depends on your use case, I guess.

## your data preparation until tt_melt

## the cut approach seems good to me, but make the cuts reasonable
tt_melt$cut <- cut(tt_melt$performance, breaks=c(-Inf, 0.4, 0.5, Inf))
## you will need to add an alpha relative to your midpoint
tt_melt$dist = abs(tt_melt$performance - .5)

ggplot(tt_melt, aes(x = columns, y = rows)) +
  geom_tile(aes(fill = cut)) + 
  scale_fill_manual(values = c("Red", "grey", "green")) +
## now add a layer of grey values based on performance, I am using alpha in order to avoid new fill scale
## remove your neutral values
  geom_tile(data = dplyr::filter(tt_melt, cut != "(0.4,0.5]"), 
            aes(alpha = dist), fill = "black") +
  geom_text(aes(label = performance), color = "white") +
  scale_alpha(range = c(.5,.1))

enter image description here

Upvotes: 1

mindlessgreen
mindlessgreen

Reputation: 12142

Maybe something like this could be an option, using a custom color palette.

pal <- c("#238b45",
         "#74c476",
         "#bae4b3",
         "#edf8e9",
         "#cccccc",
         "#fee5d9",
         "#fcae91",
         "#fb6a4a",
         "#cb181d")

ggplot(data=tt_melt, aes(x=columns, y=rows, fill=performance))+
  geom_tile()+
  geom_text(aes(label=performance))+
  scale_fill_gradientn(colors=pal,breaks=c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0))+
  labs(title = '')+
  coord_equal()+
  theme_minimal(base_size = 12)+
  theme(legend.position="none")

enter image description here


Update: Random test data and fixed gradient scale limits. By adding limits=c(0,1) to gradient scale, the palette is fixed from 0 to 1 regardless of input data range. Makes images comparable.

library(dplyr)
library(tidyr)

pal <- c("#238b45",
         "#74c476",
         "#bae4b3",
         "#edf8e9",
         "#cccccc",
         "#fee5d9",
         "#fcae91",
         "#fb6a4a",
         "#cb181d")
breaks <- c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0)

# randomly generate data
m <- as.data.frame(matrix(sample(seq(0,1,0.01),36),nrow=6)) %>%
  mutate(rows=factor(row_number())) %>%
  tidyr::pivot_longer(-rows,names_to="columns",values_to="performance")

# limits used in gradient scale
ggplot(data=m, aes(x=columns, y=rows, fill=performance))+
  geom_tile()+
  geom_text(aes(label=performance))+
  scale_fill_gradientn(colors=pal, breaks=breaks, limits=c(0,1))+
  labs(title = 'Range 0:1')+
  coord_equal()+
  theme_minimal(base_size = 12)

enter image description here

Upvotes: 2

Related Questions