使用ComplexHeatmap与circlize在R中绘制文献级环形热图
2026/9/3 9:17:14 网站建设 项目流程

如果你在生物信息学或数据可视化领域工作,一定见过那些发表在顶级期刊上的精美热图。它们色彩绚丽、布局精巧,不仅能展示海量数据,还能清晰地揭示样本间的聚类关系和基因表达模式。其中,圆形热图(Circular Heatmap)因其独特的环形布局和强大的信息承载能力,尤其引人注目。

你可能尝试过用基础的heatmap()函数,却发现它连基本的行列注释都难以实现;你也可能用过pheatmap,但在面对复杂的、分面式的环形布局时感到束手无策。这时,你需要的不是一个简单的绘图工具,而是一个能够让你像搭积木一样自由构建复杂热图的“可视化引擎”。

这篇文章要解决的,正是这个痛点:如何利用 R 语言中的“核武器”级绘图包——ComplexHeatmap,从零开始复现一篇文献中极具设计感的圆形热图。我们不止步于“画出图”,更要深入理解其背后的设计逻辑:如何组织数据、如何分层注释、如何控制美学细节,以及如何将多个简单热图组合成一个复杂的环形叙事。读完本文,你将掌握一套可复用的方法论,不仅能还原文献中的图,更能将其思路应用于你自己的数据分析项目,产出可直接用于发表的高质量可视化结果。

1. 为什么是 ComplexHeatmap?超越基础热图工具的边界

在深入代码之前,我们必须先理解为什么选择 ComplexHeatmap。R 语言中绘制热图的工具不少,但它们在面对复杂需求时,短板非常明显:

  • 基础heatmap()函数:功能极其有限,自定义选项少,几乎无法添加行列注释,更别提复杂布局。
  • pheatmap:比基础函数强大许多,提供了注释和聚类功能,是许多人的入门选择。但其布局是固定的“方块”,难以实现非矩形的(如圆形)、分面的或高度定制化的组合图形。
  • ggplot2+geom_tile:灵活性极高,理论上可以画出任何图形。但对于热图这种高度结构化的数据,你需要手动处理聚类、树状图、注释对齐等大量细节,代码会变得异常冗长和复杂。

ComplexHeatmap 的核心优势在于其“组合”与“声明式”的绘图哲学。它将一个热图分解为多个可独立定义的“组件”(Heatmap、行注释、列注释、标题、图例等),然后像搭积木一样将它们组合起来。更重要的是,它允许你将多个独立的热图对象(HeatmapHeatmapList)再次组合,形成更复杂的图形,比如并排对比、上下堆叠,或者我们本文的重点——环形布局

简单来说,pheatmap是给你一个装修好的“精装房”,而ComplexHeatmap 是给了你一套功能强大的“建筑工具”和“预制件”,让你可以自由设计并建造出任何你想要的“房子”,包括一座“圆形城堡”。学习它虽然有一定门槛,但一旦掌握,你在数据可视化上将获得前所未有的自由。

2. 核心概念拆解:理解 ComplexHeatmap 的构建模块

要建造“圆形城堡”,我们先要认识手中的“砖块”和“工具”。以下是 ComplexHeatmap 中最关键的几个概念:

  1. Heatmap对象:这是核心。一个Heatmap对象不仅仅包含矩阵数据对应的色块,还隐式地包含了与之关联的行名、列名、行列树状图(如果进行了聚类)以及该热图专属的图例。你可以通过Heatmap()函数创建它。
  2. 注释(Annotation):用于在行或列旁边添加额外的信息条带,如样本的分组、基因的染色体位置、表达量的高低等。分为行注释 (rowAnnotation) 和列注释 (columnAnnotation)。注释可以包含多种图形元素(箱线图、条形图、点图、文本等)。
  3. 热图列表(HeatmapList:当调用draw()函数绘制一个或多个Heatmap对象时,内部会创建一个HeatmapList来管理这些热图的整体布局和所有全局图例。
  4. %v%+操作符:这是实现组合的关键。%v%用于垂直(上下)连接热图,+用于水平(左右)连接热图或添加注释。
  5. circos.heatmapcircos.track:这是实现圆形布局的秘诀。ComplexHeatmap 与circlize包深度集成。circlize包提供了在圆形画布上绘图的基础设施,而circos.heatmap等函数则允许我们将矩形热图“映射”到圆环的一个扇形轨道上。多个轨道叠加,就形成了环形热图。

理解这些概念后,我们的复现思路就清晰了:将文献中的圆形热图,拆解为多个叠加的同心圆环轨道,每个轨道可能是一个热图主体,也可能是一圈行/列注释。然后利用circlize框架,将这些轨道依次绘制出来。

3. 环境准备:安装与加载必要的 R 包

工欲善其事,必先利其器。确保你的 R 环境已准备好以下包。如果未安装,请先运行安装命令。

# 安装核心包(如果尚未安装) if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager") BiocManager::install("ComplexHeatmap") # 安装 circlize 包用于圆形布局 install.packages("circlize") # 安装其他可能用到的工具包,如颜色调色板 install.packages("RColorBrewer") install.packages("viridis")

安装完成后,在脚本开头加载它们:

# 加载必要的库 library(ComplexHeatmap) library(circlize) # 必须加载,以使用 circular 布局函数 library(RColorBrewer) library(viridis) # 设置随机种子,确保示例结果可重现 set.seed(123)

4. 数据模拟:创建用于演示的模拟数据集

由于我们无法获取原文献的真实数据,这里我们模拟一个类似的数据集来演示全过程。假设我们研究的是10个基因在20个样本中的表达量,并且样本分为3组,基因属于4个不同的通路。

# 1. 模拟表达矩阵 (10 genes x 20 samples) n_genes <- 10 n_samples <- 20 expr_matrix <- matrix(rnorm(n_genes * n_samples, mean = 0, sd = 1), nrow = n_genes, ncol = n_samples) # 为增加可视化效果,人为制造一些差异 expr_matrix[1:3, 1:7] <- expr_matrix[1:3, 1:7] + 2 # 前3个基因在前7个样本高表达 expr_matrix[8:10, 15:20] <- expr_matrix[8:10, 15:20] - 1.5 # 后3个基因在后6个样本低表达 rownames(expr_matrix) <- paste0("Gene_", LETTERS[1:n_genes]) colnames(expr_matrix) <- paste0("Sample_", sprintf("%02d", 1:n_samples)) # 2. 创建样本分组注释 (列注释) sample_groups <- rep(c("Group_A", "Group_B", "Group_C"), times = c(7, 7, 6)) # 分配样本数 names(sample_groups) <- colnames(expr_matrix) # 3. 创建基因通路注释 (行注释) gene_pathways <- rep(c("Pathway_W", "Pathway_X", "Pathway_Y", "Pathway_Z"), times = c(2, 3, 2, 3)) names(gene_pathways) <- rownames(expr_matrix) # 查看数据 print(dim(expr_matrix)) head(expr_matrix[, 1:5]) table(sample_groups) table(gene_pathways)

5. 构建矩形热图:掌握基础,迈向复杂

在画圆之前,我们先在传统的矩形坐标系下构建热图及其注释,这是理解所有组件的基础。

# 1. 定义颜色映射函数 # 表达量热图的颜色:从蓝色(低)到白色(中)到红色(高) col_fun_expr <- colorRamp2(c(-2, 0, 2), c("blue", "white", "red")) # 样本分组的颜色 group_colors <- c(Group_A = "#E41A1C", Group_B = "#377EB8", Group_C = "#4DAF4A") # 基因通路的颜色 pathway_colors <- c(Pathway_W = "#FF7F00", Pathway_X = "#984EA3", Pathway_Y = "#A65628", Pathway_Z = "#F781BF") # 2. 创建注释对象 # 列注释(样本分组) ha_column <- HeatmapAnnotation( Group = sample_groups, col = list(Group = group_colors), annotation_name_side = "left", # 注释名称的位置 simple_anno_size = unit(0.5, "cm") # 注释条的宽度 ) # 行注释(基因通路) ha_row <- rowAnnotation( Pathway = gene_pathways, col = list(Pathway = pathway_colors), simple_anno_size = unit(0.5, "cm") ) # 3. 构建主热图对象 (不立即绘制) ht_main <- Heatmap( expr_matrix, name = "Expression\n(Z-score)", # 图例标题 col = col_fun_expr, top_annotation = ha_column, # 添加列注释到顶部 left_annotation = ha_row, # 添加行注释到左侧 column_title = "Samples", row_title = "Genes", row_names_gp = gpar(fontsize = 10), # 行名字体 column_names_gp = gpar(fontsize = 8), # 列名字体 # 聚类选项:这里对行和列都进行聚类 cluster_rows = TRUE, cluster_columns = TRUE, show_row_dend = TRUE, show_column_dend = TRUE, row_dend_width = unit(2, "cm"), column_dend_height = unit(2, "cm") ) # 4. 绘制矩形热图 draw(ht_main, heatmap_legend_side = "right", # 图例放在右侧 annotation_legend_side = "bottom") # 注释图例放在底部

运行以上代码,你将得到一个标准的、带有行列注释和聚类树状图的矩形热图。这已经是一张信息丰富、可直接用于报告的热图。但我们的目标是圆形。

6. 核心实战:拆解与复现圆形热图

现在进入最关键的部分。假设文献中的圆形热图由内到外包含以下轨道:

  1. 最内圈:基因通路注释(行注释)。
  2. 中间圈:基因表达量热图主体。
  3. 最外圈:样本分组注释(列注释)。

我们将使用circlizecircos.heatmap函数来逐一构建这些轨道。

# 第一步:初始化圆形画布并设置全局参数 # 清除之前的 circlize 设置(非常重要!) circos.clear() # 初始化圆形布局 # `sector.index` 和 `track.index` 参数用于精确定位,我们先设置画布 circos.par(gap.after = c(2), # 设置扇区间隙,这里只有一个扇区(所有数据) start.degree = 90, # 起始角度(12点钟方向) clock.wise = FALSE) # 逆时针方向排列 # 第二步:准备数据并排序(为了与聚类树一致) # 为了美观,我们通常希望热图在圆形中也是聚好类的。 # 我们可以从之前创建的 `ht_main` 对象中提取行列顺序。 # 注意:`circos.heatmap` 需要数据按其在圆环上出现的顺序排列。 # 提取矩形热图中聚类后的行、列顺序 row_order <- row_order(ht_main) # 获取行的索引顺序 col_order <- column_order(ht_main) # 获取列的索引顺序 # 按照聚类顺序重排表达矩阵 expr_matrix_ordered <- expr_matrix[row_order, col_order] # 按照聚类顺序重排注释向量 gene_pathways_ordered <- gene_pathways[rownames(expr_matrix_ordered)] sample_groups_ordered <- sample_groups[colnames(expr_matrix_ordered)] # 第三步:绘制最内圈轨道 - 基因通路注释(行注释) # 在第一个轨道上绘制行注释 circos.heatmap(gene_pathways_ordered, col = pathway_colors, # 使用之前定义的颜色映射 track.height = 0.08, # 轨道高度 rownames.side = "outside", # 行名(基因名)画在外侧 rownames.cex = 0.7) # 行名字体大小 # 第四步:绘制中间圈轨道 - 表达量热图主体 # 这是最核心的部分。我们需要将数值矩阵映射到颜色。 # `col` 参数接受一个颜色映射函数。 circos.heatmap(expr_matrix_ordered, col = col_fun_expr, # 使用之前定义的连续颜色函数 track.height = 0.4, # 主热图轨道较宽 rownames.side = "none", # 基因名已在上一轨道显示,这里隐藏 cluster = FALSE) # 数据已预先排序,此处不重新聚类 # 注意:`circos.heatmap` 本身也支持聚类,但为了与注释对齐,我们使用预排序。 # 第五步:绘制最外圈轨道 - 样本分组注释(列注释) # 样本注释是针对列的,我们需要将列信息也映射成一个“矩阵”或向量。 # 这里我们为每个样本画一个色块。 circos.heatmap(sample_groups_ordered, col = group_colors, track.height = 0.08, rownames.side = "outside", # 列名(样本名)画在外侧 rownames.cex = 0.6) # 第六步:添加图例 # 在圆形图外部添加图例,需要使用 ComplexHeatmap 的图例函数,并用 `draw` 控制位置。 # 由于我们已经用了 circlize,更简单的方法是在绘图完成后,在旁白区用 base R 或 grid 系统添加图例。 # 这里演示一种方法:先完成圆形绘图,然后在新的绘图设备上组合。 # 首先,结束 circlize 的绘图,将控制权交还给常规图形设备 lgd_list <- list() # 创建一个列表存放图例 # 创建表达量图例 lgd_expr <- Legend(title = "Expression", col_fun = col_fun_expr, at = c(-2, -1, 0, 1, 2)) lgd_list <- c(lgd_list, list(lgd_expr)) # 创建通路图例 lgd_pathway <- Legend(title = "Pathway", at = names(pathway_colors), legend_gp = gpar(fill = pathway_colors)) lgd_list <- c(lgd_list, list(lgd_pathway)) # 创建样本分组图例 lgd_group <- Legend(title = "Group", at = names(group_colors), legend_gp = gpar(fill = group_colors)) lgd_list <- c(lgd_list, list(lgd_group)) # 绘制所有图例到旁白区域 # 注意:这一步需要在 `circos.clear()` 之后,在新的图形页面上进行。 # 为了自动化,我们可以将圆形图和图例画在同一个页面的不同区域。 # 以下代码展示了如何利用 grid 图形系统进行组合排版。 library(grid) # 打开一个新的图形页面,或清除当前页面 grid.newpage() # 定义一个复杂的布局,左边大部分区域画圆,右边窄条画图例 pushViewport(viewport(layout = grid.layout(1, 2, widths = unit(c(0.85, 0.15), "npc")))) # 在左边区域绘制圆形热图 pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1)) # 重新执行一遍圆形绘图(因为之前画在了默认设备上) circos.clear() circos.par(gap.after = c(2), start.degree = 90, clock.wise = FALSE) circos.heatmap(gene_pathways_ordered, col = pathway_colors, track.height = 0.08, rownames.side = "outside", rownames.cex = 0.7) circos.heatmap(expr_matrix_ordered, col = col_fun_expr, track.height = 0.4, rownames.side = "none", cluster = FALSE) circos.heatmap(sample_groups_ordered, col = group_colors, track.height = 0.08, rownames.side = "outside", rownames.cex = 0.6) upViewport() # 返回上一层 viewport # 在右边区域绘制图例 pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 2)) draw(lgd_list, x = unit(0.1, "npc"), y = unit(0.9, "npc"), just = c("left", "top")) upViewport() # 最终,清除 circlize 设置,为下次绘图做准备 circos.clear()

这段代码是复现圆形热图的核心。它演示了如何:

  1. 使用circos.par设置圆形画布。
  2. 使用circos.heatmap分轨道绘制数据。
  3. 通过track.height控制轨道厚度。
  4. 通过rownames.side控制标签位置。
  5. 使用预排序的数据保证热图、行注释、列注释的对应关系一致。
  6. 利用grid系统将圆形图与图例组合在同一画面中。

运行后,你应该能看到一个由三个同心圆环组成的圆形热图,并配有完整的图例。

7. 高级定制与美化:让热图更接近文献

文献中的热图往往更加精致。以下是一些常见的美化技巧:

# 技巧1:添加轨道间的分隔线,并调整轨道背景色 circos.clear() circos.par(gap.after = c(2), start.degree = 90, clock.wise = FALSE) # 绘制内圈注释,并自定义轨道背景和边框 circos.heatmap(gene_pathways_ordered, col = pathway_colors, track.height = 0.1, rownames.side = "outside", rownames.cex = 0.7, bg.border = "black", # 轨道背景边框 bg.lwd = 1, # 边框线宽 bg.lty = 1) # 边框线型 # 技巧2:在主热图轨道添加数值刻度或分割线 # 我们可以通过 `circos.track` 在热图轨道上叠加其他元素,比如高亮某些区域。 # 假设我们想高亮表达量大于1.5的区域。 circos.heatmap(expr_matrix_ordered, col = col_fun_expr, track.height = 0.35, rownames.side = "none") # 在热图轨道上叠加一层“高亮”层 # 首先,获取当前轨道的索引 current_track_index <- get.cell.meta.data("track.index") # 可能需要根据实际情况调整 # 然后,使用 circos.track 在该轨道上绘制矩形(这里逻辑较复杂,通常文献热图不这么做,此处仅示意) # 更常见的做法是直接调整热图颜色断点来突出显示。 # 技巧3:使用更专业的颜色方案 # 例如,使用 viridis 色系,它对色盲友好且在黑白打印时清晰。 library(viridisLite) col_fun_viridis <- colorRamp2(c(min(expr_matrix), 0, max(expr_matrix)), viridis(3)) # 将上面主热图中的 `col_fun_expr` 替换为 `col_fun_viridis` 即可。 # 技巧4:精细控制标签显示 # 样本/基因太多时,可以间隔显示标签。 circos.heatmap(sample_groups_ordered, col = group_colors, track.height = 0.1, rownames.side = "outside", rownames.cex = 0.6, show.sector.labels = FALSE) # 不显示扇区标签(默认的基因名/样本名标签由 rownames.side 控制) # 更精细的控制需要直接操作 `circos.text`,这超出了 `circos.heatmap` 的封装。 # 重新绘制一个美化版的简图 circos.clear() par(mar = c(1, 1, 1, 1)) # 调整图形边距 circos.par(gap.after = c(2), start.degree = 90, clock.wise = FALSE, cell.padding = c(0, 0, 0, 0)) circos.heatmap(gene_pathways_ordered, col = pathway_colors, track.height = 0.08, bg.border = "grey60", rownames.cex = 0.6) circos.heatmap(expr_matrix_ordered, col = col_fun_viridis, track.height = 0.3) circos.heatmap(sample_groups_ordered, col = group_colors, track.height = 0.08, bg.border = "grey60", rownames.cex = 0.5) # 添加一个主标题 title(main = "Circular Heatmap of Gene Expression", line = -1) circos.clear()

8. 常见问题与排查思路

在复现过程中,你可能会遇到以下问题:

问题现象可能原因排查方式解决方案
运行circos.heatmap报错:Error in circos.heatmap(...)1. 未加载circlize包。
2. 数据不是矩阵或命名向量。
3. 颜色映射函数col参数格式错误。
1. 检查library(circlize)
2. 用class()dim()检查数据。
3. 检查col是颜色向量还是colorRamp2函数。
1. 确保加载包。
2. 确保输入是数值矩阵或命名向量/因子。
3. 分类数据用命名颜色向量,连续数据用colorRamp2函数。
圆形图中各个轨道的数据错位(不对齐)行/列的顺序在不同轨道不一致。检查用于每个circos.heatmap的数据的行名和列名是否严格对应。关键步骤:使用统一的排序顺序。从同一个聚类对象(如ht_main)获取row_ordercolumn_order,并以此排序所有相关数据。
图例不显示或位置不对1. 在circos.heatmap循环内尝试添加图例。
2. 图例被画在图形区域外。
1. 确认图例绘制代码在circos.clear()之后或独立的图形设备操作中。
2. 调整draw()函数中的x,y,just参数。
采用本文示例的方法,使用grid系统划分画布区域,分别绘制圆形热图和图例。
图形设备混乱,残留之前绘图未在开始新的圆形绘图前调用circos.clear()每次开始绘制一个新的圆形图前,都应调用circos.clear()养成习惯:在circos.par()之前,务必先circos.clear()
标签重叠或显示不全数据维度太高(基因/样本太多)。观察图形输出。1. 减小rownames.cex
2. 设置rownames.side = “none”隐藏标签。
3. 考虑只显示部分重要标签(需手动筛选数据)。
颜色不美观或对比度差颜色映射范围 (colorRamp2的断点) 与数据实际范围不匹配。summary()range()查看数据实际范围。根据数据实际的最小值、中位数、最大值来设置colorRamp2的断点。

9. 最佳实践与工程建议

将圆形热图复现用于实际科研项目时,请遵循以下建议:

  1. 数据预处理是核心:热图的美观度 80% 取决于数据质量。务必进行适当的标准化(如 Z-score)、过滤低表达基因、处理异常值。确保输入ComplexHeatmap的数据是清洁、有意义的。
  2. 脚本化与可重复:将整个绘图过程封装在一个 R 函数或 R Markdown 文档中。使用set.seed()保证随机过程可重复,并详细注释每一步的目的。
  3. 颜色选择原则
    • 分类数据:使用色盲友好、对比鲜明的调色板,如RColorBrewerSet2,Set3
    • 连续数据:表示差异时(如表达上调/下调),使用发散色系(如RColorBrewer::brewer.pal(11, “RdBu”))。表示梯度时(如丰度),使用连续色系(如viridis,magma)。
    • 保持一致:同一项目中,相同含义的注释应使用相同的颜色。
  4. 版本控制:记录你使用的ComplexHeatmapcirclize的版本号(packageVersion(“ComplexHeatmap”)),因为函数接口可能更新。
  5. 从简到繁:不要试图一步到位复现最复杂的图。先画出矩形热图,确保数据、注释、聚类正确。然后逐步转换为圆形布局,先画一个轨道,成功后再叠加第二个、第三个。
  6. 保存高清图片:使用pdf(),png(),tiff()等函数保存最终结果,并设置足够高的分辨率(如png(“heatmap.png”, width=3000, height=3000, res=300)),以满足期刊出版要求。
  7. 理解限制:圆形热图在样本数极多时可能显得拥挤,不如矩形热图直观。它更适合展示中等规模、且有强烈层级或循环关系的数据。

通过本文的拆解,你应该已经掌握了使用 ComplexHeatmap 和 circlize 包复现文献级圆形热图的完整流程。从数据模拟、基础热图构建,到圆形轨道的分层绘制、美学定制,再到问题排查和最佳实践,这套方法具有很高的通用性。下次当你看到一张令人惊艳的环形热图时,你不再只是欣赏,而是可以自信地说:“我知道这是怎么画出来的,并且我可以用 R 代码实现它。” 这才是数据可视化能力真正的提升。建议你将本文代码保存为模板,根据自己数据的特点进行调整和发挥,创造出属于你的高质量科研图表。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询