4. 数据预处理:缺失值处理与数据标准化
在MRM(多反应监测)定量蛋白质组学或代谢组学流程中,原始下机数据在进入特征筛选之前,必须经过严格的预处理。缺失值处理与数据标准化是决定后续统计功效和模型稳定性的两个关键环节。本章将系统讲解缺失值的三种处理策略(删除、填充、KNN插补)以及两种主流标准化方法(Z-score、分位数归一化),并给出MRM场景下的推荐流程。
4.1 缺失值处理
MRM数据中的缺失值通常来源于:信号低于检测限、峰识别失败或仪器短暂故障。缺失比例超过50%的特征通常建议直接剔除;对于缺失比例较低的特征,可根据数据分布特性选择填充或插补策略。
| 处理方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 删除(列删除) | 缺失率 > 50% 或样本量极大 | 简单快速,不引入偏差 | 丢失信息,可能降低统计功效 |
| 填充(均值/中位数/最小值) | 缺失率 < 20%,数据呈对称分布 | 计算量小,易于实现 | 低估变异,可能扭曲相关性 |
| KNN插补 | 缺失率 5%–30%,存在局部相似结构 | 利用样本间相似性,保留数据结构 | 计算成本高,对K值敏感 |
4.1.1 删除法
删除法分为按特征删除和按样本删除。在MRM特征筛选中,优先按特征删除:若某个肽段/代谢物在超过50%的样本中缺失,则直接剔除该特征。按样本删除仅当某样本缺失率极高(如>80%)时使用。
MRM实践建议: 先设定缺失率阈值(如30%),高于阈值的特征直接删除;低于阈值的特征进入填充或插补步骤。切勿在未评估缺失模式前盲目删除。
4.1.2 填充法
对于低缺失率特征,常用以下三种填充策略:
- 均值填充: 用该特征在所有样本中的均值填补缺失值。适用于正态分布数据,但会压低方差。
- 中位数填充: 用中位数填补,对异常值更稳健,推荐用于偏态分布。
- 最小值填充: 用该特征检测限或最小值填补,适用于“缺失即低于检测限”的假设(MNAR机制)。
# R语言示例:三种填充方式
# 假设 data 为样本×特征矩阵,缺失值标记为 NA
# 均值填充
fill_mean <- function(x) {
x[is.na(x)] <- mean(x, na.rm = TRUE)
return(x)
}
data_mean <- apply(data, 2, fill_mean)
# 中位数填充
fill_median <- function(x) {
x[is.na(x)] <- median(x, na.rm = TRUE)
return(x)
}
data_median <- apply(data, 2, fill_median)
# 最小值填充(假设检测限为0.001)
fill_min <- function(x) {
x[is.na(x)] <- min(x, na.rm = TRUE)
return(x)
}
data_min <- apply(data, 2, fill_min)
4.1.3 KNN插补
KNN(K-Nearest Neighbors)插补利用样本间的相似性来估计缺失值。对于每个含有缺失值的样本,找到与其欧氏距离最近的K个完整样本,用这些邻居的加权平均值填充缺失位置。K值通常取3–10,可通过交叉验证选择。
KNN插补优势: 相比简单填充,KNN能更好地保留数据的协方差结构,在后续特征筛选中减少假阳性。但需注意:K值过小易受噪声影响,K值过大则模糊局部模式。
# Python示例:使用scikit-learn的KNNImputer
from sklearn.impute import KNNImputer
import numpy as np
# 假设 data 为 numpy 数组,缺失值为 NaN
imputer = KNNImputer(n_neighbors=5, weights='distance')
data_knn = imputer.fit_transform(data)
# 输出插补前后的缺失数量
print("插补前缺失数:", np.isnan(data).sum())
print("插补后缺失数:", np.isnan(data_knn).sum())
4.2 数据标准化
标准化旨在消除不同特征间的量纲差异和分布偏移,使后续特征筛选(如t检验、LASSO、随机森林)对每个特征公平对待。MRM数据中,不同肽段/代谢物的原始强度可能相差数个数量级,标准化是必要步骤。
| 标准化方法 | 计算公式 | 输出分布 | 对异常值敏感度 |
|---|---|---|---|
| Z-score | z = (x − μ) / σ | 均值0,标准差1 | 敏感 |
| 分位数归一化 | 将各样本的分位数对齐到同一分布 | 所有样本分布相同 | 稳健 |
4.2.1 Z-score标准化
Z-score将每个特征值减去该特征的均值,再除以标准差。标准化后的数据均值为0,标准差为1,无量纲。适用于数据近似正态分布的场景。
# R语言示例:Z-score标准化
zscore_norm <- function(x) {
return((x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE))
}
data_zscore <- apply(data_knn, 2, zscore_norm)
# Python示例:使用scipy.stats.zscore
from scipy.stats import zscore
data_zscore = zscore(data_knn, axis=0, nan_policy='omit')
注意: Z-score对异常值非常敏感。若MRM数据中存在极端高表达特征(如某些高丰度蛋白),Z-score会压缩正常特征的变异,导致后续筛选偏向异常值。此时建议先进行对数转换或使用稳健Z-score(基于中位数和MAD)。
4.2.2 分位数归一化
分位数归一化强制所有样本具有相同的统计分布(如分位数)。具体步骤:
- 将每个样本的特征值按升序排序。
- 计算每个排位上的均值(或中位数),作为参考分布。
- 将每个样本的原始值替换为对应排位的参考分布值。
该方法在芯片数据和质谱数据中广泛应用,能有效消除批次效应和系统性偏移。
# R语言示例:分位数归一化(使用preprocessCore包)
# install.packages("BiocManager")
# BiocManager::install("preprocessCore")
library(preprocessCore)
data_qn <- normalize.quantiles(data_knn)
# Python示例:使用自定义函数
import numpy as np
def quantile_normalize(df):
"""
df: 样本×特征矩阵 (n_samples, n_features)
返回分位数归一化后的矩阵
"""
rank_mean = df.stack().groupby(
df.rank(method='first').stack().astype(int)
).mean()
qn = df.rank(method='min').stack().astype(int).map(rank_mean).unstack()
return qn.values
data_qn = quantile_normalize(pd.DataFrame(data_knn))
4.3 MRM预处理推荐流程
综合以上方法,针对MRM特征筛选前的数据预处理,推荐以下标准流程:
- 缺失率评估: 计算每个特征的缺失比例,删除缺失率 > 50% 的特征。
- 缺失机制判断: 若缺失与信号强度相关(MNAR),使用最小值填充;否则进入下一步。
- KNN插补: 对缺失率 5%–30% 的特征使用KNN插补(K=5,距离加权);缺失率 < 5% 的特征使用中位数填充。
- 对数转换(可选): 若数据呈右偏分布,先进行log2转换,使数据更接近正态。
- 标准化: 单批次数据推荐Z-score;多批次或存在明显批次效应时,推荐分位数归一化。
关键提醒: 预处理步骤的顺序不可颠倒。必须先处理缺失值,再进行标准化。若先标准化后插补,标准化步骤会因缺失值而计算错误,且插补时使用的距离度量也会失真。