上周有个师弟拿着他跑完的富集分析和单细胞数据来找我,说投稿前要补几张图,问我能不能帮他搞定多组气泡图和marker基因矩阵气泡图。我一看他桌面,RStudio 开着,Seurat 装了一半,ggplot2 的报错红了一片,我就直接让他换个思路:这类图完全可以在线绘制,不用在本地死磕代码,只要把数据整理成平台认得出的格式,几分钟就能出一张可发表级别的图。
这篇文章不是来推广某个工具的,而是把我这些年实际用下来的经验梳理一遍。主要围绕富集分析多组气泡图和单细胞分析 marker 基因矩阵气泡图这两类最常见的需求,讲清楚它们到底在画什么、数据应该怎么整理、有哪些在线平台能直接出图,以及那些文档里不会明说的坑。适合正在做组学数据分析的人、刚接触单细胞测序结果的可视化新手,以及只想要一张干净、规范、能放进论文的图的科研人员。
1. 富集分析多组气泡图:先想清楚你究竟要展示什么
1.1 气泡图在富集分析里的价值
富集分析的结果文件大家都不陌生,GO、KEGG 跑完就是一张几百行的表,每行是一个功能条目或通路,后面跟着 p 值、q 值、基因数、基因比例。常规做法是挑 p 值最小的几个画柱状图,但柱状图信息量太少了,只能表达一个数值维度,你很难同时看出这个通路富集到多少基因、显著性如何、在不同条件下变化是怎样的。
气泡图在一个二维平面里塞进了四个信息量:横坐标是富集比例或基因计数,纵坐标是通路或功能条目,气泡大小代表基因数量,气泡颜色代表显著性水平。这样一张图就能把富集结果的核心信息全部覆盖,而且放在多组比较场景下,气泡图的优势更明显——你可以把不同处理组、不同时间点、不同细胞类型的结果合并到一张图上,用分面或者分组的方式直接对比,视觉冲击力强,审稿人也喜欢这种信息密度高的图。
我自己平时用的场景主要有三类:差异基因 GO/KEGG 富集的多组对比、不同细胞类型 marker 基因功能富集的横向比较、药物处理前后一条通路在不同时间点的动态变化。无论哪一种,最终拿到的都是标准富集结果表格,所以绘图这一步完全是一致的。
1.2 一张标准的多组富集结果长什么样
很多人在线绘图失败,不是不会点按钮,而是数据表格式不对。在线平台最反感“人类可读但机器看不懂”的表格。富集气泡图的输入数据,核心就是下面这几列:
| group | ID | Description | GeneRatio | pvalue | p.adjust | Count |
|---|---|---|---|---|---|---|
| Treatment_A | GO:0006915 | apoptotic process | 15/120 | 1.2e-08 | 3.5e-06 | 15 |
| Treatment_A | GO:0006954 | inflammatory response | 12/120 | 2.1e-07 | 4.2e-05 | 12 |
| Treatment_B | GO:0006915 | apoptotic process | 18/125 | 5.3e-09 | 1.6e-06 | 18 |
| Treatment_B | GO:0006954 | inflammatory response | 9/125 | 8.4e-06 | 8.7e-04 | 9 |
group 列是你自定义的分组标签,比如不同的处理、不同的细胞类型、不同的时间点;ID 是用条目编号,比如 GO 号或 KEGG 通路号,也可以直接放通路名;Description 是描述;GeneRatio 是关键中的关键,它是分数形式,写成“15/120”表示该通路富集到 15 个差异基因,背景基因总数是 120。后台绘图时它会自动换算成小数;pvalue 和 p.adjust 是显著性指标,多数平台用 p.adjust 或负 log10 转化后的值来映射颜色;Count 是具体基因数,用来映射气泡大小。
多组合并时有一个容易犯的错:不同组的背景基因总数不同,GeneRatio 的分母可能不一样,这没有问题,直接用平台默认的换算即可。但如果你用的是基因数量而不是比例,那不同组之间会因为背景差异产生假象,所以更推荐保留比值形式。如果某一组在某个通路没有富集到任何一个基因,建议不要留空,直接填 0 或删掉这一行,否则很多在线工具会解析失败或画出一个奇怪的大气泡。
2. 实操:在线工具绘制多组富集气泡图的完整流程
2.1 平台选型:不追求大而全,只求稳定可复现
现在搜“气泡图 在线绘制”“富集分析 在线出图”,出来的平台少说也有十几个。我个人的筛选标准有三个:一是平台有没有提供示例数据,没有示例数据的平台直接放弃,因为你无法快速验证它的输入格式;二是能不能导出 PDF/SVG 等矢量格式,PNG 只配用来预览,投稿基本都要矢量图;三是工具是不是持续在更新,有些老平台两三年没维护了,浏览器新版一跑就白屏,你辛辛苦苦整理的数据传上去直接打水漂。
基于这些标准,我实际用过且体验稳定的有两类:一类是综合生信云平台,这类平台集成了很多组学可视化模块,富集分析、单细胞绘图都有,数据格式相对标准化,导出选项丰富;另一类是轻量级的在线绘图小工具,界面简单、上传后立刻出图,适合快速看效果。两类各有取舍。我的习惯是先用轻量工具快速验证数据格式对不对、颜色映射是否符合预期,确认无误后再到功能更全的平台精修参数并导出矢量图。这个流程能避免一上来就在大平台里翻参数把他翻到晕头转向。
2.2 数据准备阶段:Excel 与 R 的配合
在线绘图的前置工作还是离线完成的。我自己一般用 R 跑完富集分析后直接导出精简表格,但这里有一个细节值得注意:如果从 clusterProfiler 的 as.data.frame 直接导出,Description 列里通常带有 GO 的完整描述,ID 列是 GO:xxxxxxx,其中冒号是全角还是半角在导出到 CSV 时可能出问题。所以建议在 R 里做个统一处理:
library(clusterProfiler) ego <- enrichGO(gene = diff_genes, OrgDb = org.Hs.eg.db, keyType = "ENTREZID", ont = "BP") # 提取标准结果并筛选 res <- as.data.frame(ego) res <- res[res$p.adjust < 0.05, ] res$Description <- gsub(",", ";", res$Description) # 去掉描述里的逗号 res$GeneRatio <- sub("/", "/", res$GeneRatio) # 保持英文斜杠 write.csv(res[, c("group", "ID", "Description", "GeneRatio", "pvalue", "p.adjust", "Count")], "enrich_result.csv", row.names = FALSE)这里有一个我很看重的操作:把描述中的逗号替换掉。很多在线平台用逗号做 CSV 分隔符,如果 Description 里出现逗号,整个表格就会被拆得四分五裂。这是一个极其隐蔽但高频的报错来源。
如果你不想写 R,用 Excel 同样可以完成。但 Excel 另存为 CSV 时有个老毛病:中文乱码。解决方案不是去改编码选项,而是用“数据 → 从文本/CSV”导入时选择 UTF-8 编码,或者先另存为 UTF-8 格式再上传。技巧很老旧但非常有效。
2.3 参数设置:横轴、颜色、气泡分组逐个说清
上传数据后,平台一般会提供几个关键参数。横轴有两种常见选择,GeneRatio 和 Count。我的经验是:如果各组背景基因数差异不大,用 Count 更直观,气泡数量越多,气泡越大;如果背景差异明显,就必须用 GeneRatio,否则会误导人。至于纵轴,大部分平台会按描述文本自动显示,如果条目太多,建议先只保留显著性和基因数排在前 15 到 20 的通路,否则整张图会变成一条长长的瀑布,可读性很差。
颜色映射通常选 pvalue 或 p.adjust。我用 p.adjust 更多一点,因为多组比较时 p 值本身受多重检验影响,而 p.adjust 已经做了校正,更稳妥。颜色梯度一般默认是蓝色到红色,我习惯调成低显著性用灰色或浅蓝色,高显著性用深红色,这样拐点在视觉上很清楚。如果平台支持,把颜色映射取 -log10(p.adjust),效果会更均匀,否则大量极小 p 值会堆在一个色阶里,图面全是深红,区分度很低。
面对多组数据,平台通常提供两个呈现方式:把所有组混在一张图里,用不同的形状或外圈颜色做区分;或者按组别分面,横向排列。只要样本量不大、通路数量控制在合理范围内,我更推荐分面。混在一张图的信息量大,但如果两组之间的通路排名差异很大,空白区域会很多,反而显得乱。
3. 单细胞 marker 基因矩阵气泡图:把 Seurat 的成果搬到在线工具
3.1 先理解 marker 基因气泡图在说什么
单细胞分析的 marker 基因气泡图,和富集分析气泡图虽然都叫气泡图,但底层的逻辑完全不同。富集气泡图展示的是通路显著性,而 marker 基因气泡图展示的是每个细胞类型里特定基因的表达情况。
这类图的经典形式是:每一行是一个 marker 基因,每一列是一个细胞类型或细胞群,气泡的大小表示表达该基因的细胞占该群的比例(pct.exp),气泡的颜色表示该群细胞中该基因的平均表达量(avg.exp)。一张图你看下来,就能快速判断某个 marker 基因是只在某一类细胞里表达,还是广谱表达。这在细胞类型注释、marker 基因鉴定结果展示里几乎是标配。
我见过不少人用 Seurat 的 DotPlot 直接出图,也挺快,但问题是那个图只是预览级。想调整配色、字体、排序、分组间隙,就得写一堆 ggplot2 代码去改 DotPlot 返回的对象,学习成本不低。在线工具的价值就在这里:它把这一层参数全部暴露成拖拽和下拉框,你只要把 DotPlot 计算出来的矩阵传上去,就能在几分钟内得到一版风格统一、可以直接用的图。
3.2 从 Seurat 导出在线工具需要的数据
在线工具能识别的,一般是两个矩阵:一个是平均表达量矩阵,行叫 identities(细胞类型),列是 marker 基因,值是平均表达量或 scaled 值;另一个是表达比例矩阵,行和列不变,值是 pct.exp,范围在 0 到 100 之间。更省事的方式是把 Seurat 的 DotPlot 数据对象直接导出来,因为里面已经同时包含这两列。
下面这段代码是我每次做单细胞气泡图都要用到的:
library(Seurat) # 假设 seu 是 Seurat 对象,markers 是你需要展示的 marker 基因向量 p <- DotPlot(seu, features = markers, group.by = "celltype") # 提取背后的数据 dot_data <- p$data head(dot_data) # 保存为 CSV 备用 write.csv(dot_data, "dotplot_data.csv", row.names = FALSE)p$data 里一般包含几列:avg.exp.scaled 是缩放后的平均表达量,会以热图颜色形式展示;pct.exp 是表达该基因的细胞百分比,会映射气泡大小;features.plot 是基因名;id 是细胞类型。这个 CSV 拿给支持 Seurat 格式的在线平台,直接就能识别。
但也有不少平台不认这种原始长格式,它们要的是重新排列后的“宽矩阵”。这时候你需要多走一步:
library(tidyr) # 宽格式平均表达量 avg_exp <- dot_data %>% dplyr::select(features.plot, id, avg.exp.scaled) %>% pivot_wider(names_from = features.plot, values_from = avg.exp.scaled) write.csv(avg_exp, "avg_exp_wide.csv", row.names = TRUE) # 宽格式表达比例 pct_exp <- dot_data %>% dplyr::select(features.plot, id, pct.exp) %>% pivot_wider(names_from = features.plot, values_from = pct.exp) write.csv(pct_exp, "pct_exp_wide.csv", row.names = TRUE)宽格式的矩阵,行名通常是细胞类型,列名是基因,上传时平台会基于这两个文件自动生成气泡图。这个转换过程看似麻烦,但能极大拓宽你兼容的平台范围,值得花十分钟搞定。
3.3 参数设置中的几个关键判断
在线工具的 marker 气泡图模块,参数设计大同小异。基因和细胞群默认按字母排序,这绝对不是你想要的。细胞群一般建议按照注释结果的逻辑排序,比如 T 细胞、B 细胞、NK 细胞这样有生物学意义的顺序,或者按照 UMAP 上的簇编号顺序;基因的顺序则建议按照你手头的 marker 列表顺序排列,通常分成几大块,比如每类细胞的特异性 marker 排在一起,这样图面自上而下读起来就像一张系统发育表,信息层次很清晰。
气泡大小映射的逻辑也容易踩坑。pct.exp 是百分比,有些平台要求你填 0-1 的小数,有些平台直接接收 0-100 的值,如果你把像 45.6 这样的值传进一个只接受 0-1 的平台,画面会失控成一片实心圆形。这个只能靠示例数据快速验证。颜色映射一般默认灰到红、黄到紫,我习惯选从浅灰到深紫红,低表达时灰色显得干净,高表达时深色显眼,整体学术感很强。
如果平台支持自定义气泡大小上限和颜色阈值,我建议把气泡直径的上限控制在 12 到 15 像素之间,太大会互相遮挡,太小则难以辨认。颜色方面可以尝试把最大值限定在数据的 90% 分位,避免个别高表达基因把所有颜色拉到最深处。
4. 出图前的设计细节:配色、排序与标签直接影响专业度
4.1 配色方案别用红配绿
气泡图配色这件事,说大不大,但直接影响审稿人第一印象。富集分析气泡图最常见的问题是颜色跨度太大,比如从蓝色突然跳到亮黄,中间没有过渡,气泡看起来像一堆彩蛋,非常廉价。我个人最常用的配对是浅色到深色的单色渐变,或者浅黄到深红这样的双色渐变。如果平台支持自定义配色,直接用 d3 风格的配色字符串也行,比如“#f7fbff,#6baed6,#08306b”这种。
单细胞 marker 基因气泡图则更讲究。因为你的图上同时有颜色和大小两层信息,颜色层次必须清晰。我强烈建议使用灰到紫、灰到蓝这类色系,让低表达区域保持安静,高表达区域成为视觉焦点。避免用红色到绿色这样的色盲雷区配色,这在投稿时容易被评审提意见。
4.2 排序是图上最值钱的功夫
排序问题看似只是拖拽行和列,但决定了图能不能讲出故事。富集分析的多组气泡图,如果描述里涉及 GO 的三个子类(BP、CC、MF),建议先按子类分组,再在每个子类内部按显著性排序。这比把所有通路按照单一 p 值排序要科学得多,因为 BP、CC、MF 本来就不是同一类概念,混排会显得混乱。
单细胞 marker 基因矩阵的排序更关键。理想状态下,你应该把同一类细胞的 marker 基因排在一起,而且要按表达特异性排列:最特异、只在目标细胞类型里表达的基因放在前面,不那么特异的放后面。这样一眼就能看出“每一列对应一个亮色竖条”,而不是一片杂乱的光斑。
4.3 长标签和坐标轴文字的处理
GO 描述动不动就是四五十个字符,放在纵轴上一排会占掉小半个图幅。我自己的处理原则是:图中展示 ID 或精简描述,完整描述放在图注里。如果平台支持自定义标签映射,就把原文替换成“GO:0006915(凋亡过程)”这样的组合;如果平台不支持,我宁可在数据准备阶段就把描述截短,也不要后期靠挨个挪图例。
单细胞矩阵图的行标签是基因名,字数不多,但列标签是细胞类型,有的注释名字很长,比如“CD8+ T cell_Naive-like”,加上加号、下划线,看起来很乱。在线绘图前把细胞类型重命名成简洁标签,比如“CD8T_Naive”,出图后再在论文图注里写全称,这是最实用的做法。
5. 常见问题与排查技巧实录
5.1 数据格式引发的经典问题
这几年我帮人排过的在线绘图报错,至少有六成是从数据格式起的问题。最常见的有三类:表头带空格或特殊符号,导致平台无法匹配列名;分组名里有中文或括号,传到后端变成乱码;CSV 里混入了隐藏的不可见字符,平台解析后出现大量空行。这些问题用肉眼很难发现,但只要你遵守一条原则就能规避大部分:表头只用英文字母、数字、下划线;分组名用英文且不要有空格,用下划线代替;Description 里的逗号统一替换成“;”;如果可能,先把数据在记事本里打开看一眼,确认没有明显乱码再上传。
另外,很多平台对重复行是零容忍的。同样是 group 等于 Treatment_A、ID 等于 GO:0006915 这一行,如果表格里出现了两次,后台可能会报“duplicate rows”错误,或者更隐蔽地,绘图时同一个位置画两个气泡导致比例失真。所以在合并多组数据时,记得检查一下是否有重叠通路被重复添加。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 上传后提示“column not found” | 表头有空格或中文,列名不匹配 | 改用纯英文列名,去除空格 |
| 图表里出现全黑大圆 | GeneRatio 传成了字符型或填错格式 | 用分数或小数,勿填文本 |
| 横轴数值全是 0 | Count 列里存在空值或字符 | 补 0 并统一数值格式 |
| 中文描述乱码 | Excel 保存的 CSV 不是 UTF-8 编码 | 另存为 UTF-8 CSV 或用记事本另存 |
| 分组顺序乱 | 平台按字母排序,非预期顺序 | 在分组名加数字前缀,如“1_Treatment” |
| 气泡重叠看不清 | 气泡过大或分组行数过密 | 缩小气泡上限,通过分面拆开 |
5.2 单细胞气泡图独有的坑
单细胞分析 marker 基因气泡图有一个特殊问题:很多基因在所有细胞里都低表达或者表达比例很低,画出来整张图灰蒙蒙一片,没有层次。解决思路不是把颜色调深,而是在上游做过滤。我一般在导出矩阵前先过滤一遍:pct.exp 最大值低于 10% 或者 avg.exp.scaled 最大值低于 0.5 的基因,直接从 marker 列表里拿掉。这样剩下的基因至少在一个类群里有可靠表达,图面立刻精神很多。
另一个常见问题是部分平台要求列名是基因名,而你的数据里可能混入了“GAPDH-AS1”这种带横杠的基因名。有些平台的解析逻辑会把横杠当成特殊符号,导致列名被改写。解决办法是改用“GAPDH_AS1”占位,出图后手动在论文里改回标准命名,或者在数据处理时用 Ensembl ID,等出图后再替换为基因名。
5.3 导出与投稿之间的最后一公里
在线出图最大的槽点就是分辨率。很多平台默认输出的是 1200 像素宽的 PNG,放大一点就模糊。投稿的话,线稿图怎么也得 300 dpi 以上,单栏宽度 8.8 cm 对应约 1040 像素,双栏 17.8 cm 对应约 2100 像素。如果你用的平台只能导出 PNG,至少把宽度锁在 2000 像素以上再下载。如果平台提供 PDF 或 SVG,务必导成矢量格式,后面在 AI 里调整排版会省很多事。
还有一点容易被忽略:字体。在线工具的默认字体很多是 Arial 或 Helvetica,字体大小可能在 10 到 12 之间,单独看还行,放进多图组合的 Figure 里会显得突兀。期刊一般要求全文字体统一,所以建议导出后自己在矢量编辑软件里统一字体。如果平台支持自定义字体大小也提前设好,避免后期逐个改。
6. 一点个人操作的补充心得
在线工具做得再顺手,我也会在本地保留一份 ggplot2 的备选脚本。原因很简单:在线平台迭代太快,今天这个参数能用,下个月改版后可能位置就变了;而本地脚本只要数据格式不变,永远能复现同一张图。我通常先在线快速看效果,再用本地脚本做最终版,两者结合,既能跟上平台的新特性,又不至于被平台绑定。
还有一个容易被忽视的操作习惯:每次出图前,把上传的 CSV 文件和当时的参数选择截图一起存进项目文件夹。这样做的好处是,等几个月后审稿人要求你换一种颜色重画,你能快速知道自己当初用了什么阈值、什么排序方式,而不需要重新猜。这个习惯帮我避免过太多次重复劳动。
如果你也是第一次尝试用在线工具画富集分析多组气泡图或者单细胞 marker 基因矩阵气泡图,我的建议很直接:先拿平台自带的示例数据跑通整个流程,再把自己的数据套进去。这一步看似多花了十分钟,实际上能帮你避开九成的格式坑。等你用熟了,这类图基本十分钟以内就能从原始数据做到可投稿的版本。