1. 为什么需要双向条形图展示富集结果
在生物信息学分析中,富集分析是解读高通量实验数据的关键步骤。当我们拿到一组差异表达基因后,通常会用GO、KEGG等数据库进行功能富集分析,找出这些基因显著富集的生物学通路或功能模块。但传统的柱状图或气泡图在展示富集结果时存在明显局限:
- 信息密度不足:普通柱状图只能展示p值或富集分数等单一维度信息
- 对比困难:当需要比较上下调基因的富集差异时,需要分别绘制两个图表
- 视觉干扰:多组数据并列展示时容易造成视觉混乱
Cancer Cell等顶级期刊常用的双向条形图(也称为旋风图、蝴蝶图)完美解决了这些问题。这种图表的核心优势在于:
- 将上下调基因的富集结果对称展示在同一坐标系中
- 用左右方向区分调控方向(左为下调,右为上调)
- 用条形长度表示富集显著性(通常取-log10(p-value))
- 允许同时展示多个比较组的结果
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据准备与预处理
2.1 富集分析结果的标准格式
一个典型的富集分析结果应包含以下字段(以GO富集为例):
text复制Term Description p.adjust Count GeneRatio Direction
GO:0006915 apoptosis 1.23E-08 35 35/200 Up
GO:0043067 regulation 3.45E-05 28 28/200 Down
关键字段说明:
Term:功能术语IDDescription:功能描述p.adjust:校正后的p值Count:富集到的基因数GeneRatio:富集基因比例Direction:调控方向(Up/Down)
2.2 数据清洗与转换
在R中我们可以用以下代码处理数据:
r复制library(tidyverse)
# 读取富集结果
enrich_data <- read_csv("enrichment_results.csv")
# 数据转换
plot_data <- enrich_data %>%
mutate(
log_p = -log10(p.adjust),
Direction = factor(Direction, levels = c("Up", "Down"))
) %>%
arrange(Direction, desc(log_p)) %>%
slice_max(n = 10, order_by = log_p, by = Direction)
这段代码完成了:
- 计算-log10(p.adjust)作为条形长度指标
- 确保Direction因子水平顺序正确
- 筛选每个方向top10最显著的条目
3. 使用ggplot2绘制基础双向条形图
3.1 基本绘图代码
r复制library(ggplot2)
base_plot <- ggplot(plot_data,
aes(x = ifelse(Direction == "Up", log_p, -log_p),
y = reorder(Description, log_p),
fill = Direction)) +
geom_col(width = 0.7) +
scale_x_continuous(
labels = abs,
expand = expansion(mult = c(0.05, 0.05))
) +
labs(x = "-log10(adjusted p-value)",
y = NULL,
title = "GO Enrichment Analysis") +
theme_minimal(base_size = 12)
print(base_plot)
关键参数解析:
ifelse(Direction == "Up", log_p, -log_p):这是实现双向展示的核心技巧reorder(Description, log_p):按显著性排序描述文本scale_x_continuous(labels = abs):确保坐标轴标签显示为正值
3.2 颜色与主题优化
Cancer Cell风格的典型配色方案:
r复制cancer_cell_palette <- c("Up" = "#E64B35", "Down" = "#3182bd")
enhanced_plot <- base_plot +
scale_fill_manual(values = cancer_cell_palette) +
theme(
panel.grid.major.y = element_blank(),
panel.grid.minor = element_blank(),
axis.line.y = element_line(color = "black"),
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "top"
)
4. 高级定制技巧
4.1 添加富集基因数标签
在条形末端添加基因数量信息:
r复制enhanced_plot +
geom_text(
aes(label = Count,
x = ifelse(Direction == "Up", log_p + 0.5, -log_p - 0.5)),
size = 3.5,
color = "black"
)
4.2 多组比较展示
当有多个比较组(如不同时间点)时,可以使用分面:
r复制multi_group_plot <- ggplot(multi_data,
aes(x = ifelse(Direction == "Up", log_p, -log_p),
y = reorder(Description, log_p),
fill = Direction)) +
geom_col(width = 0.7) +
facet_grid(~ Group, scales = "free_x") +
# 其他样式设置...
4.3 交互式可视化
使用plotly创建可交互版本:
r复制library(plotly)
interactive_plot <- ggplotly(enhanced_plot, tooltip = c("y", "x", "fill"))
htmlwidgets::saveWidget(interactive_plot, "enrichment_plot.html")
5. 常见问题与解决方案
5.1 文本重叠问题
当条目描述过长时,可以:
- 手动换行:
stringr::str_wrap(Description, width = 30) - 调整绘图区域比例:
coord_fixed(ratio = 0.5) - 减小字体:
theme(axis.text.y = element_text(size = 10))
5.2 显著性差异过大导致比例失衡
解决方案:
- 设置对称坐标限:
xlim = c(-max_p, max_p) - 对p值进行winsorize处理:
r复制max_p <- 10 # 设置最大显示范围
plot_data <- plot_data %>%
mutate(log_p = pmin(log_p, max_p))
5.3 导出高质量图片
期刊通常要求300dpi以上的TIFF或PDF:
r复制ggsave("enrichment_plot.tiff",
plot = enhanced_plot,
device = "tiff",
dpi = 300,
width = 8,
height = 6,
units = "in")
6. 完整代码示例
r复制library(tidyverse)
library(ggplot2)
# 数据准备
enrich_data <- read_csv("enrichment_results.csv")
plot_data <- enrich_data %>%
mutate(
log_p = -log10(p.adjust),
Direction = factor(Direction, levels = c("Up", "Down"))
) %>%
group_by(Direction) %>%
slice_max(n = 10, order_by = log_p) %>%
ungroup() %>%
arrange(Direction, log_p) %>%
mutate(Description = str_wrap(Description, width = 30))
# 颜色设置
cancer_cell_palette <- c("Up" = "#E64B35", "Down" = "#3182bd")
# 绘图
ggplot(plot_data,
aes(x = ifelse(Direction == "Up", log_p, -log_p),
y = reorder(Description, log_p),
fill = Direction)) +
geom_col(width = 0.7) +
geom_text(
aes(label = Count,
x = ifelse(Direction == "Up", log_p + 0.5, -log_p - 0.5)),
size = 3.5,
color = "black"
) +
scale_x_continuous(
labels = abs,
limits = c(-max(plot_data$log_p) * 1.1, max(plot_data$log_p) * 1.1),
expand = expansion(mult = c(0.05, 0.05))
) +
scale_fill_manual(values = cancer_cell_palette) +
labs(x = "-log10(adjusted p-value)",
y = NULL,
title = "GO Enrichment Analysis") +
theme_minimal(base_size = 12) +
theme(
panel.grid.major.y = element_blank(),
panel.grid.minor = element_blank(),
axis.line.y = element_line(color = "black"),
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "top"
)
