第4章:数据预处理与质量控制——归一化、标准化、异常值处理
数据预处理这块,我个人习惯把它叫做“数据上台面”的过程。你从文献里抽出来的原始数据,说白了就是毛坯房。归一化、标准化、异常值处理,就是刮大白、铺地板、把歪掉的窗户换掉。不做这步,后续的批次效应校准全是白搭。
4.1 为什么必须先做数据清洗?
我在项目里遇到过最典型的例子——从GEO下载了两个批次的数据,直接跑差异分析。结果出来的“差异基因”全是批次之间的技术差异,跟生物学根本不沾边。为什么会这样?因为原始数据的量纲不一样,分布偏斜严重,还有一堆缺失值和离群点。
核心原则:数据预处理的目标只有一个——让不同实验条件下产生的数据,在统计特征上具备可比性。
4.2 缺失值处理
先扫一眼缺失值。很多芯片数据或质谱数据,某些基因或蛋白的表达量就是没测到。直接删掉?太亏。全填零?太糙。
常用策略
- 行删除:缺失比例超过50%的基因或样本,直接去掉。我个人建议阈值设在70%以上再删,别太狠。
- 列删除:某个样本若大部分特征都缺失,这个样本就没留着的必要了。
- 均值/中位数填充:针对低缺失率(小于5%)的特征,用该特征的非缺失值均值或中位数补齐。
- KNN填充:根据最近邻样本的表达模式估算缺失值。效果更好,但计算量大。
经验之谈:我曾经在图森癌症中心做过一个肺癌数据集整合,样本A的表达量几乎全缺失。用KNN填充后跑PCA一看,这个样本照样和其他样本挤在一起,说明填充策略选对了。
4.3 归一化 vs 标准化——别搞混
这两个概念,说白了就是两种不同的“摆平”但摆平的方式有本质差别。
| 方法 | 计算公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | [0, 1] | 特征值有明确边界,如图像像素 |
| Z-score标准化 | (x - μ) / σ | 均值为0,标准差为1 | 数据近似正态分布,或后续用到PCA、聚类 |
| 分位数归一化 | 将每个样本的值映射到同一分布 | 与原始分布一致 | 微阵列、RNA-seq等批次效应校正常用 |
| Log2转换 | log2(x + 1) | 无严格范围 | 表达数据偏斜严重时必用 |
注意:不要对已经做过归一化的数据再做一遍Min-Max。你想想看,两次映射之后信息的原始尺度就全丢了。我在做TCGA数据整合时吃过这个亏,跑完标准化结果所有基因的表达值几乎完全一样——那就是失效了。
4.4 异常值检测与处理
异常值是个大坑。我见过有人把离群点全删了,结果关键信号也跟着没了。异常值不一定是错误,它可能是你真正感兴趣的生物学差异。
常用检测方法
- 箱线图法:Q1 - 1.5×IQR 和 Q3 + 1.5×IQR 之外的点。简单粗暴,适合快速筛查。
- Z-score法:|Z| > 3 的点。前提是数据接近正态分布。
- DBSCAN聚类:基于密度,能发现孤立点,适合高维数据。
- PCA + 霍特林T²:在降维空间里看哪个样本离主成分模型太远。我常用这个,尤其当样本数量不多时。
处理策略
- 先查明原因:是测量错误还是真实信号?我曾在某个数据集中发现一个样本的几乎所有基因表达都异常高,后来发现是那个批次用了不同的检测芯片。这就是明显的批次效应,不是异常值。
- 若是技术原因:要么删除该样本,要么用中位数替换该样本的异常值。
- 若是真实生物学差异:保留。但要在后续分析中注明。
关键词:异常值和批次效应经常混在一起。你可以在做异常值检测之后,顺便画一张PCA图,看看那些“可疑样本”是否刚好按批次聚类。如果是,那就要小心了——不是异常值,是批次效应。
4.5 质量验证——怎么判断处理得好不好?
数据预处理做完,总要验证一下。我个人习惯看三个指标:
- 分布一致性:所有样本的表达值分布应该大致相同。画个密度图叠在一起看。
- 批次均一性:如果不考虑生物学差异,不同批次的样本应该混在一起,而不是分成两堆。
- 方差稳定:归一化或标准化之后,高表达和低表达基因的方差应该不再随均值变化。
给你一段简单的代码片段,用来做预处理的快速流水线:
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
# 假设 df 是 gene × sample 的表达矩阵
# 步骤1:删除缺失比例超过70%的基因
df = df.dropna(thresh=0.3 * df.shape[1], axis=0)
# 步骤2:中位数填充剩余缺失值
df = df.fillna(df.median(axis=1), axis=0)
# 步骤3:Log2转换(避免负数)
df_log2 = np.log2(df + 1)
# 步骤4:Z-score标准化(按基因)
scaler = StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df_log2.T).T,
index=df_log2.index,
columns=df_log2.columns)
print("预处理完成,形状:", df_scaled.shape)
小技巧:标准化之前保留一份原始表达量备份。我在项目里常常需要回头检查某个基因在原始数据中的表现,没有备份就麻烦了。
4.6 本章知识流程图
下面这张图把数据预处理的核心流程串起来了。你可以把它贴在墙上,每次做批次效应校准前扫一眼:
图中每一步都不是孤立的。缺失值没处理好,后面的归一化会扭曲;异常值没揪出来,批次效应校准会把异常当成正常模式去拟合。嗯,这里要注意:质量验证要放在最前面提,但实际做的时候,你可以在每一步都跑个小验证,别等到最后才看结果。
我曾经... 在一个单细胞数据分析项目里,三个批次的数据直接合并跑聚类,结果所有细胞都按批次分开。后来发现原始数据根本没有做过归一化——每个批次的测序深度和文库大小不同。补齐这一步之后,细胞才开始按细胞类型聚类。所以说,预处理做不好,后面全是假的。
好了,本章的内容就是这些。有了干净的、标准化的数据,你才算真正准备好进入批次效应校准的核心环节。记得先跑一遍我上面的缺失值处理示例,看看你的数据里有没有明显的模式问题。