4. 数据预处理

数据预处理,说实在的,是DIABLO建模里最容易被低估的一步。我见过不少团队,模型选得花里胡哨,结果因为数据没处理好,跑出来的结果一塌糊涂。你想想看,输入的是垃圾,输出当然也是垃圾。多组学数据来自不同平台,量纲、分布差异巨大,不统一处理,模型根本没法正常工作。

4.1 为什么预处理如此关键?

DIABLO的核心,是从多个组学视图里提取共享的低维结构。这要求每个视图的数据分布不要过于离谱。举个例子,转录组数据通常是count值,范围从0到几万;而代谢组数据可能是峰面积,或者已经归一化过的相对丰度。把这两种数据直接塞进模型,高量纲的变量会主导距离计算和协方差结构。

我习惯把预处理的目标归结为三点:

  • 消除平台差异 – 让不同组学的变量具有可比性
  • 稳定方差 – 尤其是高表达基因通常会方差更大,需要控制
  • 去噪与质量控制 – 剔除低质量的样本和特征,不让它们带偏模型
个人小经验:预处理没有唯一的"金标准"。我经常根据数据特性选2-3种方案,在DIABLO里跑一遍交叉验证,挑性能最优的那个。这种做法费时,但值得。

4.2 归一化 vs 标准化 – 别搞混了

这两个概念经常被混用,我来理清楚。

方法 公式 结果范围 适用场景
Min-Max 归一化 (x - min) / (max - min) [0, 1] 当数据已有明确上下界,或后续要用到距离度量
Z-score 标准化 (x - mean) / sd 均值为0,标准差为1 数据近似正态,或后续需要协方差结构
总和归一化 x / sum(all) [0, 1] 且总和为1 测序深度归一化常用(如CPM)

DIABLO底层用的是偏最小二乘的思想,它本质上对数据的尺度比较敏感。我个人倾向于先做Z-score标准化,让每个变量都在同一水平上。但有一种情况例外:如果数据里包含大量零值(比如稀疏的微生物组数据),Z-score会放大那些零点的噪声。这时候我会先用log2转换,再做标准化。

曾经踩过的坑:有一次我把一个包含极端离群值的代谢物数据集直接用Z-score标准化,结果标准差被离群值撑得很大,正常值的Z-score全部缩到接近0,模型几乎失效。后来我改用了稳健的标准化方法(基于中位数和MAD),效果好了很多。

4.3 log2转换 – 为什么是2不是10?

log转换的核心目的,是让数据分布更接近正态,同时让方差与均值解除关联。你想想看,表达量10000的基因,随机误差可能是几百;而表达量10的基因,误差可能只有1。直接在原始尺度上建模,高表达变量会在协方差矩阵里称王称霸。

为什么选log2?两个原因:

  • 生物学上,fold change以2倍为单位,log2之后差异可以直接解读为倍数变化
  • 计算上,log2转换后,均值和方差更容易处理

常见的做法是,对count类数据做log2(CPM + 1)或者log2(FPKM + 1),加1是为了避免log(0)的问题。代谢组数据如果已经是相对丰度,也可以直接log2转换。

# R语言示例:用edgeR做log2转换
library(edgeR)

# 假设count_matrix是基因表达计数矩阵
dge <- DGEList(counts = count_matrix)
dge <- calcNormFactors(dge)
cpm_matrix <- cpm(dge, log = TRUE, prior.count = 1)

# 结果就是log2(CPM + 0.5)或log2(CPM + 1)
# 具体取决于prior.count参数

你可能会问:为什么不log10?其实都可以,只是log10之后解读差异没那么直观。我在项目中统一用log2,方便后续做fold change分析时直接对应。

4.4 数据质量控制 – 坏数据会毁掉模型

这一步我建议放在所有处理的最前面。先做QC,选好样本和特征,再去做归一化和转换。否则垃圾数据经过转换,看起来规整了,但本质还是垃圾。

QC主要做两件事:

  1. 样本层面:
    • 缺失率:某个样本缺失超过30%的特征,直接删除
    • 离群值:用PCA或者层次聚类看是否有明显的离群样本
    • 批次效应:如果数据来自不同批次,先用ComBat或limma矫正
  2. 特征层面:
    • 低表达过滤:转录组中平均CPM小于0.5的基因,大概率是噪声
    • 缺失率:某个特征在超过50%的样本中缺失,删掉
    • 方差过滤:方差接近于0的特征对模型没有信息贡献
实操建议:我通常在QC阶段画一张样本相关性的热图。如果发现某个样本与其他样本的平均相关系数低于0.6,我会仔细检查它的原始数据,看是不是污染或者实验误差。
# R语言示例:特征过滤与样本QC
# 使用mixOmics包中的数据(diablo示例数据)

# 加载数据
data("breast.TCGA")
X <- breast.TCGA$data.train

# 特征缺失率过滤
filter_features <- function(mat, threshold = 0.5) {
  missing_rate <- apply(mat, 2, function(x) sum(is.na(x)) / length(x))
  keep <- missing_rate <= threshold
  message(paste("保留", sum(keep), "个特征,删除了", sum(!keep), "个"))
  return(mat[, keep])
}

# 样本缺失率过滤
filter_samples <- function(mat, threshold = 0.3) {
  missing_rate <- apply(mat, 1, function(x) sum(is.na(x)) / length(x))
  keep <- missing_rate <= threshold
  message(paste("保留", sum(keep), "个样本,删除了", sum(!keep), "个"))
  return(mat[keep, ])
}

# 执行过滤
X_filtered <- filter_samples(X)
X_filtered <- filter_features(X_filtered)

4.5 完整预处理流程 – 我一般这么做

下面是我在项目里常用的三步走流程。当然,具体细节会根据数据类型微调,但大方向不变。

  1. QC过滤:删除低质量样本和特征
  2. 归一化:测序数据用CPM/TPM,代谢组数据用总和归一化或PQN
  3. log2转换 + Z-score标准化:先log2,再Z-score,最后整合
# 完整预处理示例(R语言)
preprocess_diablo <- function(mat_list, qc_threshold = 0.5) {
  # 假设mat_list是一个命名列表,每个元素是一个组学矩阵
  processed <- list()
  
  for (name in names(mat_list)) {
    mat <- mat_list[[name]]
    
    # 步骤1: 样本QC - 删除缺失过多的样本
    sample_na <- apply(mat, 1, function(x) sum(is.na(x))/length(x))
    mat <- mat[sample_na < 0.3, ]
    
    # 步骤2: 特征QC - 删除缺失过多的特征
    feature_na <- apply(mat, 2, function(x) sum(is.na(x))/length(x))
    mat <- mat[, feature_na < qc_threshold]
    
    # 步骤3: 低表达过滤(针对转录组)
    if (name == "mrna") {
      expressed <- apply(mat, 2, function(x) sum(x >= 10)) >= 0.5 * nrow(mat)
      mat <- mat[, expressed]
    }
    
    # 步骤4: 归一化(用总和归一化作为示例)
    mat <- sweep(mat, 1, rowSums(mat, na.rm = TRUE), "/") * 1e6
    
    # 步骤5: log2转换
    mat <- log2(mat + 1)
    
    # 步骤6: Z-score标准化
    mat <- scale(mat)
    
    processed[[name]] <- mat
  }
  
  return(processed)
}
注意:Z-score标准化是按特征做的,即每个基因/代谢物在样本间标准化。不要跨特征标准化,那会把不同生物学过程的差异抹掉。

4.6 知识体系一览

下面的SVG图梳理了本章的核心逻辑,从原始数据到DIABLO输入,中间的每一步都有对应的决策点。你可以拿它当一个工作流检查清单。

数据预处理核心流程 多组学数据预处理工作流 原始多组学数据 1. 质量控制 (QC) 样本 & 特征过滤 + 缺失率检查 2. 归一化 CPM / TPM / 总和归一化 3. log2转换 稳定方差 + 可加性 4. Z-score标准化 均值为0,标准差为1 输入 DIABLO 建模 避坑提醒 • 先QC,再归一化 • 不同组学用不同归一化策略 • log2前必须处理零值(加1) • Z-score按特征做,别弄反 • 保留原始数据,随时回溯

4.7 不同组学数据的预处理策略

每一种组学都有它自己的"脾气",不能一刀切。我整理了一下常见的处理方式:

组学类型 原始数据形式 推荐归一化 QC关注点
转录组(RNA-seq) count矩阵 CPM / TPM / FPKM 低表达基因、批次效应
代谢组(LC-MS) 峰面积 总和归一化或PQN 缺失值、批次漂移
蛋白质组 iBAQ / TMT ratio 中位数标准化或IRS 缺失比例、定量准确性
微生物组(16S) OTU/ASV count CSS / TMM / 稀有化 稀疏性、测序深度

你看这张表,右边几列都不是固定的。比如代谢组数据,如果样本量小且缺失率高,可以先做缺失值填充(用kNN或者半最小值),再做归一化。蛋白组数据如果来自TMT标记实验,还得考虑通道效应。

我的惯例:在正式建模前,至少准备2-3种预处理组合,分别输入DIABLO跑一次。比较一下模型的稳定性(通过交叉验证的Q²值)和可解释性。预处理这一步赌对了,后续建模就顺畅得多。

4.8 总结一下

数据预处理听起来琐碎,但它在DIABLO流程里的分量,真的不亚于模型本身。你想想看,原始数据带着不同量纲、不同分布、不同噪声水平直接进去,模型的第一主成分很可能就被某个高方差变量垄断了,那多组学整合的意义就全没了。

我的建议是:先把QC做到位,剔除那些明显有问题的样本和特征;然后根据组学类型选合适的归一化方法;最后统一做log2转换加Z-score标准化。这样处理之后,不同组学视图的数据才算真正"对齐"了,模型才能从生物学信号里学到东西。

嗯,这章内容我们先到这里。把预处理功底打扎实,后面建模的事就好办了。