ComplexHeatmap 之 A Single Heatmap 续(一)
这节继续 A Single Heatmap 内容部分。
4、设置行和列的顺序
聚类用于调整热图的行序和列序,但仍然可以通过 row_order
和 column_order
手动设置顺序。如果例如 row_order 设置,行聚类默认关闭:
Heatmap(mat, name = "mat",
row_order = order(as.numeric(gsub("row", "", rownames(mat)))),
column_order = order(as.numeric(gsub("column", "", colnames(mat)))),
column_title = "reorder matrix")
顺序可以是 字符向量 ,如果它们只是矩阵的行名或列名:
Heatmap(mat, name = "mat", row_order = sort(rownames(mat)),
column_order = sort(colnames(mat)),
column_title = "reorder matrix by row/column names")
5、维度名称
默认情况下,行名称 和 列名称 绘制在热图的右侧和底部。维度名称
的可见性和图形参数可以设置如下:
Heatmap(mat, name = "mat",
row_names_side = "left",
row_dend_side = "right",
column_names_side = "top",
column_dend_side = "bottom")
不显示:
Heatmap(mat, name = "mat", show_row_names = FALSE)
设置字体样式:
Heatmap(mat, name = "mat", row_names_gp = gpar(fontsize = 20))
设置颜色样式:
Heatmap(mat, name = "mat", row_names_gp = gpar(col = c(rep("red", 10), rep("blue", 8))))
字体居中显示:
Heatmap(mat, name = "mat", row_names_centered = TRUE, column_names_centered = TRUE)
使用 column_names_rot
参数旋转字体标签:
Heatmap(mat, name = "mat", column_names_rot = 45)
Heatmap(mat, name = "mat", column_names_rot = 45, column_names_side = "top",
column_dend_side = "bottom")
如果你的 行名
或 列名太长
,可以使用 row_names_max_width
或 column_names_max_height
来设置它们的最大空间。行名和列名的默认最大空间均为 6 厘米 。在以下代码中,max_text_width()
是一个辅助函数,用于从文本向量快速计算最大宽度:
mat2 = mat
rownames(mat2)[1] = paste(c(letters, LETTERS), collapse = "")
Heatmap(mat2, name = "mat", row_title = "default row_names_max_width")
Heatmap(mat2, name = "mat", row_title = "row_names_max_width as length of a*",
row_names_max_width = max_text_width(
rownames(mat2),
gp = gpar(fontsize = 12)
))
除了直接使用矩阵中的行/列名称,您还可以提供另一个对应于行或列的 字符向量 ,并通过 row_labels
或 column_labels
进行设置。这很有用,因为你无需更改矩阵的维度名称即可更改热图上的标签,而可以直接提供新标签。
row_labels
或 column_labels
允许 重复的标签 ,而矩阵中不允许重复的行名或列名。
# use a named vector to make sure the correspondance between
# row names and row labels is correct
row_labels = structure(paste0(letters[1:24], 1:24), names = paste0("row", 1:24))
column_labels = structure(paste0(LETTERS[1:24], 1:24), names = paste0("column", 1:24))
row_labels
## row1 row2 row3 row4 row5 row6 row7 row8 row9 row10 row11 row12 row13
## "a1" "b2" "c3" "d4" "e5" "f6" "g7" "h8" "i9" "j10" "k11" "l12" "m13"
## row14 row15 row16 row17 row18 row19 row20 row21 row22 row23 row24
## "n14" "o15" "p16" "q17" "r18" "s19" "t20" "u21" "v22" "w23" "x24"
Heatmap(mat, name = "mat", row_labels = row_labels[rownames(mat)],
column_labels = column_labels[colnames(mat)])
还可以添加特殊数学字符:
Heatmap(mat, name = "mat", row_labels = expression(alpha, beta, gamma, delta, epsilon,
zeta, eta, theta, iota, kappa, lambda, mu, nu, xi, omicron, pi, rho, sigma))
6、热图分割
ComplexHeatmap 包的一个主要优点是它支持按行和列拆分热图以更好地对特征进行分组并另外突出表达模式。
以下参数控制拆分:row_km
、row_split
、column_km
、column_split
。下面,我们将分割产生的子簇称为 “切片”。
6.1、通过 k-means clustering 分割
行分割:
Heatmap(mat, name = "mat", row_km = 2)
列分割:
Heatmap(mat, name = "mat", column_km = 3)
同时分割:
Heatmap(mat, name = "mat", row_km = 2, column_km = 3)
6.2、通过分类变量分割
向量:
# split by a vector
Heatmap(mat, name = "mat",
row_split = rep(c("A", "B"), 9), column_split = rep(c("C", "D"), 12))
数据框:
# split by a data frame
Heatmap(mat, name = "mat",
row_split = data.frame(rep(c("A", "B"), 9), rep(c("C", "D"), each = 9)))
行列一起分割:
# split on both dimensions
Heatmap(mat, name = "mat", row_split = factor(rep(c("A", "B"), 9)),
column_split = factor(rep(c("C", "D"), 12)))
实际上,k-means 聚类只是生成一个聚类类向量并附加到 row_split
或 column_split
。row_km/column_km 可以与 row_split 和 column_split 混合使用:
Heatmap(mat, name = "mat", row_split = rep(c("A", "B"), 9), row_km = 2)
等价于:
# code only for demonstration
cl = kmeans(mat, centers = 2)$cluster
# classes from k-means are always put as the first column in `row_split`
Heatmap(mat, name = "mat", row_split = cbind(cl, rep(c("A", "B"), 9)))
如果对默认的 k-means 分割不满意,只需将分割向量分配给 row_split/column_split 即可轻松使用其他分割方法:
pa = cluster::pam(mat, k = 3)
Heatmap(mat, name = "mat", row_split = paste0("pam", pa$clustering))
如果设置了 row_order 或 column_order,则在每个行/列切片中,仍然是有序的:
# remember when `row_order` is set, row clustering is turned off
Heatmap(mat, name = "mat", row_order = 18:1, row_km = 2)
字符矩阵只能通过 row_split/column_split 参数进行分割:
# split by the first column in `discrete_mat`
Heatmap(discrete_mat, name = "mat", col = 1:4, row_split = discrete_mat[, 1])
如果设置 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)
6.3、通过聚类树分割
拆分的第二种情况是你可能仍然希望保留从完整矩阵生成的全局树状图,而不是首先将其拆分。在这种情况下,row_split
/column_split
可以设置为单个数字,它将在行/列树状图上应用 cutree()。当 cluster_rows/cluster_columns 设置为 TRUE 或分配有 hclust/dendrogram 对象时,这才会起作用。
对于这种情况,树状图仍然和原来的一样,只是树状图叶子的位置被切片之间的间隙稍微调整了一下。(没有虚线,因为这里树状图被计算为一个完整的树状图,并且没有父树状图或子树状图):
Heatmap(mat, name = "mat", row_split = 2, column_split = 3)
dend = as.dendrogram(hclust(dist(mat)))
dend = color_branches(dend, k = 2)
Heatmap(mat, name = "mat", cluster_rows = dend, row_split = 2)
如果你想结合 cutree()
和其他分类变量,你需要首先从 cutree() 生成分类,附加到例如 row_split 作为数据框,然后将其发送到 row_split 参数:
# code only for demonstration
split = data.frame(cutree(hclust(dist(mat)), k = 2), rep(c("A", "B"), 9))
Heatmap(mat, name = "mat", row_split = split)
6.4、切片排序
当 row_split/column_split
设置为 分类变量 (向量或数据框)或设置 row_km/column_km
时,默认情况下,会对切片的均值应用额外的聚类以显示切片级别的层次结构。在这种情况下,无法精确控制切片的顺序,因为它是由切片的聚类控制的。
不过,您可以将 cluster_row_slices
或 cluster_column_slices
设置为 FALSE 以关闭切片上的聚类,现在可以精确控制切片的顺序。
当没有切片聚类时,可以通过 row_split/column_split
中每个变量的 levels 来控制每个切片的顺序(在这种情况下,每个变量都应该是一个因子)。如果所有变量都是字符,则默认顺序是唯一的(row_split) 或唯一的(column_split)。比较以下热图:
Heatmap(mat, name = "mat",
row_split = rep(LETTERS[1:3], 6),
column_split = rep(letters[1:6], 4))
# clustering is similar as previous heatmap with branches in some nodes in the dendrogram flipped
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]))
# now the order is exactly what we set
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)
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 和 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")
对于 sprintf() 模板,你只能将 A、B、C、D 级别放在标题中,并且 C、D 始终位于 A、B 之后(即始终是 A、C 和 A、D)。但是,在制作热图时,你可能希望放置更有意义的文本而不是内部级别。一旦知道如何将文本与级别对应起来,就可以通过以下两种模板方法来添加它。
在以下两个模板方法中,特殊标记用于标记可执行的 R 代码(称为变量插值,其中提取并执行代码并将返回值放回字符串)。有两种类型的模板标记 @{}
和 {}
。第一个来自 GetoptLong 包,在您安装 ComplexHeatmap 包时应该已经安装,第二个来自你需要先安装 glue 包。
当你使用后两个模板时,应该使用一个内部变量 x 。x 只是一个包含当前类别级别的简单向量(例如 c("A", "C")):
# We only run the code for the first heatmap
map = c("A" = "aaa", "B" = "bbb", "C" = "333", "D" = "444")
Heatmap(mat, name = "mat", row_split = split, row_title = "@{map[ x[1] ]}|@{map[ x[2] ]}")
Heatmap(mat, name = "mat", row_split = split, row_title = "{map[ x[1] ]}|{map[ x[2] ]}")
行标题默认旋转,您可以设置 row_title_rot = 0
使其水平:
Heatmap(mat, name = "mat", row_split = split, row_title = "%s|%s", row_title_rot = 0)
当 row_split/column_split 设置为数字时,还可以使用模板来调整切片的标题:
Heatmap(mat, name = "mat", row_split = 2, row_title = "cluster_%s")
如果你知道最后的行切片数,你可以直接设置一个 title 的向量给 row_title。请注意行切片的数量并不总是与 level_1level_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)
如果将 row_title 的长度指定为 单个字符串,则所有切片只有单个标题:
Heatmap(mat, name = "mat", row_split = split, row_title = "there are four slices")
如果您仍然想要每个切片的标题,但也需要全局标题,可以执行以下操作:
ht = Heatmap(mat, name = "mat", row_split = split, row_title = "%s|%s")
# This row_title is actually a heatmap-list-level row title
draw(ht, row_title = "I am a row title")
如果 row_title 设置为 NULL,则不绘制行标题:
Heatmap(mat, name = "mat", row_split = split, row_title = NULL)
6.6、分割的图形参数
在 行/列
上应用拆分时,可以将 行/列
标题和 行/列
名称的图形参数指定为与切片数相同的长度:
# by defalt, there no space on the top of the title, here we add 4pt to the top.
# it can be reset by `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)))
6.7、切片的间距
行/列 切片之间的间隙空间可以由 row_gap/column_gap
控制。该值可以是单个单位或单位向量:
单个单位:
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(5, "mm"))
多个向量:
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(c(2, 4), "mm"))
行列分割:
Heatmap(mat, name = "mat", row_km = 3, row_gap = unit(c(2, 4), "mm"),
column_km = 3, column_gap = unit(c(2, 4), "mm"))
添加边框:
Heatmap(mat, name = "mat", row_km = 2, column_km = 3, border = TRUE)
间距设为 0,添加边框:
Heatmap(mat, name = "mat", row_km = 2, column_km = 3,
row_gap = unit(0, "mm"), column_gap = unit(0, "mm"), border = TRUE)
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)))
)
收官!
代码 我上传到 QQ 群 老俊俊生信交流群
文件夹里。欢迎加入。加我微信我也拉你进 微信群聊 老俊俊生信交流群
哦。
群二维码:
老俊俊微信:
知识星球:
所以今天你学习了吗?
欢迎小伙伴留言评论!
今天的分享就到这里了,敬请期待下一篇!
最后欢迎大家分享转发,您的点赞是对我的鼓励和肯定!
如果觉得对您帮助很大,赏杯快乐水喝喝吧!
推 荐 阅 读