
如果你在生物信息学或数据可视化领域工作一定见过那些发表在顶级期刊上的精美热图。它们色彩绚丽、布局精巧不仅能展示海量数据还能清晰地揭示样本间的聚类关系和基因表达模式。其中圆形热图Circular Heatmap因其独特的环形布局和强大的信息承载能力尤其引人注目。你可能尝试过用基础的heatmap()函数却发现它连基本的行列注释都难以实现你也可能用过pheatmap但在面对复杂的、分面式的环形布局时感到束手无策。这时你需要的不是一个简单的绘图工具而是一个能够让你像搭积木一样自由构建复杂热图的“可视化引擎”。这篇文章要解决的正是这个痛点如何利用 R 语言中的“核武器”级绘图包——ComplexHeatmap从零开始复现一篇文献中极具设计感的圆形热图。我们不止步于“画出图”更要深入理解其背后的设计逻辑如何组织数据、如何分层注释、如何控制美学细节以及如何将多个简单热图组合成一个复杂的环形叙事。读完本文你将掌握一套可复用的方法论不仅能还原文献中的图更能将其思路应用于你自己的数据分析项目产出可直接用于发表的高质量可视化结果。1. 为什么是 ComplexHeatmap超越基础热图工具的边界在深入代码之前我们必须先理解为什么选择 ComplexHeatmap。R 语言中绘制热图的工具不少但它们在面对复杂需求时短板非常明显基础heatmap()函数功能极其有限自定义选项少几乎无法添加行列注释更别提复杂布局。pheatmap包比基础函数强大许多提供了注释和聚类功能是许多人的入门选择。但其布局是固定的“方块”难以实现非矩形的如圆形、分面的或高度定制化的组合图形。ggplot2geom_tile灵活性极高理论上可以画出任何图形。但对于热图这种高度结构化的数据你需要手动处理聚类、树状图、注释对齐等大量细节代码会变得异常冗长和复杂。ComplexHeatmap 的核心优势在于其“组合”与“声明式”的绘图哲学。它将一个热图分解为多个可独立定义的“组件”Heatmap、行注释、列注释、标题、图例等然后像搭积木一样将它们组合起来。更重要的是它允许你将多个独立的热图对象Heatmap或HeatmapList再次组合形成更复杂的图形比如并排对比、上下堆叠或者我们本文的重点——环形布局。简单来说pheatmap是给你一个装修好的“精装房”而ComplexHeatmap 是给了你一套功能强大的“建筑工具”和“预制件”让你可以自由设计并建造出任何你想要的“房子”包括一座“圆形城堡”。学习它虽然有一定门槛但一旦掌握你在数据可视化上将获得前所未有的自由。2. 核心概念拆解理解 ComplexHeatmap 的构建模块要建造“圆形城堡”我们先要认识手中的“砖块”和“工具”。以下是 ComplexHeatmap 中最关键的几个概念Heatmap对象这是核心。一个Heatmap对象不仅仅包含矩阵数据对应的色块还隐式地包含了与之关联的行名、列名、行列树状图如果进行了聚类以及该热图专属的图例。你可以通过Heatmap()函数创建它。注释Annotation用于在行或列旁边添加额外的信息条带如样本的分组、基因的染色体位置、表达量的高低等。分为行注释 (rowAnnotation) 和列注释 (columnAnnotation)。注释可以包含多种图形元素箱线图、条形图、点图、文本等。热图列表HeatmapList当调用draw()函数绘制一个或多个Heatmap对象时内部会创建一个HeatmapList来管理这些热图的整体布局和所有全局图例。%v%和操作符这是实现组合的关键。%v%用于垂直上下连接热图用于水平左右连接热图或添加注释。circos.heatmap与circos.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. 核心实战拆解与复现圆形热图现在进入最关键的部分。假设文献中的圆形热图由内到外包含以下轨道最内圈基因通路注释行注释。中间圈基因表达量热图主体。最外圈样本分组注释列注释。我们将使用circlize的circos.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()这段代码是复现圆形热图的核心。它演示了如何使用circos.par设置圆形画布。使用circos.heatmap分轨道绘制数据。通过track.height控制轨道厚度。通过rownames.side控制标签位置。使用预排序的数据保证热图、行注释、列注释的对应关系一致。利用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_order和column_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. 最佳实践与工程建议将圆形热图复现用于实际科研项目时请遵循以下建议数据预处理是核心热图的美观度 80% 取决于数据质量。务必进行适当的标准化如 Z-score、过滤低表达基因、处理异常值。确保输入ComplexHeatmap的数据是清洁、有意义的。脚本化与可重复将整个绘图过程封装在一个 R 函数或 R Markdown 文档中。使用set.seed()保证随机过程可重复并详细注释每一步的目的。颜色选择原则分类数据使用色盲友好、对比鲜明的调色板如RColorBrewer的Set2,Set3。连续数据表示差异时如表达上调/下调使用发散色系如RColorBrewer::brewer.pal(11, “RdBu”)。表示梯度时如丰度使用连续色系如viridis,magma。保持一致同一项目中相同含义的注释应使用相同的颜色。版本控制记录你使用的ComplexHeatmap和circlize的版本号packageVersion(“ComplexHeatmap”)因为函数接口可能更新。从简到繁不要试图一步到位复现最复杂的图。先画出矩形热图确保数据、注释、聚类正确。然后逐步转换为圆形布局先画一个轨道成功后再叠加第二个、第三个。保存高清图片使用pdf(),png(),tiff()等函数保存最终结果并设置足够高的分辨率如png(“heatmap.png”, width3000, height3000, res300)以满足期刊出版要求。理解限制圆形热图在样本数极多时可能显得拥挤不如矩形热图直观。它更适合展示中等规模、且有强烈层级或循环关系的数据。通过本文的拆解你应该已经掌握了使用 ComplexHeatmap 和 circlize 包复现文献级圆形热图的完整流程。从数据模拟、基础热图构建到圆形轨道的分层绘制、美学定制再到问题排查和最佳实践这套方法具有很高的通用性。下次当你看到一张令人惊艳的环形热图时你不再只是欣赏而是可以自信地说“我知道这是怎么画出来的并且我可以用 R 代码实现它。” 这才是数据可视化能力真正的提升。建议你将本文代码保存为模板根据自己数据的特点进行调整和发挥创造出属于你的高质量科研图表。