数据准备与预处理:标准化、降维

预处理这一步,我见过太多人跳过了。上来直接调 BBKNN 参数,结果跑出来一团糟,还以为是算法的问题。其实呢,问题出在数据本身。

标准化和降维,是单细胞数据进入 BBKNN 前的必修课。我个人习惯先把这几步理清楚,后面调参才能有的放矢。

为什么要标准化?

你想想看,原始的单细胞表达矩阵,不同基因的数值范围差太多了。有的基因表达量只有个位数,有的能到几千、几万。如果直接丢进 BBKNN,距离计算会被那些高表达的基因主导,低表达但重要的基因信息全被淹没了。

核心思路:让所有特征在同一尺度下参与计算,消除量纲影响。

我在项目中遇到过一件事:某批次数据里有个基因高度表达,占走了 70% 的邻居权重,结果 BBKNN 硬是把不同细胞类型拉到一起。标准化之后,这种偏差就消失了。

常用的标准化方法

我一般用这三种,场景各不相同。

方法 公式思路 适合场景 我的建议
StandardScaler 减去均值,除以标准差 数据近似正态分布,无异常值 最常用,跑 PCA 前首选
MinMaxScaler 缩放到 [0,1] 区间 范围已知,或需要保持稀疏性 小心被离群值压扁正常分布
RobustScaler 用中位数和 IQR 代替均值和标准差 数据存在较多离群基因 单细胞数据我偏爱这个

小技巧:单细胞数据通常先做 CP10K 或 LogNormalize,再做标准化。千万别直接对原始 counts 做 StandardScaler,不然结果会很奇怪。

标准化代码实战

咱们用 scanpy 和 scikit-learn 来演示。我当时第一次调 BBKNN 时,直接用 scanpy 的 sc.pp.scale,发现它默认做的就是 Z-score 标准化。后来遇到一个数据集,离群值特别多,换成 RobustScaler 效果才上来。

import scanpy as sc
import numpy as np
from sklearn.preprocessing import RobustScaler

# 读数据
adata = sc.read_h5ad('my_data.h5ad')

# 方法一:扫描式标准化(Z-score)
sc.pp.scale(adata, max_value=10)

# 方法二:RobustScaler(我常用)
scaler = RobustScaler()
adata.X = scaler.fit_transform(adata.X.toarray() if hasattr(adata.X, 'toarray') else adata.X)

注意:sc.pp.scale 默认会在每个基因上减去均值、除以标准差。如果你的数据是稀疏矩阵,记得先转成稠密再传 sklearn,或者用 scanpy 的稀疏支持方法。我曾经直接传 CSR 矩阵给 RobustScaler,结果报错耗了半天。

降维:为什么需要降维?

说白了,高维空间的距离度量会失效。随着维度增加,所有点之间的距离趋于相等——这叫“维数灾难”。BBKNN 构建的是近邻图,在高维里邻居关系根本不可靠。

我一般降到 50 维左右。太少了(比如 10 维)会丢掉太多信息,太多了(比如 200 维)又起不到降噪效果。

常用降维方法对比

方法 适合任务 速度 我的经验
PCA BBKNN 预处理首选 快 稳定,几乎必用
t-SNE 可视化,不适合作为 BBKNN 输入 慢 别用它做距离计算,保存结构不好
UMAP 可视化 + 部分场景可替代 PCA 中等 参数敏感,新手慎用

BBKNN 官方推荐:先用 PCA 降维到 20-50 维,再传给 BBKNN。我试过用 UMAP 降维替代,效果不稳定,最后还是老老实实 PCA。

降维代码实战

# 用 scanpy 做 PCA
sc.tl.pca(adata, n_comps=50, svd_solver='arpack')
print("PCA 方差解释率:", adata.uns['pca']['variance_ratio'][:5])

# 也可以手动用 sklearn
from sklearn.decomposition import PCA
pca = PCA(n_components=50)
pca_result = pca.fit_transform(adata.X)
print("累计方差比:", np.sum(pca.explained_variance_ratio_))

嗯,这里要注意:PCA 前一定要确保数据已经中心化(均值为0)。标准化就帮我们做了这件事。所以标准化和 PCA 通常是连用的。

标准化 + 降维的完整流程

我一般会画个流程图来理清思路,现在也用 SVG 画一张,方便你对照。

数据预处理:从原始表达矩阵到 BBKNN 输入 原始表达矩阵 标准化 (Standard / Robust) 降维(PCA) n_comps=20~50 BBKNN 🔑 要点:标准化消除量纲,PCA 降维去噪加速 标准化后必须检查方差解释率,确保前 50 维解释 >70% 否则考虑增加维度或换降维方法 🚫 常见陷阱 跳过标准化 → 低表达基因信息丢失;直接 t-SNE → 结构扭曲

我自己的流程笔记:先 sc.pp.filter_genes 过滤低表达基因,再用 RobustScaler 标准化,最后跑 PCA 到 50 维。这个组合在大多数数据集上效果都很棒。

补充:标准化与降维的潜规则

  • 标准化必须在降维之前做。顺序反了,PCA 的第一主成分可能就是基因表达量的差异,而非真实的生物学差异。
  • 降维后的特征记得检查一下方差解释率。如果前 50 维加起来不到 60%,说明数据方差太分散,可能需要增加维度或考虑其他降维方法(比如 TruncatedSVD)。
  • 不要把降维后的数据再做一次标准化。PCA 后各主成分已经是正交且方差的,再标准化会破坏它们的相对重要性。

曾经踩过的坑:有一批数据,我偷懒只做了对数归一化就直接跑 BBKNN,结果批次矫正效果极差。后来发现不同批次的表达量整体范围不同,加上标准化后一切正常。记住:归一化不等于标准化,两者目的不同。

好了,数据准备好,下一步就可以进入 BBKNN 参数调优环节了。基础打牢,后面调参才能看到真正的效果。