文字叠加的富集分析柱状图

作者

[编辑] 严彬

[贡献] 郑虎.

修改于

2026-01-17

文字叠加富集柱状图是一种用于高密度展示功能富集分析结果(如 GO、KEGG)的可视化工具。它通常以圆角柱状图的长度映射富集显著性(adjust p value),并利用图形内部空间直接叠加标注通路名称与核心基因列表,同时辅以左侧色块与气泡来区分功能分类及基因数量。

这种图表形态改变了传统条形图文本与图形分离的布局,特别适用于单细胞亚群标记基因的功能注释展示,以及其他需要同时呈现“通路显著性”与“具体驱动基因”的研究场景。其可视化效果不仅优化了绘图空间利用率,还便于研究者在概览生物学功能的同时,快速锁定导致通路富集的关键基因特征。

Example

TextEnrichmentBarplot DEMO

环境配置

  • 系统要求 跨平台(Linux/MacOS/Windows)

  • 编程语言: R

  • 依赖包: tidyverse, ggprism, gground

# 安装包
if (!requireNamespace("tidyverse", quietly = TRUE)){
  install.packages("tidyverse")
} 
if (!requireNamespace("ggprism", quietly = TRUE)){
  install.packages("ggprism")
} 
if (!requireNamespace("gground", quietly = TRUE)) {
  remotes::install_github("dxsbiocc/gground")
}
# 加载包
library(tidyverse)
library(ggprism)
library(gground)

数据准备

1.上游富集分析流程 (参考)

在实际的生物信息学分析中,我们通常使用 clusterProfiler 进行富集分析。

注记

以下代码仅用于演示上游分析流程(获取原始数据),本教程绘图将直接使用下文的示例数据。

# 1. 环境准备
library(clusterProfiler)
library(org.Hs.eg.db)

#2. 运行富集分析
# 假设 gene_list 是差异基因的 Entrez ID 向量

# 2.1 GO 富集分析 (同时获取 BP/CC/MF)
ego <- enrichGO(
  gene     = gene_list,
  OrgDb    = org.Hs.eg.db,
  ont      = "ALL",
  readable = TRUE  # 自动将结果中的 ID 转换为 Symbol
)

# 2.2 KEGG 富集分析
ekegg <- enrichKEGG(
  gene     = gene_list,
  organism = 'hsa'
)
# KEGG 结果默认为 Entrez ID,需手动转为 Symbol 以匹配绘图数据格式
ekegg <- setReadable(ekegg, OrgDb = org.Hs.eg.db, keyType = "ENTREZID")

# 3. 数据清洗与合并
# 提取 GO 结果 (重命名 ONTOLOGY 为 Category)
df_go <- as.data.frame(ego) %>% 
  select(Category = ONTOLOGY, Description, p.adjust, Count, geneID)

# 提取 KEGG 结果 (手动添加 Category 列并赋值为 "KEGG")
df_kegg <- as.data.frame(ekegg) %>% 
  mutate(Category = "KEGG") %>% 
  select(Category, Description, p.adjust, Count, geneID)

# 合并所有结果 -> 得到最终用于绘图的 raw_data
raw_data <- rbind(df_go, df_kegg)

2.示例数据加载与清洗

为了方便教程运行,我们导入来自 DAVID 的网络药理学注释分析通路富集数据,该数据包含了 GO (BP/CC/MF) 和 KEGG 的富集结果。

#1.读取数据
raw_data <- read_tsv("https://bizard-1301043367.cos.ap-guangzhou.myqcloud.com/DAVID.txt")

# 2. 数据清洗
# 将 DAVID 的格式转换为绘图所需的标准格式
raw_data <- raw_data %>%
  # 2.1 提取分类标签
  mutate(Category = case_when(
    grepl("BP_DIRECT", Category) ~ "BP",
    grepl("CC_DIRECT", Category) ~ "CC",
    grepl("MF_DIRECT", Category) ~ "MF",
    grepl("KEGG_PATHWAY", Category) ~ "KEGG",
    TRUE ~ "Other"
  )) %>%
  # 只保留GO、KEGG结果
  filter(Category %in% c("BP", "CC", "MF", "KEGG")) %>%
  
  # 2.2 清洗通路名称 (去掉 ID)
  mutate(Description = sub("^.*~|.*:", "", Term)) %>%
  
  # 2.3 重命名列
  rename(
    p.adjust = FDR,
    geneID = Genes
  ) %>%
  
  # 2.4 格式化基因列表
  mutate(geneID = gsub(", ", "/", geneID)) 

3.创建绘图数据

# 1.筛选每个分类下最显著的 Top 5 通路
use_pathway <- raw_data %>%
  arrange(p.adjust) %>%
  group_by(Category) %>%
  slice_head(n = 5) %>%
  ungroup()

# 2. 构造绘图辅助列
use_pathway <- use_pathway %>%
  # 设置因子水平:决定图例和绘图的垂直顺序
  mutate(Category = factor(Category, levels = rev(c('BP', 'CC', 'MF', 'KEGG')))) %>%
  # 二次排序
  arrange(Category, p.adjust) %>%
  # 固定 Y 轴顺序
  mutate(Description = factor(Description, levels = Description)) %>%
  # 添加 index 列作为 Y 轴坐标
  tibble::rowid_to_column('index')
# 3. 构造左侧分类色块的数据 (rect.data)
#布局参数
width <- 0.5 
# 计算 X 轴最大长度,用于绘制底部横线
xaxis_max <- max(-log10(use_pathway$p.adjust)) + 1

rect.data <- use_pathway %>%
  arrange(Category) %>%
  group_by(Category) %>%
  summarize(n = n(), .groups = "drop") %>%
  mutate(
    xmin = -3 * width,          
    xmax = -2 * width,         
    ymax = cumsum(n),          
    ymin = lag(ymax, default = 0) + 0.6, 
    ymax = ymax + 0.4          
  )

# 查看数据结构
head(use_pathway)
# A tibble: 6 × 15
  index Category Term        Count   `%`   PValue geneID `List Total` `Pop Hits`
  <int> <fct>    <chr>       <dbl> <dbl>    <dbl> <chr>         <dbl>      <dbl>
1     1 KEGG     hsa04080:N…    15  36.6 2.07e-10 GABRB…           40        368
2     2 KEGG     hsa04726:S…    10  24.4 1.16e- 9 GABRB…           40        115
3     3 KEGG     hsa05022:P…    11  26.8 3.19e- 5 IL1A/…           40        483
4     4 KEGG     hsa05321:I…     5  12.2 1.89e- 4 IL10/…           40         66
5     5 KEGG     hsa04728:D…     6  14.6 2.6 e- 4 GSK3B…           40        132
6     6 MF       GO:0051378…     6  14.6 2.37e-11 HTR1A…           41         12
# ℹ 6 more variables: `Pop Total` <dbl>, FoldEnrichment <dbl>,
#   Bonferroni <dbl>, Benjamini <dbl>, p.adjust <dbl>, Description <fct>

可视化

1. 基础绘图(简化版)

该版本主要展示了核心的圆角柱状图和左侧的分类色块,去除了基因列表等过多文本信息,适合需要简洁展示的场景。

# 定义配色
pal <- c('#eaa052', '#b74147', '#90ad5b', '#23929c')

#其它推荐配色
#pal <- c('#c3e1e6', '#f3dfb7', '#dcc6dc', '#96c38e')
#pal <- c('#7bc4e2', '#acd372', '#fbb05b', '#ed6ca4')

# 调整简化版的左侧色块位置参数
rect.data.simple <- rect.data %>%
  mutate(
    xmin = -1.5 * width,
    xmax = -0.5 * width
  )

p1 <- ggplot(use_pathway, aes(-log10(p.adjust), y = index, fill = Category)) +
  # 1. 圆角柱状图主体
  geom_round_col(aes(y = Description), width = 0.6, alpha = 0.8) +
  
  # 2. 通路名称文本
  geom_text(aes(x = 0.05, label = Description), hjust = 0, size = 4) +
  
  # 3. 左侧分类色块
  geom_round_rect(
    aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = Category),
    data = rect.data.simple,
    radius = unit(2, 'mm'),
    inherit.aes = FALSE
  ) +
  
  # 4. 左侧分类文字
  geom_text(
    aes(x = (xmin + xmax) / 2, y = (ymin + ymax) / 2, label = Category),
    data = rect.data.simple,
    size = 3,
    inherit.aes = FALSE
  ) +
  
  # 5. 底部装饰横线
  geom_segment(
    aes(x = 0, y = 0, xend = xaxis_max, yend = 0),
    linewidth = 1,
    inherit.aes = FALSE
  ) +
  
  # 6. 样式调整
  labs(y = NULL, x = "-log10(p.adjust)") +
  scale_fill_manual(name = 'Category', values = pal) +
  scale_x_continuous(breaks = seq(0, xaxis_max, 2), expand = expansion(c(0, 0.1))) +
  theme_prism() +
  theme(
    axis.text.y = element_blank(),
    axis.line.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.line.x = element_blank(), # 隐藏系统自带的 X 轴线,只保留我们用 geom_segment 画的那条
    legend.title = element_text(),
    legend.position = "right" 
  )

p1

基础富集分析柱状图

2. 进阶绘图 (详细信息版)

该版本在简化版的基础上,增加了:

  • Gene List: 在通路名称下方显示基因列表(灰色斜体)。

  • Count Bubbles: 在左侧展示富集基因的数量(气泡大小)。

  • Layout: 调整了左侧色块的位置,为气泡留出空间。

p2 <- ggplot(use_pathway, aes(-log10(p.adjust), y = index, fill = Category)) +
  # 1. 主体圆角柱
  geom_round_col(aes(y = Description), width = 0.6, alpha = 0.8) +
  
  # 2. 通路名称 
  geom_text(aes(x = 0.05, label = Description), hjust = 0, size = 5) +
  
  # 3. 基因列表
  geom_text(
    aes(x = 0.1, label = geneID, colour = Category),
    hjust = 0, vjust = 2.6, size = 3, fontface = 'italic',
    show.legend = FALSE
  ) +
  
  # 4. 左侧基因计数气泡
  geom_point(aes(x = -width, size = Count,fill = Category), shape = 21, stroke = 1) +
  geom_text(aes(x = -width, label = Count), size = 3) +
  
  # 5. 左侧分类色块
  geom_round_rect(
    aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = Category),
    data = rect.data,
    radius = unit(2, 'mm'),
    inherit.aes = FALSE
  ) +
  geom_text(
    aes(x = (xmin + xmax) / 2, y = (ymin + ymax) / 2, label = Category),
    data = rect.data,
    inherit.aes = FALSE,
    color = "black", fontface = "bold"
  ) +
  
  # 6. 底部横线
  geom_segment(
    aes(x = 0, y = 0, xend = xaxis_max, yend = 0),
    linewidth = 1.5,
    inherit.aes = FALSE
  ) +
  
  # 7. 标尺与配色
  labs(y = NULL, x = "-log10(p.adjust)") +
  scale_fill_manual(name = 'Category', values = pal) +
  scale_colour_manual(values = pal) + 
  scale_size_continuous(name = 'Count', range = c(4, 10)) +
  scale_x_continuous(breaks = seq(0, xaxis_max, 2), expand = expansion(c(0, 0))) +
  
  # 8. 主题美化
  theme_prism() +
  theme(
    axis.text.y = element_blank(),
    axis.line = element_blank(),
    axis.ticks.y = element_blank(),
    legend.title = element_text(),
    plot.margin = margin(l = 10, r = 10) 
  )

p2

进阶富集分析柱状图

参考资料

[1] 生信医道. 超好看的单细胞GO,KEGG富集分析图!确定不来看看吗? [Internet]. 微信公众号: 生信医道. 2025 [cited 2025 Dec 30]. Available from: https://mp.weixin.qq.com/s/pY1S0EJQAhdezvxeNt-SzA