Geek On Acid Geek On Acid - 28 days ago 20
R Question

Diagonal labels orientation on x-axis in heatmap(s)

Creating heatmaps in R has been a topic of many posts, discussions and iterations. My main problem is that it's tricky to combine visual flexibility of solutions available in lattice

levelplot()
or basic graphics
image()
, with effortless clustering of basic's
heatmap()
, pheatmap's
pheatmap()
or gplots'
heatmap.2()
. It's a tiny detail I want to change - diagonal orientation of labels on x-axis. Let me show you my point in the code.

#example data
d <- matrix(rnorm(25), 5, 5)
colnames(d) = paste("bip", 1:5, sep = "")
rownames(d) = paste("blob", 1:5, sep = "")


You can change orientation to diagonal easily with
levelplot()
:

require(lattice)
levelplot(d, scale=list(x=list(rot=45)))


enter image description here

but applying the clustering seems pain. So does other visual options like adding borders around heatmap cells.

Now, shifting to actual
heatmap()
related functions, clustering and all basic visuals are super-simple - almost no adjustment required:

heatmap(d)


enter image description here

and so is here:

require(gplots)
heatmap.2(d, key=F)


enter image description here

and finally, my favourite one:

require(pheatmap)
pheatmap(d)


enter image description here

But all of those have no option to rotate the labels. Manual for
pheatmap
suggests that I can use
grid.text
to custom-orient my labels. What a joy it is - especially when clustering and changing the ordering of displayed labels. Unless I'm missing something here...

Finally, there is an old good
image()
. I can rotate labels, in general it' most customizable solution, but no clustering option.

image(1:nrow(d),1:ncol(d), d, axes=F, ylab="", xlab="")
text(1:ncol(d), 0, srt = 45, labels = rownames(d), xpd = TRUE)
axis(1, label=F)
axis(2, 1:nrow(d), colnames(d), las=1)


enter image description here

So what should I do to get my ideal, quick heatmap, with clustering and orientation and nice visual features hacking? My best bid is changing
heatmap()
or
pheatmap()
somehow because those two seem to be most versatile in adjustment. But any solutions welcome.

Answer

To fix pheatmap, all you really want to do is to go into pheatmap:::draw_colnames and tweak a couple of settings in its call to grid.text(). Here's one way to do that, using assignInNamespace(). (It may need additional adjustments, but you get the picture ;):

library(grid)     ## Need to attach (and not just load) grid package
library(pheatmap)

## Your data
d <- matrix(rnorm(25), 5, 5)
colnames(d) = paste("bip", 1:5, sep = "")
rownames(d) = paste("blob", 1:5, sep = "")

## Edit body of pheatmap:::draw_colnames, customizing it to your liking
draw_colnames_45 <- function (coln, ...) {
    m = length(coln)
    x = (1:m)/m - 1/2/m
    grid.text(coln, x = x, y = unit(0.96, "npc"), vjust = .5, 
        hjust = 1, rot = 45, gp = gpar(...)) ## Was 'hjust=0' and 'rot=270'
}

## For pheatmap_1.0.8 and later:
draw_colnames_45 <- function (coln, gaps, ...) {
    coord = pheatmap:::find_coordinates(length(coln), gaps)
    x = coord$coord - 0.5 * coord$size
    res = textGrob(coln, x = x, y = unit(1, "npc") - unit(3,"bigpts"), vjust = 0.5, hjust = 1, rot = 45, gp = gpar(...))
    return(res)}

## 'Overwrite' default draw_colnames with your own version 
assignInNamespace(x="draw_colnames", value="draw_colnames_45",
ns=asNamespace("pheatmap"))

## Try it out
pheatmap(d)

enter image description here