气泡图的数据结构:三个核心指标
讲气泡图,很多人上来就看图。但我建议你先搞清楚图背后的数据长啥样。
说白了,气泡图上每一个泡泡背后都对应一行表格数据。这行数据至少有四个字段:基因数、P值、富集因子,还有通路名字。我刚开始做富集分析时,只看颜色和大小,结果有一次把富集因子算反了,画出来的图完全反直觉。嗯,从那以后我每次都先检查数据框的列名。
下面我拆开一个个讲。
基因数(Count)—— 泡泡的大小
基因数就是这个通路上真正匹配到你基因列表里的基因个数。比如你的输入基因有200个,KEGG的「细胞周期」通路里背景基因是300个,其中你输入列表里有25个,那Count就是25。
Count值越大,气泡直径越大。这里有个坑:
Count 映射 size,而某些在线工具会用 GeneRatio。碰上气泡图显得特别小,先看看纵坐标是不是比率。
P值(或调整后P值)—— 泡泡的颜色
P值代表富集结果的统计学显著性。你想想看,一个通路里偶然出现几个基因很正常,但出现太多就不太可能是巧合了。P值越小,颜色通常越红或越深。
我个人习惯用 调整后P值(p.adjust) 来做颜色映射。因为KEGG通路数不多(几百条),但如果你用ORA(Over-Representation Analysis),不做多重假设校正,P值很容易虚低。
p.adjust 取负对数(-log10),这样小P值会拉开差距,颜色渐变更清晰。
富集因子(Rich Factor)—— 泡泡的X/Y位置
富集因子定义多样,但最常见的一种是:
富集因子 = (输入基因中属于该通路的基因数)/ (该通路总背景基因数)
这个值范围在0到1之间,越接近1说明你输入基因对该通路的覆盖度越高。
注意:有些工具把 GeneRatio 当纵坐标,它和富集因子有点像,但分母不同:
GeneRatio = 输入基因中属于该通路的基因数 / 你的总输入基因数。
我曾经在对比几个工具结果时发现,同一个通路在clusterProfiler里Rich Factor=0.3,在DAVID里却显示0.15,差点以为算错了。后来才发现分母定义不一样。所以看气泡图前,一定先确认X轴/Y轴标签是什么。
一张表说清楚
| 字段 | 常用别名 | 取值范围 | 气泡图映射 | 典型问题 |
|---|---|---|---|---|
| Count | 基因数、GeneCount | 正整数(通常≥2) | 气泡大小 | 若通路Count=0会被过滤 |
| Pvalue / p.adjust | 显著性、FDR | [0,1] | 气泡颜色 | 未校正时易出现假阳 |
| Rich Factor | Enrichment Ratio | [0,1] | X或Y轴 | 定义需统一 |
| GeneRatio | 比例 | [0,1] | 常替代Rich Factor | 分母不同,数值不同 |
从R数据框看数据结构
我们用clusterProfiler跑出来的结果,直接 head() 就能看到这些列。
# 假设你已经做了 enrichKEGG
library(clusterProfiler)
data(geneList, package="DOSE")
kegg_res <- enrichKEGG(gene = names(geneList)[1:100],
organism = 'hsa',
pvalueCutoff = 0.05)
head(kegg_res[, c("ID", "Description", "GeneRatio", "BgRatio", "pvalue", "p.adjust", "qvalue", "Count")])
输出大概长这样:
ID Description GeneRatio BgRatio pvalue p.adjust qvalue Count
hsa04110 hsa04110 Cell cycle 25/200 300/8000 3.45e-08 1.23e-05 1.01e-05 25
hsa03030 hsa03030 DNA replication 12/200 150/8000 1.22e-04 2.56e-03 2.34e-03 12
...
这里 GeneRatio 是"25/200"这样的字符串,表示25个输入基因中属于该通路;BgRatio 是背景比例。你画图时如果要计算Rich Factor,可以把两个Ratio相除,但更常见的做法是直接用 Count 和 p.adjust 加 GeneRatio。
我画气泡图时用到的R代码
下面是我常用的一个代码片段,直接基于clusterProfiler的结果画图,并手动调整映射变量。
library(ggplot2)
library(DOSE)
# 转换数据为数据框
df <- as.data.frame(kegg_res)
# 计算 GeneRatio 数值 (分子/分母)
df$GeneRatio_num <- sapply(strsplit(df$GeneRatio, "/"), function(x) as.numeric(x[1])/as.numeric(x[2]))
# 画气泡
ggplot(df, aes(x = GeneRatio_num, y = Description)) +
geom_point(aes(size = Count, color = p.adjust)) +
scale_color_gradient(low = "red", high = "blue") +
theme_bw()
注意:我没有用 scale_size_continuous 限制大小范围,有时几个极端大通路会撑爆版面。你可以加 scale_size_area(max_size = 10) 让气泡面积与Count成正比。
避坑:富集因子到底怎么算
GeneRatio 和 BgRatio。你需要用 RichFactor = (GeneRatio分子/BgRatio分子) 自己算,或者干脆用 GeneRatio 作为横轴。而WebGestalt会直接给出 Enrichment Ratio。所以当你看到别人文献里的气泡图横轴标着“Rich Factor”,一定要看方法部分他们用的哪个工具,不然你照着参数画会完全不一样。
气泡图数据结构总览
下面这张SVG图把整个映射关系串起来。你可以把它当成一个“数据结构→气泡图元素”的快速参考。
常见错误与调优建议
- 气泡过小或过大: 如果Count范围很大(比如从2到200),默认的
scale_size可能会让最小的泡泡几乎看不见。可以设置scale_size_area(max_size = 12)或手动指定limits。 - 颜色映射失真: p.adjust 值通常集中在0.01~0.05之间,直接用连续色标可能会让大部分点颜色接近。我一般先取
-log10(p.adjust)再映射,色带用scale_color_gradientn(colors = c("blue", "yellow", "red"))拉开中部。 - X轴混淆: 一定确认横轴标签是
Rich Factor还是GeneRatio。如果横轴值都小于0.1但纵轴通路很多,很可能是用了GeneRatio(因为分母是总输入基因数,而KEGG通路本身基因数都在几十到几百,比率会小)。
核心思路: 气泡图本质上是三维数据(大小、颜色、位置)映射到二维平面。你只要确保数据框里有 Count、p.adjust 和 RichFactor 这三列,就能随心所欲调整图形。不要被工具默认的美观参数带偏,先检查原始数据。