R可视化之ComplexHeatmap【三】:拆分
特别声明:本部分(系列)内容均来自顾祖光博士对ComplexHeatmap的介绍,仅为学习交流,尊重原创。
热图系列我们已经有:
今天分享:拆分热图。
为什么要进行热图拆分
很多时候我们进行热图拆分的目的无非不在于将我们想研究的feature进行“分组”,或者通过热图的拆分来突出显示不同的pattern,总之热图的拆分在有些时候能帮助我们更有结构地去了解数据,能从中更容易提取出我们想要的信息。
拆分方法
首先直接抛出控制热图拆分的参数:
row_km
row_split
column_km
column_split
我们有多种不同的拆分方法,下面依次来进行介绍。
-
基于k-均值聚类的拆分
该种拆分通过row_km
和column_km
参数来实现,这很好记,km就是k-means。至于什么是k-均值聚类,这是一种无监督学习的方法,在machine learning中应用广泛,有机会在后面会进行进一步的分享。那么怎么使用呢?
简单来说,你想把行和列分成几类,就把row_km
和column_km
指定为相应的数值即可:
mat <- matrix(rnorm(15*15), ncol = 15)
rownames(mat) <- paste('row', 1:15, sep = "_")
colnames(mat) <- paste('col', 1:15, sep = "_")
Heatmap(matrix = mat,
name = 'mat',
column_km = 2,
row_km = 3)
但是k均值聚类本身是一个随机的过程(初始类的选择),所以你把上面的代码重新运行一遍,你会发现结果就不一样了:
Heatmap(matrix = mat,
name = 'mat',
column_km = 2,
row_km = 3)
解决的办法就是进行多次聚类,取最稳定的聚类结果,例如进行100次:
Heatmap(matrix = mat,
name = 'mat',
column_km_repeats = 100,
row_km_repeats = 100,
column_km = 2,
row_km = 3)
这个时候你再重复这段代码,你就会发现结果是可重复的了,当然啦,repeat次数越多,结果越稳定。
-
基于分类变量的拆分
先来看段代码:
Heatmap(matrix = mat,
name = 'mat',
column_split = c(rep(c('A', 'B'), 7), 'A'),
row_split = c(rep(c('A', 'B'), 7), 'B'))
怎么理解这个结果呢?
注意到这部分代码:
c(rep(c('A', 'B'), 7), 'A')
# [1] "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "A"
c(rep(c('A', 'B'), 7), 'B')
# [1] "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "A" "B" "B"
实际上这段代码是给每一行(列)赋给了一个“类”,这个类就是A或者B,那么我们在绘图时就会根据这个类来进行热图的拆分,拆分后的结果或分别作为进一步聚类的单位。
除了以向量的形式来进行分类的指定,我们还可以通过因子和数据框来进行。
因子很简单:
Heatmap(matrix = mat,
name = 'mat',
column_split = factor(rep(c('A', 'B'), 7), 'A'),
row_split = factor(rep(c('A', 'B'), 7), 'B'))
而基于数据框:
Heatmap(matrix = mat,
name = 'mat',
column_split = data.frame(c(rep(c('A', 'B'), 7), 'A'),
c(rep(c('C', 'D'), each = 7), 'D')))
其实也很好理解,你自己运行一下这部分代码就理解了:
data.frame(c(rep(c('A', 'B'), 7), 'A'),
c(rep(c('C', 'D'), each = 7), 'D'))
为了更加个性化一点,我们甚至可以完全关闭默认的聚类功能,这也是通过cluster_column
来实现的:
Heatmap(matrix = mat,
name = 'mat',
column_split = data.frame(c(rep(c('A', 'B'), 7), 'A'),
c(rep(c('C', 'D'), each = 7), 'D')),
cluster_columns = F)
总结来说就是,这部分是妥妥的个性化。
此外你会注意到图上会有一些虚线,这些虚线实际上表征了热图拆分所依据的聚类层级,那么如何对其进行隐藏呢?只需要添加show_parent_dend_line = FALSE
即可。
-
基于聚类树的拆分
前面已经讲到ComplexHeatmap
会默认进行对行和列的聚类,那我们当然也能根据聚类的结果来进行热图的拆分,还记得基于k均值聚类的拆分吗?我们可以通过把row_km
和column_km
指定为相应的数值来将热图分割成几个部分,那么相似的原理,基于聚类树的拆分只需要把row_km
和column_km
换成row_split
和column_split
就行了。
Heatmap(matrix = mat,
name = 'mat',
column_split = 3,
row_split = 2)
进一步的,我们想将不同部分的热图对应的聚类树标识成不同的颜色:
library(dendextend)
dend = as.dendrogram(hclust(dist(mat)))
dend = color_branches(dend,
k = 2,
col = c('red', 'blue'))
Heatmap(mat, name = "mat", cluster_rows = dend, row_split = 2)
这里我将两部分的树分别标识成红色和蓝色。你想做类似的工作只需要借鉴这段代码就可以了。
你可能会问了,那怎么对列进行分别上色呢?很简单哦,
dist()
函数是计算矩阵行之间的距离,那我们把矩阵转置一下就实现了对列之间的距离的计算:
dend = as.dendrogram(hclust(dist(t(mat))))
dend = color_branches(dend,
k = 2,
col = c('red', 'blue'))
Heatmap(mat, name = "mat", cluster_columns = dend, column_split = 2)
显然,上面两个组合起来你就实现了同时对行和列分割的染色。
分割美化
实际上前面的上色过程本身就已经是一个美化的过程了,更进一步,我们还有其它的美化,下面进行一一介绍:
-
行列标题及行列名属性
这个部分,如果你是追更到这里的,实际上就很简单了,就是一个gpar()
函数:
Heatmap(mat,
name = 'mat',
row_split = 2,
row_title_gp = gpar(col = c("red", "blue")),
row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)),
column_split = 3,
column_title_gp = gpar(fill = c("red", "blue", "green")),
column_names_gp = gpar(col = c("green", "orange", "purple"), fontsize = c(10, 14, 8)))
这里一个问题就是,行列标题是一些数字,当然了,我们也可以个性化一下,只要将其改成一个等长的向量就行了:
Heatmap(mat,
name = 'mat',
row_split = 2,
row_title_gp = gpar(col = c("red", "blue")),
row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)),
column_split = 3,
column_title = c('one', 'two', 'three'),
column_title_gp = gpar(fill = c("red", "blue", "green")),
column_names_gp = gpar(col = c("green", "orange", "purple"), fontsize = c(10, 14, 8)))
最后一个问题,我们怎么样给列标题添加一个大标题呢?显然
column_title
参数不可行,它已经被占用了,给大家一个方法:
map <- Heatmap(mat,
name = 'mat',
row_split = 2,
row_title_gp = gpar(col = c("red", "blue")),
row_names_gp = gpar(col = c("green", "orange"), fontsize = c(10, 14)),
column_split = 3,
column_title = c('one', 'two', 'three'),
column_title_gp = gpar(fill = c("red", "blue", "green")),
column_names_gp = gpar(col = c("green", "orange", "purple"), fontsize = c(10, 14, 8)))
draw(map, column_title = 'I am a column title')
用一个draw()
函数就可以了。
-
拆分距离
距离距离,就是“gap”,这就很简单了,看看下面这段代码就清楚了:
Heatmap(matrix = mat,
name = 'mat',
column_split = 3,
row_split = 2,
column_gap = unit(3, 'mm'),
row_gap = unit(5, 'mm'))
同时也可以通过添加
border=TRUE
参数来把分割出来的方块加上黑色边框:
Heatmap(matrix = mat,
name = 'mat',
column_split = 3,
row_split = 2,
column_gap = unit(3, 'mm'),
row_gap = unit(5, 'mm'),
border = T,
border_gp = gpar(col = 'orange', lwd = 5))