第4章 数据预处理:质量控制、标准化、高变基因选择

数据预处理这一步,说白了就是给后面的分析“打好地基”。我刚开始做空间转录组项目时,总觉得数据拿到手直接跑聚类就完事了,结果跑出来的聚类结果一团糟——后来才明白,前处理没做好,后面再怎么调参数也是白费力气。咱们这一章就把 QC、标准化、高变基因选择这三个关键步骤拆开来讲,每一步我都会结合自己的踩坑经验给你说透。

4.1 为什么预处理这么关键

空间转录组数据有个特点:不仅维度高(几万个基因),还有空间坐标信息。如果原始数据里混着太多空液滴或死细胞,聚类结果就会把噪声当成真实信号。你想想看,一个只有20个UMI的点,它连基本的表达量都测不准,怎么能用来识别空间域?我个人的习惯是,预处理花的时间至少要占整个分析流程的30%,后面才能少走弯路。

咱们要做的三件事其实是一条流水线:

  1. 质量控制:剔除低质量的空间点(spots)
  2. 标准化:消除测序深度、点大小等技术偏差
  3. 高变基因选择:只保留信息量最大的基因用于聚类
个人经验:我在10x Visium数据上试过跳过标准化直接跑BayesSpace,聚类结果里出现了明显的批次效应——每个样本都聚到自己的区域里去了。所以这三步,一步都不能省。

4.2 质量控制(QC)

QC的核心目标就是识别并过滤掉“坏点”。空间转录组的每个点相当于一个“微组织样本”,它可能会因为组织褶皱、气泡、切片残片等原因质量很差。我常用的几个指标如下:

指标典型阈值说明
UMI计数(nCount)500 ~ 20000(视组织类型调整)太低可能是空液滴,太高可能是重叠组织
检测基因数(nFeature)300 ~ 6000低于300可能是空点,高于6000可能带气泡
线粒体基因比例(percent.mt)< 20%超过20%提示细胞/组织可能已受损

实际做的时候,我会先用散点图把这三个指标的分布画出来,手动选一个“拐点”作为阈值。记住:没有一劳永逸的固定阈值,每个数据集都有自己的脾气。

# Seurat 中常用的QC代码片段(以空间数据为例)
library(Seurat)
library(SeuratData)

# 创建Seurat对象
st <- Load10X_Spatial("path/to/spatial/")

# 计算线粒体比例
st[["percent.mt"]] <- PercentageFeatureSet(st, pattern = "^MT-")

# 可视化QC指标
VlnPlot(st, features = c("nCount_Spatial", "nFeature_Spatial", "percent.mt"),
        ncol = 3, pt.size = 0)

# 过滤
st <- subset(st, subset = nFeature_Spatial > 300 & 
             nFeature_Spatial < 6000 & 
             percent.mt < 20)
我曾经踩过的坑:有一次我用默认阈值(nFeature > 200)过滤一个高密度的肿瘤样本,结果保留了至少30%的空点,聚类后出现了大片“假区域”。后来把阈值提高到500,那些空点才被剔除干净。所以,强烈建议先可视化再定阈值,而不是直接套用别人的数字。

4.3 标准化

标准化要解决一个现实问题:不同空间点的测序深度可能差很大(比如有的点跑了5000 UMI,有的只有1000)。如果不做标准化,高深度点会被当成高表达,这显然不合理。

空间转录组最常用的标准化方法有:

  • LogNormalize:每个点的UMI除以该点总UMI,再乘以缩放因子(默认10000),最后log1p。
  • SCTransform:基于正则化负二项回归,效果更好,但计算量大。
  • BayesSpace 自己的预处理:它推荐先用log(counts + 1)做简单标准化,再配合其内置的马尔可夫随机场建模。

我个人在BayesSpace流程中通常这样操作:

# 用 Seurat 完成标准预处理
st <- NormalizeData(st, normalization.method = "LogNormalize", scale.factor = 10000)
st <- FindVariableFeatures(st, selection.method = "vst", nfeatures = 2000)
st <- ScaleData(st)
# 这里ScaleData会把数据缩放到均值为0、方差为1,便于后续降维
关键注意点:BayesSpace的输入要求是经过log标准化的表达矩阵(不能是原始counts),而且最好是没有经过ScaleData的——因为BayesSpace会自己处理空间相关性。所以如果你用Seurat做预处理,只要运行到NormalizeData和FindVariableFeatures即可,千万不要ScaleData。我当初就是忘了这一点,导致BayesSpace一直报错,排查了半天才发现问题。

4.4 高变基因选择

空间转录组数据通常检测到1~2万个基因,但其中大部分基因在点之间的表达变化很小,它们对区分空间域没有贡献。高变基因选择就是挑出那些“表现最活跃”的基因——它们在空间点上的表达水平波动最大。

常用方法有:

  • 方差稳定变换(vst):Seurat默认方法,拟合基因均值-方差关系,挑选标准化后方差最大的基因。
  • mean.var.plot:Seurat旧版方法,基于平均表达和方差绘制散点图。
  • dispersion-based:基于离散度(如TPM高表达时方差更大)。

我一般选2000个高变基因,如果组织类型很单纯(比如正常脑组织),1500个也够;如果是肿瘤异质性很强的样本,可以加到3000个。选择太多会把噪声带进来,太少又会丢失信息。

# Seurat 中直接运行
st <- FindVariableFeatures(st, selection.method = "vst", nfeatures = 2000)

# 查看挑选的基因
top10 <- head(VariableFeatures(st), 10)
print(top10)

# 可视化高变基因
VariableFeaturePlot(st)
一个实用的检查方法:高变基因列表里有没有线粒体基因?如果MT-CO1这类基因排在前列,说明你的QC可能没做好,组织损伤严重。这时候要回去检查percent.mt的阈值,而不是继续往下做。

4.5 数据预处理流程概览

为了让你对整个流程有更直观的理解,我画了一张流程图,把三步串联起来:

空间转录组数据预处理流程 原始表达矩阵 质量控制 标准化(log转换) 高变基因选择 PCA / BayesSpace ⚠ 重要提醒: 高变基因列表里若线粒体基因为主, 说明QC不到位,需要返回调整。

可以看到,从原始数据到最终用于聚类的特征矩阵,每一步都有明确的输入和输出。我个人习惯是把这份流程固化成一个脚本,每次跑新数据前先跑一遍QC诊断图,确认无误后再走标准化和高变基因选择,这样能避免很多后期返工。

总结三条铁律:
  1. QC阈值必须基于数据可视化手动确定,别用默认值。
  2. 标准化用log转换即可,不要额外做ScaleData(除非你知道自己在做什么)。
  3. 高变基因数量控制在1500~3000,优先检查线粒体基因占比。

做好了这三步,你的数据就已经准备好了。下一阶段我们就可以用它来运行BayesSpace的空间聚类算法了。不过在那之前,我建议你先拿这一套预处理流程跑一个简单的实例,感受一下每个参数变化对结果的影响——有时候“坏点”被过滤后,聚类的轮廓会变得清晰得多。