Skip to content

谱图峰拾取与积分

在从 JCAMP-DX 或 mzML 文件读取的 NMR、IR 和质谱谱图上拾取峰并积分,可接受或拒绝建议峰,每个峰导出为一行 CSV。

v2.13.0 新增

spectrum 显示类型在浏览器中为每个条目绘制一张谱图,peak_picking 方案记录标注者拾取的峰、积分的区域,以及标注者接受或拒绝的建议峰。化学家和波谱学研究者把这两者配合起来构建带标签的谱图数据集,例如核磁共振(NMR)波谱的峰列表、红外光谱(IR)中的谱带归属,以及质谱(MS)中的峰标注。教师收集每名学生对同一张谱图给出的峰列表,在导出后为这些列表评分。运行自动峰拾取程序的研究组把程序给出的峰作为建议载入,标注者逐一接受或拒绝每个建议峰。

峰拾取列出谱图中每个峰的位置和强度。在 1H NMR 谱图中,一个多重峰的积分与产生该多重峰的质子数成正比,标注者在多重峰上拖动即可对它积分。

JCAMP-DX 是由 IUPAC 维护的波谱数据文本格式。mzML 是 HUPO 蛋白质组学标准倡议(Proteomics Standards Initiative)制定的质谱数据 XML 格式,一个 mzML 文件存放一次运行的全部谱图。

  • 引擎: Plotly 2.27.0(MIT),随 Potato 一起提交,只在含有谱图的页面上载入。Plotly 绘制 SVG,这个显示类型不需要 WebGL。
  • 格式: JCAMP-DX(.jdx、.dx、.jcamp)、mzML(.mzML)以及两列文本(.csv、.tsv、.txt、.xy)。谱图也可以写成两个列表,直接放在条目里。
  • 长谱图: 文件在服务器上解析,浏览器最多收到 max_points 个点(默认 4,000)。原始谱图的每个分桶保留其最低点和最高点,因此窄的 NMR 谱线仍保持完整高度。放大时会获取可见范围内的原始点,只有当该范围内的点数超过 max_points 时,服务器才会再次降采样。
  • 坐标轴约定: 化学位移轴和波数轴从左到右由高到低。以 Hz 存储的 NMR 谱图会用文件中的 .OBSERVE FREQUENCY 换算为 ppm。
  • 离线: 谱图文件由 Potato 服务器从媒体目录中读取。以 URL 指定的谱图会被拒绝,Potato 不下载任何谱图。

JCAMP-DX 和 CSV 文件不需要额外安装,峰检测使用核心依赖 scipy。只有 mzML 需要 spectra 可选依赖(pyteomics、psims 和 lxml):

bash
pip install 'potato-annotation[spectra]'

配置

下面的配置用于 1H NMR 峰拾取。显示类型给出自动检测到的建议峰,标注者逐一接受或拒绝建议,为每个峰填写归属和多重性,并对每个多重峰积分:

yaml
annotation_task_name: "1H NMR peak picking"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/spectra.json]
item_properties: {id_key: id, text_key: note}
 
instance_display:
  fields:
    - key: spectrum               # item field naming the spectrum file
      type: spectrum
      label: "1H NMR"
      display_options:
        prepick:                  # suggested peaks, off unless set
          method: detect
          prominence: 0.05
          max_peaks: 30
 
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: peak_picking
    name: peaks
    description: "Pick the peaks, assign each one, and integrate each multiplet"
    viewer_field: spectrum
    peak_fields:
      - name: assignment
        placeholder: "e.g. OCH2"
      - name: multiplicity
        options: [s, d, t, q, m, br]
    region_fields:
      - name: protons
        label: "nH"
    min_peaks: 1
    require_review: true          # every suggestion accepted or rejected
    label_requirement: {required: true}

带 options 的字段是选择字段,不带 options 的字段是文本框。对于 NMR 线状谱,显示类型会自动开启 Integrate 模式。min_peaks、require_review 与 label_requirement 同时设置时,标注者至少拾取一个峰并接受或拒绝了每个建议之前,Next 一直不可用。方案的 viewer_field 必须等于某个 spectrum 字段的 key,两者不一致时服务器拒绝启动。

每个条目给出相对于 media_directory 的谱图文件路径:

json
{"id": "ethyl_acetate", "note": "1H NMR, 400 MHz, CDCl3.", "spectrum": "spectra/ethyl_acetate_1h.jdx"}

审核自动峰拾取程序的结果

审核峰拾取程序的输出时,把程序给出的峰放进每个条目,标注者只能逐一接受或拒绝建议峰:

yaml
annotation_task_name: "Peak picker review"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
data_files: [data/review.json]
item_properties: {id_key: id, text_key: note}
 
instance_display:
  fields:
    - key: spectrum
      type: spectrum
      display_options:
        prepick: {method: item}   # the peaks listed in each item
 
user_config:
  allow_all_users: true
 
annotation_schemes:
  - annotation_type: peak_picking
    name: review
    description: "Accept the real peaks and reject the rest"
    viewer_field: spectrum
    peak_fields: []
    region_fields: []
    require_review: true
    allow_manual: false
    label_requirement: {required: true}
json
{"id": "ethanol", "note": "Peaks from the automatic picker.",
 "spectrum": {"file": "spectra/ethanol_1h.jdx", "peaks": [3.69, 2.61, 1.22, {"x": 7.26, "label": "CHCl3"}]}}

建议峰是条目 peaks 列表中的一个数字或一个 {x, y, label} 映射。缺少 y 时从谱图在 x 处读取;标注者接受建议时,label 会保存为该峰的 label 字段。设置 allow_manual: false 后,在建议峰以外的地方点击不会拾取任何峰,“Add a peak at”输入框也会被移除。设置 peak_fields: [] 和 region_fields: [] 后,方案不要求在每个峰旁填写任何文本。

质谱与红外光谱

质心化(centroided)的 mzML 扫描绘制为棒状图,点击会拾取最近的谱线棒。只含峰表的 JCAMP-DX 文件也绘制为棒状图,大多数 JCAMP-DX 质谱都属于这类文件。下面这段部分配置展示质谱的显示选项,把文件自带的峰表作为建议,只给出最高的 20 个峰:

yaml
instance_display:
  fields:
    - key: spectrum
      type: spectrum
      display_options:
        prepick: {method: file, max_peaks: 20}

mzML 条目指定扫描,JCAMP-DX 复合文件指定数据块:

json
{"id": "r7", "spectrum": {"file": "runs/sample1.mzML", "scan": 1043}}
{"id": "c2", "spectrum": {"file": "spectra/compound.jdx", "block": 1}}

对于以透射率或反射率表示的红外光谱,显示类型拾取极小值。噪声较大的红外光谱需要更高的 prominence,例如 0.1。

谱图数据

条目字段存放媒体目录中的文件名,或一个包含下表中各键的映射:

键含义
file 或 path谱图文件,相对于 media_directory
scan、index显示 mzML 运行中的哪一张谱图。index 是在运行中从 0 开始的位置,scan 是原生 ID(native ID)中 scan=N 的部分。两者都不给时显示第一张谱图
block显示 JCAMP-DX 复合文件中的哪个数据块,从 0 开始,只计含有数据的块
peaks供 prepick 方法 item 使用的建议峰,写成数字或 {x, y, label} 映射
x、y以两个等长列表表示的内联谱图
x_units、y_units、kind、title、sticks内联谱图的单位、谱图类型、标题和棒状绘制

其他任何键都会被拒绝,提示信息会列出可用的键。内联 NMR 谱图如下所示:

json
{"id": "s1", "spectrum": {"x": [0.5, 1.0, 1.5], "y": [3, 40, 2], "x_units": "PPM", "kind": "nmr"}}

Potato 自行读取 JCAMP-DX,支持所有 ASDF 压缩形式的 ##XYDATA(并校验 DIF Y 值检查)、##XYPOINTS、##PEAK TABLE、Bruker 和 MestReNova 写出的 ##NTUPLES 形式,以及带 ##BLOCKS 的复合文件。与 XYDATA 并存的峰表就是文件自带的峰列表,prepick 方法 file 会把这个列表作为建议给出。没有横坐标的 FID、二维 NMR 和 JCAMP-CS 结构不会被读取。

CSV 文件有两列,先 x 后 y,以逗号、制表符、分号或空格分隔。以 # 开头的行是注释,可选的表头行用括号中的文字设置单位,例如 shift (ppm)、wavenumber (cm-1) 或 transmittance (%T)。声明了 DOCTYPE 或实体的 mzML 文件在解析前就会被拒绝,因为 mzML 两者都不使用。

选项

spectrum 显示选项默认值含义
spectrum_typeautonmr、ir、raman、uv、ms 或 other。auto 读取 JCAMP-DX 的 DATA TYPE,或根据 x 单位推断谱图类型
x_reversedautotrue 让 x 轴由高到低绘制。auto 会反转 NMR(ppm 或 Hz)和 IR(cm⁻¹)坐标轴
x_label、y_label由单位决定坐标轴标题,例如“Chemical shift (ppm)”或“Wavenumber (cm⁻¹)”
modeautoline 或 sticks。auto 把峰表或质心化的 mzML 扫描绘制为棒状图
peak_directionautomax 或 min。auto 对透射率和反射率使用 min
integrateauto提供 Integrate 模式。auto 只对 NMR 线状谱开启 Integrate 模式
prepick关闭建议峰。true,或包含下一张表中各键的映射
snaptrue点击时拾取指针 9 像素范围内的局部极大值(或极小值),而不是指针的确切位置
max_points4000发送到浏览器的点数,取值 200 到 100,000
max_file_mb50更大的谱图文件会被拒绝。最大为 500
scan、index无为每个条目显示的 mzML 谱图。条目自身的 scan 或 index 优先。两者给出其一
block无为每个条目显示的 JCAMP-DX 数据块
height380图的高度,以像素或 CSS 长度表示
engineplotly唯一的引擎
allow_remotefalsetrue 会被拒绝,因为谱图在服务器上读取
prepick 键默认值含义
methodautodetect 运行峰检测,file 使用 JCAMP-DX 峰表,item 使用条目的 peaks 列表。auto 依次尝试 item、file、detect,使用第一个可用的
prominence0.05用于 detect,表示峰必须比周围高出多少,以 y 范围的比例表示,大于 0 且小于 1。对于棒状谱,表示相对于最高谱线棒的最小高度比例
max_peaks40建议数量上限,取值 1 到 500,保留最显著的峰
min_distance无用于 detect,建议峰之间的最小间距,以 x 单位表示

检测在全分辨率谱图上以显著性(prominence)阈值运行 scipy.signal.find_peaks。建议峰默认关闭,因为建议峰可能对标注者产生锚定效应。若研究要测量标注者接受峰拾取程序结果的频率,就需要开启建议。

peak_picking 选项默认值含义
viewer_field必填instance_display 中 spectrum 字段的 key
peak_fields一个文本字段 label每个峰旁边的输入项,写成 {name, type, options, required, placeholder, label} 的列表。[] 表示不设输入项
region_fields一个文本字段 label同上,用于每个积分区域
min_peaks无方案为必填时,拾取的峰达到这个数量之前,条目保持未完成
max_peaks无超过这个数量的峰会被拒绝,并给出提示
require_reviewfalse方案为必填时,每个建议都被接受或拒绝之前,条目保持未完成
allow_manualtruefalse 让峰只能来自建议,并移除“Add a peak at”
instructions无显示在问题下方的文本
label_requirement无{required: true} 使方案成为必填
字段键默认值含义
name必填由字母、数字、_ 或 - 组成的简短标识符。列表中的单独字符串会被读作名称
typetext,给出 options 时为 choicetext 或 choice
options无choice 字段的选项。yes、no、on 和 off 要加引号,否则 YAML 会把这四个词读作布尔值
requiredfalse方案为必填时,每个峰(或区域)都必须填写这个字段
placeholder无空文本框中的提示文字
label首字母大写的名称图下方表格中的列标题

拾取峰与积分

图上方的工具栏有三种模式:

模式点击拖动
Pick(默认)拾取指针下的峰。点击已拾取的峰会移除它,点击建议峰会接受这个建议缩放到拖出的框
Pan无操作沿谱图移动
Integrate(integrate 开启时)移除指针下的区域对拖出的 x 范围积分

在图上双击或点击 Reset view 按钮会缩放回原状。在触摸屏上,轻触的作用等同于点击。

开启 snap 时,在谱线附近点击会拾取该谱线的顶点;标注者需要先放大,把多重峰的各条谱线分开,再逐条拾取。建议峰画成空心橙色三角形,已拾取的峰画成实心三角形,并标注该峰的字段值。每个区域都带阴影,并标注其相对于第一个区域的积分,第一个区域显示为 1.00。

服务器把每个积分计算为区域两端之间所有原始点上的梯形积分,不做基线校正。因此,画得比多重峰更宽的区域读数会偏高;当相对积分需要固定的参照时,说明中应写明先对哪个信号积分。对于设置了 integrate: true 的棒状谱,积分是区域内谱线棒强度之和。

方案在图下方列出建议峰,每个都带 Accept 和 Reject 按钮,另有 Accept all 和 Reject all。被拒绝的建议显示为一个小按钮,按下即可恢复该建议。Peaks 表每个峰一行,包含峰的位置、强度、来源和字段,以及一个 Remove 按钮。Integrals 表每个区域一行,包含区域的范围、积分、相对积分和字段。在“Add a peak at”输入框中,标注者键入一个位置后按 Enter;如果这个位置与某个建议峰一致,就会接受该建议。每个控件都是按钮或表单字段,标注者只用键盘就能审阅一张谱图。

存储的答案

peak_picking 方案为每个条目存储一个 JSON 对象:

json
{"viewer": "spectrum",
 "items": [{"kind": "peak", "x": 2.0399, "y": 3003, "source": "auto", "width": 0.0031,
            "fields": {"assignment": "CH3CO", "multiplicity": "s"}},
           {"kind": "peak", "x": 7.26, "y": 132, "source": "manual"},
           {"kind": "region", "from": 1.969, "to": 2.120, "integral": 15.77,
            "fields": {"protons": "3"}}],
 "rejected": [4.1464]}

source 对接受的建议为 auto,对标注者添加的峰为 manual。width 是检测到的峰在半显著性处的全宽,以 x 单位表示。rejected 列出标注者拒绝的建议的位置。既未接受也未拒绝的建议不会被存储;在必填方案中,require_review 确保每个建议最终都进入这两个列表之一。

导出

peak_table 导出为每个峰、每个区域和每个被拒绝的建议各写一行:

bash
python -m potato.export --config config.yaml --format peak_table --output out/

导出写出 peaks.csv 和 peaks.jsonl,--option format=csv 或 --option format=jsonl 只写其中一个。JSONL 行省略空列。

列内容
user_id、instance_id、schema标注者、条目和方案
kindpeak、region 或 rejected
x、y、width峰的位置、强度和宽度(仅限检测到的峰)。rejected 行只有 x
from、to、integral、relative区域范围、积分,以及相对于同一答案中第一个区域的积分
source峰为 auto 或 manual。区域为 manual,被拒绝的建议为 auto
label该行的字段值,以“ / ”连接
每个字段一列例如 assignment 和 multiplicity;字段名与上面某列同名时加前缀 field_

多名标注者可以在同一张谱图上拾取峰,人数由 num_annotators_per_item 设置。通用的 CSV 和 JSONL 导出也包含答案,每个方案占一个 JSON 单元格。Potato 的一致性指标不覆盖 peak_picking 方案。请在导出后用 peaks.csv 比较标注者的峰列表,例如在 ppm 或 cm⁻¹ 的容差范围内匹配同一条目的峰。对于峰拾取程序的审核,source: auto 峰行和 rejected 行的数量给出每名标注者的接受率。

示例

bash
python potato/flask_server.py start examples/advanced/spectrum-peak-picking/config.yaml -p 8000

这个示例包含两张合成的 1H NMR 谱图,分别是乙酸乙酯和乙醇的谱图,以 Hz 为单位、采用 DIFDUP 压缩的 JCAMP-DX 格式;另有一张以 CSV 存储的合成红外透射谱。示例开启了检测建议和 require_review。同一目录下的 generate_spectra.py 会重新生成这三个文件,数据是虚构的。

用编程智能体搭建研究

potato-skill 1.2.0 面向 Potato 2.13.1,认得 peak_picking 和 spectrum 这两个名称,并指导研究设计这一侧,例如向标注者问什么、每张谱图由几名标注者拾取峰、培训和注意力检查,以及导出。这个技能目前还没有记录谱图文件格式、显示选项和 spectra 可选依赖。安装 potato-skill,把研究描述给你的智能体,再让智能体参考本页的 Markdown 副本 https://www.potatoannotator.com/docs/annotation-types/spectra.md,以了解格式和选项。

延伸阅读

有关实现细节,请参阅源文档。