R 数据可视化 —— 聚类热图 ComplexHeatmap(一)

前言

使用 pheatmap 已经能够绘制满足大多数要求的聚类热图了。

pheatmap 包的启发,ComplexHeatmap 提供了对热图更多更灵活的控制,如多数据热图的排列比较以及多种图形注释等。

下面我们详细介绍 ComplexHeatmap

设计理念

一张热图分为主体和组件两部分,热图的主体可以分割为多行多列的热图块,热图的组件包括标题、树状图、行列名以及注释,这些组件可以分布在主体的上下左右四个方向,如下图

而对于多个热图列表的排列,分为水平排列和竖直排列两种方式,标题和图例放置在四周,行列注释跟随在热图之后

ComplexHeatmap 包是基于 grid 包的,使用面向对象的方式实现热图及其组件,主要包含以下几个类:

  • Heatmap:绘制单个热图
  • HeatmapList:绘制热图列表
  • HeatmapAnnotation:定义热图的行、列注释列表,可以是热图的一部分,也可以独立于热图

以及一些内部类:

  • SingleAnnotation:定义单个行、列注释,组成 HeatmapAnnotation 的列表元素
  • ColorMapping:定义值到颜色的映射
  • AnnotationFunction:用于自定义注释图形

绘制热图

安装导入

install.packages("ComplexHeatmap")
library(ComplexHeatmap)

我们还是使用上次的数据

> data <- as.matrix(read.csv("~/Downloads/RPKM_DEG.csv", row.names = 1))
> head(data)
      control1 control2 control3   treat1  treat2   treat3
Gene1  178.413  180.616  111.951   44.264  44.251   35.842
Gene2 1790.491   33.799 3076.195  533.618 527.694  493.286
Gene3   55.313   72.512   42.625   26.291  31.324   26.485
Gene4  138.170   10.234  168.461 1014.917 968.487 1013.409
Gene5  400.783  417.081  422.252   16.958  21.532   18.350
Gene6   16.466   10.856   17.788  117.155 113.056  117.383

由于 ComplexHeatmap 包并不会对数据进行标准化,为了让图形更好看,我们先手动对数据进行标准化

exp <- apply(data, 1, scale)
rownames(exp) <- colnames(data)
exp <- t(exp)

绘制热图

Heatmap(data)

在我们不设置参数的情况下,图片长这样,不太好看。

1. 配色

通常,我们绘制的矩阵都是连续型数据,因此颜色映射函数需要接受一个向量输入,并返回一个向量输出

该包的作者推荐使用他写的另一个包 circlize 的函数 colorRamp2 来设置颜色映射,colorRamp2 函数接受两个参数,第一个参数用来设置值映射范围断点,第二个参数用于设置对应的颜色值

例如

library(circlize)

col_fun <- colorRamp2(
  c(-2, 0, 2), 
  c("#8c510a", "white", "#01665e")
  )

我们限定值为 -2 映射为 green0 映射为 white2 映射为 red

在这之间的值以线性内插的方式获取到相应的值,如果值超出了 [-2,2] 范围

> col_fun(seq(-3, 3))
[1] "#8C510AFF" "#8C510AFF" "#CCA482FF" "#FFFFFFFF" "#8BB0ABFF" "#01665EFF" "#01665EFF"

可以看到,超出范围的值都被赋值为了临界值

现在,我们可以重新绘制不同颜色的热图

Heatmap(exp, name = "expression", col = col_fun)

name 参数的作用是设置颜色图例的名称

颜色并不会受异常值的影响,但是会影响聚类的结果,例如,我们添加一个很大的值

mat <- exp
mat[1, 1] <- -100000
Heatmap(mat, name = "expression", col = col_fun)

如果矩阵的值是连续型的,也可以提供一个颜色向量,但是向量的前后两个值会对应到矩阵的最小值和最大值,因此,会受异常值的影响。

Heatmap(exp, name = "expression", col = rainbow(10))

这种方式等价于

col = colorRamp2(seq(min(exp), max(exp), length = 10), rainbow(10))

而对于矩阵为离散型数值,需要将颜色设置为命名向量,名称与矩阵的值相对应,例如

discrete_mat <- matrix(
  sample(1:4, 100, replace = TRUE), 
  10, 10
  )
colors <- structure(
  rainbow(4), 
  names = c("1", "2", "3", "4")
  )
Heatmap(
  discrete_mat, 
  name = "expression", 
  col = colors
  )

我们使用 structure 来定义一个命名向量,如果向量的值不是颜色值,会自动映射到颜色

如果矩阵的值为字符型,绘制方法也是类似

discrete_mat <- matrix(
  sample(LETTERS[1:4], 100, replace = TRUE), 
  10, 10
  )
colors <- structure(
  rainbow(4), 
  names = LETTERS[1:4]
  )
Heatmap(
  discrete_mat, 
  name = "expression", 
  col = colors
  )

但是,我们可以看到,对于字符型的矩阵不会进行聚类,如果想要对其进行聚类,需要传递相应的距离度量

如果矩阵中存在 NA 值,可以使用 na_col 参数来设置 NA 值的颜色

mat_with_na <- exp
na_index <- sample(
  c(TRUE, FALSE), 
  nrow(exp)*ncol(exp), 
  replace = TRUE, 
  prob = c(1, 9)
  )
mat_with_na[na_index] <- NA
Heatmap(
  mat_with_na, 
  name = "expression", 
  col = col_fun, 
  na_col = "black"
  )

默认情况下,颜色的线性内插的方式是遵循 LAB 颜色空间的变化,还有其他的颜色空间可供选择,如

热图的最外层边框使用 border 参数设置,网格线使用 rect_gp 参数来设置

border 参数可以是逻辑值或颜色值,而 rect_gp 参数需要接受一个 grid::gpar 对象,

Heatmap(
  exp, 
  name = "expression", 
  border = "black",
  col = col_fun,
  rect_gp = gpar(col = "white", lwd = 2)
  )

rect_gp 还支持一个非标准参数 type,如果设置为 none,则不会绘制中间的热图主体,但是可以使用 cell_funlayer_fun 添加自定义图形

Heatmap(
  exp, 
  name = "expression", 
  rect_gp = gpar(type = "none")
  )

2. 标题

标题设置比较简单,我们设置列标题的样式

Heatmap(
  exp, 
  col = col_fun,
  name = "expression", 
  column_title = "sample",
  column_title_side = "bottom",
  column_title_rot = 0,
  column_title_gp = gpar(
    col = "red",
    fontsize = 18,
    fontface = "italic",
    fill = "green",
    border = "black"
  )
  )

对于行标题也是类似的

Heatmap(
  exp, 
  col = col_fun,
  name = "expression", 
  row_title = "Genes",
  row_title_side = "left",
  row_title_rot = 90,
  row_title_gp = gpar(
    col = "red",
    fontsize = 18,
    fontface = "italic",
    fill = "green",
    border = "black"
  )
  )

标题的内容支持表达式格式

Heatmap(
  exp, 
  name = "expression", 
  col = col_fun,
  column_title = expression(
    hat(beta) == (X^t * X)^{-1} * X^t * y
    )
  )

如果想要设置更复杂的文本内容,可以使用 gridtext 包对文本进行渲染

3. 聚类

ComplexHeatmap 包中的层次聚类有很大的灵活性,可以有多种方法来指定

  • 现有的距离度量(如 euclideanpearson 等)
  • 自定义距离函数
  • 聚类函数
  • 聚类对象(hclustdendrogram

3.1 距离度量

距离度量的指定有三种方式:

  • 现有度量,如 dist() 函数包含的各种度量函数
  • 自定义的计算矩阵距离度量的函数
  • 自定义的计算两个向量之间距离度量的函数

例如,spearman 相关

Heatmap(
  exp, 
  col = col_fun,
  name = "expression", 
  clustering_distance_rows = "spearman"
)

自定义计算矩阵度量函数

Heatmap(
  exp, 
  col = col_fun,
  name = "expression", 
  clustering_distance_rows = function(m) dist(m)
)

自定义两个向量相关性函数

Heatmap(
  exp, 
  col = col_fun,
  name = "expression", 
  clustering_distance_rows = function(x, y) 1 - cor(x, y)
)

基于自定义函数的方法,我们可以定义一个度量函数,在计算两个向量的度量之前,去除数据中的离群点,使距离更稳定。例如

robust_dist = function(x, y) {
    # 计算 0.1 和 0.9 分位数
    qx = quantile(x, c(0.1, 0.9))
    qy = quantile(y, c(0.1, 0.9))
    # 只取 0.1-0.9 之间的数值
    l = x > qx[1] & x < qx[2] & y > qy[1] & y < qy[2]
    x = x[l]
    y = y[l]
    # 计算距离
    sqrt(sum((x - y)^2))
}

聚类

col_fun <- colorRamp2(
  c(-2, 0, 2), 
  c("#ff7f00", "white", "#1f78b4")
  )

mat <- exp
mat[sample(1:20, 7), sample(1:6, 1)] <- -100000

Heatmap(mat, name = "expression", col = col_fun,
        clustering_distance_rows = robust_dist,
        clustering_distance_columns = robust_dist)

我们也可以定义字符向量之间的距离度量函数,来对字符型矩阵进行聚类

首先,定义字符向量之间的距离度量,将字符转换为 · 码数值,然后计算欧氏距离

dist_letters <- function(x, y) {
  # 使用 utf8ToInt 将 utf-8 字符串转换为 ASCII 码向量
  x = utf8ToInt(paste(x, collapse = ""))
  y = utf8ToInt(paste(y, collapse = ""))
  sqrt(sum((x - y)^2))
}

绘制热图

Heatmap(
  discrete_mat, name = "mat", 
  # 定义离散值颜色映射
  col = structure(3:6, names = LETTERS[1:4]),
  # 对行列应用度量函数
  clustering_distance_rows = dist_letters,
  clustering_distance_columns = dist_letters,
  # 显示单元格文本
  cell_fun = function(j, i, x, y, w, h, col) {
    grid.text(discrete_mat[i, j], x, y) }
  )

3.2 聚类函数

层次聚类的聚类方法与 hclust() 函数一样,使用 clustering_method_rowsclustering_method_columns 两个参数来分别指定行列的聚类方法

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  clustering_method_rows = "single"
  )

或者使用其他包的聚类函数,如 cluster

library(cluster)

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  cluster_rows = diana,
  cluster_columns = agnes
  )

如果需要绘制的热图比较多,可以使用快速版的 hclust,通过设置全局变量来开启

ht_opt$fast_hclust = TRUE

3.3 树状图渲染

树状图的渲染需要使用 dendextend 包来创建一个 dendrogram 对象,下面我们只简单介绍一下

library(dendextend)

# 创建 dendrogram 对象
col_dend <- as.dendrogram(hclust(dist(t(exp))))
# 添加颜色分支
col_dend <- color_branches(col_dend, k = 2) 

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  # 直接传递给 cluster_columns 参数
  cluster_columns = col_dend
  )

注意,我们在聚类的时候对矩阵进行了转置,表示对矩阵的列进行聚类,对行进行聚类不需要转置

也可以使用 row_dend_gpcolumn_dend_gp 参数来设置树形图的图形属性

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  cluster_columns = col_dend,
  column_dend_gp = gpar(
    col = "red")
  )

3.4 树状图重排

row_dend_reordercolumn_dend_reorder 用于控制是否对树形图进行重排,默认值为

row_dend_reorder = is.logical(cluster_rows) || is.function(cluster_rows)
column_dend_reorder = is.logical(cluster_columns) || is.function(cluster_columns)

即,如果 cluster_rows/columns 为逻辑值或函数,会对树状图重排,而如果是聚类对象,则不会对树状图重排

4. 设置行列顺序

我们可以使用 row_ordercolumn_order 参数自定义行列的顺序,但意味着将不会再对数据进行聚类

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  column_order = order(colnames(exp)),
  row_order = order(as.numeric(gsub("Gene", "", rownames(exp))))
  )

或者,之间传递字符向量

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  column_order = order(colnames(exp)),
  row_order = rownames(exp)
  )

还可以与 Seriation 包结合使用,利用 Seriation 提供的函数和方法,对矩阵的行列进行排序,然后获取相应的顺序,并设置到对应的参数上。

5. 设置行列名称

设置行列名称相关的参数包括

  • show_row/column_names:是否显示行/列标签
  • row/column_labels:重置行/列标签
  • row/column_names_side:标签放置位置
  • row/column_names_gp:图形属性
  • row/column_names_centered:居中对齐
  • row/column_names_max_width:最大宽度
  • row/column_names_rot:旋转角度
Heatmap(
  exp, name = "expression", 
  col = col_fun,
  # 行标签放在左边
  row_names_side = "left",
  # 让行树状图去右边
  row_dend_side = "right",
  # 居中对齐
  row_names_centered = TRUE,
  # 设置选择角度
  row_names_rot = 45,
  # 设置行标签字体大小
  row_names_gp = gpar(
    col = "#1f78b4",
    fontsize = 16
  ),
  # 设置列标签颜色
  column_names_gp = gpar(
    col = c(rep("#de77ae", 3), rep("#7fbc41", 3))
  )
  )

有时候,我们的行名或者列名比较长,例如

mat <- exp
rownames(mat)[5] <- paste(c(letters, LETTERS), collapse = "")

Heatmap(
  mat, name = "expression", 
  col = col_fun,
  )

默认大小为 6 厘米,我们可以设置行名或列名的最大宽度

对于基因表达分析,我们的行名通常会设置为 gene ID,但是 ID 是不能让人直观地知道到底代表哪个基因,所以将 ID 转换为 Symbol 会利于阅读。

因此,row/column_labels 参数的重置标签是很有用的。同时,由于矩阵的行列名是不允许重复的,但是重置热图行列标签不会受这一规则限制,例如

row_labels <- structure(
  paste0(
    sample(letters, 20), 
    sample(1:9, 20, replace = TRUE)
    ), 
  names = rownames(exp)
  )

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_labels = row_labels
  )

而且,还支持表达式作为行列名

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_labels = row_labels,
  column_labels = col_labels
  )

6. 分割热图

ComplexHeatmap 支持按行或按列分割热图,对热图进行分组

6.1 k-means

使用 row_kmcolumn_km 来对矩阵进行 k-means 分组,然后对分组聚类

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_km = 3,
  column_km = 2
  )

Heatmap() 在内部调用的 kmeans() 算法是使用随机起始点的,因此,某些情况下,可能每次运行的结果不同,可以设置 *_km_repeats 来设置运行次数,并取一致的聚类结果

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_km = 3,
  column_km = 2,
  row_km_repeats = 100,
  column_km_repeats = 100
  )

运行次数越多,速度越慢

你可能会好奇,树状图中的虚线代表什么,我们将矩阵进行分割后,在每个分组内进行了层次聚类,然后有使用分组内的均值来对分组进行层次聚类,虚线是标注各子树形图与父树形图

6.2 分类变量

row_splitcolumn_split 参数可以设置分类向量或数据框来分隔热图,例如

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = rep(LETTERS[1:5], each = 4),
  column_split = rep(LETTERS[6:7], each = 3)
  )

数据框

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = data.frame(
    rep(LETTERS[1:2], 10), 
    rep(LETTERS[24:25], each = 10)
    )
  )

事实上,k-means 聚类只是生成一个簇向量,然后添加到 row_splitcolumn_split 参数中,我们可以混合二者

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = factor(rep(LETTERS[1:2], 10)),
  row_km = 2
  )

也可以传递其他聚类方法的结果

pa <- cluster::pam(exp, k = 3)

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = pa$clustering
  )

如果同时还设置了 row_ordercolumn_order,则会分别在每个分组中进行排序,例如

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  km = 2,
  row_order = 20:1
  )

对于字符矩阵,只能使用 row/column_split 来分隔热图

Heatmap(
  discrete_mat, name = "expression", 
  col = 3:6,
  row_split = discrete_mat[, 1]
  )

6.3 按树形图分割

有时候,我们希望的是根据层次聚类的结果来对矩阵进行分割,而不是先分割再聚类,这时,可以为 row/column_split 设置单个数值

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = 3,
  column_split = 2
  )

6.4 热图块顺序

当设置了 k-means 聚类,或 row/column_split 参数值为分类变量或数据框,默认会对分块进行聚类,在这种情况下,是无法控制分块的顺序的

但是可以将 cluster_row/column_slices 参设置为 FALSE 来关闭分块聚类

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = factor(rep(LETTERS[1:2], 10), levels = LETTERS[1:2]),
  column_split = factor(rep(LETTERS[24:25], each = 3), levels = LETTERS[24:25]),
  cluster_row_slices = FALSE,
  cluster_column_slices = FALSE
  )

6.5 热图块的标题

在前面的例子中,我们对热图进行分块,每个热图块都有对应的标题,如果想要设置这些标题的名称要怎么做呢?

我们看到,如果分块变量为 1 个,则标题名称为该变量的 level,如果分类变量有多个,则标题为 level1,level2,...

ComplexHeatmap 支持三种类型的模板文本,第一种 sprintf()

使用 %s 来替换对应的 level,例如

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = data.frame(
    rep(LETTERS[1:2], 10), 
    rep(LETTERS[24:25], each = 10)
  ),
  row_title = "%s&%s"
)

以及 GetoptLong 包的 @{} 标记或 glue{} 标记

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = data.frame(
    rep(LETTERS[1:2], 10), 
    rep(LETTERS[24:25], each = 10)
  ),
  # row_title = "@{map[ x[1] ]} and @{map[ x[2] ]}"
  row_title = "{map[ x[1] ]} and {map[ x[2] ]}"
)

默认会旋转标题,我们将它转回来,只要添加 row_title_rot = 0 就行

row_title_rot = 0

也可以使用数值或者 *_km

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  # row_km = 3,
  row_split = 3,
  row_title = "cluster_%s"
)

如果还要绘制全局的标题,则可以

hm <- Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = data.frame(
    rep(LETTERS[1:2], 10), 
    rep(LETTERS[24:25], each = 10)
  ),
  row_title = "%s&%s"
)

draw(hm, row_title = "Gene set")

6.6 热图块图形属性

要为热图块的行列标题或行列名称指定图形属性,可以传递一个与热图块数量相等的向量值

例如

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = 3,
  column_split = 2,
  row_title_gp = gpar(
    col = rainbow(3),
    font = 1:3
  ),
  row_names_gp = gpar(
    col = rainbow(3),
    fontsize = 10:12
  ),
  column_title_gp = gpar(
    fill = rainbow(5)[4:5],
    alpha = 0.5
  ),
  column_names_gp = gpar(
    col = rainbow(5)[4:5]
  )
)

6.7 热图块间距

row/column_gap 可以控制热图块之间的间距,可以是单个值或向量

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = 3,
  column_split = 2,
  row_gap = unit(c(2, 4), "mm"),
  column_gap = unit(2, "mm")
)

设置 border = TRUE 可以为每个热图块设置边框

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  row_split = 3,
  column_split = 2,
  row_gap = unit(c(2, 4), "mm"),
  column_gap = unit(2, "mm"),
  border = TRUE
)

注意:分割热图时会将相应的热图注释也一并切开了

7. 自定义热图主体

热图主体默认是由不同颜色填充的矩形框组成,我们姑且称之为单元格。

我们可以使用 cell_funlayer_fun 参数来自定义主体的图形样式

cell_fun 用于绘制每个单元格,layer_fun 是其矢量化的版本,cell_fun 使用简单,layer_fun 执行速度快,样式多

7.1 cell_fun

cell_fun 参数接受一个带 7 个参数的函数,分别是:

  • j: 数据矩阵的列索引
  • i: 数据矩阵的行索引
  • x: 热图主体 viewport 中对应的单元格中点 x 坐标
  • y: 热图主体 viewport 中对应的单元格中点 y 坐标
  • width: 该单元格宽度
  • height: 该单元格高度
  • fill: 单元格填充色

在每个单元格中执行时,函数会自动接收到这 7 个参数值

我们可以将矩阵的值添加到热图中

cell_fun <- function(j, i, x, y, width, height, fill) {
  grid.text(
    round(exp[i, j], 2), 
    x, y,
    gp = gpar(
      fontsize = 10
    ))
}

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  cell_fun = cell_fun
)

添加过滤,只显示正值

cell_fun <- function(j, i, x, y, width, height, fill) {
  if (exp[i, j] > 0) {
    grid.text(round(exp[i, j], 2),
              x, y,
              gp = gpar(fontsize = 10))
  }
}

与热图分块无缝衔接

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  cell_fun = cell_fun,
  row_km = 3,
  column_km = 2
)

我们可以使用 cell_fun 来绘制与 corrplot 类似的相关系数矩阵

cell_fun <- function(j, i, x, y, width, height, fill) {
  grid.rect(x, y, width = width, height = height,
            gp = gpar(col = "grey"))
  if (i < j) {
    grid.text(
      round(exp[i, j], 2),
      x, y,
      gp = gpar(fontsize = 10)
      )
  } else if (i == j) {
    grid.text(colnames(exp)[i], x, y)
  } else {
    grid.circle(
      x, y, 
      r = abs(exp[i, j]) / 4 * min(width, height),
      gp = gpar(
        fill = col_fun(exp[i, j]),
        col = NA
      ))
  }
}

Heatmap(
  exp[1:6,], name = "expression", 
  col = col_fun,
  cell_fun = cell_fun,
  show_row_names = FALSE,
  show_column_names = FALSE,
  cluster_rows = FALSE,
  cluster_columns = FALSE
)

7.2 layer_fun

layer_fun 是按照热图块的方式添加图形,与 cell_fun 类似,也包含 7 个参数,但它们的值都是向量的形式

由于 ij 都是向量,要获取其在矩阵中对应的值就不能直接用 mat[i,j] 的方式,因为行列坐标分别保存在 ij 向量的对应位置

例如

> mat <- matrix(1:9, nr = 3)
> mat[1:2, c(1, 3)]
     [,1] [,2]
[1,]    1    7
[2,]    2    8

但其实我们要获取的是位于 (1,1)(2,3) 的值

但可以使用 ComplexHeatmap 提供的函数 pindex()

> pindex(mat, 1:2, c(1, 3))
[1] 1 8

绘制 layer_fun 版

layer_fun <- function(j, i, x, y, w, h, f) {
  grid.text(round(pindex(exp, i, j), 2),
            x, y, gp = gpar(fontsize = 10))
}

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  layer_fun = layer_fun
)

当热图被分割时,layer_fun 会应用于每个热图块中。我们可以为所有值之和大于 0 的热图块添加矩形框线

layer_fun <- function(j, i, x, y, w, h, f) {
  vt <- pindex(exp, i, j)
  grid.text(round(vt, 2),
            x, y, gp = gpar(fontsize = 10))
  if (sum(vt) > 0) {
    grid.rect(
      gp = gpar(
        lwd = 2, 
        fill = NA,
        col = "red")
    )
  }
}

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  layer_fun = layer_fun,
  row_split = 3,
  column_split = 2
)

layer_fun 还可以接受两个额外的参数,即当前热图块的行列索引,例如

layer_fun <- function(j, i, x, y, w, h, f, r, c) {
  vt <- pindex(exp, i, j)
  grid.text(round(vt, 2),
            x, y, gp = gpar(fontsize = 10))
  if (r != c) {
    grid.rect(
      gp = gpar(
        lwd = 2, 
        fill = NA,
        col = "green")
    )
  }
}

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  layer_fun = layer_fun,
  row_split = 3,
  column_split = 2
)

为行列索引不一致的热图块添加绿色框线

对于每个热图块,我们可能想要获取该热图块索引的矩阵形式,即将向量化的 i,j,x,y 参数,转换为对应热图块的矩阵形式。我们可以使用 restore_matrix() 函数

例如,直接将 4 个参数传递进去,它会返回与热图块维度相同的矩阵

ind_mat = restore_matrix(j, i, x, y)

例如,我们第一个热图块的大小是 8 行 3 列

     [,1] [,2] [,3]
[1,]    1    9   17
[2,]    2   10   18
[3,]    3   11   19
[4,]    4   12   20
[5,]    5   13   21
[6,]    6   14   22
[7,]    7   15   23
[8,]    8   16   24

该矩阵的值对应其在 4 个参数中的位置

例如,我们可以在每个热图块的第一行第二列单元格绘制一个圆点

layer_fun <- function(j, i, x, y, w, h, f, r, c) {
  ind_mat <- restore_matrix(j, i, x, y)
  ind <- ind_mat[1, 2]
  grid.points(
    x[ind], 
    y[ind], 
    pch = 16, 
    size = unit(4, "mm"),
    gp = gpar(col = "green"))
}

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  layer_fun = layer_fun,
  row_split = 3,
  column_split = 2
)

8. 热图尺寸

widthheight 用于控制热图主体的宽度和高度,heatmap_widthheatmap_height 控制整个热图的宽度和高度。

默认情况下,热图的组件都有固定宽度,而热图主体自动填充剩余区域

Heatmap(
  exp, name = "expression", 
  col = col_fun,
  width = unit(8, "cm"),
  height = unit(8, "cm")
)
Heatmap(
  exp, name = "expression", 
  col = col_fun,
  heatmap_width = unit(14, "cm"), 
  heatmap_height = unit(14, "cm")
)

9. 热图绘制

在上面的例子中,我们都是直接使用 Heatmap 函数来绘制热图,但其实这只是在交互式模式下的行为,它本质上只是一个构造函数,如果你把 Heatmap 放置在

  • 一个函数中
  • iffor 代码块中、
  • 脚本中,并在命令行运行

都不会有任何输出的

因为,上述情况下,都不会调用 show() 方法,也不会执行 draw() 方法。

所以绘制热图的正确方式是

ht <- Heatmap(...)
draw(ht)

draw() 函数实际上用于绘制热图列表,即 HeatmapList 类。单个热图 Heatmap 类的绘制只是将其转换为包含一个热图的 HeatmapList,然后再调用 HeatmapListdraw 方法

10. 获取顺序及树状图

row_order()column_order() 函数可以获取热图的行、列顺序

hm <- Heatmap(
  exp, name = "expression", 
  col = col_fun
  )

dhm <- draw(hm)

> row_order(hm)
 [1] 13 18  9  5 19 20 16  3  7 17  1  2  8 12  4  6 15 14 11 10
> row_order(dhm)
 [1] 13 18  9  5 19 20 16  3  7 17  1  2  8 12  4  6 15 14 11 10
> column_order(dhm)
[1] 2 1 3 6 5 4

虽然两种方法都可以,但还是有所区别

row_order(hm) 会先执行聚类,然后获取聚类的结果

row_order(dhm) 是获取图形输出中的行顺序

如果有添加 k-means 聚类,两次的结果有可能会不一致,推荐使用后一种方式。

如果有对热图进行拆分,获取顺序将会返回一列 list

hm <- Heatmap(
  exp, name = "expression", 
  row_km = 3,
  col = col_fun
  )

hm <- draw(hm)
> row_order(hm)
$`1`
[1]  8 12  4  6 15 14 11 10

$`2`
[1] 16  3  7 17 19  5 20  1

$`3`
[1] 13  9 18  2

类似地,row_dend()column_dend() 用于获取聚类的树形图,如果热图被分割,也会返回一个 list

11. 热图子集

Heatmap 对象可以用切片的方式获取聚类结果的子集

hm <- Heatmap(
  exp, name = "expression", 
  row_km = 3,
  col = col_fun
  )
hm[1:6, 1:6]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345