信息速览
心电图判读里,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 — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 评测基准
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。
需要更高工程化程度或合规出域的商业版本?
当前展示的公开版本适用于学术研究与原型验证。千方医数集依托三甲医院直采网络,提供经专家标注、符合伦理合规的专病数据集交易服务。

