2.6 热图分割
ComplexHeatmap包的一个主要优点是它支持按行和列拆分热图,以便更好地分组特性,并额外突出显示模式。
以下参数可以控制拆分:row_km, row_split, column_km, column_split。下面,我们将通过分割生成的子聚类称为 “slices”(片)。
2.6.1 用k-means聚类分割
row_km和column_km应用k-means分区。
Heatmap(mat, name = "mat", row_km = 2)
singleheatmap_49
Heatmap(mat, name = "mat", column_km = 3)
singleheatmap_50
行分割和列分割可以同时执行。
Heatmap(mat, name = "mat", row_km = 2, column_km = 3)
Heatmap()内部使用随机的起始点调用kmeans(),在某些情况下,这会导致重复运行产生不同的集群。为了消除这个问题,可以将row_km_repeats和column_km_repeats设置为大于1的数字,以便多次运行kmeans(),并使用最终一致的k-means集群。注意,k-means的最终簇数可能小于row_km和column_km中设置的簇数。
# of course, it will be a little bit slower Heatmap(mat, name = "mat", row_km = 2, row_km_repeats = 100, column_km = 3, column_km_repeats = 100)
2.6.2 通过分类变量分割
可以将row_split或column_split设置为分类向量或数据帧,其中不同的级别组合拆分热图中的行/列。
# 通过一个向量拆分 Heatmap(mat, name = "mat", row_split = rep(c("A", "B"), 9), column_split = rep(c("C", "D"), 12))
singleheatmap_52
#矩阵分割 Heatmap(mat, name = "mat", row_split = data.frame(rep(c("A", "B"), 9), rep(c("C", "D"), each = 9)))
singleheatmap_53
# 多维度分割 Heatmap(mat, name = "mat", row_split = factor(rep(c("A", "B"), 9)), column_split = factor(rep(c("C", "D"), 12)))
singleheatmap_54
row_km/column_km与row_split和column_split混合使用。
Heatmap(mat, name = "mat", row_split = rep(c("A", "B"), 9), row_km = 2)
singleheatmap_55
如果对默认的k-means分区不满意,只需将其它分区向量赋值给row_split/column_split。
pa = cluster::pam(mat, k = 3) Heatmap(mat, name = "mat", row_split = paste0("pam", pa$clustering))
singleheatmap_56
如果设置了row_order或column_order,那么在每个行/列切片中,它仍然是有序的。
Heatmap(mat, name = "mat", row_order = 18:1, row_km = 2)
singleheatmap_56
字符矩阵只能通过row_split/column_split参数进行分割。
# 通过' discrete_mat '中的第一列进行分割 Heatmap(discrete_mat, name = "mat", col = 1:4, row_split = discrete_mat[, 1])
singleheatmap_58
如果设置row_km/column_km或将row_split/column_split设置为向量或数据帧,则首先对每个切片进行分层聚类,生成k个树状图,然后根据每个切片的均值生成一个父树状图。通过在所有子树切片中添加树状图的最大高度来调整父树状图的高度,并将父树状图添加到子树状图的顶部,形成单个全局树状图。这就是为什么会在之前的热图中看到树状图中的虚线。它们被用来区分父树图和子树图,并提醒用户它们是以不同的方式计算的。这些虚线可以通过在Heatmap()中设置show_parent_dend_line = FALSE来删除,或者将其设置为全局选项:ht_opt$show_parent_dend_line = FALSE。
#隐藏虚线 Heatmap(mat, name = "mat", row_km = 2, column_km = 3, show_parent_dend_line = FALSE)
singleheatmap_59
2.6.3 根据树状图分割
分割的第二个场景是,保留全局树形图,而不是在第一个地方分割它。在本例中,可以将row_split/column_split设置为单个数字,这将在行/列树形图上应用cutree()。
在这种情况下,树状图仍然与原来的树状图相同,只是树状图叶子的位置通过切片之间的间隙稍有调整。(这里没有虚线,因为这里的树状图是作为一个完整的树状图计算的,没有父树状图或子树状图。)
Heatmap(mat, name = "mat", row_split = 2, column_split = 3)
singleheatmap_60
dend = hclust(dist(mat)) dend = color_branches(dend, k = 2) Heatmap(mat, name = "mat", cluster_rows = dend, row_split = 2)
singleheatmap_61
2.6.4 切片顺序
默认情况下,当row_split/column_split被设置为一个分类变量或row_km/column_km被设置时,会对切片的均值应用额外的聚类来显示切片级别的层次结构。在此场景下,无法精确控制切片的顺序,因为它是由切片的聚类控制的。
不过,可以将cluster_row_slices或cluster_column_slices设置为FALSE来关闭切片上的聚类来精确地控制切片的顺序。
当没有切片的聚类时,每个切片的顺序可以由row_split/column_split中每个变量的级别来控制(在这种情况下,每个变量都应该是一个因子)。如果所有变量都是字符,则默认顺序是unique(row_split)或唯一的 unique(column_split)。比较以下的热图:
Heatmap(mat, name = "mat", row_split = rep(LETTERS[1:3], 6), column_split = rep(letters[1:6], 4))
singleheatmap_62
#聚类与之前的热图相似,只是在树形图的某些节点上翻转了分支 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]))
singleheatmap_63
# 设定顺序
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)
singleheatmap_64
2.6.5 切片标题
当row_split/column_split为单个数字时,只有一个分类变量,而当row_km/column_km和/或row_split/column_split为分类变量时,会有多个分类变量。默认情况下,标题的形式是"level1,level2,..."。
ComplexHeatmap支持三种类型的模板。
第一个是sprintf(),其中%s被相应的级别替换。在下面的示例中,由于split的所有组合都是 A,C, A,D, B,C and B,D,如果row_title设置为%s|%s,那么四个行标题将是 A|C, A|D, B|C, B|D。
split = data.frame(rep(c("A", "B"), 9), rep(c("C", "D"), each = 9)) Heatmap(mat, name = "mat", row_split = split, row_title = "%s|%s")
singleheatmap_65
默认情况下,行标题是旋转的,可以设置row_title_rot = 0使其水平:
Heatmap(mat, name = "mat", row_split = split, row_title = "%s|%s", row_title_rot = 0)
当row_split/column_split设置为一个数字时,还可以使用template来调整切片的标题。
Heatmap(mat, name = "mat", row_split = 2, row_title = "cluster_%s")
singleheatmap_66
如果知道行切片的最终数量,可以直接将标题向量设置为row_title。注意,行片的数量并不总是与 nlevel_1*nlevel_2*...相同。
Heatmap(mat, name = "mat", row_split = split, row_title = c("top_slice", "middle_top_slice", "middle_bottom_slice", "bottom_slice"), row_title_rot = 0)
singleheatmap_67
2.6.6 分割图形参数
# 默认情况下,标题顶部没有空格,这里我们在顶部添加4pt。 #可以通过' ht_opt(reset = TRUE) '来重置 ht_opt$TITLE_PADDING = unit(c(4, 4), "points") 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)))
singleheatmap_68
2.6.7 切片间隙
行/列片之间的间隙空间可以通过row_gap/column_gap来控制。可以是单个单位,也可以是单位向量。
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(5, "mm"))
singleheatmap_69
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(c(2, 4), "mm"))
singleheatmap_70
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(c(2, 4), "mm"), column_km = 3, column_gap = unit(c(2, 4), "mm"))
singleheatmap_71
当通过border = TRUE添加热图边界时,每个切片的边界都会被添加。
Heatmap(mat, name = "mat", row_km = 2, column_km = 3, border = TRUE)
singleheatmap_72
2.6.8 分割热图注释
当热图被分割时,所有的热图组件都相应地被分
Heatmap(mat, name = "mat", row_km = 2, column_km = 3, top_annotation = HeatmapAnnotation(foo1 = 1:24, bar1 = anno_points(runif(24))), right_annotation = rowAnnotation(foo2 = 18:1, bar2 = anno_barplot(runif(18))) )
singleheatmap_73
2.7 自定义热图主体
热图主体可以自定义以添加更多类型的图形。默认情况下,热图主体是由带有不同填充颜色的小矩形组成的矩阵(在本文档的其他部分可能称为网格,但我们在这里称它为“单元格”)。然而,也可以在热图上添加更多的图形或符号作为额外的层。有两个参数cell_fun和layer_fun,它们都是用户定义的函数。
2.7.1 cell_fun
cell_fun 要求函数有7个参数(参数名称可以和下面不同,但顺序必须相同),它们是:
j: 矩阵的列索引。
i: 矩阵的行索引。
x:在热图主体视口测量的单元格中点的x坐标。
y: 在热图主体视口测量的单元格中点的y坐标。
width: 单元格的宽度。单位为 unit(1/ncol(sub_mat), "npc") 。其中sub_mat通过行分割和列分割的方式对应子矩阵。
height: 单元格的高度。单位 unit(1/nrow(sub_mat), "npc").
fill: 单元格的颜色。
最常见的用法是在热图上添加矩阵中的值:
small_mat = mat[1:9, 1:9] col_fun = colorRamp2(c(-2, 0, 2), c("green", "white", "red")) Heatmap(small_mat, name = "mat", col = col_fun, cell_fun = function(j, i, x, y, width, height, fill) { grid.text(sprintf("%.1f", small_mat[i, j]), x, y, gp = gpar(fontsize = 10)) })
singleheatmap_74
我们也可以选择只为正值的单元格添加文本:
Heatmap(small_mat, name = "mat", col = col_fun, cell_fun = function(j, i, x, y, width, height, fill) { if(small_mat[i, j] > 0) grid.text(sprintf("%.1f", small_mat[i, j]), x, y, gp = gpar(fontsize = 10)) })
singleheatmap_75
在下面的例子中,绘制了一个热图,它显示的相关矩阵类似于corrplot包:
cor_mat = cor(small_mat) od = hclust(dist(cor_mat))$order cor_mat = cor_mat[od, od] nm = rownames(cor_mat) col_fun = circlize::colorRamp2(c(-1, 0, 1), c("green", "white", "red")) # `col = col_fun` here is used to generate the legend Heatmap(cor_mat, name = "correlation", col = col_fun, rect_gp = gpar(type = "none"), cell_fun = function(j, i, x, y, width, height, fill) { grid.rect(x = x, y = y, width = width, height = height, gp = gpar(col = "grey", fill = NA)) if(i == j) { grid.text(nm[i], x = x, y = y) } else if(i > j) { grid.circle(x = x, y = y, r = abs(cor_mat[i, j])/2 * min(unit.c(width, height)), gp = gpar(fill = col_fun(cor_mat[i, j]), col = NA)) } else { grid.text(sprintf("%.1f", cor_mat[i, j]), x, y, gp = gpar(fontsize = 10)) } }, cluster_rows = FALSE, cluster_columns = FALSE, show_row_names = FALSE, show_column_names = FALSE)
singleheatmap_76
当设置非标准参数rect_gp = gpar(type = "none")时,热图主体上没有绘制任何内容。
最后一个例子是可视化围棋游戏。输入数据记录游戏中的动作。
str = "B[cp];W[pq];B[dc];W[qd];B[eq];W[od];B[de];W[jc];B[qk];W[qn] ;B[qh];W[ck];B[ci];W[cn];B[hc];W[je];B[jq];W[df];B[ee];W[cf] ;B[ei];W[bc];B[ce];W[be];B[bd];W[cd];B[bf];W[ad];B[bg];W[cc] ;B[eb];W[db];B[ec];W[lq];B[nq];W[jp];B[iq];W[kq];B[pp];W[op] ;B[po];W[oq];B[rp];W[ql];B[oo];W[no];B[pl];W[pm];B[np];W[qq] ;B[om];W[ol];B[pk];W[qp];B[on];W[rm];B[mo];W[nr];B[rl];W[rk] ;B[qm];W[dp];B[dq];W[ql];B[or];W[mp];B[nn];W[mq];B[qm];W[bp] ;B[co];W[ql];B[no];W[pr];B[qm];W[dd];B[pn];W[ed];B[bo];W[eg] ;B[ef];W[dg];B[ge];W[gh];B[gf];W[gg];B[ek];W[ig];B[fd];W[en] ;B[bn];W[ip];B[dm];W[ff];B[cb];W[fe];B[hp];W[ho];B[hq];W[el] ;B[dl];W[fk];B[ej];W[fp];B[go];W[hn];B[fo];W[em];B[dn];W[eo] ;B[gp];W[ib];B[gc];W[pg];B[qg];W[ng];B[qc];W[re];B[pf];W[of] ;B[rc];W[ob];B[ph];W[qo];B[rn];W[mi];B[og];W[oe];B[qe];W[rd] ;B[rf];W[pd];B[gm];W[gl];B[fm];W[fl];B[lj];W[mj];B[lk];W[ro] ;B[hl];W[hk];B[ik];W[dk];B[bi];W[di];B[dj];W[dh];B[hj];W[gj] ;B[li];W[lh];B[kh];W[lg];B[jn];W[do];B[cl];W[ij];B[gk];W[bl] ;B[cm];W[hk];B[jk];W[lo];B[hi];W[hm];B[gk];W[bm];B[cn];W[hk] ;B[il];W[cq];B[bq];W[ii];B[sm];W[jo];B[kn];W[fq];B[ep];W[cj] ;B[bk];W[er];B[cr];W[gr];B[gk];W[fj];B[ko];W[kp];B[hr];W[jr] ;B[nh];W[mh];B[mk];W[bb];B[da];W[jh];B[ic];W[id];B[hb];W[jb] ;B[oj];W[fn];B[fs];W[fr];B[gs];W[es];B[hs];W[gn];B[kr];W[is] ;B[dr];W[fi];B[bj];W[hd];B[gd];W[ln];B[lm];W[oi];B[oh];W[ni] ;B[pi];W[ki];B[kj];W[ji];B[so];W[rq];B[if];W[jf];B[hh];W[hf] ;B[he];W[ie];B[hg];W[ba];B[ca];W[sp];B[im];W[sn];B[rm];W[pe] ;B[qf];W[if];B[hk];W[nj];B[nk];W[lr];B[mn];W[af];B[ag];W[ch] ;B[bh];W[lp];B[ia];W[ja];B[ha];W[sf];B[sg];W[se];B[eh];W[fh] ;B[in];W[ih];B[ae];W[so];B[af]"
把它转换成一个矩阵:
str = gsub("\\n", "", str) step = strsplit(str, ";")[[1]] type = gsub("(B|W).*", "\\1", step) row = gsub("(B|W)\\[(.).\\]", "\\2", step) column = gsub("(B|W)\\[.(.)\\]", "\\2", step) go_mat = matrix(nrow = 19, ncol = 19) rownames(go_mat) = letters[1:19] colnames(go_mat) = letters[1:19] for(i in seq_along(row)) { go_mat[row[i], column[i]] = type[i] } go_mat[1:4, 1:4]
> go_mat[1:4, 1:4] a b c d a NA NA NA "W" b "W" "W" "W" "B" c "B" "B" "W" "W" d "B" "W" "B" "W"
黑色和白色的棋子是根据矩阵中的值摆放的:
Heatmap(go_mat, name = "go", rect_gp = gpar(type = "none"), cell_fun = function(j, i, x, y, w, h, col) { grid.rect(x, y, w, h, gp = gpar(fill = "#dcb35c", col = NA)) if(i == 1) { grid.segments(x, y-h*0.5, x, y) } else if(i == nrow(go_mat)) { grid.segments(x, y, x, y+h*0.5) } else { grid.segments(x, y-h*0.5, x, y+h*0.5) } if(j == 1) { grid.segments(x, y, x+w*0.5, y) } else if(j == ncol(go_mat)) { grid.segments(x-w*0.5, y, x, y) } else { grid.segments(x-w*0.5, y, x+w*0.5, y) } if(i %in% c(4, 10, 16) & j %in% c(4, 10, 16)) { grid.points(x, y, pch = 16, size = unit(2, "mm")) } r = min(unit.c(w, h))*0.45 if(is.na(go_mat[i, j])) { } else if(go_mat[i, j] == "W") { grid.circle(x, y, r, gp = gpar(fill = "white", col = "white")) } else if(go_mat[i, j] == "B") { grid.circle(x, y, r, gp = gpar(fill = "black", col = "black")) } }, col = c("B" = "black", "W" = "white"), show_row_names = FALSE, show_column_names = FALSE, column_title = "One famous GO game", heatmap_legend_param = list(title = "Player", at = c("B", "W"), labels = c("player1", "player2"), border = "black") )
singleheatmap_77
2.7.2 layer_fun
cell_fun按单元格添加图形,而layer_fun以块的方式添加图形。与cell_fun类似,layer_fun也需要7个参数,但它们都是向量形式:
#参数演示
Heatmap(..., layer_fun = function(j, i, x, y, w, h, fill) {...}) Heatmap(..., layer_fun = function(J, I, X, Y, W, H, F) {...})
Heatmap(small_mat, name = "mat", col = col_fun, row_km = 2, column_km = 2, layer_fun = function(j, i, x, y, w, h, fill) { ind_mat = restore_matrix(j, i, x, y) ind = unique(c(ind_mat[2, ], ind_mat[, 3])) grid.points(x[ind], y[ind], pch = 16, size = unit(4, "mm")) } )
singleheatmap_78
2.8 热图大小
width, heatmap_width, height和heatmap_height控制热图的大小。默认情况下,所有热图组件都有固定的宽度或高度,例如,行树状图的宽度是1cm。
heatmap_width和heatmap_height控制包括所有热图组件(图例除外)在内的完整热图的宽度/高度,而width和height仅控制热图主体的宽度/高度。所有这四个参数都可以设置为绝对单位。
Heatmap(mat, name = "mat", width = unit(8, "cm"), height = unit(8, "cm"))
Heatmap(mat, name = "mat", heatmap_width = unit(8, "cm"), heatmap_height = unit(8, "cm"))
2.9 热图子集
ht = Heatmap(mat, name = "mat") dim(ht)
> dim(ht) [1] 18 24
ht[1:10, 1:10]
注释也相应地进行了细分。
ht = Heatmap(mat, name = "mat", row_km = 2, column_km = 3, col = colorRamp2(c(-2, 0, 2), c("green", "white", "red")), top_annotation = HeatmapAnnotation(foo1 = 1:24, bar1 = anno_points(runif(24))), right_annotation = rowAnnotation(foo2 = 18:1, bar2 = anno_barplot(runif(18))) ) ht[1:9*2 - 1, 1:12*2] # 奇数行,偶数列
singleheatmap_80