热图(Heatmap)是矩阵类型数据最为常用的可视化方法,在生物学领域常用于各种组学数据的可视化,例如基因表达量数据、物种丰度分布、表观遗传信号数据等,我们前面也介绍过普通的热图绘制方法,例如pheatmap以及环形热图gheatmap等。但是,现如今多组学数据不断涌现, 现有的R语言包例如 gplots、pheatmap、ggplot2绘制热图的功能较为单一,已无法满足大家从多个维度、对多类型数据更加充分可视化的需求,例如下面的图。ComplexHeatmap 由德国国家肿瘤疾病中心顾顾祖光博士开发,该R包正如其名一样复杂,但是其绘制热图功能却堪称全面。所以我们也陆续学习一下这个包的功能和应用。
=========安装=======
library(devtools)
# 检查热图包,没有则通过github安装最新版
if (!require("ComplexHeatmap"))
install_github("jokergoo/ComplexHeatmap")
# ComplexHeatmap依赖关系
if (!require("circlize"))
install_github("jokergoo/circlize")
# 用于绘制矩阵列方向上的层次聚类结果
if (!require("dendextend"))
install.packages('dendextend')
# 加载包
library(ComplexHeatmap)
library(circlize)
library(dendextend)
=========测试==========
生成一组测试数据:
nr1 = 4; nr2 = 8; nr3 = 6; nr = nr1 + nr2 + nr3
nc1 = 6; nc2 = 8; nc3 = 10; nc = nc1 + nc2 + nc3
mat = cbind(rbind(matrix(rnorm(nr1*nc1, mean = 1, sd = 0.5), nr = nr1),
matrix(rnorm(nr2*nc1, mean = 0, sd = 0.5), nr = nr2),
matrix(rnorm(nr3*nc1, mean = 0, sd = 0.5), nr = nr3)),
rbind(matrix(rnorm(nr1*nc2, mean = 0, sd = 0.5), nr = nr1),
matrix(rnorm(nr2*nc2, mean = 1, sd = 0.5), nr = nr2),
matrix(rnorm(nr3*nc2, mean = 0, sd = 0.5), nr = nr3)),
rbind(matrix(rnorm(nr1*nc3, mean = 0.5, sd = 0.5), nr = nr1),
matrix(rnorm(nr2*nc3, mean = 0.5, sd = 0.5), nr = nr2),
matrix(rnorm(nr3*nc3, mean = 1, sd = 0.5), nr = nr3))
)
mat = mat[sample(nr, nr), sample(nc, nc)] # 打乱数据
rownames(mat) = paste0("row", seq_len(nr)) # 行命名
colnames(mat) = paste0("column", seq_len(nc)) # 列命名
这样子一组简单的测试数据就准备好了:
Heatmap(mat) #生成一个最基本的heatmap
从默认结果来看:这个包中最重要的函数Heatmap()绘制一张简单的热图,默认参数下,会生成图例、行列名并对行列分别聚类,聚类方法用complete层次聚类方法。
下面我们开始美化,比如控制着色。
比如我们相对列的聚类树按cluster着色,首先需要分成不同的cluster。
# 层次聚类
column_dend = as.dendrogram(hclust(dist(t(mat)))) #获得列进化树信息
# 分支着色
column_dend = color_branches(column_dend, k = 3) #分成3个分支,进行不同颜色着色
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3 # 列分割数
)
依据前面我们的列指定信息,可以看出列的聚类树分成了3个颜色。column_split则把整个图按列分成了3个部分。
对于长度的控制,由于ComplexHeatmap基于grid绘图框架,所以凡是量化长度的参数都需要用unit()函数制定,例如下面的row_dend_width,可以看出在对行聚类树的宽度的控制。
p1<-Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
#column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
)
p1
p2<-Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
#column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(10, "cm"), # 行聚类树的宽度
)
p2
另外,ComplexHeatmap包的一个主要优点是它支持按行和列拆分热图以更好地对特征进行分组,便于突出显示各组的信息。以下参数控制拆分:row_km, row_split, column_km, column_split。比如上面我们用column_split就是把列分成了3个部分。row_km和column_km是按照 k-means方法分割,就如我们上述指定的column方法是按距离分割,这是两个不一样的分割算法。而row_split或者column_split可以设置为分类向量或数据框,从分割算法来看是和默认的距离算法是一致的。
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
row_km = 2 # 行k-means聚类的类别数
)
下图就是把行按kmeans分成了2类。
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 2, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
row_split = rep(c("A", "B"), 9), # 行分割后的命名
#row_km = 2 # 行k-means聚类的类别数
)
这个是用row_split的方式,rep(c("A", "B"), 9)相当于生成了ABAB...这样子的数据框,然后A的列放在一起,B的列放在一起。从而把row分成了2个部分。
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
#row_split = rep(c("A", "B"), 9), # 行分割后的命名
#row_km = 2 # 行k-means聚类的类别数
row_split = data.frame(rep(c("A", "B"), 9), rep(c("C", "D"), each = 9))
)
这样子就是生成了一个更复杂的数据框,生成了两列,但是unique的数据的话有A.C A.D B.C B.D所以按unique的属性分成了4不部分。
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
row_split = rep(c("A", "B"), 9), # 行分割后的命名
row_km = 2 # 行k-means聚类的类别数
)
通过两种分割方法一起用,就相当于有了2个属性,一个是AB属性框的分割,一个是kmeans的分割。所以2*2就是4个属性,分成了4个部分。
Heatmap(mat, name = "mat",
cluster_columns = column_dend, # 列聚类方法指定为前面设置的dendrogram对象
column_title = "(A) A heatmap with column annotations", # 标题
show_column_names = FALSE, # 不显示列名
column_split = 3, # 列分割数
row_dend_width = unit(2, "cm"), # 行聚类树的宽度
row_split = rep(c("A", "B"), 9), # 行分割后的命名
row_km = 2, # 行k-means聚类的类别数
row_order = 1:18
)
如果设置了row_order或column_order,在每一行/列分割切片中,它仍然是有序的。
还可以对于聚类树显示的顺序进行控制。当row_split/column_split或row_km/column_km设置为分类变量(向量或数据框)时,默认情况下,会对切片的均值应用额外的聚类以显示切片级别的层次结构。在这种情况下,您无法精确控制切片的顺序,因为它是由切片的聚类控制的。
不过,你可以设置cluster_row_slices或cluster_column_slices为 FALSE进而关闭聚类上切片,现在你可以精确地控制切片的顺序。
当没有切片聚类时,每个切片的顺序可以由levels中row_split/column_split 的每个变量的控制(在这种情况下,每个变量都应该是一个因子)。如果所有变量都是字符,则默认顺序为unique(row_split)orunique(column_split) 。
比如下面的结果:
Heatmap(mat, name = "mat",
row_split = factor(rep(LETTERS[1:3], 6), levels = LETTERS[3:1]),
column_split = factor(rep(letters[1:6], 4), levels = letters[6:1]),
cluster_row_slices = FALSE,
cluster_column_slices = FALSE)
我们通过设置cluster_row_slices或cluster_column_slices为 FALSE进而关闭聚类上切片。所以就严格按照我们设置的因子顺序来显示了。但是由于高一层级的聚类树打乱了,所以就没有办法显示了。
Heatmap(mat, name = "mat",
row_split = factor(rep(LETTERS[1:3], 6), levels = LETTERS[3:1]),
column_split = factor(rep(letters[1:6], 4), levels = letters[6:1]),
cluster_row_slices = FALSE,
cluster_column_slices = FALSE,
row_title_rot = 0)
#行标题默认旋转,您可以设置row_title_rot = 0为水平,这个类似于gggplot里面对于文字角度的控制。
还可以通过row_title,column_title指定标题。当指定为单个字符串长度,则它就像所有切片的单个标题。当指定向量的时候,则是每个切片一个标题。
Heatmap(mat, name = "mat",
row_split = factor(rep(LETTERS[1:3], 6), levels = LETTERS[3:1]),
column_split = factor(rep(letters[1:6], 4), levels = letters[6:1]),
cluster_row_slices = FALSE,
cluster_column_slices = FALSE,
row_title_rot = 0,
#row_title = "(A) A heatmap with column annotations",
row_title = c("t1","t2","t3")
)
在行/列上应用拆分时,可以将行/列标题和行/列名称的图形参数指定为与切片数相同的长度。
比如row_title_gp 和column_title_gp用来修改title的一些参数。row_names_gp 和column_names_gp用来修改names的一些图形参数。
Heatmap(mat, name = "mat",
row_km = 2, row_title_gp = gpar(col = c("red", "blue"), font = 1:2),
row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)),
column_km = 3, column_title_gp = gpar(fill = c("red", "blue", "green"), font = 1:3),
column_names_gp = gpar(col = c("green", "orange", "purple"), fontsize = c(10, 14, 8)))
行/列切片之间的间隙空间可以通过row_gap/ column_gap控制。该值可以是单个单位或单位向量。
可以设置一致的缝隙:row_gap=unit(5,"mm"),也可以分别设置,例如:row_gap=unit(c(2,4),"mm")
Heatmap(mat, name = "mat",
row_km = 2, row_title_gp = gpar(col = c("red", "blue"), font = 1:2),
row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)),
column_km = 3, column_title_gp = gpar(fill = c("red", "blue", "green"), font = 1:3),
column_names_gp = gpar(col = c("green", "orange", "purple"), fontsize = c(10, 14, 8)),
row_gap = unit(5, "mm")
)
通过border=TRUE来设置边框。
这次是complexheatmap基本热图的一些画法,下面的帖子我们学习如果添加各种annotation。
通过设置添加热图边框时,会添加border = TRUE每个切片的边框。
通过设置添加热图边框时,会添加border = TRUE每个切片的边框。