第4章 质谱数据预处理:基线校正、平滑、峰检测基础
拿到原始质谱数据,你千万别急着直接往上扔模型。原始数据又脏又乱,基线漂移、噪声干扰、峰形畸变,这些问题不处理,特征质量肯定打折扣。我在这个环节吃过不少亏,一开始总觉得预处理是浪费时间,后来发现——偷懒的代价就是模型精度上不去。
这一章,我们聚焦三步核心操作:基线校正、平滑、峰检测。这三步做好了,后续的特征提取才有意义。
4.1 基线校正:把地板找平
质谱的基线,并不是一条平直的线。它往往会随着时间或质量数缓慢漂移。为什么?原因很多:样品基质效应、色谱柱流失、检测器温度波动……说白了,你看到的信号是“真实峰 + 一个低频的干扰信号”。
我个人习惯用以下三种方法,按场景选:
| 方法 | 核心思想 | 我的使用场景 |
|---|---|---|
| TopHat 变换 | 形态学操作,用结构元素腐蚀再膨胀,减掉背景 | MALDI-TOF 数据,峰宽差异大的情况 |
| 多项式拟合 | 对非峰区域(噪声段)拟合低阶多项式,减去拟合曲线 | LC-MS 连续谱,基线缓慢变化时 |
| 局部最小值法 | 滑动窗口取最小值,插值成基线 | 低分辨率数据,峰比较稀疏时 |
举个 Python 实现片段,TopHat 变换用 scipy 就能做:
from scipy.ndimage import white_tophat
import numpy as np
# spectrum: 原始谱图向量,structure: 结构元素(圆形或方形)
structure = np.ones(int(peak_width)) # 结构元素宽度根据峰宽估算
baseline_corrected = spectrum - white_tophat(spectrum, structure=structure)
注意这个 peak_width 参数。设小了,基线校正不彻底;设大了,可能把窄峰当作基线给减掉。我记得第一次用 MALDI-TOF 数据时没调这个参数,结果几个重要的代谢物峰直接消失了……排查了好久才找到原因。
4.2 平滑:跟噪声说拜拜
基线校完之后,谱图里仍高频噪声还在。这些噪声会让峰检测产生大量假阳性。我的目标很简单:去掉噪声,同时尽量保留峰的边缘信息。
两张主流“武器”:
4.2.1 移动平均平滑
简单粗暴,计算窗口内均值。优点是快,缺点是把峰也抹平了。我一般只在快速预览数据时用。
4.2.2 Savitzky-Golay 滤波
这才是正解。它在窗口内用多项式拟合中心点的值,能在平滑的同时较好地保持峰高和峰宽。你想想看,如果你用普通均值,峰尖肯定被砍矮,而 SG 滤波能避免这个问题。
from scipy.signal import savgol_filter
# window_length 必须是奇数,polyorder 建议 2 或 3
smoothed = savgol_filter(spectrum, window_length=11, polyorder=2)
4.3 峰检测:找真正的信号
基线校了,噪声也压了,接下来就是找峰。峰检测听着简单,但陷阱不少。我总结的核心三要素:峰位置(质荷比)、峰强度、峰边界。
常用方法我列了个表:
| 方法 | 原理 | 我的推荐值 |
|---|---|---|
| 阈值法 | 信号超过某个信噪比阈值(如 SNR > 3) | SNR 阈值设为 3~5,噪声水平用 MAD 估计 |
| 导数法 | 一阶导过零点 + 二阶导负值判断峰顶 | 适合基线干净的谱图 |
| 匹配滤波器 | 用高斯或洛伦兹模板与信号做卷积 | 峰形已知时效果好,比如 TOF 数据 |
下面是我常用的一段峰检测代码,基于 scipy.signal:
from scipy.signal import find_peaks
peaks, properties = find_peaks(
smoothed,
height=noise_level * 3, # 高度阈值(SNR=3)
distance=5, # 峰之间最小间隔(点数)
prominence=0.5, # 峰相对周围的最小凸起
width=(2, 20) # 峰宽范围(点数)
)
- prominence(显著度)—— 我最看重的参数。它能防止把基线波动误判为峰。建议根据谱图噪声水平调一个经验值。
- distance(最小距离)—— 防止同一个峰被检测成两个。如果你在数据里看到一堆“双子峰”,八成是这个参数设小了。
- width(峰宽范围)—— 我觉得这个参数特别有用。设一个合理的范围,能把那些太尖(可能是噪音)或太宽(可能是基线隆起)的假峰过滤掉。
4.4 峰合并与去卷积
实际数据里,一个峰可能对应多个同位素峰或共流出物。这时候需要做峰合并或者去卷积。
我常用的策略是:先做基本峰检测,然后对 m/z 距离在某个容忍度(例如 0.01 Da 或 10 ppm)内的峰进行合并,保留强度最大的那个或者求和。在 LC-MS 数据中,我习惯用 0.01 Da 的容忍度来合并同位素峰簇。
0.5 Da 的合并窗口,结果把相邻的 m/z 峰全部吞并了,后面做特征关联时一团糟。记住——合并窗口要跟你仪器的质量精度匹配,不是越大越好。