1. 质谱数据基础:原理、数据类型与常见格式

好,我们正式开始。第一节课,我带你打下最核心的基础——质谱数据到底长什么样。

我见过不少做机器学习的同事,直接拿原始数据丢进模型,结果一团糟。为什么?因为他们不明白质谱数据的“脾气”。你想想看,一个传感器输出的原始电压信号,跟一张经过峰检测、去噪的列表,完全不是一回事。搞混了它们,后续特征工程全是白费。

1.1 质谱仪的工作原理:它到底在测什么?

质谱仪的核心原理并不复杂。说白了,它就是一台精密的天平,专门用来称量分子的质量。

但分子的质量太小了,没法直接称。所以质谱仪干了三件事:

  1. 离子化:把样品分子带上电荷,变成离子。
  2. 分离:根据离子的质荷比(m/z)把它们分开。
  3. 检测:记录每个 m/z 对应的离子数量(强度)。

你会得到一个二维数据:横轴是 m/z,纵轴是强度。这就是一张质谱图。

核心概念: m/z(质荷比) = 离子质量 ÷ 电荷数。它直接决定了离子在分析器里的飞行轨迹或时间。

我在项目中遇到过一个问题:有人把同位素峰误判为不同的化合物。其实同位素峰是同一个化合物因天然同位素丰度产生的“影子”,m/z 相差大约 1 个原子质量单位。做特征提取时,一定要把它们合并处理,否则模型会学到虚假的“多峰模式”。

1.2 常见数据类型:千万别搞混

数据进了电脑,就不再是模拟信号了。你会遇到几种不同层级的数据类型。我刚开始做质谱分析时,就曾把 profile 数据和 centroided 数据混在一起做 PCA,结果聚类图案完全离谱。

这里我画了一张图,帮你理清从仪器到特征矩阵的完整链条:

质谱数据层级:从模拟信号到特征矩阵 原始信号 模拟电压 Profile 谱图 连续峰型 Centroided 列表 离散峰列表 特征矩阵 样本×峰 说明 • 原始信号 → Profile:ADC 数字化,仍保留峰型轮廓 • Profile → Centroided:峰检测+去噪,只保留 (m/z, 强度) 对 • Centroided → 特征矩阵:跨样本对齐 + 缺失值填充 • 机器学习特征直接从特征矩阵或 centroided 列表提取

下面我逐个展开讲讲:

数据类型特点用于机器学习?
原始信号模拟电压,未经任何处理❌ 几乎不直接用
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 文件导致的。

格式对比速览

格式类型压缩支持工具链成熟度
mzMLXML + 二进制✅ zlib★★★★★
mzXMLXML + 二进制✅ 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 是首选格式、原始数据一定要备份。后面第二章,我们正式进入特征提取的细节。