4. 空间转录组数据预处理:质控、标准化与高变基因选择

预处理这个环节,我把它比作「地基中的地基」。你想想看,如果原始数据里掺了大量空点、死细胞或者批次噪音,后面再花哨的校正算法也救不回来。我个人习惯把预处理拆成三个递进阶段:质控过滤、标准化、高变基因选择。下面逐一拆开细讲。

一句话核心:预处理的目的不是「让数据变好看」,而是剔除技术噪音、保留真实的生物学信号,为后续批次校正和空间分析铺路。

空间转录组数据预处理流程 阶段一 · 质控过滤 阶段二 · 标准化 阶段三 · 高变基因选择 原始计数矩阵 组织切片 + 对齐 点级QC指标 空点/背景去除 标准化 LogNormalize / SCTransform 高变基因选择 vst / dispersion / 空间特异性 预处理完成数据 每个阶段的输出都会直接影响后续分析的可靠性,建议逐阶段验证后再推进。

4.1 质控过滤:先清理,再分析

说白了,质控就是三件事:切面对齐对不对、点质量够不够好、是不是空点。我在项目中见过不少人跳过组织对齐检查,直接跑标准化,结果一半「组织点」实际上是载玻片背景,后续差异分析全是假阳性。

4.1.1 组织切片与对齐检查

拿到空间数据,第一件事不是跑代码,而是看组织切片图像。10x Visium 会给你一张 H&E 或免疫荧光染色图,你得确认组织覆盖区域和捕获点阵是对齐的。我记得有一次,切片边缘有一块折叠组织,软件自动识别时把折叠区域也算进去了,那部分的基因表达完全失真。

  • 组织掩膜:手动或自动勾勒组织边界,排除空白区域
  • 对齐偏差:如果图像坐标与捕获点坐标偏移超过 1-2 个点直径,需要重新配准
  • 组织碎片:直径小于 3 个点的组织碎片建议剔除

4.1.2 点级别 QC 指标

每个捕获点的质量,主要看三个指标。我习惯先画一组小提琴图,直观判断阈值。

指标 含义 常见阈值范围 异常信号
nCount_Spatial 每个点的总 UMI 数 500 – 15000 过低→空点;过高→双点或组织堆积
nFeature_Spatial 每个点检测到的基因数 200 – 5000 过低→RNA 降解或空点
percent.mito 线粒体基因 UMI 占比 < 20% 过高→细胞受损或凋亡

注意:阈值不是死的!不同组织类型、不同平台差异很大。比如 Slide-seq 数据 UMI 深度通常比 Visium 低一个数量级,硬套 Visium 的阈值会丢掉大量有效点。

这是我常用的一段 QC 过滤代码:

# R / Seurat — 点级QC
sp_data <- subset(sp_data,
  subset = nCount_Spatial > 500 &
           nFeature_Spatial > 200 &
           percent.mito < 20)

# 可视化检查
VlnPlot(sp_data, features = c("nCount_Spatial",
  "nFeature_Spatial", "percent.mito"),
  pt.size = 0.1)

4.1.3 空点与背景点识别

空间转录组特有的一个坑:捕获点覆盖了组织,但也可能覆盖了载玻片上的背景区域。我曾经遇到一个项目,空点比例高达 30%,这些空点的基因表达谱其实是载玻片上的残留 RNA 或者环境菌群。怎么筛?

  • UMI 绝对阈值:nCount_Spatial < 200 直接标记为空点
  • 与组织图像交叉验证:点坐标是否落在组织掩膜内
  • 空点评分模型:用 SpotClean 或 EmptyDrops 方法,基于背景 RNA 分布做概率判断

小技巧:筛选完空点后,建议保留原始计数矩阵中空点的数据作为「背景信号分布」,后续去卷积或反卷积分析时能用上。

4.2 标准化:让不同切片具有可比性

标准化要解决的核心问题——不同切片、不同捕获区域的测序深度不一样。你想想看,一张切片测了 15 万 UMI,另一张只测了 5 万,直接比较原始计数就是刻舟求剑。

我个人最常用的两种方法,对比在这里:

方法 数学本质 适用场景 注意事项
LogNormalize log(CPM + 1) 快速、稳妥,适合大多数空间数据 对高表达基因有压缩,但丢失零膨胀结构
SCTransform 正则化负二项回归 数据稀疏性高、存在批次效应时 计算量大,但能保留更多生物学变异
Pearson残差 基于预测值的残差 需要去除测序深度影响时 对异常值敏感,建议配合 HVG 使用

标准化的代码实现很简单,但参数选择有讲究:

# LogNormalize(默认scale.factor=10000)
sp_data <- NormalizeData(sp_data,
  normalization.method = "LogNormalize",
  scale.factor = 10000)

# SCTransform(推荐在QC后直接运行)
sp_data <- SCTransform(sp_data,
  vars.to.regress = "percent.mito",
  verbose = FALSE)

我的建议:如果后续要做批次校正(比如 Harmony、Seurat CCA),优先用 SCTransform。它内置了方差稳定化,高变基因筛选的准确率会比 LogNormalize 高出不少。我对比过多次,SCTransform 选出的基因在空间模式上更清晰。

4.3 高变基因选择:把精力花在刀刃上

空间转录组通常会检测 1-3 万个基因,但真正有空间表达模式的只有几百到两千个。高变基因选择的目的,就是找出这些「信息量最大」的基因,降维和聚类时才能看到结构。说白了,就是让你的计算资源用在正确的地方。

4.3.1 三种主流选择策略

  • vst(方差稳定变换):Seurat 默认方法,速度快、普适性好,适合大多数空间数据
  • dispersion(离散度):基于均值-方差关系,对低表达基因灵敏,但容易被高表达基因主导
  • 空间特异性选择:用 Moran's I 或 SPARK-X 检测具有空间自相关的基因,计算量大但针对性最强
# vst 方法 —— 选2000个
sp_data <- FindVariableFeatures(sp_data,
  selection.method = "vst",
  nfeatures = 2000)

# 空间特异性方法(SPARK-X)
# sparkx_results <- sparkx(counts, location,
#   numCores = 4)
# hvgs_spatial <- rownames(
#   sparkx_results[sparkx_results$adjusted_pvalue < 0.05, ])

4.3.2 选多少个合适?

这个问题没有标准答案。我遇到过选 500 个聚类没结构,选 5000 个又跑不动的尴尬。一般来说:

  • 10x Visium:1500 – 3000 个
  • Slide-seq / Stereo-seq:2000 – 5000 个(数据更稀疏)
  • 空间特异性基因:通常 200 – 800 个(严格筛选)

我曾经踩过的坑:为了追求「信息量」,我把高变基因数拉到 5000,结果后续 PCA 和 UMAP 不仅慢,还出现了明显的技术噪音聚类。后来发现是选入了大量核糖体基因和线粒体基因——这些基因在多数条件下都高表达,并不代表空间异质性。建议在筛选前先去除 RPS、RPL、MT- 等基因家族。

4.4 预处理流程在批次校正中的角色

到这里,你可能会问:「预处理和批次校正到底什么关系?」简单回答:预处理是批次校正的前置条件。如果每张切片单独做 QC、标准化、选 HVG,然后合并起来跑校正,效果远好于把原始计数直接合起来再处理。我试过两种路径,前者在校正后的 LISI 指标和生物分离度上都有明显优势。

具体操作时,我建议对每个样本独立完成以下步骤,再进入合并校正阶段:

  1. 独立 QC(阈值可以统一,但要检查每张切片的质量分布)
  2. 独立标准化(推荐 SCTransform)
  3. 独立高变基因选择(每张切片选 2000 个,然后取并集)
  4. 合并所有样本的标准化表达矩阵,开始批次校正

高效工作流:用 split() + lapply() 把三步骤写成函数,一行代码跑完所有样本的预处理,再 merge() 合并。千万别手动一张一张处理,既慢又容易出错。

预处理做扎实了,后续的批次校正才能放心。下一阶段我们会正式进入校正方法的实战对比,到时候你会看到,预处理质量直接决定了校正结果的上下限——这个关系,我希望你在自己的数据集上也验证一次。