基因数与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数的关系分析流程 原始计数矩阵 步骤1:UMI数基础过滤 剔除UMI数<500的barcode 步骤2:基因数阈值判断 保留基因数≥200的细胞 步骤3:基因数-UMI数关系分析 计算相关系数、拟合曲线、检查偏离 正常:相关系数≥0.9 异常:相关系数<0.85 进一步拆分检查 注意:每个步骤的阈值需要根据实际实验数据微调;不同类型样本的基线不同

这张图展示了我处理单细胞数据时的标准流程。从原始矩阵开始,先做粗过滤,再做精分析,最后通过基因数和UMI的关系来判断整个数据集的健康度。

我曾经在一个肿瘤样本的数据里,发现大批细胞的这个相关系数只有0.6左右。按照这个流程一步步排查,最终定位到是组织解离时消化过度,导致大量细胞破碎,释放出的RNA污染了整个悬液。

这个案例让我确信一句话:基因数和UMI数的关系,是你手里最强大的质控诊断工具之一。