使用ComplexHeatmap与circlize包复现文献中的圆形热图
2026/9/14 12:14:46 网站建设 项目流程

在实际生物信息学或数据可视化项目中,我们经常需要复现高水平学术文献中的图表,这不仅是对分析结果的验证,也是提升自身绘图技能的重要途径。R语言的ComplexHeatmap包以其强大的灵活性和定制化能力,成为绘制复杂热图的首选工具。然而,当文献中出现一张设计精巧、布局独特的圆形热图(Circular Heatmap)时,许多开发者会感到无从下手,因为标准的矩形热图与圆形布局在数据映射、坐标转换和美学控制上存在显著差异。

本文将聚焦于一个具体目标:使用ComplexHeatmap包,完整复现一篇文献中出现的圆形热图。我们将从理解圆形热图的数据结构开始,逐步拆解其视觉元素(如环形分区、径向轴、扇形标签、颜色映射),并利用ComplexHeatmap的底层绘图单元和circlize包提供的环形布局框架进行实现。无论你是正在尝试重现特定论文结果的研究生,还是希望将环形热图应用于自己多组学数据整合展示的生物信息分析师,本文提供的步骤和代码都将为你提供一个清晰、可复现的解决方案。最终,你将掌握如何将矩阵数据转化为一个信息丰富、视觉吸引力强的环形热图,并理解其中每个自定义参数的控制逻辑。

1. 理解圆形热图:从数据到视觉的环形映射

在开始编码之前,必须厘清圆形热图与普通热图的核心区别,这决定了我们后续所有代码的设计思路。普通热图在笛卡尔坐标系下工作,行和列在二维平面上正交排布。而圆形热图将数据映射到极坐标系下,行(或列)沿着圆周排列,数据值则通过扇形的径向长度或扇区内的颜色块来呈现。

1.1 圆形热图的数据与视觉元素解构

假设文献中的热图展示了10个样本(Sample1-Sample10)在5条通路(PathwayA-PathwayE)上的活性得分矩阵。在圆形布局中,常见的呈现方式是:

  • 扇区:每个样本或每条通路占据圆周上的一个扇形区域。这通常通过将矩阵的行或列映射到圆周角度来实现。
  • 径向轴:从圆心向外辐射的轴线,用于表示数值的大小。数值可以映射为扇形的径向长度(类似于条形图),或者在固定半径的环形带内用颜色块表示(更常见于热图)。
  • 环形带:如果热图包含多层信息(例如,同时展示基因表达、突变状态和临床分组),每一层信息会占据一个同心圆环。
  • 颜色映射:每个数据单元格的颜色由数值和指定的颜色梯度函数决定,这与普通热图一致。

ComplexHeatmap包本身主要处理矩形热图,但它可以与circlize包无缝协作。circlize包专为环形可视化设计,提供了初始化画布(circos.initialize)、创建轨道(circos.track)和在轨道上绘图(如circos.heatmap)的函数。我们的策略是,利用circlize搭建环形框架和轨道,然后使用ComplexHeatmapHeatmap对象或低层函数来绘制每个轨道内的热图区块,并精细控制其样式。

1.2 环境准备与依赖安装

首先确保你的R环境已就绪。我们将主要依赖ComplexHeatmapcirclize以及用于数据处理的dplyr和色彩处理的circlize(它也包含颜色函数)。如果尚未安装,请在R控制台执行以下命令:

# 安装CRAN上的稳定版本 install.packages("ComplexHeatmap") install.packages("circlize") install.packages("dplyr") # 或者从Bioconductor安装ComplexHeatmap(通常更新更快) if (!require("BiocManager", quietly = TRUE)) install.packages("BiocManager") BiocManager::install("ComplexHeatmap")

安装完成后,加载必要的库:

library(ComplexHeatmap) library(circlize) library(dplyr)

2. 构建示例数据与复现目标定义

为了模拟真实的复现过程,我们创建一个与文献描述相似的模拟数据集。假设我们要复现的图展示了8个肿瘤样本在6个关键信号通路上的富集分数。

# 设置随机种子保证结果可重现 set.seed(123) # 创建样本和通路名称 samples <- paste0("Sample", sprintf("%02d", 1:8)) pathways <- c("Wnt/Beta-catenin", "PI3K/AKT/mTOR", "MAPK", "JAK/STAT", "TGF-Beta", "Hippo") # 生成一个8行(样本)x 6列(通路)的矩阵,数值范围在-2到2之间,模拟富集分数 data_matrix <- matrix(rnorm(8*6, mean=0, sd=1), nrow=8, ncol=6) rownames(data_matrix) <- samples colnames(data_matrix) <- pathways # 查看数据矩阵前几行 print(head(data_matrix))

接下来,我们需要定义复现的视觉目标。假设文献中的圆形热图具有以下特征(这是我们需要通过代码实现的):

  1. 整体布局:热图呈现在一个完整的圆环上,样本位于圆周,通路形成同心圆环。
  2. 颜色映射:数值从-2(蓝色)到0(白色)再到2(红色),使用连续型颜色梯度。
  3. 轨道分割:每个样本占据的扇形区域有细线分隔。
  4. 标签显示:样本标签显示在圆环外侧,通路标签显示在圆环内侧或末端。
  5. 图例:在图形一侧有一个连续型颜色图例。

3. 使用circlize构建环形画布与轨道

circlize是绘制环形图的基础。我们首先需要初始化环形画布,并定义扇区(这里对应我们的样本)。

# 1. 初始化环形画布 # 参数‘sectors‘定义了扇区,这里用样本名。‘xlim‘定义了每个扇区在径向的起始范围,这里我们暂时设为c(0,1),后续在轨道中具体定义。 circos.clear() # 清除之前的环形布局,避免叠加 circos.initialize(factors = samples, xlim = c(0, 1)) # 2. 创建第一个轨道(Track),用于放置通路热图。 # ‘track.height‘控制轨道高度(占整个半径的比例),‘bg.border‘设置轨道背景边框颜色。 circos.track(ylim = c(0, ncol(data_matrix)), track.height = 0.3, bg.border = NA, # 无背景边框 panel.fun = function(x, y) { # 当前扇区的名称 sector.index <- CELL_META$sector.index # 获取当前扇区对应的数据(一行) data_row <- data_matrix[sector.index, ] # 我们需要在这个扇区内,沿着径向(y轴方向)为每个通路绘制一个颜色块。 # 由于panel.fun在每个扇区内独立运行,我们循环每个通路(列) nr = ncol(data_matrix) for (i in 1:nr) { # 计算当前颜色块在径向的起始和结束位置 y_start = nr - i y_end = nr - i + 1 # 根据数值定义颜色 value = data_row[i] color_fun <- colorRamp2(c(-2, 0, 2), c("blue", "white", "red")) cell_color <- color_fun(value) # 绘制矩形颜色块 circos.rect(xleft = 0, xright = 1, ybottom = y_start, ytop = y_end, col = cell_color, border = "black") } }) # 添加样本标签到圆环外侧 circos.trackText(factors = samples, x = rep(0.5, length(samples)), y = rep(-0.1, length(samples)), labels = samples, facing = "clockwise", niceFacing = TRUE, adj = c(0.5, 1), cex = 0.8) # 添加通路标签(需要更精细的控制,通常放在另一个轨道或使用自定义方式) # 这里先绘制一个简单的内圈文本作为示意 for(i in 1:length(pathways)) { circos.text(x = 1.1, y = ncol(data_matrix) - i + 0.5, labels = pathways[i], sector.index = samples[1], track.index = 1, facing = "inside", cex = 0.7) } # 清除环形布局,为后续使用ComplexHeatmap高级功能做准备 circos.clear()

上述代码使用circlize的基础函数绘制了一个简易的圆形热图,但它有几个明显缺点:颜色映射逻辑分散在循环中、标签布局难以精确控制、缺乏ComplexHeatmap提供的丰富注释和自动图例。对于复现复杂文献图表,我们通常需要结合两者优势。

4. 整合ComplexHeatmap与circlize绘制高级圆形热图

ComplexHeatmapHeatmap类对象本身不能直接绘制在环形画布上。但是,我们可以利用ComplexHeatmapgrid图形系统兼容性,以及circlizecircos.heatmap函数(它内部支持类似热图的绘制)。更高级的方法是,将矩阵数据视为一个特殊的“轨道”,用ComplexHeatmap生成热图对象后,将其“分解”为图形元素,再逐一绘制到circlize定义的扇区和轨道中。这里介绍一种更直接、功能更强大的方法:使用ComplexHeatmapHeatmap函数结合circos.heatmap的思维。

实际上,circlize包提供了一个名为circos.heatmap的专用函数,它简化了在环形轨道上绘制热图的过程。我们可以这样操作:

# 重新初始化画布,这次为circos.heatmap准备 circos.clear() # 设置全局图形参数,如边距 par(mar = c(1, 1, 1, 1)) # 初始化扇区(样本),并设置每个扇区的大小比例(这里等分) circos.initialize(samples, xlim = c(0, 1)) # 定义颜色映射函数(与ComplexHeatmap的colorRamp2一致) col_fun <- colorRamp2(c(-2, 0, 2), c("blue", "white", "red")) # 使用circos.heatmap绘制热图轨道 # 注意:circos.heatmap期望数据矩阵的行名与扇区名匹配。 # 我们需要将矩阵转置,因为circos.heatmap默认将矩阵的列映射到扇区,行映射到径向位置。 # 我们的目标是:扇区=样本,径向位置=通路。因此原始矩阵的行是样本,列是通路,正好符合。 circos.heatmap(data_matrix, col = col_fun, track.height = 0.4, # 轨道高度 dend.side = "inside", # 聚类树显示在内侧(如果有) rownames.side = "outside", # 行名(样本)显示在外侧 rownames.cex = 0.8, # 行名字体 cluster = FALSE) # 暂时不聚类 # 添加快通路标签。circos.heatmap没有直接提供列名标签参数,需要手动添加。 # 我们可以在热图轨道内侧添加一个文本轨道。 circos.track(ylim = c(0, ncol(data_matrix)), track.height = 0.05, bg.border = NA, panel.fun = function(x, y) { for(i in 1:ncol(data_matrix)) { circos.text(x = 0.5, y = ncol(data_matrix) - i + 0.5, labels = colnames(data_matrix)[i], cex = 0.7, facing = "bending.inside", adj = c(0.5, 0.5)) } }) # 添加图例 # 首先跳出环形绘图区域,在图形右侧添加图例 legend_x <- 1.05 legend_y <- 0.5 color_bar_height <- 0.3 color_bar_width <- 0.03 # 绘制颜色条 for (i in 1:100) { y_val <- (i-1)/100 * color_bar_height rect(legend_x, legend_y + y_val, legend_x + color_bar_width, legend_y + y_val + color_bar_height/100, col = col_fun(-2 + (i-1)/99 * 4), border = NA) } # 添加图例标签 text(legend_x + color_bar_width*1.5, legend_y, "-2", adj = c(0, 0.5), cex=0.7) text(legend_x + color_bar_width*1.5, legend_y + color_bar_height/2, "0", adj = c(0, 0.5), cex=0.7) text(legend_x + color_bar_width*1.5, legend_y + color_bar_height, "2", adj = c(0, 0.5), cex=0.7) text(legend_x, legend_y + color_bar_height*1.1, "Enrichment Score", adj = c(0.5, 0), cex=0.8) # 最终清除布局 circos.clear()

这段代码生成了一个更接近文献风格的圆形热图。circos.heatmap自动处理了每个扇区内的颜色块绘制和行标签。我们手动添加了通路标签轨道和图例。

5. 高级定制:复现文献中的复杂细节

文献中的图表往往包含更多细节,如样本分组信息、显著性标记、缺失值处理等。这需要更精细地控制circlize的绘图函数和ComplexHeatmap的注释功能。以下是一些常见需求的实现思路:

5.1 添加样本分组注释环

假设样本分为“GroupA”和“GroupB”,我们需要在热图外层添加一个分组注释环。

# 创建分组信息 sample_groups <- rep(c("GroupA", "GroupB"), each=4) names(sample_groups) <- samples group_colors <- c("GroupA" = "darkorange", "GroupB" = "steelblue") circos.clear() circos.initialize(samples, xlim = c(0, 1)) # 首先绘制分组轨道(在最外层) circos.track(ylim = c(0, 1), track.height = 0.05, bg.border = NA, panel.fun = function(x, y) { sector.index <- CELL_META$sector.index group <- sample_groups[sector.index] circos.rect(0, 0, 1, 1, col = group_colors[group], border = NA) }) # 然后绘制热图轨道(在内层) circos.heatmap(data_matrix, col = col_fun, track.height = 0.3, dend.side = "inside", cluster = FALSE) # ... (添加通路标签和图例的代码与之前类似)

5.2 在热图单元格中添加文本或符号

如果需要在特定单元格(如p值<0.05)上添加星号(*),可以在circos.heatmap之后,遍历每个扇区和单元格进行绘制。

# 假设我们有一个与data_matrix同样大小的逻辑矩阵,标记显著项 significant_matrix <- matrix(runif(8*6) < 0.2, nrow=8, ncol=6) circos.track(ylim = c(0, ncol(data_matrix)), track.height = 0.3, bg.border = NA, panel.fun = function(x, y) { sector.index <- CELL_META$sector.index data_row_idx <- which(rownames(data_matrix) == sector.index) for (i in 1:ncol(data_matrix)) { if(significant_matrix[data_row_idx, i]) { # 计算当前单元格的中心坐标 cell_center_y = ncol(data_matrix) - i + 0.5 # 在单元格中心绘制星号 circos.text(x = 0.5, y = cell_center_y, labels = "*", cex = 1.2, col = "black", adj = c(0.5, 0.5)) } } })

5.3 控制扇区顺序与聚类

circos.heatmap支持通过cluster参数对行(样本)进行聚类。你也可以通过预定义的顺序来初始化扇区。

# 方法1:使用聚类结果重新排序样本 row_dend <- hclust(dist(data_matrix)) # 对样本进行层次聚类 sample_order <- rownames(data_matrix)[row_dend$order] # 按照聚类顺序初始化扇区 circos.clear() circos.initialize(sample_order, xlim = c(0, 1)) circos.heatmap(data_matrix[sample_order, ], col = col_fun, cluster = FALSE) # 关闭内部聚类,因为我们已经排好序 # 方法2:手动指定顺序,例如按某个通路的值降序 manual_order <- rownames(data_matrix)[order(data_matrix[, "PI3K/AKT/mTOR"], decreasing = TRUE)] circos.initialize(manual_order, xlim = c(0, 1))

6. 常见问题排查与调试

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

问题现象可能原因检查与解决方式
图形空白或只有部分扇区circos.clear()未调用,导致新旧布局冲突。在开始新的环形图之前,务必先运行circos.clear()
标签重叠或显示不全扇区太窄或标签太长、字体太大。调整track.height增加轨道空间;减小cex参数;使用facing参数调整标签方向(如“clockwise”,“bending.inside”)。
颜色映射不正确colorRamp2的断点(breaks)与数据范围不匹配。使用range(data_matrix)检查数据实际范围,确保颜色断点能覆盖它。对于不对称数据,可能需要调整断点位置。
热图单元格错位circos.heatmap的数据行名与circos.initialize的扇区因子不匹配或顺序不一致。确保data_matrix的行名完全包含且顺序与初始化扇区的因子一致。使用rownames(data_matrix)samples进行比对。
图例不显示或位置错误图例是在环形绘图区域外用基础图形函数绘制的,坐标系统不同。使用par("usr")获取当前图形区域的坐标范围,根据此范围计算图例的绝对位置。或者,考虑使用ComplexHeatmap::Legend函数生成图例对象,再用grid系统绘制到特定位置。
运行后图形设备混乱多次绘图未正确关闭或清理。在脚本最后使用dev.off()关闭图形设备,或使用pdf(“filename.pdf”)等函数将图形直接输出到文件。

一个关键的调试技巧是:分步绘制。不要试图一次性写出完美的最终代码。先初始化画布并绘制一个简单的轨道,确认扇区和坐标正确。然后逐步添加热图、标签、注释环。每添加一个元素,都运行一次查看中间结果。

7. 生产环境最佳实践与扩展建议

当你需要将复现的图形用于论文或报告时,以下几点至关重要:

  1. 图形输出与分辨率:使用pdf(),png(),tiff()等函数指定输出文件、尺寸和分辨率。对于出版级图片,DPI通常需要300或更高。

    png("circular_heatmap.png", width=2000, height=2000, res=300) # ... 你的绘图代码 ... dev.off()
  2. 代码模块化与可重用性:将绘制圆形热图的代码封装成一个函数。这样,只需更换输入数据、颜色方案和标签,即可快速生成新的图形。

    draw_circular_heatmap <- function(data_mat, row_colors = NULL, col_colors = NULL, main_title = "", filename = NULL, ...) { # 参数检查、默认值设置 # 初始化图形设备 # 核心绘图流程 # 关闭设备(如果指定了文件名) # 返回图形对象或保存文件 }
  3. 颜色方案的可访问性:考虑色盲友好配色。避免使用红-绿对比。可以使用viridisRColorBrewer包中的配色。

    library(RColorBrewer) col_fun <- colorRamp2(breaks = c(-2, 0, 2), colors = brewer.pal(11, "RdBu")[c(9,6,3)]) # 蓝-白-红 # 或者使用viridis library(viridis) col_fun <- colorRamp2(c(-2, 2), viridis(10))
  4. 数据验证:在绘图函数开头,检查输入矩阵是否包含NAInf,并决定处理方式(如设为灰色或跳过)。确保行名和列名存在且唯一。

  5. 扩展方向

    • 交互式圆形热图:探索circlizeplotlyggiraph的结合,创建可悬停查看数值的交互式环形热图。
    • 多层多组学数据整合:利用多个同心圆轨道,同时展示基因表达、DNA甲基化、拷贝数变异等多组学数据,用ComplexHeatmapHeatmapAnnotation思想管理每层的注释。
    • 与Shiny集成:构建一个Shiny应用,允许用户上传自己的矩阵数据,动态调整颜色方案、聚类方法、标签显示等参数,实时生成圆形热图。

复现文献图表是提升数据可视化能力的绝佳练习。通过拆解目标、理解circlize的环形坐标系和ComplexHeatmap的美学控制,你不仅能够重现特定的图形,更能获得定制任何复杂环形可视化需求的能力。核心在于将全局的环形布局思维与局部的单元格绘图命令相结合,并充分利用R图形系统的可组合性。

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

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

立即咨询