信息速览
心电公开数据集里,绝大多数资源的选择题只有两道:要导联多,就得接受每段只有十秒;要时间长,就得接受只有一两个导联。长时程这一类里,真正把「整天」当基本单位的库并不多,而其中最早、最小、也最容易被误认的那个,就是 MIT-BIH 长时程心电数据库(ltdb)。
它只有七条记录,每条从十四个小时到将近一整天,采样率 128 Hz,六条双导联、一条三导联,标注只有心搏与信号质量两层,整库未压缩两百零五兆。这几个数字放在今天的语境里近乎寒酸,却恰好构成一个别人很难复制的组合:足够长,长到能覆盖一次完整的睡眠与清醒周期;足够小,小到可以在十分钟内下完、在笔记本上跑通全流程。
它的历史位置比它的体量更重要。它是 PhysioNet 长时程家族里第一个上线的库,比带 ST 事件标注的 ltstdb 早一年,比房颤长时程库早了近十年。理解它,等于理解这一族库共同的技术底座——同一套 WFDB 格式、同一套注释码、同一批来自波士顿的磁带。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 全名 | MIT-BIH Long-Term ECG Database |
| 常用缩写 | ltdb |
| 建设机构 | 麻省理工学院(MIT)与波士顿贝斯以色列医院(Beth Israel Hospital)合作 |
| 提交者 | George B. Moody(Harvard-MIT 健康科学与技术部) |
| 上线时间 | 1999 年 8 月 3 日,版本 1.0.0 |
| DOI | 10.13026/C2KS3F |
| 记录数 | 7 条 |
| 每条时长 | 官方称 14–22 小时;头文件换算为 13.9–23.9 小时 |
| 导联 | 6 条双导联、1 条三导联 |
| 采样率 | 128 Hz |
| 量化与增益 | 12 位(三导联记录为 10 位),每毫伏 200 单位 |
| 标注内容 | 心搏类型 + 信号质量变化标记,经人工复核 |
| 标注总量 | 约六十七万条参考注释 |
| 文件格式 | WFDB:.hea / .dat / .atr,另有 .hea- 与 .xws |
| 整库体积 | 未压缩 205.4 MB |
| 许可 | ODC-BY 1.0,无需注册 |
| 典型用途 | 长时程心搏检测的快速验证、低采样率鲁棒性对照、教学演示 |
§1 它要解决的具体麻烦
1.1 一整天的心跳,需要多细的采样才够
心电算法在实验室里的默认假设是「信号很干净、导联很清楚、片段很短」。真实场景恰好相反:设备贴在人身上一整天,电极会干、会松、会被翻身压出伪迹,而算法要在这堆东西里找出每一次心跳。这类问题没法用十秒的片段回答,因为十秒里几乎不会出现电极脱落,也不会出现连续几小时的基线漂移。
ltdb 就是为这个问题准备的早期材料。它的七条记录全部是真实的体表长时程监护,不是拼接、不是仿真、不是从短记录循环复制出来的。每条记录都带着完整的一天:入睡、醒来、走动、静坐。心搏标注覆盖全程,信号质量的变化也被单独标出来。这两件事放在一起,才构成「检测器在真实长时程里表现如何」的完整证据。
它的另一个价值在于采样率。128 Hz 是这一族库里最低的一档,每两个采样点间隔约 7.8 毫秒。对 R 峰定位来说这够用,对 QRS 形态、ST 段水平这类需要更细时间分辨率的测量来说就不够。这个限制不是缺陷,而是一个明确的边界条件:想研究低采样率可穿戴设备的人,正好需要一个「原生长时程、原生低采样」的参照。
1.2 三条常见的错误期待
第一条,把它当成 ltstdb 的旧版本。两个库名字都带 long-term,都出自波士顿同一家医院,都用同一套格式,很容易被当成同一件事的前后两版。实际上它们的记录名体系完全不同,标注内容差了一整层:ltstdb 有 ST 事件、节律变化、信号质量三类附加标注,ltdb 只有心搏与信号质量。
第二条,把它当成房颤或缺血的数据集。它的注释文件里没有任何节律段标记,也没有 ST 偏移的时间序列。想拿它做房颤,只能从心搏间隔的序列统计里间接构造标签;想拿它做缺血,连构造的原材料都没有。这一点在文献里被反复踩到,因为库名里的 ECG 二字太容易被泛化。
第三条,把它当成可以训练的数据。七条记录、最多七个人,任何带参数的模型都会在第二轮就把它记住。它的正确位置是验证端,不是训练端。把它混进训练集,得到的指标只反映记忆能力。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 长时程心搏检测的全流程演练 | 七条完整记录,逐搏标注覆盖全程 | 够,且是全库中最省事的一个 |
| 低采样率下的 R 峰定位评估 | 原生 128 Hz,无重采样 | 够,这是它最独特的用法 |
| 信号质量变化对检测的影响 | 注释内单独标出质量变化点 | 够做定性分析,样本量很小 |
| 房颤检测的训练与测试 | 无节律标注 | 不够,只能二次构造标签 |
| 心肌缺血事件研究 | 无 ST 标注 | 不够,需改用 ltstdb |
| 形态学精细测量(QRS 宽度、QT) | 128 Hz 时间分辨率 | 勉强,误差会进入结论 |
| 患者级泛化的统计结论 | 最多七个人 | 不够,只能做案例级陈述 |
| 跨库外部验证 | 与 MIT-BIH 系列同注释码 | 够,常被当作外测目标 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 你刚写好一个心搏检测器,想找个小库先跑通端到端流程 | 两百兆、七个文件、无需注册,十分钟内可完成下载到评估 | 先跑通 §9.2 的最短流程,再换大库 |
| 你在做可穿戴设备的低采样率算法 | 提供原生 128 Hz 的长时程真值 | 把同一段信号降采样到 64 Hz 与 32 Hz,观察召回如何衰减 |
| 你想验证模型在电极脱落、长时间噪声下是否稳定 | 注释里带信号质量变化标记,可作为噪声区段的近似索引 | 用质量标记切出噪声段,单独报告该段性能 |
| 你要给模型做跨库外测,需要一个与训练库不同源的集 | 与 MIT-BIH 心律失常库同注释码、不同人群与设备 | 固定映射表后零样本测试,记录掉分幅度 |
| 你在教心电信号处理,需要一条能讲完的完整记录 | 单条近二十四小时,可截取任意时段做教学 | 用 §2.3 的读取流程导出,注意标注在末尾可能缺失 |
| 你在核对某篇论文的长时程结论能否复现 | 体量小,可快速重建对方的评测条件 | 先对齐采样率与注释映射,再比较数值 |
1.5 一句话判断
它不是用来把模型训强的,而是用来把流程走通的:七条记录足够暴露管线里的每一个错误,又小到让你愿意反复重跑。
1.6 不适合谁:四条边界
不适合拿来做训练集。 七条记录、最多七个人,任何深度模型都会在极短时间内过拟合。用它做训练,得到的不是能力,是记忆。
不适合拿来做节律或缺血任务。 没有节律段标注,没有 ST 事件标注,连辅助注释字符串都是空的。所有节律类标签都得自己造,而造标签的规则一旦写不清,结论就无法复现。
不适合拿来做精确形态学测量。 128 Hz 的采样间隔约 7.8 毫秒,QRS 起点与终点的判定误差会直接进入宽度、QT 间期这类指标。要做形态学,应该换到采样率更高的库。
不适合拿来做人群层面的统计推断。 官方没有公布受试者数,也没有诊断、用药、随访。七组年龄性别不足以支撑任何关于人群分布的结论。
§2 数据构成与规格
2.1 一个记录由三个文件组成
数据库沿用 WFDB 格式,这是 PhysioNet 从 1980 年代沿用至今的物理信号存储规范。每条记录对应三个同名文件:.hea 是纯文本头文件,第一行写记录名、信号条数、采样率与采样点总数,随后每条信号一行,声明存储格式、增益、位宽、基线、初始值、校验和与信号名;.dat 是二进制信号本体,按采样点交错存放各路信号的整数值;.atr 是二进制注释文件,存放事件位置与类型。
三者缺一不可。只拿 .dat 无法还原毫伏值,因为增益写在头文件里;只拿 .atr 不知道这些时刻对应什么波形。把数据转成 CSV 或 NumPy 数组是有损操作,头文件里的增益、基线、信号名会被压进列名,注释里的类型码会被压成字符串,想回到原始语义就得靠映射表。
目录里还有三类附属物。RECORDS 是七行纯文本,按行列出记录名;ANNOTATORS 只有一行,原文是「atr reference beat and signal quality annotations」,明确说明注释文件里同时含心搏与信号质量两类内容;SHA256SUMS.txt 是整库的校验和清单。另外每个记录还带一个 .hea- 与一个 .xws:前者是 2012 年改写之前的旧版头文件,信号名写的是「record 14046, signal 0」这类机械描述;后者是 WaveBox 网页工具的会话文件,内容只有三行,其中一行还残留着服务器端模板占位符,指向的是一个早已失效的地址。
2.2 字段与记录的完整字典
| 位置 | 字段 | 含义 | 取值示例 |
|---|---|---|---|
.hea 第 1 行 |
记录名 | 五位数字,同时是文件名 | 14046 |
.hea 第 1 行 |
信号条数 | 2 或 3 | 2 |
.hea 第 1 行 |
采样率 | 每秒采样点数 | 128 |
.hea 第 1 行 |
采样点总数 | 每条信号的采样点数 | 10828800 |
.hea 信号行 |
存储格式 | 212 为双信号 12 位,310 为三信号 10 位 | 212 / 310 |
.hea 信号行 |
增益 | 每毫伏的整数单位数,0 表示按缺省 200 | 0 |
.hea 信号行 |
位宽 | 量化位数 | 12 / 10 |
.hea 信号行 |
基线 | 零电平对应的整数值 | 0 |
.hea 信号行 |
初始值 | 第一个采样点的整数值 | 32 |
.hea 信号行 |
校验和 | 全段采样点累加和,用于完整性检查 | -13462 |
.hea 信号行 |
信号名 | 只写 ECG1 至 ECG3,不含解剖位置 | ECG1 |
.hea 注释行 |
年龄 | 受检者年龄,整数 | 46 |
.hea 注释行 |
性别 | M 或 F | M |
.atr |
注释位置 | 采样点序号,除以 128 得秒 | 600 |
.atr |
心搏类型码 | 沿用 MIT-BIH 标准注释码 | N V F J S a |
.atr |
信号质量标记 | 波浪号,表示该处信号质量发生变化 | ~ |
RECORDS |
记录清单 | 七行,每行一个记录名 | 14046 |
ANNOTATORS |
注释角色 | 一行说明 | atr reference beat and signal quality annotations |
SHA256SUMS.txt |
校验和 | 逐文件 SHA256 | 64 位十六进制串 |
2.3 一条记录的完整解剖
以 14046 为例,头文件只有四行,全部信息都在里面:
14046 2 128 10828800
14046.dat 212 0 12 0 32 -13462 0 ECG1
14046.dat 212 0 12 0 -58 -28804 0 ECG2
#Age: 46 Sex: M
第一行的四个数依次是记录名、信号条数、采样率、采样点总数。用采样点总数除以采样率,得到 84600 秒,即 23.5 小时。第二、三行是两路信号,存储格式 212 表示两个 12 位采样点压进三个字节,增益字段写 0,按 WFDB 约定取缺省值 200,所以一个整数单位等于 5 微伏。第四行是唯一的临床信息:46 岁,男性。
把这条记录读进内存只需要几行代码:
import wfdb
import numpy as np
rec = wfdb.rdrecord('14046', pn_dir='ltdb')
print(rec.fs, rec.sig_len, rec.sig_name, rec.adc_gain, rec.units)
#-- 128 10828800 ['ECG1', 'ECG2'] [200.0, 200.0] ['mV', 'mV']
x = rec.p_signal[:128 * 60] #-- 取第一分钟
print(x.shape, float(np.ptp(x[:, 0])), float(np.std(np.diff(x[:, 0]))))
这段代码里最容易忽略的是 physical=True 这个缺省行为:p_signal 已经按增益换算成毫伏,用 d_signal 拿到的才是原始整数。两种口径混用会得到相差两百倍的结果,而且不会报错。注释文件则要用 rdann 单独读:
import wfdb
ann = wfdb.rdann('14046', 'atr', pn_dir='ltdb')
print(len(ann.sample), ann.symbol[:10], set(ann.symbol))
print('first beat at %.1f s, last at %.1f h' % (ann.sample[0] / 128, ann.sample[-1] / 128 / 3600))
读出来的 symbol 是字符列表,aux_note 全是空串——这意味着这个库没有任何节律段标注。这一点用一行代码就能确认,也应该是每个使用者在动手前做的第一件事。
2.4 七条记录的逐项参数
| 记录 | 时长(小时) | 采样点 | 导联 | 格式 | 年龄 | 性别 | 参考注释(条) |
|---|---|---|---|---|---|---|---|
| 14046 | 23.50 | 10828800 | 2 | 212 | 46 | M | 115278 |
| 14134 | 13.93 | 6420480 | 2 | 212 | 71 | F | 49769 |
| 14149 | 23.87 | 10997760 | 2 | 212 | 47 | M | 145139 |
| 14157 | 20.52 | 9454080 | 2 | 212 | 88 | M | 88298 |
| 14172 | 21.17 | 9753600 | 2 | 212 | 75 | M | 66413 |
| 14184 | 22.25 | 10252800 | 2 | 212 | 71 | M | 101567 |
| 15814 | 22.22 | 10237440 | 3 | 310 | 58 | M | 103388 |
表里的时长是把采样点数除以 128 得到的,不是官方公布值;注释条数是逐个读取注释文件后的合计,七条相加约六十七万条。这两列都标注了来源,与官方页面并列时可对照阅读(§7.3)。
七条记录里,14134 最短,不足十四小时;14149 最长,接近二十四小时。年龄跨度从 46 岁到 88 岁,六男一女,没有一条来自年轻受试者。这个分布不是抽样设计的结果,而是当时能拿到的监护记录的实际情况,官方没有对此做任何说明。表中唯一的三导联记录是 15814,它与其余六条在格式与形状上的差别,见 §4.3 的案例。
2.5 与同类资源的字段差异
| 维度 | ltdb | ltstdb | ltafdb | afdb | incartdb |
|---|---|---|---|---|---|
| 记录数 | 7 | 86 | 84 | 25 | 75 |
| 每条时长 | 14–24 小时 | 21–24 小时 | 24–25 小时 | 10 小时 | 30 分钟 |
| 导联数 | 2(一条 3) | 2–3 | 2 | 2 | 12 |
| 采样率 | 128 Hz | 250 Hz | 128 Hz | 250 Hz | 257 Hz |
| 心搏标注 | 有 | 有 | 有 | 有 | 有 |
| 节律段标注 | 无 | 有 | 有 | 有 | 无 |
| ST 事件标注 | 无 | 有 | 无 | 无 | 无 |
| 信号质量标注 | 有 | 有 | 部分 | 部分 | 无 |
| 整库体积 | 205.4 MB | 数 GB 级 | 数 GB 级 | 数百 MB | 数百 MB |
| 上线年份 | 1999 | 2000 | 2008 | 2000 | 2008 |
这张表回答的是「同族长时程库里该挑哪一个」。如果任务是节律分类,ltdb 直接出局;如果任务是缺血,只有 ltstdb 可用;如果任务是长时程心搏检测的快速验证,ltdb 的体量与完整度是最优组合。对照库的数字取自各自的官方页面,个别字段(ltstdb 的采样率)取自其方法学论文,不是推算值。
2.6 导联命名的空白
头文件里的信号名是 ECG1、ECG2、ECG3,不是 II、V1、V5 这类解剖位置。官方页面没有说明这三路信号对应体表的哪两个或三个位置,也没有说明电极贴法。2012 年之前,这些字段甚至只写「record 14046, signal 0」,连 ECG 这个词都没有。
这个空白带来的后果很实际:任何声称「本库的 ECG1 相当于 II 导联」的说法都没有官方依据。它可能来自某篇论文的约定俗成,也可能是从波形形态上反推的猜测。做跨库迁移时,导联语义不清会直接影响结论——如果训练库用的是 II 导联、测试库的 ECG1 实际是改良胸导联,性能下降里就混进了导联错配的成分,而不只是人群差异。
实践上的处理办法有两条:一是明确声明「按信号序号对齐,不做解剖学假设」,二是在做跨库实验时,把导联语义不确定当作一个独立的误差源写进限制段落。两条都不难,难的是记得写。
还有一层更隐蔽的影响:导联语义不清会让人误判信号质量。Holter 的两路电极通常一路接近 II 导联、一路接近 V5 或 V1,不同位置对肌电与电极位移的敏感度不同。如果按「ECG1 是肢体导联、ECG2 是胸导联」的假设去设计降噪策略,而实际贴法相反,滤波器的通带选择就失去了依据。稳妥的做法是把两路信号各自的质量特征先算出来,再决定处理策略,而不是先假定再验证。
§3 它是谁的七段心跳:一家医院的磁带存档
3.1 贡献者与机构
数据库由麻省理工学院与波士顿贝斯以色列医院合作建设,PhysioNet 页面上标注的提交者是 George B. Moody,所属单位为 Harvard-MIT 健康科学与技术部。这个组合在 1990 年代的心电数据史上出现过很多次:医院提供临床记录,实验室提供数字化与标注的工程能力,两边共同把磁带上的模拟信号变成可计算的文件。
理解这个分工很重要。它意味着数据的采集场景由医院决定,标注规范由实验室决定,两者之间没有第三方审计。库里的每一条记录都是临床监护的副产品,不是为研究专门设计的采集方案。这也解释了为什么元数据只有年龄和性别两行——临床上顺手能拿到的就这些,其余的要去翻病历,而病历不在公开范围内。
3.2 时间线
| 时间 | 事件 | 依据 |
|---|---|---|
| 1999-08-03 | v1.0.0 上线,七条记录开放下载 | 官方页面版本信息 |
| 2000-03-29 | RECORDS 文件最后修改 |
目录文件时间戳 |
| 2000-10-13 | ANNOTATORS 文件最后修改 |
目录文件时间戳 |
| 2000-11-29 | 旧版头文件 .hea- 最后修改 |
目录文件时间戳 |
| 2012-02-14 | 头文件重写,信号名改为 ECG1 至 ECG3 | 目录文件时间戳与新旧头文件对照 |
| 2019-02-20 | 增补 SHA256SUMS.txt 校验和清单 |
目录文件时间戳 |
这条时间线说明一件事:这个库在 1999 年上线之后就基本冻结了。2012 年的改动只动了头文件的信号名,信号与注释没有变化;2019 年补的只是校验和。二十多年里没有新增记录,也没有修订标注。对使用者来说,这既是好事也是坏事:好处是任何年份下载到的数据都一致,坏处是它的标注水平停留在 1999 年。
3.3 采集场景:一整天,两个电极
七条记录都是体表长时程监护,通俗说就是 Holter。受检者身上贴两到三个电极,便携记录仪挂在腰间,记录一整天的心电。这种记录方式的特点是自由活动带来的噪声:走路时的肌电、翻身时的电极位移、出汗导致的阻抗变化,全都会进到信号里。
这也正是它的价值所在。实验室里的心电信号是躺着不动录的,噪声可控;Holter 信号是生活状态录的,噪声不可控。检测算法在后者上的表现,才是它上线后真正会遇到的表现。库里的信号质量标记,恰好给这些噪声时段留了索引——标注者认为某处信号质量发生显著变化时,会在那个位置打一个波浪号。
3.4 数据来源的三个层次
| 层次 | 内容 | 是否公开 | 可信度 |
|---|---|---|---|
| 信号层 | 128 Hz 数字化后的两路或三路电压 | 公开,完整 | 高,可用校验和验证 |
| 标注层 | 心搏位置与类型、信号质量变化点 | 公开,完整 | 中,人工复核但无一致性报告 |
| 元数据层 | 年龄、性别、记录时间戳 | 部分公开,只有两行 | 中,无诊断、无用药、无随访 |
三层的信息量依次递减。信号层是完整的、可验证的;标注层是人工产物,质量取决于当时的复核流程;元数据层几乎为空。很多使用者在读完官方页面后会误以为这是一个「信息齐全的小库」,实际上它只有信号和标注齐全,临床上下文基本为零。
3.5 缺失的临床上下文值多少
对算法研究来说,诊断、用药、预后这三类信息通常用不上——检测器只关心波形与真值。但对另外两类工作,它们的缺失是硬伤。
第一类是临床相关性研究。想问「这个库里的室性异位负荷与年龄是否相关」,只有七组年龄、六男一女,连分组都做不了。想问「用药是否影响心率」,连用药字段都没有。这类问题必须换到带电子病历的库,比如 MIMIC 系的波形库。
第二类是弱监督与多任务学习。近几年的做法是把诊断文本当作弱标签,让模型同时学波形与报告。ltdb 没有报告、没有诊断、没有节律描述,这条路直接走不通。它的元数据只有两行,连最基本的「这条记录属于哪一类病人」都答不上来。
所以它的定位应该说得更准确一点:它是一个纯信号加逐搏真值的库,而不是一个临床数据集。把它当临床数据集用,会在第一步就卡住;把它当纯信号的基准用,它的完整度反而比很多新库更高,因为新库常常有大量字段是空的或不可靠的。
§4 它最独特的东西:128 Hz 下的一整天
4.1 为什么这个组合少见
长时程与低采样率的组合,在今天反而变少了。新采的长时程记录普遍用 250 Hz 甚至更高,因为存储便宜了;老记录的 128 Hz 是当年磁带容量与数字化成本的折中。结果是:想要「原生低采样 + 原生长时程」的公开数据,可选的范围很窄,ltdb 与 ltafdb 是其中最主要的两处。
这个组合对应一类具体的工程问题:可穿戴设备的功耗预算。采样率减半,模数转换与无线传输的功耗都跟着降,但 R 峰定位的精度也会降。想量化这个折中的代价,最干净的做法是拿一条原生 128 Hz 的长时程记录当基准,再把它降采样到 64 Hz、32 Hz,看检测器在每一档上的表现如何衰减。用 250 Hz 的记录降采样也能做同样的事,但那多了一层重采样滤波的影响,结论不再纯粹。
4.2 标注只有两层:心搏与信号质量
官方在 ANNOTATORS 里把注释文件的内容写成一句话:参考的心搏标注与信号质量标注。逐个读取七条记录的注释后可以确认,符号集合就是 N、V、F、J、S、a 六类心搏,外加波浪号这一类质量标记;辅助注释字符串全部为空。
| 标注层次 | ltdb 是否提供 | 对应任务 |
|---|---|---|
| 心搏位置 | 提供 | 检测、R 峰定位、心率变异性 |
| 心搏类型 | 提供,六类为主 | 心搏分类、异位搏动筛查 |
| 信号质量变化 | 提供,波浪号标记 | 噪声区段定位、质量感知建模 |
| 节律段(如房颤、窦性停搏) | 不提供 | 节律分类、事件检测 |
| ST 段偏移与缺血事件 | 不提供 | 缺血检测、动态 ST 分析 |
| 逐段诊断文本 | 不提供 | 弱监督、多任务学习 |
这张表可以直接当决策依据用:需求落在前三行,它够;落在后三行,它不够。很多人拿到库之后先写模型再想标签,结果在节律任务上折腾很久才发现标注里根本没有这一层。
4.3 案例:15814 这一条为什么特殊
七条记录里,15814 是唯一的三导联记录,也是唯一的 310 存储格式。它的头文件第一行写三个信号,每个信号行结尾是 310 0 10——三路 10 位量化,与其余六条的 212 0 12 不同。这个差异会直接打穿一批粗糙的读取脚本。
第一层影响在读文件。按 212 格式写的解析器假定每三个字节装两个 12 位采样点,遇到三路 10 位打包就会读出乱码,而且不会抛异常,只会得到一条幅度离谱的波形。用 wfdb 这类成熟库不会出问题,因为它按头文件里的格式码分派解析器;自己写二进制解析的人则必须按格式码分支。
第二层影响在内存布局。三导联记录的数组形状是「采样点 × 3」,其余是「采样点 × 2」。任何把七条记录 np.concatenate 成大矩阵的做法都会在这一条上失败,或者更糟——静默地把三列压成两列。
第三层影响在统计口径。15814 的受检者是 58 岁男性,记录时长 22.22 小时,注释约十万条。它单独构成了「三导联」这个子集,而这个子集只有一个样本。任何关于「三导联比双导联好多少」的结论,在这个库上都无法成立,因为对照组只有一条记录。
§4.3 的案例在后续章节还会出现:§5.3 会用它说明流式读取时的形状处理,§7.3 会用它说明「子集方差」这类偏差。它的意义不在于它多特殊,而在于它证明了「七条记录里就有一条不符合默认假设」,而这个比例在真实数据里从来不低。
4.4 人工复核意味着什么
官方页面用的词是 manually reviewed beat annotations,即经人工复核的心搏标注。这句话包含的信息比看起来多:标注不是纯自动生成的,有人看过;但也没有说明复核了几遍、几个人、一致性如何。
对比一下同族的其他库就能看出差别。ltstdb 的文档里明确写了标注由有经验的心电技师完成,并有复核流程;incartdb 明确写了自动初标加人工修正,且位置未逐条校正。ltdb 只有「人工复核」四个字,没有更多。
这对使用者的实际影响是:可以假定心搏类型大体可靠,但不能假定位置精确到采样点,也不能假定七条记录的复核标准完全一致。做 RR 间期类分析时,建议先用自己的检测器重新定位 R 峰,再把官方标注当作类型参考而不是时刻真值。
4.5 边界条件:什么情况下它不再适用
第一,需要节律标签的任务。没有就是没有,构造出来的标签必须自己承担可复现性的责任。
第二,需要人群统计的任务。七组年龄,六个男性,全部来自 1990 年代初的波士顿。任何「在一般人群中如何」的表述都超出了它的证据范围。
第三,需要高时间分辨率的任务。128 Hz 决定了 7.8 毫秒的量化台阶,QRS 宽度这类几十毫秒量级的测量会带上可观的相对误差。
第四,需要训练数据的任务。七条记录不足以支撑任何带参数的模型,除非做的是少样本或元学习,而那类研究本身也需要更大的元训练集。
§5 怎么拿到:两百兆之内的完整闭环
5.1 目录结构与下载面
整个库是一个平铺目录,没有子文件夹。七个记录各三个文件,加上七个旧版头文件、七个会话文件,再加快照清单与校验和,一共不到四十个文件。官方页面提供三种获取方式:网页逐文件下载、整库 ZIP、以及从开放存储桶同步。
| 路径 | 内容 | 大小 |
|---|---|---|
/files/ltdb/1.0.0/RECORDS |
七行记录名 | 42 字节 |
/files/ltdb/1.0.0/ANNOTATORS |
一行注释角色说明 | 50 字节 |
/files/ltdb/1.0.0/SHA256SUMS.txt |
逐文件校验和 | 2.7 KB |
/files/ltdb/1.0.0/<记录>.hea |
头文件 | 约 120 字节 |
/files/ltdb/1.0.0/<记录>.dat |
信号本体 | 18 至 39 MB |
/files/ltdb/1.0.0/<记录>.atr |
注释 | 97 至 290 KB |
/files/ltdb/1.0.0/<记录>.hea- |
2012 年前的旧头文件 | 约 150 字节 |
/files/ltdb/1.0.0/<记录>.xws |
WaveBox 会话文件 | 90 字节 |
5.2 批量下载
#-- 整库拉取,保留目录结构;两百兆,普通带宽几分钟
wget -r -N -c -np https://physionet.org/files/ltdb/1.0.0/
#-- 或者只取记录清单与头文件,先看清结构再决定下哪些
for f in RECORDS ANNOTATORS; do
curl -sL "https://physionet.org/files/ltdb/1.0.0/$f" -O
done
for r in $(cat RECORDS); do
curl -sL "https://physionet.org/files/ltdb/1.0.0/$r.hea" -O
done
#-- 下完核对完整性,避免半截文件
sha256sum -c SHA256SUMS.txt
第三条命令会逐个文件比对校验和。整库只有两百兆,这个检查几乎不花时间,却能挡住最常见的失败模式:.dat 下到一半连接断了,文件大小看起来正常,读出来的信号尾部全是零。
5.3 用 wfdb 直接流式读取
不想落盘时,可以让 wfdb 直接从 PhysioNet 读取。它对 .dat 是按需拉取的,取一段就只下一段:
import wfdb
#-- pn_dir 指向 PhysioNet 上的库名,无需本地副本
rec = wfdb.rdrecord('15814', pn_dir='ltdb', sampto=128 * 600)
print(rec.sig_name, rec.p_signal.shape, rec.fs)
#-- ['ECG1', 'ECG2', 'ECG3'] (76800, 3) 128
注意这里的形状是「采样点 × 3」,与其余六条记录的「采样点 × 2」不同。写批处理循环时,正确做法是按记录逐条处理并在末尾拼接统计结果,而不是先把所有记录堆成一个数组。§4.3 的案例说的就是这件事:形状不一致在七条记录里出现了一次,比例是七分之一。
5.4 转成通用的表结构
把 WFDB 转成 Parquet 或 NumPy 归档是常见需求,转换时最容易丢的是三样东西:增益、采样率、注释类型码的原始字符。
import wfdb
import numpy as np
import pandas as pd
rows = []
for name in wfdb.get_record_list('ltdb'):
rec = wfdb.rdrecord(name, pn_dir='ltdb')
for i, lead in enumerate(rec.sig_name):
rows.append({
'record': name,
'lead': lead,
'fs': rec.fs,
'n_samples': rec.sig_len,
'gain': rec.adc_gain[i],
'units': rec.units[i],
'v_min': float(rec.p_signal[:, i].min()),
'v_max': float(rec.p_signal[:, i].max()),
})
df = pd.DataFrame(rows)
print(df.groupby('record').size())
df.to_parquet('ltdb_index.parquet', index=False)
这段代码只导出索引,不导出波形——波形留在 .dat 里,需要时按段读取。这样做的好处是索引只有几十行,可以随项目一起版本管理,而信号本体不必进仓库。
5.5 心搏注释的读取与 AAMI 映射
把六类符号映射到 AAMI 五分类是心电算法的通行做法,映射表本身不长,但要注意符号的边界:
import wfdb
AAMI = {'N': 'N', 'L': 'N', 'R': 'N', 'B': 'N', 'A': 'S', 'a': 'S',
'J': 'S', 'S': 'S', 'V': 'V', 'r': 'V', 'F': 'F', 'f': 'F'}
for name in wfdb.get_record_list('ltdb'):
ann = wfdb.rdann(name, 'atr', pn_dir='ltdb')
beats = [(s, AAMI.get(sym, 'Q')) for s, sym in zip(ann.sample, ann.symbol)]
quality = sum(1 for sym in ann.symbol if sym == '~')
print(name, len(beats), quality)
映射里有一个坑:波浪号不是心搏,若把它一起映射进 Q 类,会把信号质量标记当成无法分类的心搏,多算一批样本。上面的代码把 ~ 单独计数,正是为了避免这件事。另一个坑是 a 与 A 是两个不同的码,前者是差异性房性早搏,后者是房性早搏,二者都归入 S 类,但混用时统计会失真。
5.6 五个最容易出错的地方
| 出错点 | 表现 | 处理 |
|---|---|---|
| 假设所有记录都是双导联 | 15814 读成两列,第三路丢失 | 按头文件信号条数分支 |
| 假设格式码都是 212 | 310 记录被解析成乱码 | 按 rec.fmt 分派解析器 |
| 直接拿增益字段做除数 | 增益写 0,除零或全零 | 0 按缺省 200 处理 |
| 把波浪号当心搏 | 注释总数虚高,Q 类混入非心搏 | 先按符号过滤再映射 |
用 d_signal 当毫伏 |
数值差两百倍,不报错 | 显式用 p_signal |
这五条都不是理论问题,而是初次接触这个库时最常见的失败点。它们的共同特征是「不抛异常」——脚本能跑完,结果看着也像样,只是数字错了。
5.7 本地缓存与远程读取的取舍
wfdb 支持两种拿到数据的方式:远程直读与本地缓存。远程直读的写法最省事,pn_dir='ltdb' 一行就能取到;它的代价是每次读取都会重新建立连接,训练循环里逐样本调用会把时间全花在网络上。本地缓存的代价是占盘,但整库只有两百兆,这点代价可以忽略。
| 方式 | 适合场景 | 代价 | 注意 |
|---|---|---|---|
| 远程直读 | 探索性分析、一次性核对结构 | 每次读取要建连接,慢 | 断网即失败,不适合长跑 |
| 整库下载 | 训练、批量评估、离线环境 | 占盘两百兆 | 下完必须校验 |
| 按记录下载 | 只关心其中几条 | 需要自己拼列表 | 容易漏掉 .atr |
| 存储桶同步 | 需要与上游保持同步 | 需要对象存储命令行 | 路径前缀固定 |
一个实用的折中做法是:先用远程直读把七条记录的头文件全部读一遍,确认结构,再把整库一次下到本地,之后所有批量任务都在本地跑。这样既避免了反复下载,也不会在还没弄清结构时先花时间下数据。
还有一个细节值得注意:pn_dir 的参数可以直接写库名,wfdb 会自动解析到最新版本;如果结果需要长期复现,建议写成带版本号的路径,避免上游更新后数据悄悄变了。本库自 1999 年以来只有头文件在 2012 年改过一次,风险很小,但这个习惯对同族的其他库是必要的。
§6 与库内条目的关系
6.1 关系图谱
| 关系 | 库 | 连接点 | 差异 |
|---|---|---|---|
| 同机构同期 | ltstdb | 同一家医院、同一套格式、同一批磁带来源 | 86 条记录、250 Hz、另有 ST 与节律标注 |
| 同格式同注释码 | mitdb | WFDB 格式与 MIT-BIH 注释码完全一致 | 48 段半小时、双导联、采样率 360 Hz |
| 同为长时程房颤 | ltafdb | 都是 128 Hz、二十余小时 | 84 条、有节律标注、专为房颤设计 |
| 同为长时程房颤 | afdb | 都是双导联 Holter | 25 条、每条 10 小时、250 Hz、有节律标注 |
| 同为多导联短段 | incartdb | 都可用于心搏分类 | 12 导联、每段 30 分钟、257 Hz |
| 同为低采样率 | ecgrdvq | 采样率低、记录较长 | 侧重 QT 与药物复极,标注体系不同 |
6.2 检索路径
在站内找同族库,最直接的办法是按标签与关键词检索,而不是按名称。名称里的 long-term、LT、Holter 三种写法混用,按名称匹配必然漏。
#-- 按关键词粗筛库内词条,注意 long-term 与 holter 是两种写法
bash scripts/qf_psql.sh "select record_id, url_name, left(title,60)
from ai_ready_dataset
where url_name ilike '%lt%' or title ilike '%长时程%' or title ilike '%Holter%'
order by record_id;"
import wfdb
#-- 按官方清单确认本库的七个记录名,避免凭记忆写错
print(wfdb.get_record_list('ltdb'))
#-- ['14046', '14134', '14149', '14157', '14172', '14184', '15814']
检索时的一个经验:PhysioNet 上叫 long-term 的库至少有四个(ltdb、ltstdb、ltafdb,以及记录时长同样很长的挑战赛数据),它们的区别不在名字,而在标注层。判断一个库能不能做你的任务,先看 ANNOTATORS 那一行写了什么,比看简介快得多。
6.3 与其他长时程库的分工判据
| 你的任务 | 首选 | 次选 | 判据 |
|---|---|---|---|
| 长时程心搏检测的快速验证 | ltdb | incartdb | 体量最小、全流程最快 |
| 低采样率鲁棒性 | ltdb | ltafdb | 原生 128 Hz,双导联 |
| 房颤负荷量化 | ltafdb | afdb | 有节律标注,24 小时级 |
| 心肌缺血事件检测 | ltstdb | 无 | 只有它标了 ST 事件 |
| 十二导联形态学 | incartdb | 无 | 只有它给满十二导联 |
| 跨库外部验证 | 任一异源库 | — | 关键在来源不同,不在规模 |
6.4 三张库的规模差背后是什么
同族长时程库里,三个数字最容易被拿来比:七条、八十六条、八十四条。差别不在采集能力,而在各自要回答的问题。
ltdb 的七条是最早的一次尝试,目标是证明「长时程逐搏标注」这件事在工程上可行。它不需要多,只需要完整——完整到能跑通从信号到评估的每一步。ltstdb 的八十六条是为了做缺血检测器的验证:缺血事件稀疏,需要有足够多的记录才可能积累出足够的事件数,所以规模必须上去,采样率也必须提到 250 Hz 才能测 ST 段的毫伏级偏移。ltafdb 的八十四条是为了房颤负荷:阵发性房颤的发作本身就是稀疏事件,同样需要长时程与足够多的受试者。
| 库 | 规模动因 | 由此决定的采样率 | 由此决定的标注层 |
|---|---|---|---|
| ltdb | 证明流程可行 | 128 Hz,够定位 R 峰即可 | 心搏与质量两层 |
| ltstdb | 积累缺血事件 | 250 Hz,需测 ST 偏移 | 四层,含 ST 与节律 |
| ltafdb | 积累房颤发作 | 128 Hz,节律判定不依赖高采样 | 心搏与节律两层 |
这张表解释了一个常被忽略的因果:采样率不是随便定的,而是被任务倒推出来的。理解这一点,就不会拿 ltdb 去做 ST 分析,也不会抱怨它为什么没有 250 Hz。
6.5 什么时候该放弃它
有三种情形,越早放弃越省时间。第一种是任务需要节律或 ST 标签,这在前面已经说过。第二种是需要统计显著性——七条记录做不出置信区间,硬做只会得到看似严谨实则无意义的数字。第三种是需要与临床变量联动,因为它没有临床变量。
反过来,有三种情形它几乎是最优解:需要在半天内跑通一条完整长时程链路;需要验证低采样率对检测的影响;需要一个与训练库完全不同源的小型外部集。前两种是工程需求,第三种是方法学需求,三者都不依赖规模。
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
先纠正一个容易发生的误判:把「小」直接读成「差」。七条记录在规模维度上确实拿不到分,但这不等于它在自己的用途上也差。规模小是这个库的设计前提,不是执行失误——1999 年的存储与带宽决定了它只能这么大,而它的用途从第一天起就是「完整流程的最小可用集」,不是「训练语料」。
第二个需要校正的误判是「老」。1999 年的标注流程放到今天看确实粗糙,没有一致性系数、没有双人复核记录、没有仲裁机制。但它的信号是真实的临床长时程记录,这一点不会因为年代久远而失效。评价它的时候,尺度应该是「在它能回答的问题上,证据够不够硬」,而不是「它比新库少了哪些字段」。
第三个小一点的误判是「只有七条所以不能做统计」。七条记录当然不能做人群推断,但可以做逐条的方法对比——同一批记录上比较两个检测器,结论是有效的,只是不能外推到人群。这两件事经常被混为一谈。
7.1 五维评分
按 DAIMS 的常用维度打分,每项 1 至 5 分,针对「作为长时程心电算法研究数据」这个用途:
| 维度 | 分数 | 理由 |
|---|---|---|
| 数据规模 | 1 | 七条记录,最多七人,无法支撑训练 |
| 标注质量 | 3 | 经人工复核,但无一致性报告,位置精度未知 |
| 任务适配 | 3 | 心搏检测与质量感知适配好,节律与缺血完全不适配 |
| 文档完整 | 2 | 官方页面只有一段简介,标注协议、导联、人群都未说明 |
| 可获取性 | 5 | 免注册、两百兆、无依赖,一次下完 |
五项合计 14 分(满分 25)。这个分布很有辨识度:可获取性拉满,规模与文档垫底,标注与适配居中。它说明这是一个「拿来就能用、但用完别多想」的库。
7.2 标注质量的两层
第一层是类型正确性。六类心搏码都是 MIT-BIH 标准码,含义明确,映射到 AAMI 五分类的路径也是通的。人工复核这一环保证了明显的错标不会太多,这一层可以打到中上。
第二层是位置精度与覆盖完整性。官方没有说明复核是否逐搏、是否校正了 R 峰位置,也没说明质量标记的判定标准。逐条读取注释后还能看到一处覆盖缺口:14149 的最后一条注释落在 23.19 小时,而信号总长 23.87 小时,末尾约四十分钟没有任何标注。这四十分钟是信号丢失、还是标注者提前收工,官方没有说明。
| 用途 | 类型层够不够 | 位置层够不够 | 建议 |
|---|---|---|---|
| 心搏检测 | 够 | 够 | 可直接当参考 |
| 心搏分类 | 够 | 够 | 注意类别极不均衡 |
| 心率变异性 | 够 | 勉强 | 建议自检测重定位 |
| QRS 宽度测量 | 勉强 | 不够 | 128 Hz 误差过大 |
| 噪声鲁棒性 | 够 | 不适用 | 用质量标记切段 |
7.3 已知的、可枚举的偏差
| 偏差 | 表现 | 影响 |
|---|---|---|
| 规模偏差 | 七条记录,最多七人 | 任何模型都会过拟合,结论不可外推 |
| 性别偏差 | 六男一女 | 女性样本只有一条,性别相关分析不可做 |
| 年龄偏差 | 46 至 88 岁,无年轻人 | 不代表一般人群,尤其不代表青年 |
| 地域与年代偏差 | 1990 年代初波士顿单中心 | 设备、贴电极习惯、判读标准都单一 |
| 采样率偏差 | 128 Hz | 形态学指标精度受限 |
| 导联语义缺失 | 只写 ECG1 至 ECG3 | 跨库对齐时导联错配无法排除 |
| 标注覆盖缺口 | 14149 末尾约四十分钟无标注 | 该段不能计入任何逐搏统计 |
| 官方口径不一致 | 官方称 14 至 22 小时,头文件换算 13.9 至 23.9 小时 | 规划存储与切片时以头文件为准 |
| 子集方差 | 三导联子集只有一条记录 | 「三导联 vs 双导联」类结论不成立 |
关于官方口径的那一条,需要说清两个数各自的来处:官方简介写的是每条 14 到 22 小时,而头文件里的采样点数除以 128 给出了 13.9 到 23.9 小时的范围,其中 14046 与 14149 都超过了二十二小时。两个口径的差额构成,官方没有解释。用的时候按头文件算,因为下游切片、分窗、批大小都依赖采样点数。
7.4 外部验证矩阵
| 验证角色 | 做法 | 讲得通的地方 | 风险 |
|---|---|---|---|
| 跨库心搏分类 | 在大库训练,本库零样本测试 | 注释码同源,映射直接 | 掉分里混了人群、设备、采样率三重差异 |
| 低采样率对照 | 本库原生 128 Hz,与 250 Hz 库对照 | 变量单一,结论干净 | 两个库的人群本来就不同 |
| 噪声鲁棒性 | 按质量标记切出噪声段单独评估 | 质量标记是现成的索引 | 标记稀疏,切出的段可能很少 |
| 少样本学习 | 用七条记录做 few-shot 目标域 | 域差异真实 | 七条不足以做统计检验 |
| 教学与演示 | 单条完整记录走通全流程 | 体量小,学生能一次跑完 | 结论不能当研究成果 |
跨库外测是它最常被引用的用法。这类研究的基本逻辑是:在一个数据量大、来源单一的库上训练,换到这个来自另一家医院、另一套设备的库上测试,掉多少分就代表泛化损失多少。ltdb 在这个位置上有一个天然优势——它的采样率是 128 Hz,比多数训练库低,任何忽略采样率差异的模型都会在这里暴露。
反过来,也要防止把外测成绩当绝对标准。七条记录的样本量太小,一次划分的偶然性足以颠倒两个模型的排名。合理的做法是固定记录集合、固定预处理、只改模型,在同一份数据上比较相对差异。
7.5 它为何能持续:最小的完整样本
它二十多年没有更新,却仍在被下载与引用,原因是它占着一个不会被新库挤掉的位置:能在一个下午走完全流程的最小长时程集。新库可以更大、更干净、标注更细,但只要有人需要「先跑通再说」,两百兆的七条记录就仍然是最省事的选择。这个位置不靠规模支撑,靠的是体量与完整度的比值。
§8 生态位与历史
8.1 历史沿革
1999 年上线时,PhysioNet 上的心电库还以半小时段为主,MIT-BIH 心律失常库是绝对的核心。ltdb 的加入第一次把「整天」这个尺度带进了公开数据集。它的技术底座——WFDB 格式、MIT-BIH 注释码、开放下载——后来被同族的所有库沿用,包括一年后上线的 ltstdb。
这个先后顺序值得记住,因为它解释了很多细节的由来。ltdb 的头文件信息极少,是因为当年的元数据规范还没成形;它的注释只有心搏与质量两层,是因为当时的关注点就是「检测器能不能在长时程里稳定工作」,节律与缺血的分析需求是后来才被提出来的。
8.2 与 ST 库的关系:同一批磁带,不同的野心
ltstdb 于 2000 年 11 月上线,比 ltdb 晚一年,同样是 MIT 与波士顿贝斯以色列医院的合作产物。它把标注扩到了四层:逐搏、ST 事件、节律变化、信号质量,每条记录 21 至 24 小时,带 2 或 3 路信号,采样率 250 Hz,最终扩到 86 条。2003 年先开放一半,2007 年补齐全部。
| 维度 | ltdb | ltstdb |
|---|---|---|
| 上线时间 | 1999-08-03 | 2000-11-29 |
| 记录数 | 7 | 86 |
| 每条时长 | 14–24 小时 | 21–24 小时 |
| 采样率 | 128 Hz | 250 Hz |
| 逐搏标注 | 有 | 有 |
| ST 事件标注 | 无 | 有 |
| 节律变化标注 | 无 | 有 |
| 信号质量标注 | 有 | 有 |
| 体量 | 205.4 MB | 数 GB |
两个库的关系常被误解成「旧版与新版」。官方的记录名体系完全不同(ltdb 是五位数字,ltstdb 是另一套编号),也没有任何文档说二者共享受试者。更准确的说法是:它们出自同一家医院的不同时期存档,目标不同——一个证明长时程心搏标注可行,一个把缺血事件也纳入标注。
8.3 生态位:入门件而不是主力件
| 角色 | 主力件 | 入门件 |
|---|---|---|
| 典型用途 | 训练、预训练、大规模评测 | 流程验证、教学、快速基线 |
| 需要的前提 | 数据量大、标签细 | 体量小、格式标准、一次下完 |
| 代表库 | mitdb、ltafdb、mimic 系心电 | ltdb、incartdb |
| 失败模式 | 过拟合、标签噪声累积 | 结论不可外推、统计不显著 |
ltdb 坐在右边这一列。把它当主力件用,会在第一轮就过拟合;把它当入门件用,几乎没有替代品——比它更小的库通常不够长,比它更长的库通常要大一个数量级。
8.4 谁在用它,用来做什么
从公开文献的摘要看,使用者集中在三类。第一类是信号处理组,用它验证检测器在长时程、低采样率条件下的稳定性,常与 250 Hz 的库做对照。第二类是可穿戴与嵌入式组,用它做功耗与精度的折中实验,因为原生 128 Hz 正好对应低功耗设备的采样档位。第三类是教学,用它做「一条完整记录从读到用」的演示,因为它小到可以让学生人手一份。
这三类用法有一个共同点:都不指望它提供统计结论,只指望它提供完整链路。这个定位一旦清楚,它的所有局限就都变得可接受。
§9 使用指引
9.1 三种角色的路径
| 角色 | 第一天 | 第一周 | 注意事项 |
|---|---|---|---|
| 算法研究者 | 读官方页面与七个头文件,跑通 §5.3 的读取 | 建立逐记录基线,做 128/64/32 Hz 三档对照 | 先确认注释里没有节律层(§4.2) |
| 设备工程师 | 抽一条记录做端到端推理,测每小时的误检 | 用质量标记切噪声段,单独报告该段指标 | 不要假设 ECG1 是 II 导联(§2.6) |
| 教学与临床工程 | 用 14046 做一次完整演示 | 让学生自己写解析器,体会格式码分支 | 14149 末尾有约四十分钟空档 |
9.2 一条最短的可用流程
#-- 1. 取清单与头文件,先看结构
curl -sL https://physionet.org/files/ltdb/1.0.0/RECORDS -o RECORDS
curl -sL https://physionet.org/files/ltdb/1.0.0/ANNOTATORS -o ANNOTATORS
cat ANNOTATORS
#-- 2. 只下第一条记录,验证读取链路
for ext in hea dat atr; do
curl -sL "https://physionet.org/files/ltdb/1.0.0/14046.$ext" -O
done
#-- 3. 核对采样点数与时长
awk 'NR==1{printf "%s %.2f h\n", $1, $4/$3/3600}' 14046.hea
import wfdb
#-- 读信号与注释,确认没有节律层
rec = wfdb.rdrecord('14046')
ann = wfdb.rdann('14046', 'atr')
print(rec.fs, rec.sig_name, rec.p_signal.shape)
print(sorted(set(ann.symbol)), sum(1 for a in ann.aux_note if a.strip()))
跑完这两段,就已经走完了「下载、读取、确认结构」三步。剩下的工作是把同一套代码套到其余六条记录上,并在 15814 那一条上处理三导联分支。
9.3 引用方式
| 引用对象 | 内容 | 说明 |
|---|---|---|
| 数据集 | MIT-BIH Long-Term ECG Database, version 1.0.0. PhysioNet. 1999. | 必须带版本号 |
| 标识符 | DOI 10.13026/C2KS3F | 指向 v1.0.0 |
| 平台 | PhysioNet 平台综述论文,按页面 Cite 按钮生成 | 官方页面唯一给出的引用 |
| 许可 | ODC-BY 1.0,署名即可,含商业用途 | 以许可全文为准 |
需要特别说明:官方页面没有为本库提供专属的论文引用,只有平台层面的标准引用。这意味着写论文时,引用列表里出现的是数据集著录加平台论文,而不是一篇描述该库的方法学文章。这在同族库里并不罕见,但对希望引用「原始采集方法」的人来说是个缺口。
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 文本生成 | 本文由 AI 辅助撰写,结构与事实由编辑把关 |
| 数字来源 | 规模、结构、边界三类数字来自 PhysioNet 官方页面与库内文件 |
| 文件核验 | 七个头文件、七个注释文件、RECORDS、ANNOTATORS 均经直接访问确认 |
| 统计口径 | 时长由采样点数除以 128 换算,注释总量为逐文件读取后相加 |
| 未采用 | 未做标签频次排序、未做长度分布直方图、未做字段空值交叉表 |
| 论文信息 | 仅检索 Europe PMC 摘要,未读全文 |
| 未核验内容 | 标注者人数与一致性、导联对应的体表位置、14149 末尾空档的成因 |
| 免责 | 本文不构成临床建议;使用须遵守 ODC-BY 1.0 署名要求 |
常见误读(避坑清单)
| 说法 | 实际 | 证据 |
|---|---|---|
| 「ltdb 就是 ltstdb 的旧名」 | 是两个独立的库,记录名体系与标注层都不同 | 官方两个页面并存,DOI 不同 |
| 「它有 ST 事件标注」 | 没有,只有心搏与信号质量 | ANNOTATORS 只写 beat 与 signal quality |
| 「它是二十四小时双导联」 | 六条双导联,一条三导联;时长为 14 至 24 小时 | 15814.hea 有三个信号行 |
| 「它的采样率是 250 Hz」 | 128 Hz | 每个头文件第一行第三字段 |
| 「头文件增益是 0,说明数据没标定」 | 0 是缺省值,实际按每毫伏 200 单位 | WFDB 规范;adc_gain 读出 200 |
| 「ECG1 就是 II 导联」 | 官方从未说明导联位置 | 头文件只写 ECG1 至 ECG3 |
| 「七条记录来自七个人」 | 官方未公布受试者数,也没给映射 | 官方简介只写七条记录 |
| 「注释总数就是心搏数」 | 里面混着信号质量标记 | 符号集合含波浪号 |
| 「可以直接拿它训练分类器」 | 七条记录,一两轮即过拟合 | 记录清单仅七行 |
| 「官方说的时长就是头文件时长」 | 官方 14 至 22 小时,头文件 13.9 至 23.9 小时 | 采样点数除以 128 的换算结果 |
FAQ
Q1:ltdb 和 ltstdb 到底差在哪?
差在记录数、采样率与标注层。ltdb 七条、128 Hz、只标心搏与信号质量;ltstdb 八十六条、250 Hz、另标 ST 事件与节律变化。两个库都出自波士顿同一家医院,但记录名体系不同,官方未说明受试者是否重叠。
Q2:它能做房颤检测吗?
不能直接做。注释里没有节律段,只有心搏类型与质量标记。要做房颤,只能从 RR 间期序列间接构造标签,而构造规则必须写清楚,否则结论不可复现。
Q3:为什么只有七条记录还能一直有人用?
因为它是最小的完整长时程样本:下载快、格式标准、注释齐全到心搏级。它适合验证流程与做对照,不适合训练与统计推断。
Q4:头文件里的增益写 0,是不是数据坏了?
不是。WFDB 规定增益字段为 0 时按缺省 200 处理。成熟库读出来就是 200,自己写解析器的人直接拿 0 做除数才会出错。
Q5:七条记录是七个人吗?
官方没有公布受试者数,也没有记录与人的映射。头文件给出七组年龄与性别,数值互不相同,但这不足以证明一一对应。做统计时应把记录当独立单位,并在文中声明。
Q6:ECG1、ECG2 对应哪两个导联?
官方未说明。2012 年之前的头文件甚至只写「signal 0」「signal 1」。任何把 ECG1 当作 II 导联的说法都是外部约定,不是官方信息。
Q7:标注准确吗?
心搏类型的可靠性较高,因为经人工复核;位置精度与覆盖完整性未知,官方没有一致性报告。14149 末尾还有约四十分钟没有标注,那段不能计入逐搏统计。
Q8:128 Hz 够用吗?
对 R 峰检测够用,对形态学测量不够。7.8 毫秒的采样间隔会让 QRS 宽度、QT 间期这类指标带上明显误差。要做形态学,换采样率更高的库。
Q9:能一次下完吗?要注册吗?
能,未压缩两百零五兆,无需注册,ODC-BY 1.0 直接下载。官方提供整库压缩包、逐文件下载与存储桶同步三种方式。
Q10:和 MIT-BIH 心律失常库是什么关系?
格式与注释码同源,都是 WFDB 加 MIT-BIH 标准码,但记录数、时长、采样率、人群都不同。它常被当作该库训练出的模型的外部测试集。
Q11:为什么官方说的时长和头文件对不上?
官方简介写 14 至 22 小时,头文件换算给出 13.9 至 23.9 小时,其中两条超过二十二小时。差额的构成官方没有解释,下游规划应以头文件为准。
术语表
| 术语 | 解释 |
|---|---|
| Holter | 便携式长时程心电监护,受检者随身携带记录仪 |
| ltdb | MIT-BIH Long-Term ECG Database 的官方缩写 |
| ltstdb | Long-Term ST Database,同机构带 ST 事件标注的库 |
| WFDB | PhysioNet 使用的波形数据库格式 |
.hea |
纯文本头文件,声明采样率、增益、信号名与患者信息 |
.dat |
二进制信号文件,按格式码打包 |
.atr |
二进制注释文件,存事件位置与类型 |
.hea- |
2012 年改写前的旧版头文件,保留在目录中 |
.xws |
WaveBox 网页工具的会话文件,仅三行 |
| 格式码 212 | 双信号 12 位,两个采样点压进三个字节 |
| 格式码 310 | 三信号 10 位,三个采样点压进三个字节 |
| 增益 | 每毫伏对应的整数单位数,0 表示缺省 200 |
| 基线 | 零电平对应的整数值,换算时先减 |
| 校验和 | 采样点累加和,用于检测文件完整性 |
| 逐搏标注 | 对每一次心搏标出位置与类型 |
| 信号质量标记 | 波浪号,表示该处信号质量发生变化 |
| AAMI 五分类 | N、S、V、F、Q 五组,心电算法评价的通行分组 |
| 房颤 | 心房电活动紊乱,心室率绝对不规则 |
| ST 事件 | 心电复极段偏移达到判定阈值的持续时段 |
| 导联错配 | 训练与测试用的体表导联位置不同 |
| 患者级划分 | 同一受试者的记录只进训练或只进测试 |
| 跨库测试 | 在一个库训练、另一个库测试,衡量泛化 |
| ODC-BY | 开放数据共享署名许可 |
| PhysioNet | 由 MIT 等机构维护的生物医学信号平台 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 记录数 | 7 | 官方页面;RECORDS 逐行 |
官方页面 |
| 受试者数 | 官方未公布 | 官方页面 | 官方页面 |
| 每条时长 | 14–22 小时 | 官方页面 | 官方页面 |
| 头文件换算时长 | 13.9–23.9 小时 | 七个 .hea 采样点除以 128 |
官方文件 |
| 导联数 | 2 或 3 | 七个 .hea 第一行 |
官方文件 |
| 三导联记录 | 15814 | 15814.hea |
官方文件 |
| 采样率 | 128 Hz | 七个 .hea 第一行 |
官方文件 |
| 量化位宽 | 12 位,三导联记录 10 位 | .hea 信号行 |
官方文件 |
| 增益 | 每毫伏 200 单位 | .hea 增益字段 0 加缺省规则 |
官方文件 |
| 注释总量 | 约六十七万条 | 七个 .atr 逐条读取后相加 |
本次核对 |
| 注释符号 | N、V、F、J、S、a、~ | 七个 .atr 符号集合 |
本次核对 |
| 年龄范围 | 46–88 岁 | 七个 .hea 注释行 |
官方文件 |
| 性别构成 | 六男一女 | 七个 .hea 注释行 |
官方文件 |
| 上限单文件 | 39.1 MB | 目录列表 | 官方页面 |
| 整库体积 | 205.4 MB | 官方页面 | 官方页面 |
| 上线日期 | 1999-08-03 | 官方页面 | 官方页面 |
| 许可 | ODC-BY 1.0 | 官方页面 | 官方页面 |
| DOI | 10.13026/C2KS3F | 官方页面 | 官方页面 |
附录 B:数字的来源分层
第一层:官方页面与官方文件。 记录数、时长区间、导联数、采样率、体积、日期、许可、DOI 来自官方页面;每条记录的采样点数、年龄、性别、格式码来自七个头文件;注释角色来自 ANNOTATORS。这一层的数字可以直接引用。
第二层:本次核对。 逐条注释条数、注释符号集合、标注覆盖的首尾位置、换算得到的逐条时长。这些数字不改变官方结论,只是把官方没展开的部分补上。引用时应写明「根据库内文件统计」,不要写成官方口径。
第三层:未核实。 标注者人数与一致性、导联对应的体表位置、14149 末尾空档的成因、记录与受试者的映射关系。这些官方从未公布,正文中凡涉及处只做定性表述或标注为未知。
附录 C:完整参考文献
| 编号 | 文献 | 链接 | 用途 |
|---|---|---|---|
| 1 | MIT-BIH Long-Term ECG Database v1.0.0 | https://physionet.org/content/ltdb/1.0.0/ | 规模、结构、边界数据的主来源 |
| 2 | 该库文件目录 | https://physionet.org/files/ltdb/1.0.0/ | 七组文件、清单、校验和 |
| 3 | Long-Term ST Database v1.0.0 | https://physionet.org/content/ltstdb/1.0.0/ | §8.2 的对照事实 |
| 4 | Jager F 等,Long-term ST database,MBEC 41(2):172-183,2003 | https://doi.org/10.1007/BF02344885 | ltstdb 的方法学说明 |
| 5 | PhysioNet 平台综述,Nature Health,2026 | https://doi.org/10.1038/s44360-026-00096-z | 托管模式与引用规范 |
| 6 | WFDB 格式与注释码规范 | https://physionet.org/physiotools/wag/ | 格式码、增益缺省规则 |
| 7 | Europe PMC 检索结果 | https://www.ebi.ac.uk/[europepmc](https://www.qianfanghub.com/ai-ready-dataset/europepmc/1353)/ | 外部用法与验证角色 |
| 8 | 库内词条:MIT-BIH 房颤库 | https://physionet.org/content/afdb/ | §2.5 与 §6.1 的对比 |
附录 D:任务代码
任务一:七条记录的完整索引
import wfdb
import pandas as pd
rows = []
for name in wfdb.get_record_list('ltdb'):
h = wfdb.rdheader(name, pn_dir='ltdb')
rows.append({
'record': name,
'leads': h.n_sig,
'fmt': h.fmt[0],
'fs': h.fs,
'samples': h.sig_len,
'hours': round(h.sig_len / h.fs / 3600, 2),
'age': h.comments[0].split('Age:')[1].split()[0],
'sex': h.comments[0].split('Sex:')[1].strip(),
})
print(pd.DataFrame(rows).to_string(index=False))
任务二:降采样后的检测召回对照
import wfdb
import numpy as np
def resample_decimate(x, factor):
#-- 简单抽取,不做抗混叠;用于演示采样率对定位的影响
return x[::factor]
for name in ['14046', '14172']:
rec = wfdb.rdrecord(name, pn_dir='ltdb', sampto=128 * 3600)
ann = wfdb.rdann(name, 'atr', pn_dir='ltdb', sampto=128 * 3600)
truth = set(ann.sample)
for factor in (1, 2, 4):
x = resample_decimate(rec.p_signal[:, 0], factor)
fs = rec.fs // factor
#-- 以极简阈值法示意:超过三倍标准差的局部极值算一次候选
thr = 3 * np.std(x)
cand = np.where(np.abs(x) > thr)[0] * factor
hit = sum(1 for c in cand if min(abs(c - t) for t in truth) <= fs // 4)
print(name, 'factor', factor, 'candidates', len(cand), 'matched', hit)
第二段代码的用途是演示,不是评测:它的候选点没有做不应期抑制,也没有做多导联融合,命中判据用的是四分之一秒的宽松窗。把它跑一遍能看到的是「采样率降低时定位误差如何累积」这个趋势,而不是一个可以写进论文的召回率。
附录 E:如果只记五件事
| 序号 | 记住什么 | 为什么 |
|---|---|---|
| 1 | 七条记录,14–24 小时,128 Hz,双导联为主 | 这四个数字定义了一切用法与限制 |
| 2 | 只有心搏与信号质量两层标注 | 节律与缺血任务直接出局 |
| 3 | 它是入门件,不是主力件 | 用来跑通流程与做对照,不用来训练 |
| 4 | 15814 是三导联 310 格式 | 批处理脚本必须按格式码分支 |
| 5 | 官方时长口径与头文件换算不一致 | 下游规划一律以采样点数为准 |
编辑说明
本篇的核验时间为 2026 年 10 月 28 日。核对方式为直接访问 PhysioNet 项目页与文件目录,逐个确认七个头文件、七个注释文件、RECORDS、ANNOTATORS、SHA256SUMS.txt 的可访问性与内容;注释统计用 wfdb 4.3.1 读取,只用来看符号集合与总量,未做类型频次排序。官方简介的「14 至 22 小时」与头文件换算出的「13.9 至 23.9 小时」并存,两个口径在正文中并列陈述,不做取舍判断。未覆盖的内容包括:标注者人数与一致性、导联对应的体表位置、14149 末尾约四十分钟空档的成因、记录与受试者的映射关系。这些官方从未公布,任何声称掌握的说法都需要另行求证。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- wctecgdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- ahadb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- chfdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- crisdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- cudb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- ltstdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- iafdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- nsr2db — 共享标签:生理信号 / 心电信号 / 心血管疾病
- nsrdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- stdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

