I'm having trouble trying to fit heatmap with two dendrograms. Right now it looks like this (I use dashed lines just to show plots' borders):
My goal is to get something like this without using any magic constants in plot.margin
(this particular image was created using them), so I could use one script to get heatmaps with dendrograms for hundreds of data sets, not just one:
(note free space between dendrogram on the top and heatmap as well as heatmap's labels and the bottom edge of the plot).
I think that main issue is that I don't really know how to calculate heatmap's width and height properly as well as space for axes' text.
Full reproducable example code:
library(ggplot2)
library(ggdendro)
library(reshape2)
library(gridExtra)
n_samples <- 20
n_modules <- 11
my_data <- dget("http://pastebin.com/raw/bKSXY5kP")
base_size <- 9
longest_sample_name <- max(nchar(my_data$names))
heatmap_height <- round((10 / 27.1) * n_samples, digits=3)
heatmap_width <- round((10 / 27.1) * n_modules, digits=3)
title_width <- round((10 / 27.1) * 0.25 * longest_sample_name, digits=3)
heatmap <-
ggplot(my_data, aes(Module, Sample)) +
geom_tile(aes(fill = value), colour = "black", size=0.5) +
scale_fill_gradientn(colours=c("blue", "blue", "white", "red", "red"), space="Lab") +
theme_grey(base_size=base_size, base_family = "mono") +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
coord_fixed() +
theme(
panel.border = element_rect(linetype = "dashed", colour = "black", fill=NA),
plot.margin=unit(c(0, 0, 0, 0), "in"),
legend.position ="none",
axis.title = element_blank(),
axis.text.y = element_text(size=base_size * 1.2, colour="black"),
axis.text.x = element_text(size=base_size * 1.2, colour="black"))
x <- matrix(my_data$value, nrow = n_samples, ncol = n_modules + 1)
dd.col <- as.dendrogram(hclust(dist(t(x))))
dd.row <- as.dendrogram(hclust(dist(x)))
ddata_x <- dendro_data(dd.row)
ddata_y <- dendro_data(dd.col)
theme_dendro <- theme(
panel.border = element_rect(linetype = "dashed", colour = "black", fill=NA),
plot.margin=unit(c(0, 0, 0, 0), "in"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank())
dendro_horizontal <-
ggplot(segment(ddata_x)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
theme_dendro +
coord_flip()
dendro_vertical <-
ggplot(segment(ddata_y)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
theme_dendro
layout_matrix <- rbind(c(NA, 1, NA),
c(2, 2, 3))
widths <- c(title_width, heatmap_width, heatmap_width * 2 / 3)
heights <- c(heatmap_height / 2, heatmap_height)
grobs <- arrangeGrob(grobs = list(dendro_vertical, heatmap, dendro_horizontal),
layout_matrix = layout_matrix,
widths = widths,
heights = heights)
ggsave("heatmap_with_dendro.png",
plot = grobs,
width = sum(widths),
height = sum(heights),
units = "in")
This is how I calculate grobs' sizes ('magic' numbers are inherited from previous coder, didn't dare to touch them):
longest_sample_name <- max(nchar(my_data$names))
heatmap_height <- round((10 / 27.1) * n_samples, digits=3)
heatmap_width <- round((10 / 27.1) * n_modules, digits=3)
title_width <- round((10 / 27.1) * 0.25 * longest_sample_name, digits=3)
How I create heatmap:
heatmap <-
ggplot(my_data, aes(Module, Sample)) +
geom_tile(aes(fill = value), colour = "black", size=0.5) +
scale_fill_gradientn(colours=c("blue", "blue", "white", "red", "red"), space="Lab") +
theme_grey(base_size=base_size, base_family = "mono") +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
coord_fixed() +
theme(
panel.border = element_rect(linetype = "dashed", colour = "black", fill=NA),
plot.margin=unit(c(0, 0, 0, 0), "in"),
legend.position ="none",
axis.title = element_blank(),
axis.text.y = element_text(size=base_size * 1.2, colour="black"),
axis.text.x = element_text(size=base_size * 1.2, colour="black"))
And dendrograms:
x <- matrix(my_data$value, nrow = n_samples, ncol = n_modules + 1)
dd.col <- as.dendrogram(hclust(dist(t(x))))
dd.row <- as.dendrogram(hclust(dist(x)))
ddata_x <- dendro_data(dd.row)
ddata_y <- dendro_data(dd.col)
theme_dendro <- theme(
panel.border = element_rect(linetype = "dashed", colour = "black", fill=NA),
plot.margin=unit(c(0, 0, 0, 0), "in"),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank())
dendro_horizontal <-
ggplot(segment(ddata_x)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
theme_dendro +
coord_flip()
dendro_vertical <-
ggplot(segment(ddata_y)) +
geom_segment(aes(x=x, y=y, xend=xend, yend=yend)) +
scale_x_discrete(expand=c(0,0)) +
scale_y_discrete(expand=c(0,0)) +
theme_dendro
How I arrange them with gridExtra
:
layout_matrix <- rbind(c(NA, 1, NA),
c(2, 2, 3))
widths <- c(title_width, heatmap_width, heatmap_width * 2 / 3)
heights <- c(heatmap_height / 2, heatmap_height)
grobs <- arrangeGrob(grobs = list(dendro_vertical, heatmap, dendro_horizontal),
layout_matrix = layout_matrix,
widths = widths,
heights = heights)
And eventually save image:
ggsave("heatmap_with_dendro.png",
plot = grobs,
width = sum(widths),
height = sum(heights),
units = "in")