谱图峰拾取与积分
在从 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):
pip install 'potato-annotation[spectra]'配置
下面的配置用于 1H NMR 峰拾取。显示类型给出自动检测到的建议峰,标注者逐一接受或拒绝建议,为每个峰填写归属和多重性,并对每个多重峰积分:
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 的谱图文件路径:
{"id": "ethyl_acetate", "note": "1H NMR, 400 MHz, CDCl3.", "spectrum": "spectra/ethyl_acetate_1h.jdx"}审核自动峰拾取程序的结果
审核峰拾取程序的输出时,把程序给出的峰放进每个条目,标注者只能逐一接受或拒绝建议峰:
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}{"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 个峰:
instance_display:
fields:
- key: spectrum
type: spectrum
display_options:
prepick: {method: file, max_peaks: 20}mzML 条目指定扫描,JCAMP-DX 复合文件指定数据块:
{"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 谱图如下所示:
{"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_type | auto | nmr、ir、raman、uv、ms 或 other。auto 读取 JCAMP-DX 的 DATA TYPE,或根据 x 单位推断谱图类型 |
x_reversed | auto | true 让 x 轴由高到低绘制。auto 会反转 NMR(ppm 或 Hz)和 IR(cm⁻¹)坐标轴 |
x_label、y_label | 由单位决定 | 坐标轴标题,例如“Chemical shift (ppm)”或“Wavenumber (cm⁻¹)” |
mode | auto | line 或 sticks。auto 把峰表或质心化的 mzML 扫描绘制为棒状图 |
peak_direction | auto | max 或 min。auto 对透射率和反射率使用 min |
integrate | auto | 提供 Integrate 模式。auto 只对 NMR 线状谱开启 Integrate 模式 |
prepick | 关闭 | 建议峰。true,或包含下一张表中各键的映射 |
snap | true | 点击时拾取指针 9 像素范围内的局部极大值(或极小值),而不是指针的确切位置 |
max_points | 4000 | 发送到浏览器的点数,取值 200 到 100,000 |
max_file_mb | 50 | 更大的谱图文件会被拒绝。最大为 500 |
scan、index | 无 | 为每个条目显示的 mzML 谱图。条目自身的 scan 或 index 优先。两者给出其一 |
block | 无 | 为每个条目显示的 JCAMP-DX 数据块 |
height | 380 | 图的高度,以像素或 CSS 长度表示 |
engine | plotly | 唯一的引擎 |
allow_remote | false | true 会被拒绝,因为谱图在服务器上读取 |
prepick 键 | 默认值 | 含义 |
|---|---|---|
method | auto | detect 运行峰检测,file 使用 JCAMP-DX 峰表,item 使用条目的 peaks 列表。auto 依次尝试 item、file、detect,使用第一个可用的 |
prominence | 0.05 | 用于 detect,表示峰必须比周围高出多少,以 y 范围的比例表示,大于 0 且小于 1。对于棒状谱,表示相对于最高谱线棒的最小高度比例 |
max_peaks | 40 | 建议数量上限,取值 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_review | false | 方案为必填时,每个建议都被接受或拒绝之前,条目保持未完成 |
allow_manual | true | false 让峰只能来自建议,并移除“Add a peak at” |
instructions | 无 | 显示在问题下方的文本 |
label_requirement | 无 | {required: true} 使方案成为必填 |
| 字段键 | 默认值 | 含义 |
|---|---|---|
name | 必填 | 由字母、数字、_ 或 - 组成的简短标识符。列表中的单独字符串会被读作名称 |
type | text,给出 options 时为 choice | text 或 choice |
options | 无 | choice 字段的选项。yes、no、on 和 off 要加引号,否则 YAML 会把这四个词读作布尔值 |
required | false | 方案为必填时,每个峰(或区域)都必须填写这个字段 |
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 对象:
{"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 导出为每个峰、每个区域和每个被拒绝的建议各写一行:
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 | 标注者、条目和方案 |
kind | peak、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 行的数量给出每名标注者的接受率。
示例
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,以了解格式和选项。
延伸阅读
- 化学结构 - 分子上的结构编辑与原子选择
- 实例显示 - 如何配置显示字段
- 导出格式 - Potato 的导出格式和导出命令
- 你需要多少位标注者? - 为每个条目选择标注者人数
- 生命科学 - 用 Potato 做生命科学与生物信息审编
有关实现细节,请参阅源文档。