Reputation: 59
I have a data which has two variables and I want to see a single plot with heatmap for each of them overlaid on one another and showing two color scales for the two different variables. My code while not correct should clearly indicate what I am trying to achieve.
I have looked through several examples none of those indicate how to do this for geom_tile(). It would have been easy for geom_point. I am providing a synthetic example to show what I am doing. I get the error saying "Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale." Evidently it is accepting only the second scale_fill_gradient, but I would like to view both the color gradients corresponding to the variables in the same heatmap. It would be great if I could find a way to get this plot. Thank you!
library(reshape2)
library(ggplot2)
set.seed(2)
m1 = matrix(rnorm(100), nrow=10)
m2 = matrix(rnorm(100), nrow=10)
M1 = melt(m1)
M2 = melt(m2)
names(M1) = c("Var1", "Var2", "value1")
names(M2) = c("Var1", "Var2", "value2")
pp1 <- ggplot() +
geom_tile(data=M1, aes(x=Var1, y=Var2, fill=value1)) +
scale_fill_gradient(low="white", high="red") +
geom_tile(data=M2, aes(x=Var1, y=Var2, fill=value2)) +
scale_fill_gradient(low="blue", high="yellow")
pp1
Upvotes: 0
Views: 3811
Reputation: 669
A bivariate color legend. The intervals should maybe be the corresponding quantile.
library(tidyverse)
library(cowplot)
set.seed(2)
m1 = matrix(rnorm(100), nrow=10)
m2 = matrix(rnorm(100), nrow=10)
M1 = melt(m1)
M2 = melt(m2)
names(M1) = c("Var1", "Var2", "value1")
names(M2) = c("Var1", "Var2", "value2")
M1$value_cut <- cut(M1$value1, breaks = 3)
M2$value_cut <- cut(M2$value2, breaks = 3)
M1$value_cut2 <- M2$value_cut
M1$cuts <- paste(M1$value_cut, M1$value_cut2, sep = "-")
levels_comb <- expand.grid(lev1 = levels(M1$value_cut), lev2 = levels(M2$value_cut))
levels_comb$cuts <- paste(levels_comb$lev1, levels_comb$lev2, sep = "-")
levels_comb$filling <- c("#be64ac","#8c62aa","#3b4994","#dfb0d6","#a5add3","#5698b9","#e8e8e8","#ace4e4","#5ac8c8")
data_m <- left_join(M1, levels_comb, by = "cuts")
plot_tile <- ggplot(data_m, aes(x = Var1, y = Var2, fill = filling)) +
geom_tile() +
scale_fill_identity() +
coord_equal() +
theme_minimal()
legend_tile <- ggplot(levels_comb, aes(x = lev1, y = lev2, fill = filling)) +
geom_tile() +
scale_fill_identity() +
coord_equal() +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
ggdraw() +
draw_plot(plot_tile, 0, 0, 1, 1) +
draw_plot(legend_tile, .75, .4, .3, .3)
Upvotes: 3
Reputation: 38053
So the legends themselves are no problem with the ggnewscale
package, the problem lies in choosing the actual colours that you want to display. So let's make a new matrix with the actual colours you want to display:
library(ggnewscale)
library(scales)
r <- rescale(M1$value1)
# 1 - rescaled value because yellow should be bottom
g <- 1 - rescale(M2$value2)
# Second scale goes from yellow (low) to blue (high)
# Yellow is 100% blue, 100% green, so blue stays invariant
rgb <- rgb(r, g, 1)
# Make new matrix
M3 <- M1
M3$value1 <- rgb
And now plotting would occur as follows:
ggplot(mapping = aes(x = Var1, y = Var2)) +
# This bit is for making scales
geom_tile(data=M1, aes(fill = value1)) +
scale_fill_gradient(low = "white", high = "red") +
new_scale_fill() +
geom_tile(data=M2, aes(fill=value2)) +
scale_fill_gradient(low="yellow", high="blue") +
new_scale_fill() +
# This is the actual colours
geom_tile(data=M3, aes(fill = M3$value1)) +
scale_fill_identity()
The legends aren't 100% accurate since ggplot mixes colours in 'Lab' space, while we've mixed colours in rgb space, but you could replace the scale_fill_gradient()
with for example scale_fill_gradientn(colours = rgb(seq(0, 1, length.out = 100), 0, 0))
. Also be aware that the white-to-red scale should technically be a black-to-red scale in this example.
Upvotes: 4
Reputation: 10671
I find geom_col() + facet_grid()
to be a useful pattern to get at your goal of visualizing the multiple values from the same area together.
There is a little set-up overhead from your starting data:
names(M1) = c("Var1", "Var2", "value")
names(M2) = c("Var1", "Var2", "value")
M1$type <- "M1"
M2$type <- "M2"
M <- rbind(M1, M2)
But the plot is straight forward. You don't really need the fill scale anymore, but I like to keep for highlighting the value changes.
ggplot(M) +
geom_col(aes(type, value, fill = value)) +
facet_grid(Var2 ~ Var1) +
scale_fill_gradient(low="blue", high="yellow")
Not sure if this is palatable for you or not, but at least you get to see an alternative viz option.
Upvotes: 0