01 质谱数据简介
做质谱数据分析这几年,我最常被新手问到的一个问题就是:“这些文件到底是什么东西?”拿到一台质谱仪,随便跑一针样品,出来的文件后缀可能是 .raw、.mzML、.mzXML……看着一堆乱码一样的二进制文件,确实挺懵的。
别急,这一节我就带你把质谱数据的底子摸清楚。搞懂了它,后面清洗、建模、验证的路才会走得顺。
质谱仪到底干了件什么事?
说白了,质谱的核心就四个字:“称重”。但不是称你手里的东西,而是称离子在真空中的飞行能力。
我习惯把它分成三步来理解:
- 离子化:样品进来,被电离成带电粒子。
- 质量分离:根据质荷比(m/z)把这些离子分开。
- 检测:记录每个 m/z 对应的离子强度。
你最终拿到的,就是一大堆 (m/z, 强度) 的数据对。不同时间点扫一次,就得到一张质谱图。连续扫下去,就有了时间轴上的谱图序列——我们叫它 LC-MS 数据。
一个小比喻:你可以把质谱仪想象成一个超市收银台。每个商品(离子)都有条形码(m/z),收银员(检测器)扫一下,告诉你这个商品卖了多少件(强度)。
嗯,这里有个坑要注意:质谱数据不是单一的数值数组,而是 三维的——保留时间、质荷比、强度。你清洗数据时,这三样东西一个都不能丢。
常见的数据格式,哪个最靠谱?
说到格式,我之前踩过不少坑。2018 年我第一次处理某品牌高分辨质谱的 .raw 文件,差点崩溃——只能用厂商自己的软件打开,换一台电脑就报错。所以我现在做项目,第一件事就是问:“能转成 mzML 吗?”
来,我把三种主流格式的特点整理了一下:
| 格式 | 全称 | 类型 | 典型特点 |
|---|---|---|---|
| .raw | Thermo 专有格式 | 二进制,厂商封闭 | 只能用 Xcalibur、FreeStyle 打开;数据量大,通常 1 GB+ |
| .mzXML | Mass Spectrometry XML | XML,开放 | 早起标准,结构清晰;但文件膨胀,解析慢 |
| .mzML | Mass Spectrometry Markup Language | XML,开放(HUPO-PSI 标准) | 当前最推荐的格式;同时支持索引化和压缩编码 |
我个人最推荐的是 mzML。为什么呢?
- 标准化:HUPO-PSI 组织推动的,厂商和工具都认。
- 可索引:支持随机访问,不用读完整个文件就能找到特定扫描。
- 压缩友好:可以用 zlib 对 m/z 和强度数组进行压缩,文件大小能降到 .raw 的 30%~50%。
我的经验之谈:项目开始时,如果条件允许,直接让仪器导出 mzML。能省掉后面很麻烦的格式转换环节。
mzML 文件内部长什么样?
有一次我调试一个识别算法的 bug,不得不手撕 mzML 文件。打开一看,结构其实挺清晰的:
<?xml version="1.0" encoding="UTF-8"?>
<mzML xmlns="http://psi.hupo.org/ms/mzml">
<cvList>...</cvList>
<fileDescription>...</fileDescription>
<referenceableParamGroupList>...</referenceableParamGroupList>
<sampleList>...</sampleList>
<softwareList>...</softwareList>
<instrumentConfigurationList>...</instrumentConfigurationList>
<dataProcessingList>...</dataProcessingList>
<run>
<spectrumList count="1250">
<spectrum index="0" id="scan=1">
<cvParam name="ms level" value="1"/>
<cvParam name="base peak intensity" value="4590821.0"/>
<binaryDataArrayList count="2">
<binaryDataArray encodedLength="5632">
<cvParam name="m/z array" unitName="m/z"/>
<binary>BASE64_ENCODED_DATA</binary>
</binaryDataArray>
</binaryDataArrayList>
</spectrum>
</spectrumList>
</run>
</mzML>
核心就是 <spectrum> 标签。每个 spectrum 代表一次扫描,里面记录着 m/z 和强度数据。这些数据是 Base64 编码的二进制,直接读不了,得用专门的库去解码。
注意:千万不要自己去写 Base64 解码 + 解压缩的脚本。pymzML、mzjava、OpenMS 这些库已经帮你封装好了。我曾经图省事手写过一次,结果精度对不上,排查了三天才发现是字节序搞反了。
知识体系总览
这一节的内容,我画了张图帮你把关系理清楚:
该不该自己做格式转换?
你可能会问:“我拿到的是 .raw,是不是得自己写个转换器?”我劝你千万别这么干。
市面上的开源工具已经很成熟了:
- msconvert(ProteoWizard 工具包):最常用的格式转换神器。支持从 .raw 转出 mzML、mzXML,还能做峰值挑选。
- OpenMS / TOPP tools:整个质谱数据处理流程的工具箱,FormatConverter 就是它的转换模块。
- pymzML:Python 接口,适合写脚本批量处理。我经常用它做小规模的文件格式校验。
举个例子,一条命令行搞定:
msconvert input.raw --mzML --zlib --outdir ./converted/
这条命令会把 input.raw 转成 mzML 格式,同时开启 zlib 压缩。在我的经验里,压缩后文件体积能缩小一半以上,而且解析速度一点也没变慢。
一个小技巧:在处理大规模队列样本时,我习惯先随机抽 3~5 个文件做格式转换和解析测试,确认参数一致后,再启动全量转换。能避免因为批次间参数设置不同导致后面没法对齐的坑。
数据清洗前,你至少要确认这三件事
好,工具选好了,格式也转了。但别急着开始清洗。我踩过的坑告诉你,先做三件事:
- 检查文件完整性:mzML 有没有被截断?可以用
xmllint --valid做一遍语法校验。 - 确认扫描数:跑了 60 分钟的梯度,预期的扫描数是 6000 吗?少了说明仪器可能中途掉线了。
- 看总离子流图(TIC)是否正常:TIC 曲线如果断层了,那大概率是液相那边出了问题,数据清洗再多也没用。
说到 TIC,我记得有次帮一个合作方排查数据质量,他们做了 300 个样本的蛋白质组学分析,结果鉴定到的蛋白数少得可怜。后来让我看 .raw 文件转 mzML 的过程,发现转换时默认只保留了 MS1 扫描,把 MS2 全扔了。嗯,就是这么个小参数,导致整个项目白做了一半。
所以我现在拿到任何质谱数据,第一件事就是先用 pyOpenMS 写个小脚本,快速拉一下基本信息:
import pyopenms as oms
exp = oms.MSExperiment()
oms.MzMLFile().load("sample.mzML", exp)
print("光谱数量:", exp.getSpectra().size())
print("色谱数量:", exp.getChromatograms().size())
print("MS 级别范围:", exp.getMinMSLevel(), "-", exp.getMaxMSLevel())
tic = exp.calculateTIC()
print("总离子流(TIC)曲线长度:", len(tic))
这段代码跑完,我心里就有底了。数据没问题,才敢往下做清洗和模型训练。