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)))
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)
and so is here:
require(gplots) heatmap.2(d, key=F)
and finally, my favourite one:
require(pheatmap) pheatmap(d)
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)
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.
Use the setp() Function to Rotate Labels on on Seaborn Axes We will take the tick label values using the xtick() function and rotate them using the rotation parameter of the setp() function.
Rotate X-Axis Tick Labels in Matplotlib There are two ways to go about it - change it on the Figure-level using plt. xticks() or change it on an Axes-level by using tick. set_rotation() individually, or even by using ax.
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)
It is a little more complex than my comment presumed, because heatmap
breaks up the plotting region in order to draw the dendrograms and the last plot region is not the image
plot to which you want to attach the labels.
There is a solution though as heatmap
provides the add.expr
argument which takes an expression to be evaluated when the image
is drawn. One also needs to know the reordering of the labels that takes place due to the dendrogram ordering. The last bit involves a bit of an inelegant hack as I will draw the heatmap first to get the reordering information and then use that to draw the heatmap properly with the angled labels.
First an example from ?heatmap
x <- as.matrix(mtcars) rc <- rainbow(nrow(x), start = 0, end = .3) cc <- rainbow(ncol(x), start = 0, end = .3) hv <- heatmap(x, col = cm.colors(256), scale = "column", RowSideColors = rc, ColSideColors = cc, margins = c(5,10), xlab = "specification variables", ylab = "Car Models", main = "heatmap(<Mtcars data>, ..., scale = \"column\")")
At this stage, the labels aren't how we want them, but hv
contains the information we need to reorder the colnames
of mtcars
in its component $colInd
:
> hv$colInd [1] 2 9 8 11 6 5 10 7 1 4 3
You use this like you would the output from order
e.g.:
> colnames(mtcars)[hv$colInd] [1] "cyl" "am" "vs" "carb" "wt" "drat" "gear" "qsec" "mpg" "hp" [11] "disp"
Now use that to generate the labels we want in the correct order:
labs <- colnames(mtcars)[hv$colInd]
Then we re-call heatmap
but this time we specify labCol = ""
to suppress the labelling of the column variables (using zero length strings). We also use a call to text
to draw the labels at the desired angle. The call to text
is:
text(x = seq_along(labs), y = -0.2, srt = 45, labels = labs, xpd = TRUE)
which is essentially what you have in your question. Play with the value of y
as you need to adjust this to the length of the strings so as to have the labels not overlap with the image
plot. We specify labels = labs
to pass in the labels we want draw in the order required. The entire text
call is passed to add.expr
unquoted. Here is the entire call:
hv <- heatmap(x, col = cm.colors(256), scale = "column", RowSideColors = rc, ColSideColors = cc, margins = c(5,10), xlab = "specification variables", ylab = "Car Models", labCol = "", main = "heatmap(<Mtcars data>, ..., scale = \"column\")", add.expr = text(x = seq_along(labs), y = -0.2, srt = 45, labels = labs, xpd = TRUE))
Which results in:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With