跳到主要内容

08 图表组织:figure 怎么排版讲故事

分析做完了、故事也有了,但审稿人第一眼看的是 figure。一套好的 figure 能让读者在 30 秒内抓住你的核心发现;一套差的 figure 会让审稿人觉得"这个人不专业"。这一章讲的是如何把分析结果组织成一套逻辑清晰、视觉专业的论文图表。

论文 figure 的逻辑结构

一篇典型的组学文章有 4-6 个主图(Main Figures),每个主图回答一个核心问题。主图之间的关系应该是递进的:Figure 1 的结论引出 Figure 2 的问题,Figure 2 的结论引出 Figure 3 的问题……读者看完所有主图,就能理解你的完整故事。

以 TCGA-LIHC 脂代谢预后 signature 项目为例,一个 6-figure 的布局方案:

Figure核心问题内容子图
Fig 1肝癌中哪些基因差异表达?差异分析概览(A) 火山图 (B) 热图 top50 (C) GO 富集 (D) KEGG 富集
Fig 2脂代谢基因和预后有什么关系?单因素 Cox 筛选(A) 森林图 top20 (B) 相关性网络 (C) 表达-stage 箱线图
Fig 3能否构建预后 signature?LASSO 建模(A) LASSO 系数路径 (B) CV 曲线 (C) Risk score 分布 (D) 生存状态散点
Fig 4Signature 能区分预后吗?生存验证(A) KM 曲线-训练集 (B) KM 曲线-验证集 (C) ROC 曲线 (D) 多变量 Cox 森林图
Fig 5突变和脂代谢有什么关联?突变分析(A) Oncoplot (B) 突变-表达箱线图 (C) 突变-生存 KM
Fig 6外部验证结果如何?ICGC 验证(A) KM 曲线-ICGC (B) C-index 对比 (C) 校准曲线

这个布局的逻辑链条是:发现差异 → 聚焦脂代谢 → 构建模型 → 验证模型 → 探索机制 → 外部验证。每一步都是前一步的自然延伸。

主图 vs 补充图

不是所有结果都值得放主图。判断标准很简单:如果去掉这张图,故事还能讲通吗? 如果能,它就应该是补充图(Supplementary Figure)。

主图放什么:

  • 推动故事前进的关键证据
  • 读者必须看到才能理解你的结论的图
  • 方法学上的核心步骤(如 LASSO 的 CV 曲线)

补充图放什么:

  • 质控结果(PCA、样本聚类、归一化前后对比)
  • 参数敏感性分析(不同阈值下的结果对比)
  • 多工具对比(DESeq2 vs edgeR vs limma 的 Venn 图)
  • 次要发现(和主线相关但不是核心的结果)
  • 完整的基因列表或通路列表

以 TCGA-LIHC 项目为例,以下内容适合放补充图:

  • Supplementary Fig 1:数据质控——PCA 图、样本聚类树、library size 分布
  • Supplementary Fig 2:差异分析补充——MA plot、三工具 Venn 图、不同阈值下 DEG 数量
  • Supplementary Fig 3:富集分析补充——完整 GO/KEGG 结果、enrichment map
  • Supplementary Fig 4:生存分析补充——不同 cutoff 的 KM 曲线、DFS 分析
  • Supplementary Fig 5:Signature 基因的单独 KM 曲线(每个基因一张)

配色与标注规范

配色不是审美问题,是信息传达问题。好的配色让读者一眼看出分组和趋势,差的配色让读者困惑甚至误读。

配色原则:

  1. 一致性:同一个分组在所有图中用同一种颜色。如果 tumor 在 Figure 1 是红色,在 Figure 4 也必须是红色。
  2. 色盲友好:避免红绿对比(约 8% 的男性是红绿色盲)。推荐用蓝/橙或蓝/红替代。
  3. 语义直觉:高风险用暖色(红/橙),低风险用冷色(蓝/绿);上调用红色,下调用蓝色。
  4. 不超过 5 种颜色:一张图中颜色太多会让读者无法区分。
# 推荐的配色方案
library(ggsci)

# 方案 1: Nature 风格(适合 2-4 组对比)
scale_color_npg()

# 方案 2: Lancet 风格(适合临床数据)
scale_color_lancet()

# 方案 3: 自定义色盲友好配色
my_colors <- c(
"Tumor" = "#E64B35", # 红
"Normal" = "#4DBBD5", # 蓝
"High-risk" = "#E64B35",
"Low-risk" = "#3C5488"
)

# 统一的 ggplot2 theme(投稿用)
theme_publication <- theme_classic() +
theme(
text = element_text(size = 12, family = "Arial"),
axis.title = element_text(size = 14),
axis.text = element_text(size = 11),
legend.text = element_text(size = 11),
legend.title = element_text(size = 12),
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
strip.text = element_text(size = 12)
)

标注规范:

  • 坐标轴标签要完整:不写"FC",写"log2(Fold Change)"
  • 统计标注要规范:写"P = 0.003"而不是"p<0.05";如果 p 值很小写"P < 0.001"
  • 样本量要标注:KM 曲线下方加 risk table,箱线图标注每组 n
  • 子图标签用大写字母:(A)、(B)、(C),放在左上角

输出规范:

# 投稿级别的图片输出
ggsave("Figure1.pdf", width = 10, height = 8, dpi = 300)
ggsave("Figure1.tiff", width = 10, height = 8, dpi = 300, compression = "lzw")

# 常见期刊要求
# - 单栏图: 宽 85mm (3.35 inch)
# - 双栏图: 宽 170mm (6.7 inch)
# - 分辨率: ≥300 dpi (照片), ≥600 dpi (线条图)
# - 格式: PDF/EPS (矢量) 或 TIFF (位图)

Figure Legend 写作

Figure legend(图注)是审稿人判断你是否专业的重要依据。一个好的 legend 应该让读者不看正文也能理解这张图。

Legend 的标准结构:

  1. 标题(一句话概括这张图说明了什么)
  2. 方法描述(用了什么数据、什么分析方法)
  3. 子图说明(每个子图展示什么)
  4. 统计信息(用了什么检验、显著性阈值)
  5. 样本量(每组多少样本)

示例:

Figure 3. Construction of a lipid metabolism-related prognostic signature using LASSO Cox regression. (A) LASSO coefficient profiles of 156 candidate genes. The x-axis shows log(λ), and each curve represents one gene. (B) Ten-fold cross-validation for tuning parameter selection. The vertical dashed lines indicate λ.min and λ.1se. (C) Distribution of risk scores in the training cohort (n = 185). Patients are ordered by risk score; red dots indicate deceased patients. (D) Survival status plot showing the correlation between risk score and survival time. The horizontal dashed line indicates the median risk score used for group stratification. Statistical significance was assessed using the log-rank test.

实战示例:TCGA-LIHC 6-figure 排版

让我用具体的 R 代码展示如何制作 Figure 1(差异分析概览)的四个子图:

# ============================================================
# Figure 1: Differential Expression Overview
# ============================================================
library(ggplot2)
library(ggrepel)
library(pheatmap)
library(patchwork)

# --- Fig 1A: 火山图 ---
volcano_df <- res_df %>%
mutate(
significance = case_when(
padj < 0.05 & log2FoldChange > 1 ~ "Up",
padj < 0.05 & log2FoldChange < -1 ~ "Down",
TRUE ~ "NS"
)
)

# 标注 top 基因
top_genes <- volcano_df %>%
filter(significance != "NS") %>%
arrange(padj) %>%
head(10)

p_volcano <- ggplot(volcano_df, aes(x = log2FoldChange, y = -log10(padj))) +
geom_point(aes(color = significance), size = 0.8, alpha = 0.6) +
scale_color_manual(values = c("Up" = "#E64B35", "Down" = "#3C5488", "NS" = "grey70")) +
geom_text_repel(data = top_genes, aes(label = gene_symbol), size = 3) +
geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "grey40") +
geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "grey40") +
labs(x = "log2(Fold Change)", y = "-log10(adjusted P-value)",
title = "Tumor vs Normal") +
theme_publication +
theme(legend.position = "top")

# --- Fig 1B: 热图 top 50 差异基因 ---
top50 <- deg %>% arrange(padj) %>% head(50) %>% pull(gene_id)
mat <- assay(vst(dds))[top50, ]
mat_scaled <- t(scale(t(mat)))

# 注释列
annotation_col <- data.frame(
Condition = col_data$condition,
row.names = colnames(mat)
)
ann_colors <- list(Condition = c("Tumor" = "#E64B35", "Normal" = "#4DBBD5"))

pheatmap(mat_scaled,
annotation_col = annotation_col,
annotation_colors = ann_colors,
show_colnames = FALSE,
clustering_method = "ward.D2",
color = colorRampPalette(c("#3C5488", "white", "#E64B35"))(100),
filename = "Fig1B_heatmap.pdf",
width = 8, height = 10)

# --- Fig 1C & 1D: 富集分析 ---
p_go <- dotplot(ego_simplified, showCategory = 10) +
labs(title = "GO Biological Process") +
theme_publication

p_kegg <- dotplot(ekk, showCategory = 10) +
labs(title = "KEGG Pathway") +
theme_publication

# 组合子图
(p_volcano | p_go) / (p_kegg)
ggsave("Figure1_combined.pdf", width = 14, height = 12, dpi = 300)

多面板图的组合技巧

实际投稿时,一个 Figure 通常包含 4-6 个子图(panels),需要用工具把它们组合成一张大图。R 中常用的组合工具有 patchworkcowplotgridExtra

library(patchwork)

# 基本组合:| 表示水平排列,/ 表示垂直排列
(p1 | p2) / (p3 | p4)

# 控制相对大小
(p1 | p2) / (p3 | p4) +
plot_layout(heights = c(1, 1.2))

# 添加子图标签
(p1 | p2) / (p3 | p4) +
plot_annotation(tag_levels = "A") &
theme(plot.tag = element_text(size = 16, face = "bold"))

组合时的注意事项:

  • 相关的子图放在一起(同一行或同一列)
  • 时间/逻辑顺序从左到右、从上到下
  • 所有子图的字号保持一致(组合后缩放不会改变相对字号)
  • 留足够的间距,不要让子图挤在一起

常见坑

  • 一张图塞太多信息:一个 panel 里同时展示热图、箱线图、统计标注,读者看不出重点。解决方法:一个 panel 只传达一个信息,复杂的内容拆成多个 panel。

  • 配色不一致:Figure 1 中 tumor 是红色,Figure 3 中 high-risk 也是红色但代表不同含义,读者混乱。解决方法:在项目开始时就定义好配色方案,所有图统一使用。

  • 分辨率不够:用 RStudio 的 Export 功能保存图片,分辨率只有 72 dpi,投稿时被要求重做。解决方法:始终用 ggsave() 指定 dpi = 300,输出 PDF 或 TIFF 格式。

  • 图例不完整:KM 曲线没有标注每组样本量,箱线图没有标注统计方法,热图没有说明颜色代表什么。解决方法:用 checklist 逐项检查每张图的标注是否完整。

  • 子图标签缺失或不规范:有的用 (a)(b)(c),有的用 A/B/C,有的根本没有标签。解决方法:统一用大写字母 (A)(B)(C),放在每个 panel 的左上角,字号 14-16pt,加粗。

下一步

接着深入: 图表组织好之后,下一步是写 Methods。09 Methods 撰写:审稿人最看的细节 会讲审稿人最关注的方法学细节怎么写。

横向延伸: 如果你想系统学习 ggplot2 的用法,可以看 R 语言与 ggplot2 可视化,那里有从基础到高级的完整教程。

参考资源

  • Rougier NP et al. (2014) "Ten Simple Rules for Better Figures." PLoS Computational Biology 10:e1003833. 科学图表的 10 条基本原则。
  • Wilke CO (2019) Fundamentals of Data Visualization. O'Reilly. 数据可视化的系统教材,免费在线版:https://clauswilke.com/dataviz/
  • Wong B (2011) "Points of view: Color blindness." Nature Methods 8:441. 色盲友好配色的简明指南。
  • patchwork 包文档:https://patchwork.data-imaginist.com/ 多面板图组合的最佳工具。
  • ggsci 包:https://nanx.me/ggsci/ 提供 Nature、Lancet、JAMA 等期刊风格的配色方案。
AI 陪学

让 AI 陪我学这一篇

AI 会读这篇文章后给你 3-5 步学习计划, 逐步陪你学完,最后出 1-3 道题验证你掌握得怎么样。 登录后 AI 才能记住你的进度。

静态文件

离线资料下载

手册 HTML / PDF 已在后台预生成,点击后直接下载网站静态资源。