web-dev-qa-db-fra.com

Orientation des étiquettes diagonales sur l'axe des x dans la ou les cartes thermiques

La création de heatmaps dans R a fait l'objet de nombreux articles, discussions et itérations. Mon principal problème est qu'il est difficile de combiner la flexibilité visuelle des solutions disponibles dans treillis levelplot() ou graphiques de base image(), avec un regroupement sans effort de base de heatmap(), de pheatmap's pheatmap() ou gplots 'heatmap.2(). C'est un petit détail que je veux changer - l'orientation diagonale des étiquettes sur l'axe des x. Permettez-moi de vous montrer mon point dans le code.

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

Vous pouvez facilement changer l'orientation en diagonale avec levelplot():

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

enter image description here

mais appliquer le clustering semble douloureux. Il en va de même pour d'autres options visuelles comme l'ajout de bordures autour des cellules de la carte thermique.

Maintenant, passer aux fonctions liées à heatmap(), au clustering et à tous les visuels de base est super simple - presque aucun ajustement requis:

heatmap(d)

enter image description here

et il en est de même ici:

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

enter image description here

et enfin mon préféré:

require(pheatmap)
pheatmap(d) 

enter image description here

Mais tous ceux-ci ont pas d'option pour faire pivoter les étiquettes. Le manuel de pheatmap suggère que je peux utiliser grid.text Pour orienter mes étiquettes de manière personnalisée. Quelle joie c'est - en particulier lors du regroupement et de la modification de l'ordre des étiquettes affichées. À moins que je manque quelque chose ici ...

Enfin, il y a un bon vieux image(). Je peux faire pivoter les étiquettes, en général c'est la solution la plus personnalisable, mais pas d'option de clustering.

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

Alors, que dois-je faire pour obtenir ma carte thermique idéale et rapide, avec le clustering et l'orientation et le piratage des fonctionnalités visuelles Nice? Ma meilleure offre est de modifier heatmap() ou pheatmap() d'une manière ou d'une autre, car ces deux semblent être les plus polyvalents en termes d'ajustement. Mais toutes les solutions sont les bienvenues.

26
Geek On Acid

Pour corriger pheatmap, tout ce que vous voulez vraiment faire est d'aller dans pheatmap:::draw_colnames Et de modifier quelques paramètres dans son appel à grid.text(). Voici une façon de le faire, en utilisant assignInNamespace(). (Cela peut nécessiter des ajustements supplémentaires, mais vous obtenez l'image;):

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

19
Josh O'Brien

C'est un peu plus complexe que mon commentaire ne le supposait, car heatmap décompose la région de traçage afin de dessiner les dendrogrammes et la dernière région de tracé n'est pas le image auquel vous voulez attacher les étiquettes.

Il existe cependant une solution car heatmap fournit le add.expr argument qui prend une expression à évaluer lorsque le image est dessiné. Il faut également connaître la réorganisation des étiquettes qui a lieu en raison de la commande du dendrogramme. Le dernier bit implique un peu de piratage inélégant car je vais d'abord dessiner la carte thermique pour obtenir les informations de réorganisation, puis l'utiliser pour dessiner correctement la carte thermique avec les étiquettes inclinées.

D'abord un exemple de ?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\")")

À ce stade, les étiquettes ne sont pas comme nous les voulons, mais hv contient les informations dont nous avons besoin pour réorganiser le colnames de mtcars dans son composant $colInd:

> hv$colInd
 [1]  2  9  8 11  6  5 10  7  1  4  3

Vous l'utilisez comme vous le feriez avec la sortie de order par exemple:

> colnames(mtcars)[hv$colInd]
 [1] "cyl"  "am"   "vs"   "carb" "wt"   "drat" "gear" "qsec" "mpg"  "hp"  
[11] "disp"

Maintenant, utilisez-le pour générer les étiquettes que nous voulons dans le bon ordre:

 labs <- colnames(mtcars)[hv$colInd]

Ensuite, nous rappelons heatmap mais cette fois nous spécifions labCol = "" pour supprimer l'étiquetage des variables de colonne (en utilisant des chaînes de longueur nulle). Nous utilisons également un appel à text pour dessiner les étiquettes à l'angle souhaité. L'appel à text est:

text(x = seq_along(labs), y = -0.2, srt = 45, labels = labs, xpd = TRUE)

c'est essentiellement ce que vous avez dans votre question. Jouez avec la valeur de y car vous devez l'ajuster à la longueur des chaînes afin que les étiquettes ne se chevauchent pas avec le tracé image. Nous spécifions labels = labs pour passer les étiquettes que nous voulons dessiner dans l'ordre requis. L'appel text entier est passé à add.expr sans guillemets. Voici l'appel complet:

 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))

Ce qui se traduit par:

enter image description here

8
Gavin Simpson

Je recherche également une méthode pour faire pivoter le texte de l'étiquette avec une carte thermique. Finalement, j'ai réussi à trouver cette solution:

library(gplots)

library(RColorBrewer)

heatmap.2(x,col=rev(brewer.pal(11,"Spectral")),cexRow=1,cexCol=1,margins=c(12,8),trace="none",srtCol=45)

L'argument clé est srtCol(or srtRow for row labels), qui est utilisé pour faire pivoter les étiquettes des colonnes dans les gplots.

6
Yongsheng Cheng

Une solution utilisant lattice::levelplot et latticeExtra::dendrogramGrob:

library(lattice)
library(latticeExtra)

Les données d'exemple:

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

Vous devez définir les dendrogrammes pour les lignes et les colonnes (calculées en interne dans heatmap):

dd.row <- as.dendrogram(hclust(dist(d)))
row.ord <- order.dendrogram(dd.row)

dd.col <- as.dendrogram(hclust(dist(t(d))))
col.ord <- order.dendrogram(dd.col)

et passez-les à la fonction dendrogramGrob dans l'argument legend de levelplot.

J'ai défini un nouveau thème avec des couleurs de RColorBrewer, et modifié la largeur et la couleur des bordures des cellules avec border et border.lwd:

myTheme <- custom.theme(region=brewer.pal(n=11, 'RdBu'))

levelplot(d[row.ord, col.ord],
          aspect = "fill", xlab='', ylab='',
          scales = list(x = list(rot = 45)),
          colorkey = list(space = "bottom"),
          par.settings=myTheme,
          border='black', border.lwd=.6,
          legend =
          list(right =
               list(fun = dendrogramGrob,
                    args =
                    list(x = dd.col, ord = col.ord,
                         side = "right",
                         size = 10)),
               top =
               list(fun = dendrogramGrob,
                    args =
                    list(x = dd.row,
                         side = "top"))))

levelplot with dendrogram

Vous pouvez même utiliser l'argument shrink pour mettre à l'échelle la taille des cellules proportionnellement à leur valeur.

levelplot(d[row.ord, col.ord],
          aspect = "fill", xlab='', ylab='',
          scales = list(x = list(rot = 45)),
          colorkey = list(space = "bottom"),
          par.settings=myTheme,
          border='black', border.lwd=.6,
          shrink=c(.75, .95),
          legend =
          list(right =
               list(fun = dendrogramGrob,
                    args =
                    list(x = dd.col, ord = col.ord,
                         side = "right",
                         size = 10)),
               top =
               list(fun = dendrogramGrob,
                    args =
                    list(x = dd.row,
                         side = "top"))))

levelplot with dendrogram and scaled cell sizes

5
Oscar Perpiñán

La dernière version de pheatmap (1.0.12) publiée le 2019-01-04 le prend en charge avec l'argument angle_col.

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

#update to latest version on CRAN
install.packages("pheatmap")
library("pheatmap")
pheatmap(d, angle_col = 45)

pretty heatmap with angled column labels

J'ai créé un package sur GitHub avec une version améliorée de la fonction heatmap.2. Cela prend en charge l'ajustement des étiquettes d'axe, y compris l'argument srtCol qui est passé à la fonction axis. Il peut être installé à partir de: https://github.com/TomKellyGenetics/heatmap.2x

library("devtools")
install_github("TomKellyGenetics/heatmap.2x")
library("heatmap.2x")

heatmap.2x(d, scale = "none", trace = "none", col = heat.colors, srtCol = 45)

enhanced heatmap.2x with angled column labels

Depuis version 2.12.1 de gplots , la fonction heatmap.2 Prend également en charge l'argument srtCol.

library("gplots")
heatmap.2(d, scale = "none", trace = "none", srtCol = 45)

heatmap.2 with angled column labels

3
Tom Kelly

J'ai pu prendre la réponse de Gavin Simpson et la réduire un peu pour travailler pour moi à des fins de prototypage simple, où data1 est l'objet read.csv () et data1_matrix bien sûr la matrice produite à partir de cette

heatmap(data_matrix, Rowv=NA, Colv=NA, col=heat.colors(64), scale='column', margins=c(5,10),
   labCol="", add.expr = text(x = seq_along(colnames(data1)), y=-0.2, srt=45, 
   labels=colnames(data1), xpd=TRUE))

Boom! Merci Gavin.

Un élément clé pour que cela fonctionne est la partie avant le add.expr bit où il a défini le labCol sur "", ce qui est nécessaire pour éviter que les anciennes étiquettes (droites) se chevauchent avec les nouvelles 45 degrés

2
boulder_ruby