4. 数据预处理(Visium):质控指标、过滤低质量斑点、标准化、高变基因选择
拿到 Visium 原始数据后,第一件事不是跑分析,而是做质控。这一点我吃过亏——第一次做整合时,上来就标准化,结果低质量斑点的噪声把整合效果全带偏了。
说白了,预处理就是把“脏数据”洗成可用的样子。对于 Visium 来说,我们需要关注四个步骤:质控指标评估、过滤低质量斑点、标准化、高变基因选择。
4.1 质控指标,看什么?
我个人习惯先扫一眼三个核心指标:
- n_counts:每个斑点的 UMI 总数。代表测序深度和捕获效率。
- n_genes:检测到的基因数。代表斑点本身的细胞复杂度。
- percent.mt:线粒体基因占比。代表细胞损伤或死亡的比例。
为什么会重点关注这三个?我在项目中遇到过一件事:有些样本质控看起来不错,但一查 percent.mt 已经飙到 30% 以上——结果整合后 batch effect 特别严重。其实早该在第一步就把它过滤掉。
plotQCMetrics 看一眼分布,再决定阈值。别一上来就设死数字。
4.2 过滤低质量斑点
阈值怎么设?没有标准答案。但我能给你一套实用的参考:
| 指标 | 经验阈值范围 | 说明 |
|---|---|---|
| n_counts | > 500 且 < 15,000 | 过低说明斑点可能空;过高可能是双细胞或组织碎片 |
| n_genes | > 250 | 低于此值,基因多样性不够,后续分析噪声大 |
| percent.mt | < 20% | 超过 20%,细胞状态堪忧 |
但注意,这些不是铁律。你想想看,不同组织类型差异很大——脑组织的 n_counts 通常比肿瘤组织低很多。我曾经做一个小鼠脑样本,过滤阈值放到才 200 个基因,结果发现有些有生物学意义的稀疏区域被砍掉了。后来我改用双倍 MAD(中位数绝对偏差)方法来自动判定阈值。
代码示例:用 Seurat 做基础过滤
library(Seurat)
library(ggplot2)
# 读取 Visium 数据
visium_data <- Load10X_Spatial(
data.dir = "path/to/spaceranger/outs",
filename = "filtered_feature_bc_matrix.h5"
)
# 计算质控指标
visium_data[["percent.mt"]] <- PercentageFeatureSet(
visium_data, pattern = "^MT-"
)
# 直观检查分布
VlnPlot(visium_data,
features = c("nCount_Spatial", "nFeature_Spatial", "percent.mt"),
ncol = 3, pt.size = 0.1)
# 过滤低质量斑点
visium_data <- subset(visium_data,
subset = nCount_Spatial > 500 &
nCount_Spatial < 15000 &
nFeature_Spatial > 250 &
percent.mt < 20)
subset 过滤后忘记检查剩余斑点数。有一次过滤得太狠,本来 4000 个斑点只剩 800 个,后续统计分析都没显著性。所以过滤后一定要跑 dim(visium_data) 看看总数,做到心里有数。
4.3 标准化
标准化这一步,说白了就是消除测序深度带来的差异。Visium 不同切片间的测序深度可能差好几倍,直接比较会误导下游分析。
我个人最推荐两种方式:
- LogNormalize:Seurat 默认方法。对每个斑点,将 UMI 数除以总 UMI 再乘以 scale.factor,最后 log1p 转换。
- SCTransform:基于正则化负二项回归,效果更好,但计算量大一些。
代码示例:LogNormalize 标准化
# 标准化(Seurat 默认)
visium_data <- NormalizeData(visium_data,
normalization.method = "LogNormalize",
scale.factor = 10000)
# 或者用 SCTransform(需要提前安装 sctransform)
visium_data <- SCTransform(visium_data,
vars.to.regress = "percent.mt",
verbose = TRUE)
这里有个知识点:SCTransform 内部直接包含了高变基因选择,所以当你用 SCTransform 后,可以跳过 4.4 节的手动高变基因筛选。但如果你用 LogNormalize,就得自己选一次。
4.4 高变基因选择
高变基因(HVG)就是那些在不同斑点间表达差异最大的基因。选择它们能降低维度、聚焦信号。你想想看,如果 20,000 个基因全跑进去,PCA 都会被 housekeeping 基因淹没。
Seurat 提供了两种方法:
- vst:基于方差稳定变换,速度最快。适合大数据集。
- mean.var.plot:基于均值-方差关系,传统方法,效果也不错。
选多少高变基因?我一般选 2000 个。太少会丢失信息,太多会引入噪声。我记得有一次选 5000 个,结果 PCA 的前 10 个 PC 解释方差反而下降了——说明过多基因稀释了信号。
代码示例:高变基因选择
# 识别高变基因(用 vst 方法)
visium_data <- FindVariableFeatures(visium_data,
selection.method = "vst",
nfeatures = 2000)
# 查看选中的基因
top10 <- head(VariableFeatures(visium_data), 10)
print(top10)
# 将数据缩放到标准范围(准备后续 PCA)
visium_data <- ScaleData(visium_data,
features = rownames(visium_data))
FindVariableFeatures,否则会覆盖掉 SCTransform 的结果。
到了这一步,你的 Visium 数据就备好了。接下来可以进入 PCA 降维、聚类分析,或者直接和 Slide-seq 做整合。但我要强调一句:预处理的质量直接决定后续整合的成败。我见过太多项目因为质控太松或者标准化方法选错,整合后 batch effect 满天飞,怎么调都调不回来。
把这一步走扎实,后面就顺了。