1. 质谱数据基础:原理、数据类型与常见格式
好,我们正式开始。第一节课,我带你打下最核心的基础——质谱数据到底长什么样。
我见过不少做机器学习的同事,直接拿原始数据丢进模型,结果一团糟。为什么?因为他们不明白质谱数据的“脾气”。你想想看,一个传感器输出的原始电压信号,跟一张经过峰检测、去噪的列表,完全不是一回事。搞混了它们,后续特征工程全是白费。
1.1 质谱仪的工作原理:它到底在测什么?
质谱仪的核心原理并不复杂。说白了,它就是一台精密的天平,专门用来称量分子的质量。
但分子的质量太小了,没法直接称。所以质谱仪干了三件事:
- 离子化:把样品分子带上电荷,变成离子。
- 分离:根据离子的质荷比(m/z)把它们分开。
- 检测:记录每个 m/z 对应的离子数量(强度)。
你会得到一个二维数据:横轴是 m/z,纵轴是强度。这就是一张质谱图。
我在项目中遇到过一个问题:有人把同位素峰误判为不同的化合物。其实同位素峰是同一个化合物因天然同位素丰度产生的“影子”,m/z 相差大约 1 个原子质量单位。做特征提取时,一定要把它们合并处理,否则模型会学到虚假的“多峰模式”。
1.2 常见数据类型:千万别搞混
数据进了电脑,就不再是模拟信号了。你会遇到几种不同层级的数据类型。我刚开始做质谱分析时,就曾把 profile 数据和 centroided 数据混在一起做 PCA,结果聚类图案完全离谱。
这里我画了一张图,帮你理清从仪器到特征矩阵的完整链条:
下面我逐个展开讲讲:
| 数据类型 | 特点 | 用于机器学习? |
|---|---|---|
| 原始信号 | 模拟电压,未经任何处理 | ❌ 几乎不直接用 |
| Profile 谱图 | 数字化后的连续峰型 | ⚠ 可做深度学习输入(如 1D CNN) |
| Centroided 列表 | 离散的 (m/z, 强度) 对 | ✅ 常用,需要对齐 |
| 定量矩阵 | 样本×峰的二维表格 | ✅ 标准特征矩阵格式 |
我曾经接手过一个合作项目,对方只给了我一个 centroided 峰列表,但后续想重新做基线校正已经没办法了。因为离散化后原始峰型信息丢失了。我建议你始终保留一份 profile 格式的原始数据副本。
1.3 常见数据格式:mzML、mzXML 与其他
数据格式决定了你如何读、写、交换质谱数据。我个人习惯优先用 mzML,它是 HUPO-PSI 社区推荐的开放标准格式。
mzML
- 基于 XML,结构清晰
- 同时包含原始谱图和峰列表
- 支持元数据(如仪器参数、扫描设定)
- 几乎所有现代工具链都支持
mzXML
- mzML 的前身
- 同样基于 XML,但结构不同
- 不少老旧数据集用的是它
其他格式
- mzData:另一种 XML 变体,已较少见
- mgf:峰列表纯文本格式,适合做数据库搜索
- Raw / .wiff:Thermo/SCIEX 厂商专有格式,需要厂商 SDK 才能读
下面是一个 mzML 文件的简化结构示例。你不用记住每一行,但要知道它里面藏着什么:
<?xml version="1.0" encoding="UTF-8"?>
<mzML xmlns="http://psidev.info/psi/pi/mzML/2.0">
<run id="sample_01">
<spectrumList count="1500">
<spectrum index="0" id="scan=1">
<cvParam name="ms level" value="1"/>
<binaryDataArrayList count="2">
<binaryDataArray>
<cvParam name="m/z array" />
<binary>encoded_mz_values_here</binary>
</binaryDataArray>
<binaryDataArray>
<cvParam name="intensity array" />
<binary>encoded_intensity_values_here</binary>
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
<!-- 更多谱图 -->
</spectrumList>
</run>
</mzML>
你看,核心是 spectrumList 里的每个 spectrum。它包含了 m/z 数组和强度数组。这些二进制数据通常用 zlib 压缩,读取时需要解压。我建议你用 pyOpenMS 或 pymzml 来处理——不要自己手写 XML 解析器,那是给自己找麻烦。
拿到数据集后,第一步先用
mzMLValidator 检查文件完整性。我曾因为省掉这一步,花了一周时间调试一个莫名其妙的内存溢出 bug——后来发现是一个损坏的 mzML 文件导致的。
格式对比速览
| 格式 | 类型 | 压缩支持 | 工具链成熟度 |
|---|---|---|---|
| mzML | XML + 二进制 | ✅ zlib | ★★★★★ |
| mzXML | XML + 二进制 | ✅ zlib | ★★★☆☆ |
| mgf | 纯文本 / 峰列表 | ❌ | ★★★☆☆ |
| Raw (.raw) | 厂商专有 | ⚠ 取决于厂商 | ★★☆☆☆ |
1.4 数据读取实战入门
我写了个最简单的代码片段,用 pymzml 读取一个 mzML 文件,提取第一张 MS1 谱图的 m/z 和强度。你看完就能跑:
import pymzml
msrun = pymzml.run.Reader("sample.mzML")
for spec in msrun:
if spec.ms_level == 1:
mz_arr = spec.mz
int_arr = spec.i
print(f"m/z 范围: {mz_arr[0]:.4f} – {mz_arr[-1]:.4f}")
print(f"数据点数: {len(mz_arr)}")
break
输出应该是这样:
m/z 范围: 100.0123 – 2000.4567
数据点数: 25000
嗯,这里要注意:MS1 谱图的数据点数可以很大(几万甚至几十万)。如果你目标是机器学习特征提取,通常不会直接用这 25000 个点。你会先做峰检测、对齐,得到一张稀疏的峰列表。这个问题我们后面章节会详细讲。
好了,第一章就把质谱的数据骨架搭起来了。你记住四件事:m/z 和强度是核心、profile vs centroided 要分清楚、mzML 是首选格式、原始数据一定要备份。后面第二章,我们正式进入特征提取的细节。