基因数与UMI:一对看似简单却暗藏玄机的关系
其实很多人会问:基因数和UMI数,到底有没有关系?
我刚开始做单细胞分析的时候,也以为它们就是两个独立的指标。后来踩了不少坑,才真正搞明白——这两者本质上是在描述同一件事的不同侧面。
说白了,你拿到的每一个细胞,既有检测到了多少种不同的基因,也有检测到了多少条独特的UMI分子。这两个数字之间,藏着大量关于数据质量的信息。
从第一性原理说起
每一张单细胞文库,本质是这样一个过程:
- 单个细胞里的mRNA被捕获
- 逆转录成cDNA
- 加上细胞标签和UMI标签
- 扩增后上机测序
如果这个过程是完美的,那么每个基因应该有且只有一个UMI。但现实中,同一个基因的多个mRNA分子会被测到,所以你会看到多个不同的UMI对应同一个基因。
基因数,就是检测到了多少种不同的基因。UMI数,就是检测到了多少个不同的mRNA分子。
你想想看,如果一个细胞的UMI数远少于基因数,那说明什么?或者反过来,UMI数远多于基因数,又意味着什么?
它们之间的数学关系
理论上,基因数和UMI数应该呈正相关,但不是简单的线性关系。
核心规律:
- 高质量细胞:基因数 ≈ UMI数的0.65~0.8次方
- 低质量细胞:这个比例会严重偏离
我在一个10x Genomics的PBMC项目里遇到过这样一个案例:有一个cluster的细胞,UMI数看起来不低,大概在5000左右,但基因数只有不到600。这就很奇怪。
后来一查,发现这些细胞其实是双细胞——两个细胞黏在一起被打包进一个液滴里。它们的UMI总数看起来正常,但实际是两个低质量细胞的混合体,每个细胞的基因数都很低。
这种问题,光看UMI数是发现不了的,但结合基因数就能看出来。
如何用这个关系做质控?
我在实际项目中经常做这样一步操作:把每个细胞的基因数和UMI数画成散点图,然后拟合一条曲线。
import scanpy as sc
import numpy as np
adata = sc.read_10x_h5("filtered_feature_bc_matrix.h5")
# 计算每个细胞的总UMI数和基因数
umis = np.array(adata.X.sum(axis=1)).flatten()
genes = np.array((adata.X > 0).sum(axis=1)).flatten()
# 筛选有数据的细胞
mask = (umis > 0) & (genes > 0)
umis = umis[mask]
genes = genes[mask]
# 对UMI做log变换,避免极端值的影响
log_umis = np.log10(umis)
log_genes = np.log10(genes)
# 简单看一下相关性
corr = np.corrcoef(log_umis, log_genes)[0, 1]
print(f"log10(UMI)与log10(基因数)的相关系数: {corr:.3f}")
正常情况下,这个相关系数应该在0.9以上。低于0.85,你就要警惕了——数据里可能混入了大量异常细胞。
实践经验:我个人习惯把离拟合线超过2个标准差的细胞标记为可疑。这些细胞要么是双细胞,要么是背景RNA污染严重,要么就是细胞已经破碎了。
一个容易踩的坑
我记得有一次,一个同事跑来跟我说:“我的数据里基因数和UMI数的相关性很低,只有0.6,是不是实验失败了?”
我一看数据,发现他把所有barcode都算了进去,包括那些UMI数只有几十的空液滴。那些空液滴的基因数本来就低,UMI数也低,但它们之间的相关性很弱,因为空液滴里的UMI基本都来自于背景RNA污染,不是真实的细胞内容。
所以正确的做法是:先做初步过滤,把UMI数太低的barcode排除掉,再去分析基因数和UMI的关系。
# 只保留UMI数超过500的barcode
min_umis = 500
high_qual_mask = umis >= min_umis
filtered_umis = umis[high_qual_mask]
filtered_genes = genes[high_qual_mask]
log_umis = np.log10(filtered_umis)
log_genes = np.log10(filtered_genes)
corr = np.corrcoef(log_umis, log_genes)[0, 1]
print(f"过滤后相关系数: {corr:.3f}")
过滤之后,相关系数一般能回到0.9以上。如果还是低,那才说明实验真的有问题。
阈值设置的具体建议
基于这个关系,我通常建议设置两个层次的阈值:
| 指标 | 保守阈值 | 严格阈值 | 说明 |
|---|---|---|---|
| 最低基因数 | 200 | 500 | 少于这个数,基本确定是死细胞或空液滴 |
| 最低UMI数 | 500 | 1000 | 和基因数的阈值配合使用 |
| 基因/UMI比率 | ≥0.3 | ≥0.4 | 过低说明背景RNA污染严重 |
| UMI/基因比率 | ≤5 | ≤3 | 过高可能包含双细胞 |
特别注意:基因数/UMI比率这个指标,在不同组织类型、不同物种、不同建库方法下,标准值会有变化。我做过一个脑组织的项目,神经元的这个比率天然就比胶质细胞高。所以不要死套公式,要结合你的具体实验来调整。
另一种分析方法:饱和度曲线
除了简单的比例关系,还有一种更细致的分析方法——看基因的积累曲线。
具体做法是:把每个细胞里的UMI按表达量从高到低排序,然后看前多少个UMI覆盖了多少个基因。
def gene_saturation_curve(cell_expression):
"""
输入:一个细胞的表达向量(按基因排序)
输出:随着UMI数增加,检测到的基因数的累积曲线
"""
# 只保留表达量大于0的基因
expressed = cell_expression[cell_expression > 0]
# 按表达量从高到低排序
sorted_vals = np.sort(expressed)[::-1]
cum_genes = []
seen_genes = set()
for i, val in enumerate(sorted_vals):
# 每个UMI对应一个基因(简化模型,实际中一个基因可以有多个UMI)
# 这里我们看的是累加了多少个不同的基因
# 实际实现需要根据UMI与基因的对应关系来算
pass
这个曲线有一个关键特征:它的形状可以告诉你数据的测序深度是否足够。
如果曲线很快达到平台期——比如前500个UMI就覆盖了80%的基因——说明再测更多的数据也挖不出什么新基因了,测序深度已经饱和。
如果曲线一直缓慢上升,没有明显的平台期,说明还有大量低表达基因没有被检测到,可能需要更深度的测序。
我的个人经验:
- 饱和度过高(比如前100个UMI就覆盖了90%的基因):可能是背景RNA太多,或者细胞本身活性不好
- 饱和度过低(比如前1000个UMI只覆盖了30%的基因):可能是细胞类型本身异质性太大,或者是文库质量有问题
- 正常的单细胞文库,前500个UMI一般能覆盖60%-80%的检测到的基因
用一张图总结
下面我把整个关系画成一张流程图,方便你理解从原始数据到阈值判断的完整路径:
这张图展示了我处理单细胞数据时的标准流程。从原始矩阵开始,先做粗过滤,再做精分析,最后通过基因数和UMI的关系来判断整个数据集的健康度。
我曾经在一个肿瘤样本的数据里,发现大批细胞的这个相关系数只有0.6左右。按照这个流程一步步排查,最终定位到是组织解离时消化过度,导致大量细胞破碎,释放出的RNA污染了整个悬液。
这个案例让我确信一句话:基因数和UMI数的关系,是你手里最强大的质控诊断工具之一。