上周有个师弟拿着他跑完的富集分析和单细胞数据来找我说投稿前要补几张图问我能不能帮他搞定多组气泡图和marker基因矩阵气泡图。我一看他桌面RStudio 开着Seurat 装了一半ggplot2 的报错红了一片我就直接让他换个思路这类图完全可以在线绘制不用在本地死磕代码只要把数据整理成平台认得出的格式几分钟就能出一张可发表级别的图。这篇文章不是来推广某个工具的而是把我这些年实际用下来的经验梳理一遍。主要围绕富集分析多组气泡图和单细胞分析 marker 基因矩阵气泡图这两类最常见的需求讲清楚它们到底在画什么、数据应该怎么整理、有哪些在线平台能直接出图以及那些文档里不会明说的坑。适合正在做组学数据分析的人、刚接触单细胞测序结果的可视化新手以及只想要一张干净、规范、能放进论文的图的科研人员。1. 富集分析多组气泡图先想清楚你究竟要展示什么1.1 气泡图在富集分析里的价值富集分析的结果文件大家都不陌生GO、KEGG 跑完就是一张几百行的表每行是一个功能条目或通路后面跟着 p 值、q 值、基因数、基因比例。常规做法是挑 p 值最小的几个画柱状图但柱状图信息量太少了只能表达一个数值维度你很难同时看出这个通路富集到多少基因、显著性如何、在不同条件下变化是怎样的。气泡图在一个二维平面里塞进了四个信息量横坐标是富集比例或基因计数纵坐标是通路或功能条目气泡大小代表基因数量气泡颜色代表显著性水平。这样一张图就能把富集结果的核心信息全部覆盖而且放在多组比较场景下气泡图的优势更明显——你可以把不同处理组、不同时间点、不同细胞类型的结果合并到一张图上用分面或者分组的方式直接对比视觉冲击力强审稿人也喜欢这种信息密度高的图。我自己平时用的场景主要有三类差异基因 GO/KEGG 富集的多组对比、不同细胞类型 marker 基因功能富集的横向比较、药物处理前后一条通路在不同时间点的动态变化。无论哪一种最终拿到的都是标准富集结果表格所以绘图这一步完全是一致的。1.2 一张标准的多组富集结果长什么样很多人在线绘图失败不是不会点按钮而是数据表格式不对。在线平台最反感“人类可读但机器看不懂”的表格。富集气泡图的输入数据核心就是下面这几列groupIDDescriptionGeneRatiopvaluep.adjustCountTreatment_AGO:0006915apoptotic process15/1201.2e-083.5e-0615Treatment_AGO:0006954inflammatory response12/1202.1e-074.2e-0512Treatment_BGO:0006915apoptotic process18/1255.3e-091.6e-0618Treatment_BGO:0006954inflammatory response9/1258.4e-068.7e-049group 列是你自定义的分组标签比如不同的处理、不同的细胞类型、不同的时间点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 传成了字符型或填错格式用分数或小数勿填文本横轴数值全是 0Count 列里存在空值或字符补 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 基因矩阵气泡图我的建议很直接先拿平台自带的示例数据跑通整个流程再把自己的数据套进去。这一步看似多花了十分钟实际上能帮你避开九成的格式坑。等你用熟了这类图基本十分钟以内就能从原始数据做到可投稿的版本。