R 数据可视化 —— 聚类热图 ComplexHeatmap(三
前言
我们继续介绍 ComplexHeatmap
,今天要介绍的内容是热图列表。
ComplexHeatmap
包的一个重要的功能是,能够在水平或竖直方向连接多个热图和注释,以图形化的方式展示各信息之间的关联
通常水平方向的连接比较常用,我们介绍的重点也是水平连接,竖直的连接原理基本是一致的。
在进行水平连接时,热图和注释的行数要相同
例如,我们要绘制三个热图列表,数据如下
set.seed(123)
mat1 <- matrix(rnorm(80, 2), 8, 10)
mat1 <- rbind(mat1, matrix(rnorm(40, -2), 4, 10))
rownames(mat1) <- paste0("R", 1:12)
colnames(mat1) <- paste0("C", 1:10)
mat2 <- matrix(runif(60, max = 3, min = 1), 6, 10)
mat2 <- rbind(mat2, matrix(runif(60, max = 2, min = 0), 6, 10))
rownames(mat2) <- paste0("R", 1:12)
colnames(mat2) <- paste0("C", 1:10)
le <- sample(letters[1:3], 12, replace = TRUE)
names(le) <- paste0("R", 1:12)
使用 +
以水平方向连接热图
ht1 <- Heatmap(mat1, name = "rnorm")
ht2 <- Heatmap(mat2, name = "runif")
ht3 <- Heatmap(le, name = "letters")
ht1 + ht2 + ht3
默认情况下,第二幅热图的行树状图会删除,并且其行顺序与第一幅热图一样,同时第一、二幅图的行名也被删除
连接操作会返回 HeatmapList
对象,直接打印 ht_list
会调用 draw
函数
> ht_list <- ht1 + ht2 + ht3
> class(ht_list)
[1] "HeatmapList"
attr(,"package")
[1] "ComplexHeatmap"
可以连接任意数量的热图,或者往热图列表中添加热图
ht1 + ht_list
ht_list + ht1
ht_list + ht_list
还可以将 NULL
添加到热图列表,适用于在循环外部设置一个空热图列表
ht_list <- NULL
for(s in sth) {
ht_list = ht_list + Heatmap(...)
}
Heatmap(...) + NULL
将会返回一个热图列表对象
1. 标题
单个热图的标题,可以在 Heatmap
函数内置指定,而热图列表的标题,需要在 draw
中进行设置
例如
# 首先,我们分别为三个热图设置颜色
col_rnorm <- colorRamp2(c(-3, 0, 3), c("#f46d43", "#ffffbf", "#3288bd"))
col_runif <- colorRamp2(c(0, 1.5, 3), c("#de77ae", "#f7f7f7", "#7fbc41"))
col_letters <- c("a" = "#33a02c", "b" = "#fb9a99", "c" = "#e31a1c")
# 分别在 Heatmap 函数内设置单个热图标题
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_title = "Heatmap 1",
column_title = "Heatmap 1")
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
row_title = "Heatmap 2",
column_title = "Heatmap 2")
ht3 <- Heatmap(le, name = "letters",
col = col_letters)
ht_list <- ht1 + ht2 + ht3
# 设置热图列表的标题
draw(
ht_list,
row_title = "Three heatmaps, row title",
row_title_gp = gpar(col = "red"),
column_title = "Three heatmaps, column title",
column_title_gp = gpar(fontsize = 16)
)
我们可以看到,第二张热图的行名并未显示
2. 设置大小
我们在之前的文章中已经介绍过如何设置热图的大小,即 width
、height
控制单个热图主体的宽度和高度,heatmap_width
和 heatmap_height
控制所有热图(包括注释)的总宽度和高度
可以指定一个或多个热图的宽度
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
width = unit(4, "cm")
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters,
width = unit(5, "mm")
)
ht1 + ht2 + ht3
如果 width
设置为数值,将会转换为 null
单位
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
width = 6
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
width = 4
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters,
width = 1
)
ht1 + ht2 + ht3
3. 设置间隔
ht_gap
参数用于控制两个热图之间的间隔,可以是一个单位值或向量单位值
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters
)
ht_list <- ht1 + ht2 + ht3
draw(ht_list,
ht_gap = unit(1, "cm"))
draw(ht_list,
ht_gap = unit(c(3, 10), "mm"))
4. 自动调整热图主体
在热图列表中始终有一个主热图,用于控制全局的行顺序,其他所有的热图会根据主热图的配置自动进行调整,调整方式为:
- 不对行进行聚类,按照主热图的行顺序排列
- 删除行标题
- 如果主热图进行了分割,也会进行分割
- 主热图的高度就是所有热图的高度
默认第一张热图为主热图,例如
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters
)
ht2 + ht1 + ht3
现在,ht2
为主热图,所以 ht1
的 row_km
被忽略了
可以在 draw
函数中使用 main_heatmap
参数指定主热图,可以是热图名或数值索引
ht_list <- ht2 + ht1 + ht3
draw(ht_list, main_heatmap = "rnorm")
# draw(ht_list, main_heatmap = 2)
默认只在主热图的边上绘制其行树状图和行名,可以使用 row_dend_side
和 row_sub_title_side
参数来设置其放置位置,例如
ht_list <- ht2 + ht1 + ht3
draw(ht_list, main_heatmap = "rnorm",
row_dend_side = "right",
row_sub_title_side = "left")
如果主热图不聚类,其他热图还是不会聚类
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2, cluster_rows = FALSE
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters
)
ht1 + ht2 + ht3
如果要显示所有行名,可以设置 auto_adjust = FALSE
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters
)
ht_list <- ht1 + ht2 + ht3
draw(ht_list, main_heatmap = "rnorm",
auto_adjust = FALSE)
5. 使用 draw 函数控制主热图
主热图的设置可以放在 Heatmap()
函数内部,但是在 draw()
函数内设置会更方便些,避免更改主热图时,需要重新设置参数,且 draw
的优先级更高
draw
函数中控制主热图行顺序的参数有:
cluster_rows
clustering_distance_rows
clustering_method_rows
row_dend_width
show_row_dend
row_dend_reorder
row_dend_gp
row_order
控制行切片的参数
row_gap
row_km
row_km_repeats
row_split
控制高度
height
heatmap_height
例如
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2, cluster_rows = FALSE)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif)
ht3 <- Heatmap(
le, name = "letters", col = col_letters)
ht_list <- ht1 + ht2 + ht3
draw(
ht_list, row_km = 1, row_split = le,
cluster_rows = TRUE)
Heatmap
函数内的 row_km = 2, cluster_rows = FALSE
会被 draw
覆盖
6. 注释的调整
如果热图列表中某些热图带有注释,并且,不同热图的注释的高度是不同的。
热图注释及树状图的高度会自动进行调整,但是简单注释的大小通常不会调整,除非设置 anno_simple_size
参数,或者设置全局变量 ht_opt$anno_simple_size
例如
ht1 = Heatmap(
mat1, name = "rnorm", col = col_rnorm,
top_annotation = HeatmapAnnotation(
foo1 = 1:10,
annotation_name_side = "left")
)
ht2 = Heatmap(
mat2, name = "runif", col = col_runif)
ht3 = Heatmap(
le, name = "letters", col = col_letters)
ht1 + ht2 + ht3
第二个热图的树状图变高了
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
top_annotation = HeatmapAnnotation(
foo1 = 1:10, bar1 = anno_points(1:10),
annotation_name_side = "left")
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
top_annotation = HeatmapAnnotation(
bar2 = anno_barplot(1:10)))
ht3 <- Heatmap(
le, name = "letters", col = col_letters)
ht_list <- ht1 + ht2 + ht3
draw(ht_list, ht_gap = unit(c(6, 2), "mm"))
在这个例子中,两幅热图都有注释,由于简单注视的大小不变,所有调整了第二幅热图的复杂热图的高度,使两幅热图的注释高度一致
如果第一幅热图只有简单注释,则会调整树状图的高度
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
top_annotation = HeatmapAnnotation(
foo1 = 1:10,
annotation_name_side = "left")
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
top_annotation = HeatmapAnnotation(
bar2 = anno_barplot(1:10)))
ht3 <- Heatmap(
le, name = "letters", col = col_letters)
ht_list <- ht1 + ht2 + ht3
draw(ht_list, ht_gap = unit(c(6, 2), "mm"))
如果两幅热图都只包含简单注释,但是注释的数量不同,则还是会调整树状图的高度
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
top_annotation = HeatmapAnnotation(
foo1 = 1:10,
annotation_name_side = "left")
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif,
top_annotation = HeatmapAnnotation(
bar2 = cbind(
b1 = 1:10, b2 = 11:20, b3 = 21:30))
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters)
ht_list <- ht1 + ht2 + ht3
draw(ht_list, ht_gap = unit(c(6, 2), "mm"))
如果你想让简单注释的大小也自动调整,可以在 HeatmapAnnotation()
函数中设置 simple_anno_size_adjust = TRUE
如果第一幅热图的注释在底部,第二幅热图没有注释,则第二幅热图的列名直接放置在热图主体下方
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
bottom_annotation = HeatmapAnnotation(
foo1 = 1:10, bar1 = anno_points(1:10),
annotation_name_side = "left")
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ht3 <- Heatmap(
le, name = "letters", col = col_letters)
ht_list <- ht1 + ht2 + ht3
draw(ht_list, ht_gap = unit(c(6, 2), "mm"))
7. 热图与注释串联
行注释可以添加到水平热图列表中,例如
ha1 <- rowAnnotation(
foo = 1:12, bar = anno_barplot(
1:12, width = unit(4, "cm"))
)
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2)
ht1 + ha1
上面的注释可以分割为两个独立的行注释
Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2) +
rowAnnotation(foo = 1:12) +
rowAnnotation(
bar = anno_barplot(1:12, width = unit(4, "cm"))
)
输出的结果是一样的
可是,行名怎么没了?我要怎么显示行名呢?
有两种方法:
- 将注释放在
Heatmap
函数中
ha1 <- rowAnnotation(
foo = 1:12, bar = anno_barplot(
1:12, width = unit(4, "cm"))
)
Heatmap(
mat1, name = "rnorm", col = col_rnorm,
row_km = 2, right_annotation = ha1
)
- 或者将行名作为文本注释添加
ht1 + ha1 +
rowAnnotation(rn = anno_text(
rownames(mat1), just = "left",
location = unit(0, "npc")))
通常,热图和行注释可以以任意的次序连接。例如,将注释添加到左边和中间
rowAnnotation(foo = 1:12) +
Heatmap(
mat1, name = "rnorm",
col = col_rnorm, row_km = 2) +
rowAnnotation(
bar = anno_barplot(1:12,
width = unit(4, "cm"))) +
Heatmap(mat2, name = "runif",
col = col_runif)
8. 注释之间的串联
可以只串联注释,而不需要热图参与
rowAnnotation(foo = 1:12) +
rowAnnotation(
bar = anno_barplot(
1:12, width = unit(4, "cm"))
)
如果只有一个注释,必须要添加一个 NULL
rowAnnotation(
bar = anno_barplot(
1:12, width = unit(4, "cm"))
) + NULL
事实上,注释列表本质上是一个热图列表
anno_list <- rowAnnotation(foo = 1:12) +
rowAnnotation(
bar = anno_barplot(
1:12, width = unit(4, "cm"))
)
> class(anno_list)
[1] "HeatmapList"
attr(,"package")
[1] "ComplexHeatmap"
所以,你可以使用 draw()
函数来绘制
draw(anno_list,
row_split = rep(c("A", "B"), each = 6))
9. 竖直连接
使用 %v%
操作,可以以竖直的方式连接热图和注释,其他设置基本上是类似的
下面,列举几个例子
mat1t <-t(mat1)
mat2t <- t(mat2)
ht1 <- Heatmap(
mat1t, name = "rnorm", col = col_rnorm,
row_title = "rnorm")
ht2 <- Heatmap(
mat2t, name = "runif", col = col_runif,
row_title = "runif")
ht3 <- Heatmap(
rbind(letters = le), name = "letters",
col = col_letters)
ht_list <- ht1 %v% ht2 %v% ht3
draw(ht_list)
设置分块
draw(ht_list, column_km = 2)
注释放中间
ha <- HeatmapAnnotation(
foo = anno_barplot(1:12, height = unit(2, "cm"))
)
ht_list = ht1 %v% ha %v% ht2 %v% ht3
draw(ht_list, column_km = 2)
将热图分块
ht1 <- Heatmap(
mat1t, name = "rnorm", col = col_rnorm,
row_km = 2)
ht2 <- Heatmap(
mat2t, name = "runif", col = col_runif,
row_km = 2)
ht3 <- Heatmap(
rbind(letters = le), name = "letters",
col = col_letters)
ha <- HeatmapAnnotation(
foo = anno_barplot(1:12, height = unit(2, "cm"))
)
ht_list <- ht1 %v% ha %v% ht2 %v% ht3
draw(ht_list, column_km = 2)
行注释大小的调整与列注释一样
ht1 <- Heatmap(
mat1t, name = "rnorm", col = col_rnorm,
row_km = 2, left_annotation = rowAnnotation(
foo1 = 1:10, bar1 = anno_barplot(1:10))
)
ha <- HeatmapAnnotation(
foo = anno_barplot(
1:12, height = unit(2, "cm"),
axis_param = list(side = "right"))
)
ht2 <- Heatmap(
mat2t, name = "runif", col = col_runif,
row_km = 2, left_annotation = rowAnnotation(foo2 = 1:10))
ht3 <- Heatmap(
rbind(letters = le), name = "letters",
col = col_letters)
ht_list = ht1 %v% ha %v% ht2 %v% ht3
draw(ht_list, column_km = 2)
10. 热图列表子集
与 Heatmap
对象类似,热图列表也可以进行切片操作。
对于水平热图列表,行索引对应所有热图和注释的行,列索引为相应的热图或注释名称。例如
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm,
left_annotation = rowAnnotation(
foo1 = 1:12, bar1 = anno_points(1:12))
)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif
)
ha <- rowAnnotation(
foo2 = anno_barplot(1:12), bar2 = 12:1
)
ht_list <- ht1 + ht2 + ha
获取热图列表包含的热图和注释名称
> names(ht_list)
[1] "rnorm" "runif" "foo2" "bar2"
获取子集
ht_list[1:6, c("rnorm", "bar2")]
对于竖直热图列表也是类似的。
11. 获取次序和树状图
row_order()
和 column_order()
可以获取热图列表的行列顺序。
记住:是对 draw
函数的返回对象进行操作
例如,对于如下热图列表
ht1 <- Heatmap(
mat1, name = "rnorm", col = col_rnorm)
ht2 <- Heatmap(
mat2, name = "runif", col = col_runif)
ht_list <- ht1 + ht2
ht_list <- draw(ht_list)
行顺序
> row_order(ht_list)
[1] 3 6 4 5 2 1 7 8 10 9 11 12
列顺序
> column_order(ht_list)
$rnorm
[1] 5 2 7 6 10 1 9 8 4 3
$runif
[1] 4 10 2 5 7 6 1 3 8 9
如果未分块热图,返回的是 list
row_dend()
和 column_dend()
可以获取行列树状图.
12. 全局参数
ht_opt()
函数用于控制全局参数,可以使用该函数为所有热图和注释设置参数值
获取所有全局参数
> ht_opt
Option Value
-----------------------:-------
heatmap_row_names_gp NULL
heatmap_column_names_gp NULL
heatmap_row_title_gp NULL
heatmap_column_title_gp NULL
legend_title_gp NULL
legend_title_position NULL
legend_labels_gp NULL
legend_grid_height NULL
legend_grid_width NULL
legend_border NULL
heatmap_border NULL
annotation_border NULL
fast_hclust FALSE
show_parent_dend_line TRUE
verbose FALSE
show_vp FALSE
simple_anno_size 5mm
DENDROGRAM_PADDING 0.5mm
DIMNAME_PADDING 1mm
TITLE_PADDING 2.5mm
COLUMN_ANNO_PADDING 1mm
ROW_ANNO_PADDING 1mm
这些参数都是见名知意的,不需要再做说明了
获取这些参数值的方式,可以是
> ht_opt("heatmap_row_names_gp")
NULL
> ht_opt$heatmap_row_names_gp
NULL
设置参数值
ht_opt("heatmap_row_names_gp" = gpar(fontsize = 8))
ht_opt$heatmap_row_names_gp <- gpar(fontsize = 8)
实例
ht_opt(
heatmap_column_names_gp = gpar(fontface = "italic"),
heatmap_column_title_gp = gpar(fontsize = 10),
legend_border = "black",
heatmap_border = TRUE,
annotation_border = TRUE
)
ht1 <- Heatmap(
mat1, name = "ht1", column_title = "Heatmap 1",
top_annotation = HeatmapAnnotation(foo = 1:10)
)
ht2 <- Heatmap(
mat2, name = "ht2", column_title = "Heatmap 2",
top_annotation = HeatmapAnnotation(bar = 1:10)
)
ht1 + ht2
绘制完之后,应该重置参数值
ht_opt(RESET = TRUE)
13. 设置边距
draw
函数中的 padding
参数可以设置绘图块四周的边距,padding
参数接受长度为 4
的向量,分别代表图像的下、左、上、右的边距
例如,对于行名较长的热图,会出现显示不全的问题
m <- matrix(rnorm(100), 10)
rownames(m) <- paste0("R", 1:10)
rownames(m)[1] = "a long long long long long row name"
ht <- Heatmap(
m, name = "mat", row_names_side = "left",
show_row_dend = FALSE
)
draw(ht)
添加边距
draw(ht, padding = unit(c(2, 20, 2, 2), "mm"))