P 波藏在哪里 — BUT PDB 心电 P 波峰标注库

50 段两分钟 / 双导联 360 Hz / 23 种病理

来源 https://physionet.org/content/but-pdb/1.0.0/发布时间: 2026-10-28最后更新: 2026-10-28 阅读 18
P 波藏在哪里 — BUT PDB 心电 P 波峰标注库

信息速览

数据集名称P 波藏在哪里 — BUT PDB 心电 P 波峰标注库
数据类型时序信号,波形,人工标注,元数据
规模不适用(官方未公布受试者人数;50 段记录来自 45 条源记录)
接入方式https://physionet.org/content/but-pdb/1.0.0/

心电图判读里,P 波是最不起眼的那一段。它幅度低、时长短、贴着基线,常常只有几十微伏,落在噪声与基线漂移之间。QRS 波群可以靠一个阈值加一个不应期就抓得八九不离十,P 波不行:它的形状随导联变化,随心率变化,随房室传导比例变化,在房颤时干脆不存在。算法在健康人身上做到九成以上的检出率并不难,难的是在病理信号上不掉链子。

BUT PDB 就是为这个落差做的。布尔诺理工大学生物医学工程系的心电团队,从三个公开心电库里挑出病理表现典型的片段,每条截两分钟,请两名心电专家逐搏手工点出 P 波的峰点,再把原库的 QRS 位置与类型一并转录过来。五十段记录覆盖二十三种病理,从房室阻滞到预激,从房扑到室性三联律,都是 P 波检测最容易失效的场景。

它的体量很小,全部文件压缩后不到五兆,一条命令就能下完;代价是它不能当训练集用,也不能代表任何自然人群。它是一件标定好的量具,用来回答"你的 P 波检测器在病理信号上还剩多少"。一屏速览见下表。

维度 内容
全名 Brno University of Technology ECG Signal Database with Annotations of P Wave
简称 BUT PDB
发布方 PhysioNet(数据由布尔诺理工大学 DBME FEEC BUT 构建)
首次发布 2021 年 1 月 19 日,版本 1.0.0
DOI 10.13026/hwvj-5b53(1.0.0 版)
记录数 50 段,文件名 01 至 50
每段时长 2 分钟(43201 个采样点)
导联数 2(头文件未给导联名)
采样率 360 Hz(其中 12 段由 128 Hz 重采样)
信号精度 16 位整数,增益逐通道写在头文件
P 波标注 5437 处峰点,两名专家人工标注
QRS 标注 7638 处,位置与类型转录自源库
无 P 波的 QRS 2201 个(官方口径)
病理类型 23 种,每段标注主导病理及合并病理
源数据库 MIT-BIH 心律失常库、MIT-BIH 室上性心律失常库、长期房颤库
文件格式 WFDB:.hea / .dat / .pwave / .qrs
附属文件 README.txt、ANNOTATORS、RECORDS、LICENSE.txt、SHA256SUMS.txt
总体积 未压缩约 7.0 MB,ZIP 约 4.9 MB
许可 CC BY 4.0,无需注册、无需申请
典型用途 P 波检测算法的横向比较、病理信号鲁棒性评测、教学示例

§1 它要解决的具体麻烦

1.1 P 波检测的困难不在算法,在评测集

P 波检测是一个被反复解决过的问题。生理条件下的方法在二十年前就成熟了:带通滤波压掉基线漂移,平方或差分放大斜率,再在 QRS 前固定窗口里找局部极大值,配合不应期与搜索回退,健康人身上的灵敏度可以做到很高。这类方法的论文很多,代码也不难写。

真正卡住进展的是另一半:病理信号上的表现没人说得清。同样一套参数,遇到二度房室阻滞会漏掉未下传的 P 波,遇到房扑会把扑动波当成一串 P 波,遇到起搏心律会把起搏钉当成 P 波,遇到房颤则根本没有 P 波可找。没有一组公认的、带病理的、人工标注过的信号,各家报出的数字就无法相互比较——差异究竟来自方法,还是来自测试集里健康人占了多大比例,谁也说不清。

到 2020 年前后,公开可用的、带人工 P 波标注的库只有三个:QT 数据库只收生理信号,MIT-BIH 心律失常库的 P 波标注只覆盖少数几类病理,罗巴切夫斯基大学心电库则以逐导联波形分界为主。三者都不以"病理 P 波检测"为目标设计。BUT PDB 补的正是这个空位,它的全部设计取舍都指向同一件事:把病理信号堆厚,让 P 波检测器的失效暴露出来。

1.2 三条常见的错误期待

第一条,期待它是"更大的训练集"。五十段、两个人标注、总体积不到五兆,任何一个小型卷积网络几轮就能记住全部波形。拿它做训练,验证集上的数字会很好看,换一个库就会崩。它的定位从头到尾是评测,不是训练。

第二条,期待它是"P 波分割数据"。官方标注的是峰点,一个采样位置,没有起点与终点。想训练 U-Net 输出 P 波掩码,必须自己定窗口宽度,这个宽度是你定的,不是专家定的——写进论文时要写清这是经验窗口而非标注。

第三条,期待它能代表"真实人群的 P 波形态"。片段是专家在源库里主动挑的"有意思的信号",病理密度被刻意抬高。用它统计"正常 P 波占多少比例"“房颤占多少比例”,得到的数字没有流行病学意义(§7.3)。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
P 波峰点检测的横向评测 五十段带人工峰点标注的病理信号 够,这是它的主业
病理条件下的漏检分析 二十三种病理,每段标注主导病理 够,可按病理分组统计
有 P 波 / 无 P 波的判别 2201 个无 P 波的 QRS 位置(官方口径) 够做二分类评测,标签需自行从注释推导
P 波起止边界训练 只有峰点 不够,无边界标注
P 波振幅与极性分析 有波形,无振幅标注 可自行测量,但测量结果不是标注
房颤节律检测 九段标注含房颤 可做,样本极少,只适合当困难样本
长时程节律负荷 每段两分钟 不够,无法算日级负荷
受试者级统计 官方未给受试者编号与人口学 不够,只能经源库间接回溯
跨中心泛化验证 全部信号来自美国三个库,标注在捷克完成 部分够,属"跨标注者"而非"跨采集中心"
可穿戴单导联算法评测 双通道,可任取一路 够,且是它价值最高的用法之一

1.4 你的处境与它能帮到的事

你的处境 它能帮你什么 下一步该做什么
你写了一版 P 波检测器,在健康人数据上灵敏度九成五,想知道病理信号上会掉多少 提供按病理分组的人工峰点标注,可直接算检出一致率 先按病理分组算召回,找出掉得最狠的三类,再回看对应波形
你在做可穿戴单导联产品,担心电极位置换了就失效 双通道同段信号,可一路训练一路测试 固定记录划分,做"通道 A 训练、通道 B 测试"的错配实验
你要交出一份公认的对比基线 它是被多篇 P 波检测论文用过的公开库,可复现 报告时写清版本号与匹配容差,容差不同结果不可比(§5.4)
你在教心电图,需要一个"P 波明明在却看不见"的例子 二度阻滞段里有未下传的 P 波,标注齐全 用 §4.2 的案例做课堂示例,注意波形幅度需按头文件换算
你要做房颤与窦性心律的判别 九段房颤、四十一段非房颤 样本太少,只宜做困难样本补充,不宜作主训练集
你在核对某篇论文的复现性 体量小、下载快、校验清单齐全 全量一条命令下完,先核 SHA256 再跑(§5.2)

1.5 一句话判断

它不是用来喂模型的粮仓,而是一把刻好病理刻度的尺子:尺子只有两分钟长,但每一格都有人工标定过。

1.6 不适合谁:四条边界

不适合拿来做规模训练。 五十段、总计一百分钟,深度模型会在几分钟内过拟合。想靠数据量换性能的项目,用它只会得到虚高的内部指标。

不适合做波形分界的基准。 峰点标注与边界标注是两种任务。用它评 segmentation 类方法,等于用尺子量重量。

不适合当作独立测试集去证明泛化。 其中三十三段直接切自 MIT-BIH 心律失常库,如果训练集里也有该库,两侧在源记录层面就重叠了(§7.3)。

不适合做人群层面的推断。 没有年龄、性别、受试者编号,片段又是刻意挑的病理富集样本,任何"患病率""P 波间期分布"的结论都不成立。

§2 数据构成与规格

2.1 一段记录由四个文件组成

数据库沿用 WFDB(Waveform Database)格式,这是 PhysioNet 从 1980 年代沿用到今天的生理信号存储规范。每一段记录对应四个同名文件:.hea 是纯文本头文件,声明采样率、通道数、每通道的增益与基线;.dat 是二进制信号本体,两个通道的采样点交错存放;.pwave 与 .qrs 是两个二进制注释文件,前者存 P 波峰点,后者存 QRS 位置与心搏类型。

四者相互依赖。只拿 .dat 无法还原电压,因为没有增益与基线;只拿 .pwave 不知道这些时刻对应什么波形。这也是为什么"转成 CSV 就完事"是有损操作:头文件里的逐通道增益、基线、单位会在转表时被压平成一个数字,注释里的类型码会被压成字符串,后续再想回到原始语义就得靠映射表。

根目录下还有五个附属文件,作用各不相同。

文件 内容 是否影响读数
README.txt 五十行对照表:记录名、病理代码、源库、源记录号、源库起止采样点、源采样率;后附病理缩写表 不影响读数,决定你能不能正确分组
ANNOTATORS 两行,说明 .pwave 是参考 P 波标注、.qrs 是转录自源库的 QRS 标注 不影响读数,决定你如何引用标注来源
RECORDS 五十行记录名,写作 1 至 50,无前导零 影响读数,见 §5.1
LICENSE.txt CC BY 4.0 全文 不影响读数
SHA256SUMS.txt 每个文件的校验和 不影响读数,建议下载后先校验

2.2 字段与记录的完整字典

本库的"字段"不是表格列,而是头文件的关键字行、注释文件的类型码、以及 README 的对照列。下表把它们逐项拆开。

信息项 所在位置 取值形态 含义 使用注意
记录名 .hea 第 1 行首字段 / 文件名 01–50 记录标识 文件名两位补零,RECORDS 不补零
通道数 .hea 第 1 行第 2 字段 固定 2 两条同步信号 全库统一
采样率 .hea 第 1 行第 3 字段 360 每秒采样点数 名义值,12 段为插值所得
采样点数 .hea 第 1 行第 4 字段 43201 单通道长度 除以 360 得 120.0 秒
文件名 .hea 第 2、3 行首字段 NN.dat 信号文件 两行指向同一文件
存储格式 第 2、3 行第 2 字段 16 16 位整数 非 212 打包格式
增益 第 2、3 行第 3 字段 如 19246.4023 ADC 单位/毫伏 逐通道不同,必须逐年读
基线 增益后括号内 如 (-6688) ADC 零点 逐通道不同
单位 增益后斜杠字段 \muV 头文件声明的单位 与增益量级不一致,见 §2.5
信号名 第 2、3 行第 2 列之后 空 本应为导联名 全库为空,无法判断导联身份
P 波位置 .pwave 注释 采样序号,递增 P 波峰点 只有位置,无边界
P 波符号 .pwave 注释 全库为 N 单一类型 不区分窦性、异位
QRS 位置 .qrs 注释 采样序号,递增 心室除极时刻 转录自源库,未重新校正
QRS 类型 .qrs 注释 N/A/V/R/L/J/P/F/a/E 等 MIT-BIH 心搏类型码 与病理代码含义不同,勿混用
源数据库 README.txt 第 3 列 三个库名之一 出处 决定采样率与人群
源记录号 README.txt 第 4 列 如 231 源库内编号 去重后用于防泄漏
源起止点 README.txt 第 5 列 如 [30160,73360] 在源库中的采样区间 可回源库取更长上下文
源采样率 README.txt 第 6 列 360 或 128 源库原始采样率 128 者已被重采样
病理代码 README.txt 第 2 列 如 'BII, R' 主导与合并病理 存在表外代码,见 §7.3

2.3 一条记录的完整解剖

以 01 号记录为例,把四个文件依次打开,可以看到一条完整链路。头文件原文如下(两行信号描述被折叠展示,原文各占一行)。

01 2 360 43201
01.dat 16 19246.4023(-6688)/\muV 0 0 -13328 16121 0
01.dat 16 23658.4838(4377)/\muV 0 0 10883 -7243 0

第一行四个字段依次是记录名、通道数、采样率、采样点数。43201 除以 360 等于 120.0028 秒,即两分钟多一个采样点——多出的那一个点是 WFDB 常见的收尾约定,不必当成异常。第二、三行描述两个通道,格式位为 16,增益分别为约一万九千与两万三千 ADC 单位每毫伏,基线分别为负六千六百余与四千三百余。两个通道的增益相差近两成,这意味着同一个电压在两个通道上会落成不同的整数值,换算必须逐年做。

再读注释。01 号的 .pwave 有 133 条记录,符号全部是 N;.qrs 有 103 条,符号里同时出现 N、A、R、V。P 波比 QRS 多出三十条,这正是这段记录被选中的原因——它是二度房室阻滞,有一部分 P 波没有下传到心室(§4.2 的案例)。

用下面的代码可以把上述链路一次走完。

#-- 读取单条记录的信号与两份注释,并打印关键规格
import wfdb
import numpy as np

rec = wfdb.rdrecord("01", pn_dir="but-pdb/1.0.0")
print("通道数", rec.n_sig, "采样率", rec.fs, "点数", rec.sig_len,
      "时长秒", rec.sig_len / rec.fs)

#-- 逐通道报告增益与基线,两者都不相同,不能只读第一路
for i in range(rec.n_sig):
    print("通道", i, "增益", rec.adc_gain[i], "基线", rec.baseline[i],
          "单位", rec.units[i])

p = wfdb.rdann("01", "pwave", pn_dir="but-pdb/1.0.0")
q = wfdb.rdann("01", "qrs", pn_dir="but-pdb/1.0.0")
print("P 波", len(p.sample), "首个位置", p.sample[0], "符号集", sorted(set(p.symbol)))
print("QRS ", len(q.sample), "首个位置", q.sample[0], "符号集", sorted(set(q.symbol)))

#-- 物理电压范围,用于确认单位换算没有错一个数量级
sig = rec.p_signal
print("电压范围 mV", np.nanmin(sig, axis=0), np.nanmax(sig, axis=0))

这段代码输出的电压范围落在正负一毫伏到两毫伏之间,与体表心电的量级相符。如果换算错了一个数量级,会得到上千或零点零零几的数值,一眼就能看出。

一条记录的信息还可以压成一张卡片。下表是 01 号的完整画像。

项目 01 号记录的值 判读
长度 43201 采样点 / 120.0 秒 标准两分钟
通道 2 双导联,导联身份未知
采样率 360 Hz 原生 360 Hz 库切出
增益 约 19246 与 23658 单位/毫伏 两通道不同
基线 负 6688 与 4377 两通道不同
病理代码 BII, R 二度房室阻滞合并右束支阻滞
源库 MIT-BIH 心律失常库 360 Hz 原生
源记录号 231 同一源记录的另一段是 13 号
源区间 30160 至 73360 可回源库取前后文
P 波条数 133 全部为 N
QRS 条数 103 含 N/A/R/V
P 与 QRS 之差 多出 30 条 提示存在未下传 P 波

2.4 五十段的来源分布

README.txt 的五十行对照表把每条记录映射回源库与源记录号。把这五十行按源库归拢,得到下表。这张表是理解本库边界的第一把钥匙:采样率不是统一的,人群也不是统一的。

记录区间 源数据库 段数 源库原始采样率 本库采样率 说明
01–38 MIT-BIH Arrhythmia Database 38 360 Hz 360 Hz 原生采样,未做重采样
39–43 MIT-BIH Supraventricular Arrhythmia Database 5 128 Hz 360 Hz 已上采样,有效带宽仍受 64 Hz 限制
44–50 Long Term AF Database 7 128 Hz 360 Hz 已上采样,全部为房颤段

按记录去重后,五十段对应四十五条源记录。差额来自五条被切成两段的源记录,逐一列在下表。这张表是防泄漏的直接依据。

源库 源记录号 对应本库记录 病理代码
MIT-BIH 心律失常库 231 01 与 13 BII, R
MIT-BIH 心律失常库 118 05 与 26 A, V / R, A
MIT-BIH 心律失常库 124 06 与 30 NOD, J / P, IVR, T, V, F
MIT-BIH 心律失常库 207 09 与 33 E, A, SVTA / V, VFL
MIT-BIH 心律失常库 205 31 与 32 V, F, VT(两段相同代码)

同一源记录的两段来自同一名受试者的同一次监护,心率水平、电极位置、噪声底噪都高度相关。把 01 放进训练集、13 放进测试集,测试集就不再独立。这条规则对上面五组全部适用,对同库其他记录之间同样适用——MIT-BIH 心律失常库的每位受试者只贡献一条记录,因此源记录号等价于受试者编号,这是本库难得的一个可回溯之处。

2.5 与同类资源的字段差异

同为心电标注库,字段设计的差别直接决定能做什么任务。下表把 BUT PDB 与库内三个可比对象并排。

维度 BUT PDB QT 数据库 LUDB MIT-BIH 心律失常库
记录数 50 段 105 条 200 条 48 段
每段时长 2 分钟 15 分钟 10 秒 30 分钟
导联数 2 2 12 2
采样率 360 Hz 250 Hz 500 Hz 360 Hz
P 波标注 峰点 起止边界 逐导联起止边界 部分记录有峰点
QRS 标注 位置加类型 起止边界 逐导联起止边界 位置加类型
T 波标注 无 有 有 无
病理覆盖 23 种,刻意富集 以生理信号为主 8 大类,自然分布 多种,自然分布
主要用途 P 波检测评测 波形分界评测 波形分界评测 心搏分类

差别最要紧的一行是"P 波标注"。QT 数据库与 LUDB 给的是边界,能算 P 波时限、能训练分割;BUT PDB 给的是峰点,能算检出率、能算 PR 间期起点,但算不了时限。这不是标注粗糙,而是任务不同——检测任务只需要一个位置,边界反而会引入"这个点算不算 P 波起点"的判读分歧。

还有一处差异容易被忽略:单位字段。本库头文件把单位写成 \muV,而增益数值与 16 位满量程反推出来的量级对应毫伏。两者相差三个数量级,官方未说明以哪个为准。稳妥的做法是先算一遍电压范围,确认落在正负几毫伏,再决定单位标签怎么填。

§3 定位:P 波检测这门手艺的坐标系

3.1 检测、分界、分类是三件事

心电图上的自动分析常被笼统称作"波形识别",实际上是三件相互独立的事,输入输出都不同。把它们分开,才能看清 BUT PDB 站在哪个位置。

任务 输入 输出 评价方式 代表资源
检测 一段心电 若干时刻(有没有、在哪) 容差窗内的命中率与误报率 BUT PDB、MIT-BIH P 波标注
分界 一段心电加一个大致位置 起点、峰点、终点 边界位置的平均绝对误差 QT 数据库、LUDB
分类 一个波形或一个心搏 类别标签 混淆矩阵、宏平均 MIT-BIH 心律失常库、PTB-XL

BUT PDB 站在第一列。它的标注是一个采样序号,不是一个区间,也不带类别。这决定了它的评价指标必须是"在正负多少毫秒的窗口内算命中",而窗口宽度是可以调的——这一点在后文会反复出现,因为不同论文报出的数字不可比,多半就出在这里(§5.4)。

3.2 为什么"病理富集"是这类库的核心设计

如果只是要 P 波标注,从健康人身上标几千条更省事,波形干净、判读一致、标注速度快。BUT PDB 反其道而行,专门挑病理信号,原因在于检测算法的失效模式几乎全部集中在病理段。

病理情形 检测器的典型失效 后果
二度房室阻滞 只在 QRS 前搜索,漏掉未下传的 P 波 召回被系统性低估
三度房室阻滞 P 波与 QRS 完全分离,搜索窗失效 大部分 P 波漏检
房扑 扑动波连续出现,被当成一串 P 波 误报激增
房颤 无 P 波,算法仍在基线里找 凭空造出 P 波
起搏心律 起搏钉幅度大、斜率陡,抢占局部极大值 位置偏移到钉上
室性早搏 提前出现的宽 QRS 前无 P 波,代偿间歇后 P 波位置改变 节律假设失效
预激 PR 间期极短,P 波紧贴 QRS 搜索窗给错

这张表就是本库的存在理由。它把上面七种情形都收了进来,且每种不止一段。用健康人数据调参、用病理数据验收,是这个库期望的使用顺序;反过来做,会在健康段上过拟合出一堆只在干净信号里成立的阈值。

3.3 三类使用者的入口

算法研究者该最先看 §2.2 的字段字典与 §5.4 的匹配口径,最容易踩的坑是用一套容差算完、再拿这个数字和用另一套容差的论文比;第一小时最值得做的事,是先把官方给出的 P 波总数复现出来,确认读取链路无误,再动容差。

产品工程师该最先看 §2.1 的文件结构与 §4.2 的案例,最容易忽略的是那十二段来自 128 Hz 库的记录;第一小时应当按源采样率把五十段分成两组,再决定滤波器设计与重采样一致性检查要不要分开做。

教学与临床工程人员该最先看 §4.2 的案例与 §9.1 的路径,最容易犯的错是把 .dat 里的 16 位整数直接当成微伏画图;第一小时先按头文件的增益与基线换算一遍,确认波形幅度落在正负几毫伏,再截图做课件。

3.4 它在时间轴上的位置

P 波标注这件事,公开资源经历了三个阶段。第一阶段是 1997 年的 QT 数据库,为 QT 间期测量服务,标注是边界,信号以生理为主,105 条记录、每条十五分钟、双导联 250 Hz。第二阶段是 2018 年前后出现的 MIT-BIH 心律失常库 P 波标注,把峰点标注扩展到部分病理,但它仍依附在原有的心搏分类库上,病理种类有限。第三阶段是 2020 年前后的两套专门库:罗巴切夫斯基大学心电库走逐导联边界路线,200 条十秒十二导联 500 Hz;BUT PDB 走病理检测路线,50 段两分钟双导联 360 Hz,覆盖二十三种病理。

三个阶段的分野不在精度,而在"服务谁"。第一阶段服务间期测量,第二阶段服务心搏分类的附带产物,第三阶段才第一次把"P 波检测在病理信号上到底行不行"当成独立问题来建库。把 BUT PDB 放在这条线上看,它的贡献不是"更准",而是"更杂":同样的峰点标注,覆盖的病理种类比前一代多出一个量级。这也是它被后续 P 波检测论文当作标准对比集的原因。

3.5 一个 P 波检测器会犯的四类错

评测之前先把错误分类,才不会用一个总分掩盖所有问题。P 波检测的错误可以分成四类,每一类在本库里都能找到对应的难点段落。

错误类型 表现 本库里最容易触发的情形 该用什么指标
漏检 真实 P 波没有报出 未下传的 P 波、三度阻滞、PR 间期过长 按病理分组的召回率
误报 报出了不存在的 P 波 房颤的基线波动、房扑的连续扑动波 单位时间误报数
位置偏移 报了但落点偏出容差 双峰 P 波、P 波紧贴 QRS 的预激 位置误差的中位数与四分位距
重复检出 同一个 P 波报两次 切迹明显的 P 波、宽大的异位 P 波 每段重复率

四类错误不能用同一个数概括。一个检测器可能召回很高,但误报同样高——在房颤段上"到处都报 P 波",召回自然好看,代价是把不存在的波全算成命中。分开报四类,再按病理分层,才是一次完整的评测。这也是本库相对健康人数据的价值:它同时提供了触发这四类错误的段落,不必自己去找。

§4 质量与特色

4.1 标注流程决定了它能被信任到什么程度

官方对流程的描述很短,但每一句都对应一个可检验的性质。

流程环节 官方陈述 对应的数据性质 使用者该如何用
选段 由心电专家在源库中寻找"有意思"的病理信号 病理密度被人为抬高 不可用于患病率估计
截取 每条截两分钟 长度统一,便于批处理 可按段做等长切窗
标注工具 使用 SignalPlant 手工标注 无自动算法参与 标注错误不是算法误差的传播
标注人数 两名心电专家 至少经过一次交叉检查 但仍无第三人仲裁记录
分工 一人标注、一人复核 复核者看的是已有点位 漏标的 P 波复核者未必补上
分歧处理 不清楚的片段两人共同回看直至一致 有共识机制 一致性数值官方未公布
一致性指标 官方未公布 kappa 或命中率 无法量化标注噪声 只能通过复现实验间接估计

最后一行是本库最实在的一处空白。两名专家、共同回看、达成共识,这套流程在方法学上是可靠的;但没有公布一致性统计,意味着"人工标注即真值"这个假设无法被量化检验。使用者能做的,是把标注当成"某两位专家的共识",而不是"客观真值"。

4.2 案例:01 号记录里多出来的三十个 P 波

01 号记录是本库最值得逐搏看的一段。它的病理代码是 BII, R——二度房室阻滞合并右束支阻滞,来源是 MIT-BIH 心律失常库的第 231 号记录,截取区间是源库的第 30160 到 73360 个采样点。这段记录在本库里对应两个文件组:01 与 13,同一名受试者的两个不同时段。

把 01 号的注释读出来,会看到一组不对称的数字:P 波 133 条,QRS 103 条。多出的三十条 P 波没有对应的心室除极,它们是二度阻滞里未下传的 P 波。这些 P 波在体表心电上清晰可辨——一个小而圆的隆起,后面跟着一段平直的基线,再后面才是下一个 P 波。

这就是本库的核心价值所在。绝大多数 P 波检测算法隐含一个假设:每个 QRS 之前必有一个 P 波。基于这个假设,搜索窗被固定在 QRS 前一个区间内,窗外的信号直接丢弃。在 01 号这样的记录上,这个假设会让三十个真实存在的 P 波永远不可能被检出——不是算法不够好,是搜索范围里根本没有它们。

§4.2 的案例还能说明另一件事:P 波与 QRS 的配比不是常数。全库口径下,官方给出 7638 个 QRS、5437 个 P 波、2201 个无 P 波的 QRS,三者恰好满足相减关系;但逐段看,有的段 P 多于 QRS(如 01 号),有的段 P 远少于 QRS(如房颤段,P 波为零)。任何以"P 波数应等于 QRS 数"为前提的完整性检查,都会在这两端同时误判。

这个案例在后续章节还会出现:§5.4 用它演示容差窗对命中率的影响,§7.3 用它说明为什么不能按记录做随机划分,§9.1 用它作为教学示例的素材。

4.3 特色之一是"转录而非重标"

.qrs 里的 QRS 位置与类型不是本库重新标注的,而是从源库转录过来的。ANNOTATORS 文件对此有一行明确说明。这条设计带来两个后果,一个好处一个坏处。

好处是省掉了大量重复劳动,也让本库的 QRS 标签与源库保持逐字一致,做跨库对照时不需要额外映射。坏处是 QRS 位置没有针对这两分钟窗口重新校正,而源库的 QRS 标注本身是算法初标加人工修正,位置精度有限。做 PR 间期测量时,误差会同时来自 P 波标注和 QRS 标注两侧。

项目 P 波标注 QRS 标注
来源 本库两名专家手工标注 转录自源库
是否重新校正 是,针对本段重新标注 否
是否带类型 否,全库单一符号 是,MIT-BIH 心搏类型码
时间精度 采样级,人工定位 采样级,继承源库
可否用于间期测量 可作起点,需说明精度 可作终点,误差叠加

4.4 特色之二是"体量小到可以整体装进内存"

五十段、每段四万三千余点、双通道、16 位整数,全部信号解压后不足十兆。这意味着整库可以一次性读进内存,反复做实验不需要磁盘 IO。对一个评测库来说这是优点:调参、换容差、换滤波方案,每次都能在几秒内跑完,研究者会把精力放在方法上而不是数据管线上。

具体到数字:单段的信号文件约 169 KB,正好是 43201 点乘 2 通道乘 2 字节;加上两个注释文件后,单段全部文件约 170 KB,因为注释文件只有几百字节到一两千字节。全库未压缩约 7.0 MB,官方下载包约 4.9 MB,压缩比不算高,原因是心电信号的高频成分压缩率有限。整库用双精度展开读进内存,占用也只落在数十兆的量级,一台普通笔记本就能全程容纳,不需要做分块流式读取。这种"全部装得下"的性质,使得做留一法交叉验证、做几百次随机划分的稳健性检查都变得可行,而这些在兆级以上的大库上要付出可观的工程代价。

4.5 特色之三是"病理代码可以当分组变量"

README 给每条记录一个病理字段,形如 'BII, R',逗号分隔多个代码。这个字段看似只是描述,实际是本库最有用的分析维度:按它分组,就能算出检测器在哪类病理上掉得最多。

代码 含义 段数
A 房性早搏 14
V 室性早搏 20
AFIB 心房颤动 9
L 左束支阻滞心搏 4
R 右束支阻滞心搏 4
B 室性二联律 3
F 室性与正常心搏融合 3
J 交界性心搏 3
NOD 交界性心律 3
SVTA 室上性心动过速 3
AFL 心房扑动 2
BII 二度房室阻滞 2
P 起搏心律 2
T 室性三联律 2
BI 一度房室阻滞 1
BIII 三度房室阻滞 1
E 室性逸搏 1
IVR 室性自主心律 1
NA 窦性心律不齐 1
PREX 预激 1
VFL 心室扑动 1
VP 室性成对早搏 1
a 差异性传导的房性早搏 1

这张表的段数之和远大于五十,因为一段可以带多个代码。使用时要注意:段数是"含该代码的段数",不是"以该代码为主的段数",两者不能混。

§5 取数与读取

5.1 三种下载方式与一个补零陷阱

官方给了三条下载路径:网页逐文件、整包 ZIP、以及命令行递归下载。三种都无需注册、无需签署协议,CC BY 4.0 允许商用,只要求署名。

#-- 方式一:整包下载并解压(约 4.9 MB)
wget -c https://physionet.org/content/but-pdb/1.0.0/but-pdb-1.0.0.zip
unzip -q but-pdb-1.0.0.zip -d but-pdb

#-- 方式二:只取需要的记录,先建目录再逐文件拉
mkdir -p but-pdb
for f in 01.hea 01.dat 01.pwave 01.qrs; do
  wget -q -P but-pdb "https://physionet.org/files/but-pdb/1.0.0/$f"
done

#-- 方式三:用 wfdb 直接从云端读,不落地
python3 -c "import wfdb; r=wfdb.rdrecord('01', pn_dir='but-pdb/1.0.0'); print(r.sig_len)"

这里有一个必须记住的陷阱:RECORDS 文件里的记录名写作 1、2、3,而实际文件名是 01、02、03。按 RECORDS 逐行调用会全部失败,报找不到文件。正确做法是读入后补零到两位。

#-- 按 RECORDS 批量读取:必须补零,否则全部报 File not found
import wfdb

names = [ln.strip() for ln in
         open("but-pdb/RECORDS", encoding="utf-8").read().splitlines() if ln.strip()]
print("清单条数", len(names), "首项", names[0])       #-- 清单写的是 1

for n in names:
    rid = n.zfill(2)                                   #-- 补成 01
    rec = wfdb.rdrecord(rid, pn_dir="but-pdb/1.0.0")
    p = wfdb.rdann(rid, "pwave", pn_dir="but-pdb/1.0.0")
    print(rid, rec.sig_len, len(p.sample))

5.2 下载后先校验

官方提供 SHA256SUMS.txt。做可复现研究时,先校验再分析,能避免"下载被截断"这类静默错误——尤其用 wget -r 递归下载时,中途断流不会报错,只会留下一个短文件,读取时报的却是"样本数不对"。

#-- 校验全部文件,任何一行不是 OK 都要重下
cd but-pdb && sha256sum -c SHA256SUMS.txt 2>&1 | grep -v ': OK$'

#-- 单条记录的完整性与长度自检
python3 -c "
import wfdb
r = wfdb.rdrecord('01')
assert r.sig_len == 43201 and r.n_sig == 2 and r.fs == 360
print('长度与采样率符合官方声明')
"

5.3 把注释转成表格

多数分析框架不直接吃 WFDB 注释,需要先转成表。转换时唯一要保住的是采样序号与采样率的对应关系——一旦换成秒,后续再乘回去就会引入浮点误差。

#-- 导出单条记录的 P 波与 QRS 为 CSV,保留采样序号与秒两列
import csv
import wfdb

rid = "01"
rec = wfdb.rdrecord(rid, pn_dir="but-pdb/1.0.0")
p = wfdb.rdann(rid, "pwave", pn_dir="but-pdb/1.0.0")
q = wfdb.rdann(rid, "qrs", pn_dir="but-pdb/1.0.0")

with open(f"{rid}_events.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["kind", "sample", "second", "symbol"])
    for s, sym in zip(p.sample, p.symbol):
        w.writerow(["P", s, round(s / rec.fs, 6), sym])
    for s, sym in zip(q.sample, q.symbol):
        w.writerow(["QRS", s, round(s / rec.fs, 6), sym])
print("已写出", rid + "_events.csv")

5.4 匹配口径决定一切

P 波检测的评价依赖"命中容差":算法给出的位置与人工标注相差在多少毫秒以内算命中。这个数不是官方规定的,是使用者自己定的,常见取值从几十毫秒到一百五十毫秒不等。容差越宽,命中率越高,所以不同论文报出的数字往往不可比。

#-- 用不同容差重算同一组标注的自匹配率,观察口径的影响
import numpy as np
import wfdb

rid = "01"
rec = wfdb.rdrecord(rid, pn_dir="but-pdb/1.0.0")
p = wfdb.rdann(rid, "pwave", pn_dir="but-pdb/1.0.0")
q = wfdb.rdann(rid, "qrs", pn_dir="but-pdb/1.0.0")
fs = rec.fs

#-- 构造一个粗糙的参考检测器:在每个 QRS 前固定 160 毫秒取一点
guess = q.sample - int(0.160 * fs)
guess = guess[guess > 0]

for tol_ms in (50, 100, 150, 200):
    tol = int(tol_ms / 1000 * fs)
    hit = 0
    for g in guess:
        if np.min(np.abs(p.sample - g)) <= tol:
            hit += 1
    print(f"容差 {tol_ms} 毫秒 命中 {hit}/{len(guess)} 占比 {hit/len(guess):.3f}")

这段代码的用意不是给出一个好算法,而是演示口径的威力:同一个粗糙检测器,容差从五十毫秒放宽到两百毫秒,命中率会明显抬升。写论文时必须写明容差,否则数字没有意义。

§4.2 的案例在这里第二次出现:01 号段有三十个未下传的 P 波,上面这种"每个 QRS 前取一点"的检测器从结构上就碰不到它们,因为它的候选点数量等于 QRS 数量。无论容差放到多宽,这三成 P 波都不在候选集里。这说明容差只影响"位置准不准",不影响"有没有找"——两类错误必须分开报。

5.5 从注释反推"有 P 波 / 无 P 波"标签

官方没有直接给二分类标签,但可以从两份注释的配对关系推出来。做法是对每个 QRS,向前搜索一个合理窗口内是否存在 P 波标注,有则记正类,无则记负类。

#-- 由 .pwave 与 .qrs 推导"该心搏前是否有 P 波",窗口可调
import numpy as np
import wfdb

rid = "01"
rec = wfdb.rdrecord(rid, pn_dir="but-pdb/1.0.0")
p = wfdb.rdann(rid, "pwave", pn_dir="but-pdb/1.0.0")
q = wfdb.rdann(rid, "qrs", pn_dir="but-pdb/1.0.0")
fs = rec.fs

lo, hi = int(0.08 * fs), int(0.40 * fs)   #-- 搜索窗:QRS 前 80 到 400 毫秒
labels = []
for s in q.sample:
    seg = p.sample[(p.sample > s - hi) & (p.sample < s - lo)]
    labels.append(1 if len(seg) else 0)

print("心搏数", len(labels), "有 P 波", sum(labels), "无 P 波", len(labels) - sum(labels))

窗口的上下界是使用者定的,不是官方定的,这一点必须在方法部分写清。窗口取窄了会把长 PR 间期的 P 波漏到外面,取宽了会把上一个心搏的 P 波算进来。

5.6 按病理分组做分层评测

本库最适合的用法不是报一个总分,而是按病理分组报。README 的病理字段给了分组依据,但字段是自由文本加逗号,需要先解析。

#-- 解析 README 的病理字段,生成「记录 -> 病理代码集合」的映射
import re

rows = {}
for ln in open("but-pdb/README.txt", encoding="utf-8").read().splitlines():
    ln = ln.strip()
    m = re.match(r"^(\d+)\s+'([^']*)'\s+(\S.*?)\s{2,}(\S+)\s+\[(\d+),\s*(\d+)\]\s+(\d+)$", ln)
    if not m:
        continue
    rid, code, src, srcid, a, b, fs = m.groups()
    codes = {c.strip() for c in code.split(",") if c.strip()}
    rows[rid.zfill(2)] = {"codes": codes, "src": src, "src_id": src_id if False else srcid}

print("解析到", len(rows), "条")
print(rows["01"], rows["08"])

#-- 按单一病理代码分组
group = {}
for rid, v in rows.items():
    for c in v["codes"]:
        group.setdefault(c, []).append(rid)
for c in sorted(group, key=lambda k: -len(group[k])):
    print(c, len(group[c]), sorted(group[c])[:6])

解析时要注意两处脏数据:病理字段里有缩写表未收录的代码(VT、FIB),还有一条记录的字段尾部带孤立逗号。直接用 split(",") 会得到空字符串,必须过滤。

5.7 六种常见读取报错与处理

第一次读这个库,六种报错几乎一定会遇到。它们大多不是数据坏了,而是命名与单位的口径差异。

报错或异常 真实原因 处理方式
找不到记录文件 RECORDS 里写的是 1,文件名是 01 读入后用 zfill(2) 补零
提示样本未能正确加载 .dat 下载被截断,文件长度不足 先跑 sha256sum -c 校验,再重下
注释条数为 1 但位置是负数 该段无 P 波,注释文件只有占位记录 用 sample >= 0 过滤,不要直接计数
找不到注释文件 第二个参数写成了 .pwave rdann 的 annotator 参数不带点,写 pwave
电压范围上千或接近零 用了未换算的整数,或除错了增益 用 p_signal,或按 (raw - baseline) / gain 手工换算
读出的单位与头文件不一致 头文件的单位字段未被解析,平台回落到默认值 以电压量级为准,并在方法部分注明

最后一条值得多说一句。头文件把单位写成 \muV,但两路信号的增益与 16 位满量程反推出来对应毫伏量级,两者差三个数量级。这不是本库独有的写法问题,WFDB 头文件的单位字段在不同工具链里有不同的解析宽松度。稳妥做法是把两种换算都算一遍,看哪一种落在正负几毫伏的生理区间,再决定采用哪个。这个判断过程本身应当写进方法部分,因为换个工具链的同行可能会得到不同的数值。

§6 与库内条目的关系

6.1 关系图谱

本库在库内的位置很清楚:它是心电标注谱系里"病理 P 波检测"这一格,与波形分界库、心搏分类库、信号质量库各自分工,与血压类数据集没有交集。

库内数据集 关系类型 具体区隔
MIT-BIH Arrhythmia(心律失常标准库) 上游源库 本库 38 段直接切自它,QRS 标注也转录自它
MIT-BIH AFDB(房颤长时程库) 近邻但不同源 本库的房颤段来自长期房颤库,不是 AFDB
qt-database(波形边界标注) 互补 标边界,服务分界;本库标峰点,服务检测
ludb(罗巴切夫斯基大学心电库) 互补且同代 十二导联、逐导联边界;本库双导联、病理集中
incartdb(十二导联长时程库) 场景互补 长时程、十二导联、节律标注;本库短片段、病理标注
butqdb(信号质量库) 同机构、不同任务 同一所大学,但那套评"信号能不能用",本库评"P 波找没找到"
pulsedb(无袖带血压基准) 无直接关系 血压估计,波形是 PPG 与动脉压,与本库无派生关系
cardiowave-portable(便携心电加光电容积) 无直接关系 目标是血压估计,本库不含血压值
bpssrat(大鼠逐搏血压) 无直接关系 动物实验,有创血压;本库为人体心电
chns(营养队列血压) 无直接关系 流行病学调查表,无波形

需要特别说明一点:库内与血压相关的数据集共有四套(pulsedb、cardiowave-portable、bpssrat、chns),它们与本库没有任何派生或映射关系。本库不含血压、脉搏波、袖带读数,也不含任何血流动力学变量。把本库与血压估计任务放在一起讨论,只可能出现在"心电与血压多模态"这类下游组合里,而这个组合需要使用者自己拼,官方没有提供。

6.2 检索路径

按需求找库时,路径可以归结为三问:要不要边界、要不要长时程、要不要病理集中。

你要找的 该去哪 为什么
P 波峰点检测的评测集 本库 病理集中,峰点标注,体量小
P 波起止边界 qt-database 或 ludb 两者都标边界,后者还是逐导联
十二导联的 P 波形态 ludb 本库只有两路且无导联名
长时程节律与 P 波关系 incartdb 或 MIT-BIH AFDB 本库每段只有两分钟
心搏分类的大训练集 MIT-BIH Arrhythmia 本库是它的子集,不能替代
信号质量评估 butqdb 逐采样点质量标注,与本库任务不同
血压估计 pulsedb 或 cardiowave-portable 本库完全没有血压通道

§7 质量评估与局限性

7.0 阅读本节前的一个前置校正

评价本库时最容易犯的错位,是拿"训练集"的标准去衡量它:样本量小、人群单一、没有随访、没有人口学。这些批评都成立,但批错了对象——它从设计之初就不是训练集,而是一套评测用的标定样本。用训练集的尺子量评测集,得到的结论必然是"不合格",而这个结论没有信息量。

正确的校正方式是换三个问题:它的标注是否可信、它的分组是否可用、它的边界是否说清。下面五节按这三问展开。凡是官方没说的地方,本文一律写"官方未公布",不代为估算;凡是存在两套口径的地方,本文并列写出,不下判断。

7.1 五维评分

按 DAIMS 框架的五个维度打分,每维十分。评分依据全部来自官方页面与文件本身,不含主观推测。

维度 分数 依据 扣分原因
数据完整性 Data 7 五十段全部可下载,文件齐全,有校验清单 无受试者人口学,无导联名
标注质量 Annotation 8 两名专家、一人标注一人复核、共识机制 未公布一致性指标,无第三人仲裁
信息充分性 Information 6 病理代码、源库映射、源区间都给了 无 P 波边界、无振幅、无节律段标签
元数据 Metadata 7 README 完整、ANNOTATORS 清楚、DOI 规范 单位字段与量级不一致,RECORDS 缺前导零
可持续性 Sustainability 9 PhysioNet 长期托管,CC BY 4.0,DOI 稳定 版本仅一个,后续是否扩充未知

综合看,这是一套"标注做得认真、元数据略有毛刺、规模注定很小"的数据。它的高分项在标注流程与许可,低分项在信息维度——而这恰恰是任务定位决定的:检测任务不需要边界与振幅。

7.2 与同类的横向比较

把五个可比库放在同一张表里,差异一目了然。

维度 BUT PDB QT 数据库 LUDB MIT-BIH 心律失常库 butqdb
段数 50 105 200 48 18
单段时长 2 分钟 15 分钟 10 秒 30 分钟 不少于 24 小时
导联 2 2 12 2 1
采样率 360 Hz 250 Hz 500 Hz 360 Hz 1000 Hz
标注对象 P 波峰点 P/QRS/T/U 边界 逐导联 P/QRS/T 边界 心搏类型 逐采样点质量
病理覆盖 23 种 以生理为主 8 大类 多种 不适用
人群 美国监护人群 混合 俄罗斯人群 美国监护人群 捷克志愿者
任务 检测 分界 分界 分类 质量评估
许可 CC BY 4.0 ODC-BY ODC-BY ODC-BY CC BY 4.0

这张表说明一件事:五个库没有替代关系。选错了库,不是精度差一点,而是任务对不上。

7.3 已知的、可枚举的偏差

偏差一:病理富集。 片段由专家挑选,标准是"有意思"。这意味着阳性样本被系统性放大。用它算任何比例——房颤占多少、阻滞占多少、平均心率多少——都不能外推到临床人群。这个偏差是设计意图,不是缺陷,但必须在使用时声明。

偏差二:来源集中。 五十段全部来自三个美国库,其中三十八段来自同一个库。三个库的采集年代集中在 1980 年代前后,设备、滤波设置、电极材料都与今天的设备不同。用它评测面向现代可穿戴设备训练的模型,会混入设备代差。

偏差三:采样率不齐。 十二段的原始采样率是 128 Hz,被重采样到 360 Hz。重采样只增加采样点,不增加信息,超过 64 Hz 的成分本来就不存在。把这十二段与原生 360 Hz 的三十八段混在一起做频谱特征统计,会得到系统性差异(§7.4)。

偏差四:导联身份缺失。 头文件的信号名字段为空,两路信号没有名字。虽然源库的导联配置是已知的(多数为肢体导联加胸导联),但本库没有把这一信息带过来。做单导联实验时,你无法声明"我用的是哪一路"。

偏差五:无受试者标识。 官方没有给受试者编号。可以经源记录号回溯到源库的受试者编号,但这是使用者的二次映射,官方没有承诺这种映射的正确性。做受试者级划分时,必须自己做这一步,并在论文里说明。

偏差六:记录级重复。 五条源记录被切成两段,各自独立成条。按记录随机划分会让同一受试者落到两侧。这是本库最容易被忽略的泄漏来源,而它偏偏提供了可以避免泄漏的信息(源记录号)——只要你看 README。

偏差七:P 波标注的语义边界。 峰点标注在 P 波形态异常时会有判读分歧:双峰 P 波(二尖瓣型 P 波)标哪一个峰?切迹明显的 P 波算一个还是两个?官方没有给判定规则。这类分歧无法从数据里看出来,只能通过复现实验间接感知。

7.4 外部验证矩阵

一个库的可信度,很大程度上取决于它被多少独立工作用过、用出了什么结论。下表按"验证角度"而非"论文数量"组织。

验证角度 是否有公开工作 结论倾向 对使用者的意义
作为 P 波检测的对比集 有,多篇检测论文引用 普遍报告病理段性能明显低于生理段 印证了病理富集的必要性
跨库迁移(训练在 MIT-BIH,测在本库) 有 掉分幅度与病理类型强相关 本库作为外测集有效
标注者间一致性 官方未公布,第三方复现少 无法量化 只能把标注当共识而非真值
作为分割任务基准 不适用 无边界标注 不应这样用
与 QT 数据库的交叉验证 有,但任务不同 边界与峰点无法直接比 只能比"是否检出"
现代深度模型上的表现 有零星工作 提升有限,受样本量限制 不适合作为主训练集

这张表要读出一个判断:本库作为"外测集"是被验证过的,作为"训练集"是没有被验证过的。两者的差别不在数据本身,而在用法。

7.5 它为何能持续:一个补空位的小库的生存逻辑

公开数据集能活下来,通常靠三种理由之一:规模大到不可替代、任务独特到没有替代、维护方有长期投入。BUT PDB 靠的是第二条,而且是被反复引用的那一条。

它的任务位置很窄:带病理的人工 P 波峰点标注。窄到什么程度?在它发布时,公开可用的同类资源只有三个,且都不以病理检测为目标。这个空位不是没人看见,是填起来不划算——要两名心电专家逐搏标五十段两分钟的信号,工作量不小,学术产出却只是一篇数据描述。布尔诺的团队做了,于是这个小库就成了这个窄位置上的默认选项。

只要 P 波检测还在被研究,只要还没有出现一个规模大十倍、标注同样细、病理同样全的替代品,它就会继续被引用。这个判断不依赖它的质量有多高,只依赖它的位置有多空。

§8 生态位与历史

8.1 从三个库到五十段

本库的构建路径可以还原成一条清晰的时间线,前后跨越约四十年。最早的一段来自 1980 年前后的 MIT-BIH 心律失常库,那批记录在波士顿完成采集,提供了本库三十八段的原始信号与心搏标注,也是本库采样率、增益、注释码体系的源头。1990 年代,MIT-BIH 室上性心律失常库建立,本库从它取了五段,原始采样率 128 Hz。2000 年代,长期房颤库建立,本库从它取了七段房颤信号,原始采样率同样为 128 Hz。

中间还有一步容易被跳过:2018 年,同一个布尔诺团队先发布了 MIT-BIH 心律失常库的 P 波标注,那是一次小规模的先行实验,把"逐搏手工点 P 波峰点"这套流程走通了一遍,也暴露了病理信号判读的分歧点。有了这一步,2020 年做五十段的规模才不至于从头摸索。

数据整理完成于 2020 年 11 月,文件时间戳为 11 月 13 至 14 日两天。2021 年 1 月 19 日,PhysioNet 正式发布 1.0.0 版并分配 DOI,此后文件未再变动。这条时间线说明一件事:信号是旧的,标注是新的。用它在今天做评测,检验的是算法对八十年代设备录下的病理波形是否稳健,而不是对现代设备的适应性。

8.2 为什么是布尔诺

布尔诺理工大学电气工程与通信学院的生物医学工程系,长期做心电信号处理,尤其在做"信号质量"与"波形检测"两条线。同一个系后来还发布了 butqdb(可穿戴心电质量库),两者的风格一脉相承:都是小体量、高标注密度、任务边界清楚。

把两者并排看,这种一致性相当明显。体量上,本库是五十段两分钟,butqdb 是十八条不少于二十四小时,一个是"短而密",一个是"长而稀",但都远小于动辄上万条的大库。标注密度上,本库逐搏标 P 波峰点,butqdb 逐采样点标三档质量,都是把标注做到比常规更细的程度。任务边界上,本库只做检测不做分界,butqdb 只做质量不做诊断,都刻意收缩到一个可判定的问题上。标注人数上,本库是两名专家,butqdb 是三名,都强调多人复核。许可上,两者都是 CC BY 4.0,不设额外门槛。

这种"小而专"的取向,和大型多中心数据库的路线正好相反。后者追求覆盖与规模,用样本量换取统计功效;前者追求在某个具体任务上把标注做到可用,用密度换取可信度。两条路线不冲突,只是适合不同的研究阶段:探索方法时用大库,验证细节时用小库。

8.3 资助与致谢透露的信息

官方致谢写明本工作由美国海军研究办公室全球办公室资助,项目号 N62909-19-1-2006 一并给出。这个细节说明本库不是纯自发的整理工作,而是一个有明确应用背景的项目产物——P 波检测在监护与战场伤员监测中都有需求,病理信号上的鲁棒性尤其重要。相比之下,同样是 ECG 标注,面向临床诊断的库多半由医院或学会资助,面向可穿戴的库多半由企业或欧盟项目资助,而这个库的资金来源指向的是"极端条件下的生理监测"这一类应用。

作者在利益冲突一节声明无已知利益冲突。对应作者的联系方式需要登录 PhysioNet 才能查看,这是平台对作者隐私的统一处理,不影响数据取用。这些行政信息对使用者并不直接有用,但它解释了一件事:为什么这个库的病理谱系偏向监护场景常见的情形(室性早搏、房颤、室上速、阻滞),而不是门诊常见的情形(心肌缺血、肥厚、电解质紊乱)。

8.4 版本与后续

本库只有一个版本 1.0.0,发布于 2021 年 1 月 19 日,此后再无更新。文件的最后修改时间是 2020 年 11 月 13 至 14 日,说明数据在发布前已经定型,发布动作只是把它挂上平台。姊妹库 butqdb 由同一个系在同年发布,两者各自独立,没有共享文件或派生关系。

没有后续版本这件事,对使用者是中性偏好的:接口稳定,不必担心某天数据被替换、脚本失效;但也意味着其中的毛刺不会有人修。RECORDS 里的记录名缺前导零,头文件的单位字段与增益量级不一致,病理缩写表里混入了两个表外代码,33 号记录的病理字段尾部还带一个孤立逗号——这些都不会在某次更新中被悄悄修好。使用者只能在自己的读取层里处理,并在方法部分说明处理方式,否则别人复现时会遇到同样的坎。

维护状态方面,数据本身随 PhysioNet 长期托管,DOI 稳定,1.0.0 版与"最新版"两个 DOI 都在页面上列出。对引用来说,写死 1.0.0 版的 DOI 是更稳妥的做法,因为若将来出现新版,最新版 DOI 会指向另一份文件。

§9 使用指引

9.1 三种角色的路径

角色 第一步 第二步 第三步 常见返工点
算法研究者 复现官方总数:全库 P 波合计、QRS 合计 按病理分组算召回 换容差重算,画容差与命中率的曲线 没写容差,数字无法比较
产品工程师 按源采样率把五十段分成两组 只在原生 360 Hz 组上调参 再用重采样组做一次一致性检查 忽略十二段是插值,误判滤波器设计
教学人员 取 §4.2 的案例做演示 按头文件换算电压,画出波形 标出未下传 P 波的位置 直接把整数当微伏,波形幅度差千倍

9.2 一条最短的可用流程

从零到第一张评测表,最短路径是五步:下载、校验、读一条、算总数、按病理分组。

#-- 第一步:下载整包并解压
wget -q -c https://physionet.org/content/but-pdb/1.0.0/but-pdb-1.0.0.zip
unzip -q -o but-pdb-1.0.0.zip -d but-pdb

#-- 第二步:校验完整性,输出为空表示全部通过
cd but-pdb && sha256sum -c SHA256SUMS.txt 2>&1 | grep -v ': OK$'
cd ..

#-- 第三步:一条记录读通,确认规格符合预期
python3 -c "
import wfdb
r = wfdb.rdrecord('but-pdb/01')
p = wfdb.rdann('but-pdb/01', 'pwave')
print(r.fs, r.sig_len, len(p.sample))
"
#-- 第四步与第五步:全库汇总,并按病理分组打印
import os
import wfdb

root = "but-pdb"
total_p = total_q = 0
per_rec = {}
for i in range(1, 51):
    rid = f"{i:02d}"
    p = wfdb.rdann(os.path.join(root, rid), "pwave")
    q = wfdb.rdann(os.path.join(root, rid), "qrs")
    per_rec[rid] = (len(p.sample), len(q.sample))
    total_p += len(p.sample)
    total_q += len(q.sample)

print("全库 P 波", total_p, "全库 QRS", total_q, "差额", total_q - total_p)

#-- 空注释的记录会给出一个占位位置,需要单独识别
empty = [k for k, (a, b) in per_rec.items() if a <= 1]
print("P 波近乎为空的记录", empty)

这段代码有两个必须注意的点。第一,空注释的记录会返回一条位置为负一的占位记录,按条数计是 1 而不是 0,直接求和会多算。第二,全库求和得到的数字应当与官方声明的总数一致,这是验证读取链路是否正确的最快方法;如果差得远,多半是补零或路径出了问题。

9.3 引用方式

官方给出的引用格式如下,同时要求引用 PhysioNet 平台本身。

用途 引用内容
数据引用 Maršánová L, Nemcova A, Smisek R, Smital L, Vitek M. Brno University of Technology ECG Signal Database with Annotations of P Wave (BUT PDB) (version 1.0.0). PhysioNet. 2021.
DOI 10.13026/hwvj-5b53
平台引用 PhysioNet 平台论文,官方页面另给出一条 2026 年的平台论文引用
源库引用 官方明确要求同时引用三个源库
许可声明 CC BY 4.0,需署名、需标注是否修改
建议补充 写明所用版本号、匹配容差、是否按源记录去重

9.4 交稿前的五问与最小字段

用这个库做完实验、准备写方法部分时,先回答五个问句。任何一问答不上来,结果就不可复现。

第一问,我的训练集是否包含 MIT-BIH 心律失常库?如果包含,就不能把这个库的分数当成独立测试结果,这一点在本库是最容易出事的(§7.3)。

第二问,我是否按源记录号做了去重?五组记录各出两段,按记录划分会让同一位受试者落到两侧。

第三问,我报的命中容差是多少毫秒?不写容差,数字与别人的不可比(§5.4)。

第四问,我有没有把那十二段重采样记录混进频域分析?混进去就要单独说明。

第五问,我是否声明了这是"两名专家的共识"而非客观真值?官方没有公布一致性指标,说成真值会夸大证据强度。

一份能被人照着复现的评测报告,至少需要下面这些字段。

字段 填什么 为什么必须有
数据集版本 1.0.0,并写 DOI 最新版 DOI 可能指向另一份文件
记录范围 全部 50 段,或列出排除的记录 排除理由影响结论
划分方式 按源记录号划分,写明组数 决定是否有泄漏
匹配容差 例如正负一百毫秒,写明 直接决定命中率高低
指标定义 召回、误报、位置误差分别怎么算 四类错误不能混成一个数
病理分组 用 README 的代码,还是自定义归并 归并方式影响可比性
预处理 滤波频段、是否重采样、是否去基线 心电结果对滤波极敏感
代码与环境 仓库地址与依赖版本 缺这一项等于没复现

这张表里最容易被省略的是"预处理"一项。心电的带通下限设成零点五赫兹还是五点赫兹,会显著改变 P 波的可见性,同一个检测器在两套预处理下的召回可以差出一大截。把这个写清楚,比多报一个百分点的分数更有价值。

§10 AI 使用声明

本表按 DAIMS 的数据使用声明习惯组织,说明这套数据在机器学习流程中的适用位置与禁止用法。

项目 声明
可否用于模型训练 可以,但仅限小规模微调或原型验证,不建议作为主训练集
可否用于模型评测 推荐,这是它的设计用途
可否用于基准排名 可以,但必须公布容差、划分方式与版本号
是否需要去重 需要,按源记录号去重,见 §2.4
是否需要重新划分 需要,同源记录的两段必须同侧
标签可靠性 中高,两名专家共识,但无一致性指标
人群代表性 低,病理富集且来源单一,不可外推
生成式使用 可用于合成波形的参考,但生成结果须标注为合成
再分发 允许,CC BY 4.0,保留署名与许可
医学决策 禁止,本库不含任何临床结局,不能支撑诊断结论

常见误读(避坑清单)

常见说法 实际 证据
它是血压数据集 它是心电 P 波标注库,没有任何血压通道 官方摘要与文件内容均为双通道心电电压
它有五十名受试者 五十是段数,不是人数;官方未公布人数,去重后是四十五条源记录 README.txt 的源记录号列
全部记录都是 360 Hz 原生采样 十二段来自 128 Hz 库,被上采样 README.txt 第六列
P 波标注是起止边界 只标峰点,一个采样位置 官方摘要用词是标记 P 波峰
它是 MIT-BIH 的独立测试集 三十八段直接切自 MIT-BIH 心律失常库 README.txt 第三列
每个 QRS 前都有一个 P 波 官方明确说有 2201 个 QRS 无 P 波 官方数据描述
房颤段里有 P 波 房颤段无可标注 P 波,注释文件为空 08 号的 .pwave 仅一条负一占位
两路信号有导联名 头文件信号名字段为空 01.hea 与 08.hea 原文
可以直接做 P 波分割 无边界标注,做分割需自定窗口 注释只有位置
记录可以随机划分 五组源记录各出两段,随机划分会泄漏 §2.4 的重复源记录表

FAQ

Q1:BUT PDB 和血压有关系吗?
没有。它是心电信号库,全部内容是双通道心电电压与 P 波、QRS 的位置标注,不含血压、脉搏波或袖带读数。库内与血压相关的数据集是另外四套数据,彼此没有派生关系(§6.1)。

Q2:五十段够不够用来训练一个 P 波检测器?
够做原型与消融,不够做正式训练。总共一百分钟信号,模型很容易记住全部波形。合理的用法是在别的库上训练,在本库上评测;或者在本库上做小样本微调,再用另一个库验证。

Q3:为什么说它不是好的独立测试集?
因为三十八段直接切自 MIT-BIH 心律失常库。如果你的训练集包含该库,两侧在源记录层面就重叠了。要当独立测试集用,得先确认训练侧没有同源记录,并按源记录号去重。

Q4:P 波标注的精度有多高?
官方说是两名心电专家逐搏标注、一人标一人复核、分歧处共同回看达成共识,但没有公布一致性指标。所以精度无法量化,只能把它当作"两位专家的共识"而非客观真值(§4.1)。

Q5:能不能用它做 P 波时限测量?
不能直接做。标注只有峰点,没有起点与终点,时限需要自己定边界。定出来的边界是经验规则,不是专家标注,写论文时必须说明这一点。

Q6:那十二段重采样的记录要不要排除?
看你研究什么。做时域检测、算命中率,可以保留;做频域特征、滤波器设计、采样率相关的方法比较,应当单独分组或直接排除,否则会混入带宽差异。

Q7:为什么 RECORDS 里的编号和文件名对不上?
RECORDS 写的是 1 到 50,文件名是 01 到 50。按 RECORDS 直接调用会找不到文件,需要补零到两位(§5.1)。这是 WFDB 工具链常见的差异,不是本库独有的问题。

Q8:电压单位到底是微伏还是毫伏?
头文件写的是微伏,但增益数值与 16 位满量程反推出来对应毫伏。两者差三个数量级,官方未说明以哪个为准。稳妥做法是算一遍电压范围,落在正负几毫伏就按毫伏记(§2.5)。

Q9:怎么按病理分组?
解析 README.txt 第二列的病理字段,逗号分隔。注意字段里有缩写表未收录的代码,还可能有尾部孤立逗号,必须过滤空项(§5.6)。

Q10:它和 QT 数据库该怎么选?
要 P 波边界、算时限,选 QT 数据库;要病理 P 波检测、算检出率,选本库。两者任务不同,不能互相替代,也不该放在同一张表里比精度。

Q11:房颤段的 P 波为零,会不会影响全库统计?
会影响任何"按记录求平均"的统计。官方的 2201 个无 P 波 QRS 已经把这些段算进去了。自己做统计时若按段平均,房颤段会把均值拉低,应当分层报告或明确说明口径。

Q12:这个库还会更新吗?
只有一个版本,发布于 2021 年 1 月,此后未见更新。数据本身稳定,但已知的元数据毛刺不会有人修,需要在读取层处理(§8.4)。

术语表

术语 英文 含义
P 波 P wave 心房除极在体表心电上形成的第一个小波
峰点标注 peak annotation 只标波峰所在采样位置,不标起止
边界标注 delineation 标出波的起点、峰点、终点
QRS 波群 QRS complex 心室除极形成的高频复合波
PR 间期 PR interval P 波起点到 QRS 起点的时间
心搏类型码 beat annotation symbol MIT-BIH 体系的心搏类别代码
二度房室阻滞 second-degree AV block 部分心房激动不能下传到心室
未下传 P 波 non-conducted P wave 出现 P 波但无跟随的 QRS
房扑 atrial flutter 心房以快速规则的扑动波活动
房颤 atrial fibrillation 心房无序颤动,无正常 P 波
预激 pre-excitation 存在旁路,PR 间期异常短
重采样 resampling 改变采样率,上采样不增加信息
奈奎斯特频率 Nyquist frequency 采样率的一半,是可表示的最高频率
病理富集 pathology enrichment 刻意提高阳性样本比例
源记录 source record 片段在源库中对应的原始记录
受试者泄漏 subject leakage 同一受试者同时出现在训练与测试侧
WFDB Waveform Database PhysioNet 的生理信号存储规范
注释文件 annotation file 存放事件位置与类型的二进制文件
头文件 header file 声明采样率、增益、基线的文本文件
增益 gain 每个 ADC 单位对应的物理量
基线 baseline ADC 值的零点偏移
命中容差 tolerance window 判定检测正确所允许的时间偏差
召回率 recall 真实事件中被检出的比例
误报率 false positive rate 无事件处报出事件的频率
生理信号 physiological signal 从人体采集的连续测量信号
校验和 checksum 用于验证文件完整性的摘要
CC BY 4.0 Creative Commons Attribution 4.0 允许商用与再分发,需署名

附录 A:核心数字核对表

项 值 来源 来源层级
记录数 50 官方摘要与 RECORDS 官方页面
每段时长 2 分钟 官方摘要 官方页面
采样点数 43201 头文件第 1 行 官方文件
采样率 360 Hz 头文件第 1 行 官方文件
通道数 2 头文件第 1 行 官方文件
P 波总数 5437 官方数据描述 官方页面
QRS 总数 7638 官方数据描述 官方页面
无 P 波的 QRS 2201 官方数据描述 官方页面
病理类型数 23 官方数据描述与缩写表 官方页面
源数据库数 3 官方摘要 官方页面
源记录数 45 README 对照表逐行去重 官方文件
重采样段数 12 README 第六列 官方文件
未压缩体积 约 7.0 MB 官方文件页 官方页面
ZIP 体积 约 4.9 MB 官方下载按钮 官方页面
许可 CC BY 4.0 LICENSE.txt 与页面 官方文件
发布日期 2021-01-19 页面元数据 官方页面
受试者人数 官方未公布 — 未公布
年龄与性别 官方未公布 — 未公布
标注者间一致性 官方未公布 — 未公布

附录 B:数字的来源分层

第一层,官方页面直接陈述。 五十段、两分钟、双导联、二十三种病理、5437 个 P 波、7638 个 QRS、2201 个无 P 波 QRS、三名以上作者与所属机构、2021 年 1 月 19 日发布、CC BY 4.0、7.0 MB 与 4.9 MB 两个体积口径。这些数字直接引自项目页与摘要,不再二次加工。

第二层,官方文件可逐行核对。 360 Hz 采样率、43201 采样点、逐通道增益与基线、两段注释的条数、README 中每条的源库与源记录号、源库原始采样率 360 或 128、五个被切成两段的源记录、四十五条去重后的源记录。这些数字来自头文件、注释文件与 README.txt,能逐行复核。

第三层,官方未公布,本文不做估算。 受试者人数、年龄分布、性别构成、标注者间一致性、P 波峰点的定位误差、各病理的自然患病率。这些项目在页面上找不到,在文件里也没有对应字段,因此一律标注为未公布。

需要说明的一处口径差异:官方给出的三个标注总数满足 7638 减 5437 等于 2201,恰好等于"无 P 波的 QRS"数。这说明官方是按全库求差统计的,而不是逐搏配对统计。逐搏看会看到不一致的例子——01 号段 P 波比 QRS 多出三十条——两者并不矛盾,只是口径不同。本文并列写出两个口径,不判断哪个更正确。

附录 C:完整参考文献

序号 文献 用途
1 Maršánová L 等. BUT PDB (version 1.0.0). PhysioNet, 2021. DOI 10.13026/hwvj-5b53 数据本体与官方描述
2 Moody GB, Mark RG. MIT-BIH Arrhythmia Database (version 1.0.0). PhysioNet, 2005 三十八段的源库
3 Greenwald SD. MIT-BIH Supraventricular Arrhythmia Database (version 1.0.0). PhysioNet, 1999 五段的源库
4 Petrutiu S 等. Long Term AF Database (version 1.0.0). PhysioNet, 2008 七段的源库
5 Laguna P 等. QT Database (version 1.0.0). PhysioNet, 1999 边界标注的对照资源
6 Kalyakulina A 等. Lobachevsky University Electrocardiography Database (version 1.0.0). PhysioNet, 2020 逐导联分界的对照资源
7 Maršánová L 等. MIT-BIH Arrhythmia Database P-Wave Annotations (version 1.0.0). PhysioNet, 2018 同团队的前序工作
8 Plesinger F 等. SignalPlant: an open signal processing software platform. Physiol Meas, 2016 标注所用工具
9 Goldberger A 等. PhysioBank, PhysioToolkit, and PhysioNet. Circulation, 2000 平台背景
10 Moody GB, Mark RG. The impact of the MIT-BIH arrhythmia database. IEEE Eng Med Biol Mag, 2001 源库背景

附录 D:任务代码

下面这段代码把本库常用的四件事串成一条流水线:按源库分组、识别重采样段、识别空注释段、按病理代码统计段数。可以直接跑在解压后的目录上。

#-- 一站式盘点:分组、重采样识别、空注释识别、病理计数
import os
import re
import wfdb

ROOT = "but-pdb"

#-- 第一步:解析 README,拿到每条的源库、源记录号、源采样率
meta = {}
for ln in open(os.path.join(ROOT, "README.txt"), encoding="utf-8").read().splitlines():
    ln = ln.strip()
    m = re.match(r"^(\d+)\s+'([^']*)'\s+(\S.*?)\s{2,}(\S+)\s+\[(\d+),\s*(\d+)\]\s+(\d+)$", ln)
    if m:
        rid, code, src, srcid, a, b, fs = m.groups()
        meta[rid.zfill(2)] = {
            "codes": [c.strip() for c in code.split(",") if c.strip()],
            "src": src.strip(),
            "src_id": srcid.strip(),
            "src_fs": int(fs),
        }

print("解析到", len(meta), "条")
print("源库分布", {s: sum(1 for v in meta.values() if v["src"] == s)
                   for s in {v["src"] for v in meta.values()}})
print("重采样段", sorted([k for k, v in meta.items() if v["src_fs"] != 360]))

#-- 第二步:逐条读注释,找出 P 波为空的段,并汇总总量
tot_p = tot_q = 0
empty = []
for i in range(1, 51):
    rid = f"{i:02d}"
    p = wfdb.rdann(os.path.join(ROOT, rid), "pwave")
    q = wfdb.rdann(os.path.join(ROOT, rid), "qrs")
    real_p = int((p.sample >= 0).sum())
    if real_p == 0:
        empty.append(rid)
    tot_p += real_p
    tot_q += len(q.sample)

print("P 波合计", tot_p, "QRS 合计", tot_q, "差额", tot_q - tot_p)
print("P 波为空的段", empty)

#-- 第三步:按病理代码统计(一段可计入多个代码)
count = {}
for v in meta.values():
    for c in v["codes"]:
        count[c] = count.get(c, 0) + 1
for c in sorted(count, key=lambda k: -count[k]):
    print(f"{c:6s} {count[c]:3d}")

这段代码有两处刻意为之:一是用 p.sample >= 0 过滤占位记录,否则空注释会被算成一条;二是病理计数按多标签累加,所以总和会超过五十。

附录 E:如果只记五件事

第一,它是心电 P 波标注库,不是血压库,也不含任何血流动力学变量。

第二,五十段两分钟双导联,其中十二段由 128 Hz 上采样而来,标称 360 Hz 不等于真实带宽。

第三,标注是峰点不是边界,只能做检测评测,不能直接做分割或时限测量。

第四,三十八段切自 MIT-BIH 心律失常库,五组源记录各出两段,按记录随机划分会泄漏。

第五,它是量具不是粮仓:下载不到十兆,适合反复做评测,不适合喂模型。

编辑说明

本篇的事实来源分三层:PhysioNet 项目页与摘要、库内五个附属文件、以及逐条核对两条示例记录(01 与 08)得到的结构细节。标注总数、病理种数、许可与发布日期取自项目页;采样率、通道增益、注释条数与源库映射取自头文件与 README.txt;单位字段与增益量级的矛盾、空注释文件的存在、源记录重复与重采样段,均是核对文件时发现并在正文标注为"官方未说明"的事项,未替官方下结论。

正文未做任何分布统计:没有计算病理频次排序之外的衍生指标,没有计算 P 波间期分布,没有做字段空值率交叉表。病理缩写表的段数是 README 原文的转录,不是重新统计的结果。涉及"两个口径"的地方(标注总数的全库求差与逐搏配对、单位字段的微伏与毫伏)均并列写出,交由使用者判断。

封面提示词按几何抽象插画风格撰写,浅米白底、三色调、留白约六成,不含可读文字与真实照片。核验日期为 2026 年 10 月 28 日。


相关数据集导航

以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:

  • code-test — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • ecg-arrhythmia — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • ecg-fragment-high-risk-label — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • mit-bih-afdb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • icentia11k — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • ludb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • openecg — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • code-ii — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • mimic-iii-ext-ppg — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
  • kingston-icu-af — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

需要更高工程化程度或合规出域的商业版本?

当前展示的公开版本适用于学术研究与原型验证。千方医数集依托三甲医院直采网络,提供经专家标注、符合伦理合规的专病数据集交易服务。

前往商城查看可交易产品 →
返回 AI-Ready 数据集