房颤什么时候自己停 — AF Termination Challenge 心电库

80 段一分钟双通道 / 三组终止标签 / 128 Hz

来源 https://physionet.org/content/aftdb/1.0.0/发布时间: 2026-10-28最后更新: 2026-10-28 阅读 16
房颤什么时候自己停 — AF Termination Challenge 心电库

信息速览

数据集名称房颤什么时候自己停 — AF Termination Challenge 心电库
数据类型时序信号,波形,元数据,原始数据链接
规模学习集 20 名受试者;测试集 A 30 名、测试集 B 10 名(三集合受试者互不重叠)
接入方式https://physionet.org/content/aftdb/1.0.0/
AI 就绪度

心电图算法里有一类问题,问的不是「现在是不是房颤」,而是「这场房颤还剩多久」。前者已经被大规模单导联数据集喂得很饱,后者至今没有一个与之相称的公开数据集。原因不难理解:要知道一场房颤什么时候停,就必须守在患者床边一直录,直到它真的停下——而这种等待在临床上既不可控也不便宜。

PhysioNet 在 2004 年用一个很巧的办法绕开了这个成本。它不要求记录覆盖终止的瞬间,而是把一段连续记录切成整分钟,让分钟窗口的末端去逼近终止点:有的窗口之后一分钟房颤才停,有的窗口之后一秒就停,有的窗口之后一小时都还在颤。模型只看到窗内的波形,要判断的是窗外即将发生什么。这就是 AF Termination Challenge Database 的基本构造,也是它二十多年后仍被反复提起的原因。

它的规模很小——80 段一分钟记录,按受试者算学习集只有 20 人;它的标签很粗——只有三组,没有逐条真值,也没有终止时刻标注。它更像一道设计精巧的考题,而不是一座粮仓。若把一个「有干预的房颤终止数据集」的形象套在它身上,从第一个问题开始就会走偏,这一层纠正放在导语里先说清。一屏速览见下表。

维度 内容
全名 AF Termination Challenge Database(也写作 AF Termination Database)
发布方 PhysioNet(MIT Laboratory for Computational Physiology 维护)
原始数据提供方 美国西北大学 Steven Swiryn
首次发布 2004 年 4 月 11 日,版本 1.0.0;此后未修订
记录数 80 段(学习集 30 + 测试集 A 30 + 测试集 B 20)
受试者数 学习集 20 名;测试集 A 30 名;测试集 B 10 名;三集合互不重叠
每段时长 1 分钟(7680 采样点 ÷ 128 Hz)
通道数 2(头文件里两路信号名都写作 ECG)
采样率与存储 128 Hz,16 位整数,增益约 140–202 单位/毫伏
组标签 N 不终止 / S 一分钟后终止 / T 一秒内终止
终止方式 自发终止(spontaneous),不含电复律、不含药物干预
标注内容 仅 QRS 位置,自动检测、未审核、异位搏动一律标为正常
文件格式 WFDB:.hea 头文件 / .dat 信号 / .qrs 标注
整库体积 官方页面写约 3.3 MB(解压后 2.4 MB)
许可 ODC-BY 1.0,免注册
关联赛事 PhysioNet / Computers in Cardiology Challenge 2004
典型用途 终止预测的特征工程基准、节律复杂度的对照实验、教学演示

§1 它要解决的具体麻烦

1.1 房颤终止预测是一个"与时间赛跑"的问题

一名房颤患者被送进急诊,心律不齐、心室率偏快,但循环稳定。接下来医生要做一个判断:是等着看它自己会不会转复,还是现在就上药物或电复律。等下去有自发转复的可能,代价是留观时间与床位;立刻干预有成功率高、可控的好处,代价是镇静风险、血栓栓塞风险和被过度治疗的可能。这个决策在指南里有一整套流程,但其中有一块一直是空的:能不能从眼前这段心电图上,看出它离终止还有多远。

这正是 2004 年那届挑战赛提出的问题。官方公告的原话是「预测一次房颤是否会自发终止、或何时终止」。它把问题拆成了两个层次:第一层是二分类,这场房颤会不会停;第二层是三分类,如果会停,是马上停还是再撑一会儿。两层在数据库里对应两个测试集,也对应两个独立的事件。

要理解这个问题的难点,需要先知道房颤的自发终止不是随机的。电生理研究早就发现,终止前的一段时间里,心房激动频率会发生变化,主导频率漂移、组织化程度上升、体表 f 波振幅与周期性出现渐变。这些变化微弱、个体差异大、被 QRS 与 T 波掩盖,用肉眼几乎看不出来,用算法却有可能抓住。挑战赛给了所有人同一批带标签的样本,让不同思路在同一把尺子上比。

1.2 三条常见的错误期待

第一条,期待它是"复律数据库"。 官方用词从头到尾都是 spontaneous termination,自发终止。库内没有电复律记录、没有抗心律失常药物的给药时刻、没有干预前后的对照。把这三类记录混为一谈,会让标签的含义从"预测自然病程"滑向"评估治疗效果",那是另一类数据该做的事。这一点在常见误读表里排在第一位。

第二条,期待它有"终止瞬间"的标注。 恰恰相反,终止时刻被刻意放在记录窗口之外。s 组的终止点落在记录结束后约一分钟,t 组的终止点落在结束后一秒以内(官方原话是在记录结束后一秒之内终止)。所以没有任何一段记录包含"从房颤突然转成窦性"的那一下。想研究转复瞬间的形态变化,这个库给不了。

第三条,期待它是"房颤检测数据集"。 库内每一段都是房颤,没有窦性心律的对照段,也没有非房颤的其他节律。拿它训练"是/不是房颤"的分类器,正样本率百分之百,模型学不到任何东西,只会输出常数。它的任务粒度是"房颤内部的细分",不是"房颤与正常之间"。

这三条期待的共同根源,是把这个库放进了一个它不负责的赛道上。它的定位更窄也更锋利:在房颤之内,判断终止会不会发生。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
房颤终止预测的特征工程基准 30 段带组标签的学习集加 50 段测试集,同一套预处理 够做小规模对比实验,不足以训练深度模型
研究终止前的心房电活动特征 终止前最长一分钟的双通道波形 够,但只有两个通道,且导联位置未被官方说明
房颤与窦性的二分类 无正常节律对照段 不够,全部记录都是房颤
心搏类型分类 .qrs 标注把异位搏动也标成正常 不够,没有类型信息
复律或药物疗效评估 无干预记录 不够,性质完全不同
长时程房颤负荷 每段只有一分钟 不够,用长时程库
多中心泛化验证 测试集来自不同受试者,但采集机构与设备未分开说明 只能算半边,见 §7.4
教学演示终止预测的三种情形 三组定义清晰、每段一分钟、下载极快 够,而且是最顺手的入门材料之一

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

你的处境 它能帮你什么 下一步该做什么
你在读房颤终止预测的经典论文,想复现其中一两个特征 论文用的就是这批记录,组标签与切分方式都能对上 先按 §5.2 拿到全部 80 段,再按 s/t 成对关系重建划分;对照原论文的特征定义逐项复算
你手上有一个房颤检测模型,想扩展到"严重程度"或"转归" 提供一个从"检测"往"预测"跨一步的最小练习场 先用学习集的 30 段跑通三分类,确认掉分幅度在可接受范围,再考虑换更大的私有数据
你做信号复杂度、熵、主导频率这类无监督指标 三组的生理差异恰好落在这些指标的敏感带上 用 §5.4 的脚本批量算指标,先看组间是否有区分度,再决定要不要做监督模型
你要给团队讲清楚"数据泄漏"是什么 s 与 t 本是同一段连续记录的两半,是最直观的反面教材 用 §4.3 的案例演示:按记录乱切时的成绩与按受试者切时的成绩差多少
你打算做一个房颤终止预测的深度学习模型 数据量远远不够,只能当外部小测集 去用更大的房颤库做训练,把它留作最后的"换个人群还灵不灵"的检查点
你在核对某个公开成绩是否能复现 挑战赛成绩页给出了分数上限与名次 注意满分口径(事件一 30 分、事件二 20 分),不要把分数直接当准确率比

1.5 一句话判断

它不是一座更大的房颤库,而是一道出得极好的小考题:用三个人为设定的时间断面,逼着算法去看窗外那一分钟,而这种「看不见终点却要判断终点」的构造,到今天仍然没有第二个公开库照着做。

1.6 不适合谁:四条边界

不适合谁:想用数据量换性能的团队。 80 段、按受试者算学习集 20 人。任何带参数的模型都能在几十步内把学习集拟合到零损失,泛化性能完全取决于那 50 段测试集的表现,而测试集没有逐条真值可供反复调参。

不适合谁:想研究终止瞬间电生理的人。 终止点被设计在窗口之外,这是核心构造而非缺陷。想研究"转复那一秒心房发生了什么",需要的是另一类覆盖终止时刻的连续记录。

不适合谁:把房颤当成单一标签的初学者。 这个库里没有非房颤段。用它入门房颤检测,会得到一个只输出一种结果的模型,而且不会察觉问题出在数据上。

不适合谁:需要干预信息的临床研究者。 没有用药、没有复律能量、没有抗凝状态、没有随访结局。它是一个纯电生理片段库,临床上下文只有"这段来自一位有过自发终止的患者的某一段记录"。

§2 数据构成与规格

2.1 三个目录、五个字母前缀

PhysioNet 的文件目录按集合分了三层,根目录下没有散落记录,而是 learning-set、test-set-a、test-set-b 三个子目录,RECORDS 文件里按相对路径逐行列出全部 80 条记录名。组标签不写在头文件里,而是编码在记录名的首字母:n 开头为不终止组,s 开头为一分钟后终止组,t 开头为一秒内终止组,a 开头属测试集 A,b 开头属测试集 B。

这种"标签藏在名字里"的做法在 2004 年很自然,因为当时还没有统一的元数据表规范。它带来一个具体后果:读取标签时必须解析路径或文件名,不能指望头文件里有一行写着 group。这个习惯如果带到别的库上会失效,反过来,别的库的元数据表在这个库上也不存在。

路径 条数 记录名 组标签 受试者
learning-set/ 30 n01–n10、s01–s10、t01–t10 N / S / T 各十段 20 名
test-set-a/ 30 a01–a30 约一半 N、其余 T 30 名
test-set-b/ 20 b01–b20 每对一段 S、一段 T 10 名
合计 80 — 三分类体系 约 60 名(互不重叠)

上表最后一行的人数需要注意口径:官方分别给出了三个集合的受试者数,并明确三者互不出现,所以按人加总是 20 + 30 + 10 = 60 名。官方没有单独给出"全库共多少名受试者"这句话,这个和是按三段话相加得到的,属于官方陈述的直接推论,不是官方原文。

2.2 字段与文件的完整字典

本库的"字段"分两类:一类是 WFDB 头文件里的关键字行,一类是文件名与路径本身携带的语义。逐项列出如下。

信息项 所在位置 取值形态 含义 使用注意
记录名 .hea 第 1 行首字段 n01…b20 记录标识 文件名前缀即组与集合
信号条数 .hea 第 1 行第 2 字段 2 通道数 全部记录均为 2
采样率 .hea 第 1 行第 3 字段 128 每秒采样点数 不是 250 也不是 360
采样点数 .hea 第 1 行第 4 字段 7680 总长度 7680 ÷ 128 = 60 秒整
起始时刻 .hea 第 1 行第 5 字段 如 19:32:13 该段在原长程记录中的时点 学习集与测试集 A 有,测试集 B 没有
信号文件名 .hea 信号行第 1 字段 n01.dat 等 两路信号共用一个文件 —
存储格式 .hea 信号行第 2 字段 16 16 位整数 不是浮点
增益 .hea 信号行第 3 字段 如 166.945/mV 每毫伏对应的整数个数 逐记录、逐通道不同
基线 .hea 信号行第 4 字段 0 ADC 零值 换算时先减
初始值 .hea 信号行第 5 字段 如 -9 首采样点整数 可用于完整性自查
校验和 .hea 信号行第 6 字段 如 -17906 采样点累加和 检测文件是否被改动
块大小 .hea 信号行第 7 字段 0 每帧字节数 由 wfdb 自动计算
信号描述 .hea 信号行第 8 字段 ECG 两路都写作 ECG 官方未说明具体导联位置
QRS 位置 .qrs 采样点序号 自动检测的心搏时刻 未审核,含错误
心搏类型 .qrs 一律为正常 不含异位类型 不能做类型分类
组标签 文件名首字母 n / s / t / a / b 终止与否 a、b 只表示集合不含组信息
集合归属 路径 三个子目录名 学习或测试 测试集无逐条真值

增益这一列值得单独说一句。头文件里写的是形如 166.945/mV 的表达式,意思是每毫伏 166.945 个整数单位。抽样看到的记录里,增益大致落在每毫伏 140 到 202 个单位的区间,两路信号常常不同。这意味着同一个毫伏值在两路里对应的整数不同,直接拿整数当特征会把通道间的幅度关系搞乱。

2.3 一条记录的完整解剖

以 learning-set/t01 为例。头文件第一行是:

t01 2 128 7680 19:33:14

读法是:记录名 t01,两路信号,128 Hz 采样,共 7680 个采样点,起始时刻 19 点 33 分 14 秒。7680 ÷ 128 恰好等于 60,所以每段都是精确的一分钟,切窗时不需要处理零头。

接下来两行声明信号:

t01.dat 16 166.945/mV 0 0 38 -19233 0 ECG
t01.dat 16 173.01/mV 0 0 -162 14341 0 ECG

两行末端的描述都是 ECG,没有写 II、V1 这类导联名,也没有任何注释行给出电极位置。这是一个必须记住的事实:官方从头到尾没有说明这两个通道取自哪个导联位置。文献与教程里偶有"近似 II 与 V1"的说法,但官方页面与头文件都不支持这个对应,涉及导联特异性的结论在这批数据上根本无法成立——你既不知道通道对应哪里,也就没有资格说"这个特征在 V1 上更明显"。务必把两路信号当作不带解剖含义的"通道一"与"通道二"来用。

再看同一来源的另一条 learning-set/s01:

s01 2 128 7680 19:32:13

19:32:13 与 t01 的 19:33:14 正好相差 61 秒。这不是巧合:官方明确说 t 组记录就是 s 组记录的接续,t01 紧跟在 s01 之后。61 秒而不是 60 秒,说明两段之间有一秒左右的空档,这与官方对测试集 B 成对记录"之间可能有不足一秒的短间隔"的描述是同一类现象。

读取信号与标注的标准写法:

import wfdb

#-- 直读 PhysioNet 上的库,不需要先下载
base = 'aftdb/1.0.0/learning-set'
rec = wfdb.rdrecord('t01', pn_dir=base)
ann = wfdb.rdann('t01', 'qrs', pn_dir=base)

print(rec.fs, rec.sig_len, rec.sig_name)      #-- 128 7680 ['ECG', 'ECG']
print(rec.p_signal.shape)                     #-- (7680, 2),已换算成毫伏
print(len(ann.sample), ann.symbol[:5])        #-- 心搏条数与前五个类型(全为 N)

p_signal 是已经按增益与零值换算好的浮点毫伏值,d_signal 则是原始整数。ann.symbol 里只会出现一种符号,这正是 §2.2 里说的"异位搏动也标为正常"的直接后果。

2.4 三组的构造关系

理解这个库最关键的一张表,不是记录清单,而是三组之间的时间关系:

组 记录名 终止点相对记录末端 原话依据 可否与其他组配对
N n01–n10 长程记录结束前一小时以上都未观察到终止 官方:at least an hour following the segment 不配对,各自独立
S s01–s10 记录结束后约一分钟 官方:terminates one minute after the end of the record 与同号的 t 配对
T t01–t10 记录结束后一秒以内 官方:terminates immediately (within one second) 与同号的 s 配对
A a01–a30 约一半 N、其余 T,官方未给逐条标签 官方:approximately half … belong to group N 每条独立,一受试者一段
B b01–b20 每对一段 S、一段 T 官方:one record of each pair belongs to group S 两两成对,共十对

这张表里最容易被忽略的是 N 组的定义括号:「在其后的至少一小时里都未观察到终止」。这句话的判据是"没看到终止",不是"这段房颤注定不终止"。两者的差别在统计上很重要:N 组是一个"至少在一小时内没有终止"的集合,其中可能混着后来终止了的病例,只是终止发生在观察窗之外。用这个库训练出的模型,学到的是"这一小时里没停"的先验,不是"永远不会停"的确定性。

S 与 T 的关系则是另一种:它们本来就是同一段连续记录被切开的两半。t01 紧接在 s01 之后,两者首尾相衔,合起来覆盖终止前约两分钟。这个构造让 s 与 t 之间存在极强的时间连续性,而这一点正是 §4.3 的案例要展开的。

2.5 与同类资源的字段差异

对比项 AF Termination MIT-BIH 房颤库 长期房颤库 PAF 预测挑战赛库 CinC 2017
记录数 80 段 25 段 84 段 100 段 8528 段
单段时长 1 分钟 10 小时 24–25 小时 30 分钟 9–61 秒
采样率 128 Hz 250 Hz 128 Hz 128 Hz 300 Hz 或 500 Hz
通道数 2 2 2 2 1
任务 房颤内部终止预测 房颤检测与节律标注 房颤负荷 房颤发作预测 房颤检测
节律标注 无(只有三组标签) 有(逐段节律) 有 有(发作起点) 有(四分类)
心搏标注 自动、未审核、单一类型 人工审核、多类型 人工审核、多类型 自动与人工混合 无
许可 ODC-BY 1.0 ODC-BY 1.0 ODC-BY 1.0 ODC-BY 1.0 ODC-BY 1.0
训练可用性 极低 中 中 中 高

这张表要说明的只有一件事:在"房颤"这个大类下,五个库分属五条完全不同的任务线。它们之间不存在"哪个更好"的问题,只存在"你的任务落在哪一行"的问题。AF Termination 是唯一把"终止"当作预测目标的那一行。

2.6 一分钟窗口的物理含义

7680 个采样点在 128 Hz 下是一分钟,这个长度不是随手定的。它对应临床上一段心电图打印纸的长度(25 mm/s 走纸时约 25 厘米),也对应一个医生能一眼看完、又不至于丢失节律趋势的尺度。对算法来说,一分钟大约包含 60 到 150 个心搏,取决于心室率;对房颤这种 RR 间期高度不规则的节律,这个心搏数足够估计 RR 序列的统计量,也足够做几轮频谱分析。

但它也有明确的短板。房颤终止前的电生理变化,文献里描述的时间尺度从十几秒到几分钟不等。一分钟窗口能覆盖"立刻就要停"的那类变化,对"还有五分钟才停"就力不从心。这解释了为什么 s 组(一分钟后终止)的预测难度明显高于 t 组(一秒内终止),也解释了为什么两个挑战赛事件的分数上限不同。

窗口内的心搏数(估计) 对应心室率 RR 序列可估的统计量 一分钟内是否足够
约 60 60 次/分 均值、标准差、粗略熵 够观察趋势,不够做稳定的谱估计
约 90–110 90–110 次/分 加上 LF/HF 粗分 最常见区间,足够
约 140–150 140–150 次/分 谱估计方差偏大 勉强够

上表的心搏数是按心室率反推的算术结果,不是从数据里统计出来的。它只用来帮助判断"一分钟够不够",不代表本库各组的实际心室率分布——那个分布需要逐段计算,超出了本篇的范围。

§3 它要回答的那个电生理问题

3.1 贡献者与来历

数据库的作者是 PhysioNet 的 George Moody,原始记录由美国西北大学(Northwestern University)的 Steven Swiryn 提供。PhysioNet 在致谢里写的原话是「特别感谢西北大学的 Steven Swiryn 提供了在此摘录的记录」。这句话框定了数据的性质:这是一批既有的长程记录被摘录,而不是为新数据集专门采集的样本。

这个来历解释了很多细节。为什么每段正好一分钟?因为从 20–24 小时的长程记录里摘一分钟是最省事的切法。为什么只有两个通道?因为原始长程记录本身就是两通道 Holter 式记录。为什么没有年龄、性别、用药?因为这些元数据在摘录时没有一并移交。数据的形态完全由"摘录"这个动作决定,而不是由某个采集方案决定。

3.2 时间线

时间 事件 意义
2003-10-08 挑战赛宣布启动,数据集同时可用 库的最初发布形态
2004-04-11 项目页标注的发布日,版本 1.0.0 官方版本号的起点
2004-05-01 官方公告称已有二十位参赛者提交了首次结果 赛事进行中
2004-09-14 官方公告里提到的最终提交截止日 评分口关闭
2004-10-31 最终成绩发布 成绩页与获胜软件一并公布
其后至今 版本停在 1.0.0,无修订 二十余年未变

上表后三行来自挑战赛页面的公告段落,属于官方陈述;最后一行是核对页面版本号得到的观察。整个库从 2004 年至今没有第二个版本,这一点在引用时很有用:不存在"新旧版本混用"的问题,任何一次下载得到的都是同一批数据。

3.3 为什么用"摘录"而不是"采集"

如果今天要重新做这件事,最常见的做法是建一个前瞻性队列,招募房颤患者,连续监护直到转复,然后把终止前后的完整记录公开。这种做法能得到终止瞬间、能得到临床变量、能控制样本量。2004 年没有这么做,原因至少有两条。

第一是隐私与伦理的成本。长程记录的终止意味着患者在医院或家中被持续监护,这段记录里必然包含大量与房颤无关的私人信息——睡眠、活动、服药时刻。要公开,就需要做大量脱敏,而当年的脱敏工具与流程远不如今天成熟。

第二是时间成本。前瞻性队列要等到足够多的自发终止事件才能凑够样本,而自发终止本身不是每天都会发生的事。摘录既有记录则可以在几周内完成。

这两条理由共同塑造了这个库的性格:它的样本是"恰好被录到并且被存下来"的那批,而不是"按设计被招进来"的那批。这种非设计性的采样方式,是它所有局限的源头(§7.3)。

3.4 数据来源的三个层次

理解这份数据要分清三层:长程记录(原始,未公开)、一分钟段(公开的基本单位)、QRS 标注(公开的唯一监督信号)。第一层不可得,意味着无法重建 s 与 t 之外的时间上下文,也无法知道 N 组那些记录后来到底终止了没有;第二层决定了每段都是人为切的,切点位置决定了它能提供什么信息;第三层决定了它能支持的监督任务只有"心搏在哪里",而不包括"心搏是什么类型"。

§4 它最独特的东西:把终止点推到窗外

4.1 时间断面式的标注为什么罕见

绝大多数心电数据集的标注是"描述性的":这段里有几个早搏、这段是房颤还是窦性、这个导联有没有 ST 改变。描述性标注的共同点是,被标注的对象就在记录内部,标注者看到什么就写什么。

AF Termination 的标注是"关系性的":它描述的不是记录内部的内容,而是记录与外部事件的时间关系。s 组的记录本身看起来就是一段普通的房颤,没有任何内部标记说明"一分钟后要停"。要判断它属于哪一组,必须知道窗外发生了什么。这种标注方式在公开数据集里极其罕见,因为它要求提供方掌握记录之外的随访信息。

这也是它最难被复制的地方。想再造一个类似的库,你不仅需要房颤记录,还需要知道每一段之后发生了什么——而这恰恰是自我监护记录里最不容易拿到的部分。

4.2 三种时间断面各自考验什么

三个组看起来只是终止时间的差别,实际上考验的是三种不同的能力:

组 时间断面 考验的能力 可利用的线索 难度
T 一秒内终止 捕捉终止前最后阶段的电生理征兆 f 波频率与组织化的急剧变化 挑战赛里有队伍拿到满分,说明线索确实存在
S 一分钟后终止 判断趋势,而不只是瞬时状态 较长时间窗内的频率漂移与振幅衰减 明显更难,满分上限也更高(30 分对 20 分)
N 一小时内未终止 排除假阳性 缺乏上述征兆 作为负类,其难度体现在不被误判

第三行值得展开。N 组在挑战赛里不是独立事件,而是作为 T 组的负类出现:事件一要在 30 段里挑出哪些属于 T,其余是 N。所以 N 组的作用是提供"看起来像但不会停"的干扰项。一个只学会"房颤就是不终止"的模型,在事件一上会得到接近一半的正确率,这个基线是理解任何成绩的起点。

4.3 案例:s01 与 t01,同一场房颤的两半

这是全篇反复要回到的一个具体例子,因为它把这个库最要紧的两个性质同时展示出来了。

先看时间戳。s01 的起始时刻是 19:32:13,t01 是 19:33:14,相差 61 秒。s01 长 60 秒,所以它覆盖 19:32:13 到 19:33:13;t01 覆盖 19:33:14 到 19:34:14。两者之间空了大约一秒。官方对这个空档没有解释,只说 t 组记录"紧跟" s 组记录。测试集 B 的成对记录也有类似情况,官方在那里明确说"某些成对记录之间存在不足一秒的短间隔"。

再看增益。s01 的两路增益是 166.945 与 173.01,t01 的两路增益完全相同,都是 166.945 与 173.01。而同一目录下的 n01 增益是 142.045 与 143.062,完全不同的一对数。增益一致意味着这两段来自同一次记录、同一个放大器设置,这是文件层面的直接证据,比时间戳更硬。

import wfdb

for name in ('s01', 't01', 'n01'):
    h = wfdb.rdheader(name, pn_dir='aftdb/1.0.0/learning-set')
    print(name, h.base_time, [g for g in h.adc_gain])

这段代码输出了三行,前两行的增益向量相同、时间戳相差 61 秒,第三行两者都不同。把 s01 与 t01 分到训练集和测试集两侧,就等于把同一场房颤的前后两分钟分别给了模型和考官。 模型在训练时见过的那段波形,与测试时看到的那段在噪声、电极接触、f 波形态上高度相关。这种泄漏比"同一个患者的不同记录"更严重,因为它连"不同的时间窗"这个缓冲都没有。

4.4 标注的粒度:只有位置,没有类型

.qrs 文件是本库唯一的逐事件标注。官方对它的描述有三层限定,每一层都削减了它的用途:

第一层,「由自动检测器产生」。这意味着它没有经过人工复核,误检与漏检都可能存在。官方原话承认这些标注"可能包含少量错误"。

第二层,「所有检出的心搏,包括任何异位搏动,都被标为正常」。这直接否定了它做类型分类的可能。房颤患者常见室性早搏与差异性传导,这些在 .qrs 里与正常心搏没有区别。

第三层,「未经审核」。官方给了三个选项:修正它、在未修正的形态下使用并研究对少量检测错误稳健的方法、或者完全忽略它自己做检测。这三个选项本身就说明了官方对它的定位——一个方便,不是一个保证。

用途 能否直接用 .qrs 原因
划分 RR 序列 可以,但要容忍少量错误 位置基本可用
计算心率 可以 对偶发误检不敏感
心搏类型分类 不可以 类型信息被抹平
早搏负荷统计 不可以 异位搏动被标为正常
QT 间期测量 不建议 位置未校正,且无导联信息

4.5 边界条件:什么情况下它不再适用

条件 是否适用 判断依据
你要研究自发终止前的电生理 适用,这是它唯一的设计目标 三组定义
你要研究电复律或药物转复 不适用 官方全文用 spontaneous
你要做房颤检测 不适用 全部记录都是房颤
你要做心搏分类 不适用 .qrs 无类型
你要训练深度模型 不适用 80 段、20 人
你要做外部小测集 适用,前提是能自己重建标签 学习集有标签,测试集没有
你要做多导联研究 有限适用 只有两路,且导联位置未说明
你要做节律机制研究 适用,但需自行提取 f 波 有原始波形,但无心房标注

这张表的第三、四行是最常被踩的两行。它们的共同点是"把这个库当成一个通用房颤库",而它实际上是一个任务极窄的库。把它放在正确的赛道上,它很好用;放在错误的赛道上,它会在第一次训练就暴露出问题。

§5 怎么拿到:从目录树到可训练张量

5.1 目录结构与下载面

官方页面给的下载选项有三条:ZIP 整包、wget 递归、AWS 命令行同步。文件体积很小,官方写占用约 3.3 MB,页面另一处写解压后总计 2.4 MB。这两个口径的差异官方没有说明,推测一个是含压缩头与目录的开销、一个是纯文件内容量,但不必深究——全量下载在任何网络环境下都是几秒钟的事。

路径 内容 大小量级
RECORDS 80 行相对路径 约 1.2 KB
ANNOTATORS 一行注释角色说明 31 字节
SHA256SUMS.txt 全部文件的校验和 约 20 KB
learning-set/*.hea 头文件 每条数百字节
learning-set/*.dat 两路信号 每条约 30 KB
learning-set/*.qrs QRS 标注 每条数千字节
test-set-a/、test-set-b/ 同构 同上

ANNOTATORS 文件的内容是 qrs unaudited beat annotations,只有这一行。这行字与本篇反复强调的判断一致:官方自己在文件层面就把这份标注定性为"未经审核"。

5.2 批量下载

#-- 方式一:整包下载后解压
curl -L -o aftdb.zip https://physionet.org/static/published-projects/aftdb/aftdb-1.0.0.zip
unzip -q aftdb.zip -d aftdb

#-- 方式二:按 RECORDS 逐条取(适合只想拿学习集)
mkdir -p aftdb_ls
curl -sL https://physionet.org/files/aftdb/1.0.0/RECORDS -o RECORDS
grep '^learning-set/' RECORDS | while read r; do
  for ext in hea dat qrs; do
    curl -sL "https://physionet.org/files/aftdb/1.0.0/${r}.${ext}" -o "aftdb_ls/$(basename $r).${ext}"
  done
done
ls aftdb_ls | wc -l      #-- 预期 90,即 30 条记录各三个文件

第二种方式里要注意路径:RECORDS 里写的是 learning-set/n01 这样的相对路径,下载 URL 必须原样拼接,但在本地存盘时要去掉目录层级,否则 wfdb 读取时会找不到。

5.3 用 wfdb 直接流式读取

不想下载全量时,可以让 wfdb 直接从 PhysioNet 读。这在只做几十段实验时最省事:

import wfdb

sets = {'learning-set': [f'{p}{i:02d}' for p in 'nst' for i in range(1, 11)],
        'test-set-a': [f'a{i:02d}' for i in range(1, 31)],
        'test-set-b': [f'b{i:02d}' for i in range(1, 21)]}

count = 0
for sub, names in sets.items():
    for n in names:
        rec = wfdb.rdrecord(n, pn_dir=f'aftdb/1.0.0/{sub}')
        assert rec.fs == 128 and rec.sig_len == 7680 and rec.n_sig == 2
        count += 1
print('read', count, 'records')

这段代码做了一件有用的事:它用断言把 §2.2 里那三个规格数字变成可执行的检查。如果哪天官方换了版本、采样率或长度变了,这段代码会立刻报错,而不是悄悄读进形状不对的张量。这种断言在长期维护的实验仓库里值得保留。

5.4 转成通用的表结构

WFDB 格式不适合直接喂给深度学习框架,通常要先落成 NumPy 或 Parquet。这里给一个最小的转换脚本,把每条记录存成(7680, 2)的浮点数组,并把组标签写在文件名里:

import os
import wfdb
import numpy as np

OUT = 'aftdb_npz'
os.makedirs(OUT, exist_ok=True)

labels = {'n': 'N', 's': 'S', 't': 'T', 'a': 'A', 'b': 'B'}
names = [f'{p}{i:02d}' for p in 'nst' for i in range(1, 11)]
names += [f'a{i:02d}' for i in range(1, 31)]
names += [f'b{i:02d}' for i in range(1, 21)]

for n in names:
    sub = 'learning-set' if n[0] in 'nst' else ('test-set-a' if n[0] == 'a' else 'test-set-b')
    rec = wfdb.rdrecord(n, pn_dir=f'aftdb/1.0.0/{sub}', physical=True)
    x = rec.p_signal.astype('float32')                 #-- (7680, 2),单位毫伏
    np.savez_compressed(os.path.join(OUT, f'{n}.npz'), x=x,
                        label=labels[n[0]], sub=sub)

转换之后,整个库不到 10 MB,可以方便地放进任何实验仓库。要注意 physical=True 是默认值,它会把整数按增益与基线换算成毫伏;如果出于某种原因想保留整数,用 physical=False,但那会让不同记录之间的幅度失去可比性。

5.5 组间比较的统计量

拿到波形后,最自然的第一个实验是算几个常用指标,看三组之间有没有肉眼可见的差别:

import numpy as np
import wfdb

def rr_series(name, sub):
    ann = wfdb.rdann(name, 'qrs', pn_dir=f'aftdb/1.0.0/{sub}')
    return np.diff(np.asarray(ann.sample, dtype=float)) / 128.0   #-- 秒

def metrics(rr):
    sdnn = float(np.std(rr, ddof=1))
    rmssd = float(np.sqrt(np.mean(np.diff(rr) ** 2)))
    pnn50 = float(np.mean(np.abs(np.diff(rr)) > 0.05))
    return sdnn, rmssd, pnn50

for g in 'nst':
    rows = [metrics(rr_series(f'{g}{i:02d}', 'learning-set')) for i in range(1, 11)]
    arr = np.array(rows)
    print(g.upper(), 'SDNN=%.4f RMSSD=%.4f pNN50=%.3f'
          % tuple(arr.mean(axis=0)))

这段代码只打印三组的均值,不做任何显著性检验,也不下结论。它的用途是让你先建立对尺度的直觉:房颤的 RR 序列标准差通常远高于窦性心律,三组之间的差异如果只体现在小数点后第三位,那么单靠这几个指标是不足以做预测的,需要转向 f 波频域特征。这一步不要跳过,也不要直接引用这里打印出的数字当作结论——10 段样本的组均值波动很大,任何比较都应当在全部 80 段或多重划分下重复。

5.6 五个最容易出错的地方

位置 错误写法 后果 正确做法
标签读取 去头文件找 group 字段 头文件里没有 解析文件名首字母
数据集划分 按记录名随机切分 s 与 t 成对泄漏 按 s/t 配对整体归组
通道理解 认为通道一就是 II 导联 官方未定义导联 写作"通道一/通道二"
标注使用 用 .qrs 做类型分类 类型被抹平 只用位置,或自己检测
测试集评估 声称在官方测试集上有分数 官方未公布逐条真值 自建划分或引用文献成绩

§6 与库内同类记录的关系

6.1 关系图谱

房颤相关记录在库内已有一批,它们按"任务"而不是按"病"分布:

记录 记录规模 单段时长 任务 与 AF Termination 的关系
MIT-BIH 房颤库(mit-bih-afdb) 25 段 10 小时 房颤检测与节律标注 提供上下文,本库提供终止断面
长期房颤库(ltafdb) 84 段 24–25 小时 长程负荷 同样来自长程记录,但目标是负荷而非终止
PAF 预测挑战赛库(afpdb) 100 段 30 分钟 房颤发作预测 方向相反:它预测"何时开始",本库预测"何时结束"
CinC 2017(physionet-cinc-2017) 8528 段 9–61 秒 单导联房颤检测 规模差两个数量级,任务完全不同
Kingston ICU AF(kingston-icu-af) ICU 床旁短片 10 秒 ICU 房颤检测 场景不同(ICU 床旁),本库是长程监护摘录
INCART(incartdb) 75 段 30 分钟 十二导联心律失常 导联更全,但无终止标签

这张表里最值得划一条线的是 afpdb 那一行。afpdb 预测的是"房颤什么时候开始",本库预测的是"房颤什么时候结束"。两者在时序上首尾相接,构成一对互补的命题:一个是发作的起点,一个是发作的终点。把两者混淆,是搜索时最容易发生的事故——因为它们的中文名里都有"房颤"与"预测"。

6.2 检索路径

你要找的东西 检索词 会落到哪里
房颤终止预测的原始数据 AF termination、aftdb、spontaneous termination 本篇
房颤发作预测 PAF prediction、afpdb、paroxysmal afpdb 篇
长时程房颤负荷 AFDB、LTAFDB、AF burden mit-bih-afdb / ltafdb 篇
大体量房颤检测 CinC 2017、single-lead AF physionet-cinc-2017 篇
挑战赛数据集总览 PhysioNet challenge、Computers in Cardiology 本篇章 §8 与 cinc 系列
心电信号基础格式 WFDB、annotations incartdb 篇的格式章节

6.3 与其他库的分工判据

任务 主粮仓 AF Termination 的位置 原因
房颤检测预训练 CinC 2017 或更大体量的库 不参与 无负类
房颤终止预测 无公开大库 唯一的公开基准 任务唯一
发作预测 afpdb 不参与 方向相反
长程负荷统计 ltafdb / mit-bih-afdb 不参与 只有一分钟
终止前电生理研究 无替代 主力 唯一带终止标签的公开波形
教学中的泄漏示例 任何多段同源库 最佳示例之一 s/t 配对关系官方写得最清楚

最后一行可能听起来不像正经用途,但对工程团队来说它是实打实的价值:与其用一段虚构的例子讲数据泄漏,不如用这个库——它的配对关系是官方明文写出的,不需要任何推断,任何人一看就懂。

§7 质量评估与局限性

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

在给这个库打分之前,必须先纠正一个几乎所有人都会犯的误判:把它当作一个"数据质量不高"的库来评价。

它的 80 段、20 人、无临床变量、标注未审核,单看每一项都像是缺陷。但这些都不是妥协的结果,而是设计的结果。挑战赛的目的是让全世界用同一批小样本比方法,样本必须小到人人下得完,标签必须简单到不需要临床背景就能理解。如果它当初做了 8000 段、带完整临床变量、标注由三位专家交叉复核,那么它就不是一道考题,而是一座粮仓——但2004 年的条件也做不出这样的库。

这个校正会改变五维评分的读法(§7.1):它的"规模"分很低,这是事实;但"任务唯一性"接近满分,这也是事实。把两者混在一起,就会得出"不值得用"的结论,而这个结论是错的。

7.1 五维评分

按 DAIMS 的常用维度打分,每项 1–5 分,分数针对"作为房颤终止预测研究数据"这一用途:

维度 分数 理由
数据规模 1 80 段、20 名受试者的学习集,训练价值几乎为零
标注质量 2 只有 QRS 位置且未审核,组标签虽可靠但粒度极粗
任务适配 5 公开领域内唯一以自发终止为目标的带标签波形库
文档完整 3 三组定义、切分规则写得很清楚,但导联、人群、采集设备均未说明
可获取性 5 ODC-BY、免注册、整库几兆、wfdb 可直读

五项合计 16 分(满分 25)。这个分数结构很有辨识度:两端(规模、适配)拉得很开,中间三项不高不低。它不适合作为"综合评分高"的库来推荐,但任何一个做终止预测的人,绕不开它。

7.2 标注质量的两层

第一层是组标签可不可信。这一层是整个库的地基,也是最可靠的一层。三组的定义是"终止发生在记录结束之后的某个时间范围内",这个判断由提供方在摘录时确定,不依赖任何算法。所以 N、S、T 的划分本身是准的。

第二层是逐事件标注可不可信。这一层几乎不可用。.qrs 由自动检测器生成、未经审核、异位搏动被标为正常。官方在三个地方(数据描述、ANNOTATORS 文件、数据下载说明)重复了"未审核"这一点,语气之重在其他库里不常见。

用途 组标签的影响 QRS 标注的影响 建议
三分类终止预测 直接作为监督信号 只用于切 RR 序列 可用,注意配对泄漏
RR 间期统计 无关 少量错误会引入噪声 可容忍,报告时说明
心搏类型统计 无关 完全不可用 自行检测
终止时刻定位 只能给到组级别 无关 本库不支持更细的定位

7.3 已知的、可枚举的偏差

偏差 表现 影响
样本极小 学习集 30 段、20 人 任何组间比较的置信区间都很宽
单中心 原始记录只注明来自西北大学一人提供 设备、贴电极方式、判读习惯都单一
人群未知 无年龄、性别、基础病、用药 无法做亚组分析,也无法评估代表性
终止方式混杂的澄清 官方只讲自发终止,未讲是否有人在监护中 不能推断有无抗凝、有无合并用药
配对泄漏 s/t 是同一段连续记录的两半(§4.3 的案例) 随机划分会虚高
窗口外终止 终止点不在记录内 无法学习终止瞬间的形态
N 组定义宽松 "一小时内未观察到终止"不等于不会终止 负类里混有假阴性
标注未审核 QRS 自动检测 时间敏感指标带误差
导联未说明 两路都写 ECG 无法做导联特异性结论

7.4 外部验证矩阵

这个库在文献里最常见的用法,不是训练,而是"拿来做特征有效性的检验场"。

验证角色 做法 讲得通的地方 风险
特征筛选 用学习集 30 段比较不同特征的区分度 三组时间断面清晰,特征差异容易显形 30 段上筛出的特征极易过拟合
方法对照 在同一划分下比较熵、频域、非线性方法 文献里已有一批这样的对照 划分口径不统一时结果不可比
外部小测 训练在主库,测在本库 换人群、换设备的检验 测试集无真值,只能自制划分
机理研究 提取 f 波的主导频率与组织化指数 波形原始、未做滤波,适合自己处理 只有两路,且导联未知
教学示例 演示泄漏与配对设计 官方明文写出配对关系 仅教学用途

这里要特别说明"外部小测"那一行的风险。由于官方没有公布测试集的逐条真值,任何声称"在官方测试集上取得某分数"的说法都需要核对来源:要么是当年参赛队伍的历史成绩(有成绩页可查),要么是作者自行按三组定义重建的划分(应写明重建方式)。把两者混为一谈,是复现研究里最常见的错误之一。

7.5 它为何能持续:一个小小的构造至今没人重做

二十多年过去,房颤数据集出了很多,规模从几十段涨到几万段,但"自发终止"这个标签几乎没有第二个公开库提供。原因是构造这类数据需要的东西恰好最难获得:不仅有房颤记录,还要知道每段记录之后发生了什么,且愿意把这个时间关系公开。

同时,这个库的构造方式又极其轻便——不需要额外采集,只需要在既有长程记录上做标注。理论上任何一家有长程房颤监护的医院都能复制一个更大的版本。事实上没人公开做,这本身就说明问题不在技术,而在"终止之后的随访信息"这件事的稀缺性。只要这个稀缺性还在,它作为唯一公开基准的地位就不会被规模更大的库取代。

§8 生态位与历史

8.1 历史沿革

这个库诞生在 PhysioNet 挑战赛的早期。2000 年代初,PhysioNet 每年办一次挑战赛,主题轮换,数据随赛事发布并永久留存。2004 年这一届的主题是房颤自发终止,是当年少数直接指向"预测"而非"识别"的题目。

它的历史价值有一部分来自这个时间点。2004 年深度学习尚未进入心电领域,参赛方法以特征工程加浅层分类器为主——熵、频谱、非线性动力学、RR 序列统计量。这批方法后来被大量引用,而它们全部是在这个库上比的。今天再读那些论文,这个库就是它们的共同实验台。

8.2 与挑战赛的关系

那一届挑战赛分两个事件,与本库的两个测试集一一对应:

事件 对应测试集 任务 官方给出的满分 冠军成绩
事件一 test-set A(30 段) 在 N 与 T 之间区分 30 分 29 分(97%),西北大学 Petrutiu 等
事件二 test-set B(20 段) 在 S 与 T 之间区分 20 分 20 分(100%),同一团队

这两行数字来自挑战赛成绩页。它们透露的信息比分数本身更有意思:事件二的满分被拿到了,事件一的最好的成绩差一分。这说明"一分钟后终止"与"一秒内终止"之间的区分,在当年的方法下是可以做到完美的;而"终止与不终止"的区分反而更难,因为 N 组里有大量"看起来快要停了却没停"的干扰样本。

这一点对今天的使用者仍然是提醒:这个库最难的地方不在识别终止前兆,而在排除假阳性。

8.3 生态位:考题而不是粮仓

角色 粮仓 考题
典型用法 大规模训练、预训练 小样本方法对照、外部检验
需要的前提 数据量大、标签细 任务唯一、划分清楚
代表库 CinC 2017、大体量房颤库 本库、其他挑战赛小库
失败模式 过拟合、标签噪声累积 样本太小导致结论不稳
论文里的写法 “我们在 X 上训练” “我们在 Y 上验证了该方法”

把考题当粮仓用,会立刻撞上 80 段的墙;把粮仓当考题用,会遇到"自己测自己"的问题。两者的边界不在数据格式上,而在样本量与标签粒度上。

8.4 谁在用它,用来做什么

从公开文献的摘要看,使用者大致分三类:做房颤终止预测的方法研究者(把它当作标准对照)、做心电特征工程的研究组(把它当作特征筛选的场地)、以及教学与科普场景(用它演示"预测"与"识别"的区别)。第三类最少见于正式文献,但它在实际传播中影响很大——很多人在学心电信号处理时,第一份带"预测"味道的数据就是这个库。

§9 使用指引

9.1 三种角色的路径

角色 第一天 第一周 注意事项
算法研究者 下载学习集,读头文件,跑通 §2.3 的读取 实现两三个文献特征,做受试者级划分的基线 先定划分再调参
临床研究者 看三组定义与时间断面 评估它能否回答你的临床问题 它没有用药与随访,临床问题多半答不了
教学人员 用 s01 与 t01 讲配对与泄漏 讲清"识别"与"预测"的区别 强调终止点不在记录内

9.2 一条最短的可用流程

#-- 1. 整包下载
curl -L -o aftdb.zip https://physionet.org/static/published-projects/aftdb/aftdb-1.0.0.zip
unzip -q aftdb.zip -d . && cd aftdb-1.0.0

#-- 2. 核对文件数与校验和
wc -l RECORDS
sha256sum -c SHA256SUMS.txt --quiet && echo "all files verified"

#-- 3. 列出学习集三组各多少条
grep -c '^learning-set/n' RECORDS
grep -c '^learning-set/s' RECORDS
grep -c '^learning-set/t' RECORDS
import wfdb
import numpy as np

#-- 4. 以 s/t 配对为分组键,做不泄漏的划分
pairs = {}
for i in range(1, 11):
    pairs[f's{i:02d}/t{i:02d}'] = [f's{i:02d}', f't{i:02d}']

train_keys = list(pairs)[:7]
test_keys = list(pairs)[7:]
train = [r for k in train_keys for r in pairs[k]] + [f'n{i:02d}' for i in range(1, 8)]
test = [r for k in test_keys for r in pairs[k]] + [f'n{i:02d}' for i in range(8, 11)]
print(len(train), len(test))   #-- 21 9

上面这段划分演示了一个要点:s 与 t 必须成对进出。把 s01 放进训练、t01 放进测试,就是在允许模型"记住这场房颤"——这正是 §4.3 的案例要指出的那种泄漏。

9.3 引用方式

PhysioNet 对该库给出的原文引用是:

引用对象 内容
数据集本体 Moody GB. Spontaneous Termination of Atrial Fibrillation: A Challenge from PhysioNet and Computers in Cardiology 2004. Computers in Cardiology 31:101–104 (2004).
版本号 1.0.0,DOI 10.13026/C2CC7Z
平台 按官方页面 Cite 按钮给出的平台综述引用
赛事 PhysioNet/Computers in Cardiology Challenge 2004 的成绩页与公告

写论文时应同时注明版本与使用的集合(学习集还是测试集、哪一个事件),因为"用了本库"这句话本身有几个不同含义。

§10 AI 使用声明

项目 声明
文本生成 本文由 AI 辅助撰写,结构与事实由编辑把关
数字来源 规模、结构、边界三类数字取自 PhysioNet 项目页、挑战赛页与官方文件
文件核验 RECORDS、ANNOTATORS、多条头文件(n01、s01、t01、a01、a30、b01、b20)经直接访问确认
未核验内容 全库每条记录的增益全貌、各组的 RR 统计量、测试集逐条真值
论文信息 未读到 2004 年原文全文,赛事信息取自官方成绩页与公告
导联位置 官方与头文件均未定义两路信号取自哪个导联,本文不使用 II/V1 的对应
图像 无配图;封面为生成式提示词,不含真实心电图
免责 不构成临床建议;使用须遵守 ODC-BY 1.0 署名要求

常见误读(避坑清单)

说法 实际 证据
「它是电复律房颤数据集」 官方全文用 spontaneous,指自发终止 项目页数据描述与挑战赛公告
「它是药物终止房颤的研究数据」 无用药、无干预时刻 头文件与 README 均无相关字段
「它有 100 段记录」 80 段(30 + 30 + 20) RECORDS 共 80 行
「它能用来训练房颤检测模型」 全部记录都是房颤,没有负类 三组定义都预设"这段是房颤"
「它有房颤终止时刻的标注」 终止点被放在记录结束之后 官方 S 组与 T 组的定义
「它的 QRS 标注可以拿来做类型分类」 所有心搏一律标为正常 项目页与 ANNOTATORS
「测试集有公开真值可以刷分」 官方未公布逐条真值 项目页无 ground truth 文件
「按记录随机划分就行」 s 与 t 是同一次记录的两半 时间戳差 61 秒、增益完全相同
「两个通道就是 II 与 V1」 头文件两路都只写 ECG,未见任何电极位置说明 *.hea 第 2、3 行末字段与注释行
「它和 CinC 2017 是同一届挑战赛」 相差十三年,任务与规模完全不同 两处项目页

FAQ

Q1:这个库里的房颤是被电复律或药物终止的吗?
不是。官方用词是 spontaneous termination(自发终止),指没有干预的情况下房颤自己停下。库内没有电复律记录,也没有抗心律失常药物的给药时刻。把它当成复律疗效数据用,会从根上搞错标签含义。

Q2:一共多少段、多少名受试者?
80 段:学习集 30 段(n、s、t 各十段)、测试集 A 30 段、测试集 B 20 段。受试者方面,学习集 20 名(s 与 t 共用十人)、测试集 A 30 名、测试集 B 10 名,三个集合互不重叠。

Q3:有没有标注房颤终止的具体时刻?
没有,而且是刻意不标。s 组在记录结束后约一分钟终止,t 组在结束后一秒以内终止。终止那一下本身不在任何一段记录里,能被利用的只有终止之前最长一分钟的心电形态。

Q4:能不能用它训练心搏分类?
不建议。.qrs 由自动检测器生成、未经审核,并且把所有检出的心搏(含异位搏动)一律标为正常。文件里不存在类型信息,只能当位置提示用。

Q5:测试集真值在哪里?
官方项目页没有公布逐条真值。挑战赛成绩页给出了两个事件的满分(30 与 20)以及参赛队伍的成绩。想评测自己的方法,要么自行按三组定义重建划分,要么与文献成绩做对照,不能声称在官方测试集上取得了可比分数。

Q6:为什么 s01 和 t01 看起来像同一段记录?
因为它们确实是。官方明确说 t 组记录紧跟 s 组记录,t01 是 s01 的接续。头文件的时间戳相差 61 秒,两路增益完全相同,这两个证据都支持这一点。

Q7:每段为什么正好一分钟?
7680 个采样点除以 128 Hz 等于 60 秒。这个长度是从长程记录里截取时对齐到的整分钟,好处是切窗时没有零头。

Q8:两个通道是哪两个导联?
官方没有说明,头文件里两路信号的描述字段都写作 ECG,也没有电极位置的注释行。这意味着任何"这个通道是 II 导联"的说法都缺少官方依据,做导联特异性结论无从谈起。

Q9:许可允许商业使用吗?
ODC-BY 1.0 允许,包括商业用途,条件是署名。引用时应写清数据集名、版本 1.0.0 与 DOI。

Q10:数据集有多大?下载方便吗?
官方页面写占用约 3.3 MB、解压后 2.4 MB。三条下载途径:整包 ZIP、wget 递归、AWS 命令行同步。任何一条在普通网络下都是秒级完成。

Q11:它和 afpdb(PAF 预测挑战赛库)有什么区别?
afpdb 预测"房颤什么时候开始",本库预测"房颤什么时候结束"。方向相反,样本规模与时长也不同:afpdb 是 100 段三十分钟记录,本库是 80 段一分钟记录。搜索时两者容易混淆。

Q12:为什么二十多年了没有扩版?
官方没有任何扩版说明。从构造方式看,扩版需要更多"知道终止之后发生了什么"的长程记录,而这个信息比房颤记录本身更难获得,这更像是现实约束而不是计划中断。

术语表

术语 解释
自发终止 无外部干预的情况下房颤自行转复,对应英文 spontaneous termination
时间断面 本库的核心构造:记录与终止事件之间的时间关系
N 组 在其后至少一小时内未观察到终止的房颤段
S 组 在记录结束后约一分钟终止的房颤段
T 组 在记录结束后一秒以内终止的房颤段
配对泄漏 s 与 t 本是同一段记录的两半,分置训练与测试两侧造成的信息泄漏
f 波 房颤时心房的不规则快速电活动在体表心电上的投影
主导频率 f 波能量最集中的频率,常被用作终止预测特征
组织化指数 衡量 f 波规则程度的指标,与终止倾向相关
RR 间期 相邻两次心室激动的间隔,房颤时高度不规则
SDNN RR 间期的标准差,最常用的心率变异指标之一
RMSSD 相邻 RR 间期差值的均方根
pNN50 相邻 RR 间期差值超过 50 毫秒的比例
WFDB PhysioNet 使用的波形数据库格式,含头文件、信号、注释
.hea 纯文本头文件,声明采样率、增益与起始时刻
.dat 二进制信号文件,两路交错存放
.qrs 二进制注释文件,本库存放未审核的 QRS 位置
增益 每毫伏对应的整数单位数,换算物理量时使用
校验和 采样点累加和,用于检测文件完整性
挑战赛事件 同一届挑战赛下相互独立的评分任务
自动计分器 挑战赛使用的在线评分工具,提交后即时返回分数
ODC-BY 开放数据共享署名许可
PhysioNet 由 MIT 等机构维护的生物医学信号数据平台
Holter 式记录 便携式长时程心电监护,通常一到两个通道

附录 A:核心数字核对表

项 值 来源 来源层级
记录总数 80 RECORDS 逐行计数 官方文件
学习集记录数 30 官方页面;RECORDS 官方页面
测试集 A 记录数 30 官方页面;RECORDS 官方页面
测试集 B 记录数 20 官方页面;RECORDS 官方页面
学习集受试者数 20 官方页面 官方页面
测试集 A 受试者数 30 官方页面 官方页面
测试集 B 受试者数 10 官方页面 官方页面
每段采样点 7680 多条头文件第 1 行 官方文件
采样率 128 Hz 官方页面;头文件 官方页面
每段时长 60 秒 7680 ÷ 128 官方文件推算
通道数 2 官方页面;头文件 官方页面
增益量级 约 140–202 /毫伏 抽查头文件 官方文件
s01 起始时刻 19:32:13 s01.hea 官方文件
t01 起始时刻 19:33:14 t01.hea 官方文件
整库体积 约 3.3 MB 官方页面 官方页面
事件一满分 30 挑战赛成绩页 官方页面
事件二满分 20 挑战赛成绩页 官方页面
许可 ODC-BY 1.0 官方页面 官方页面
首版日期 2004-04-11 官方页面 官方页面
版本 1.0.0 官方页面 官方页面

附录 B:数字的来源分层

第一层:官方页面与官方文件。 记录数、受试者数、三组定义、采样率、时长、体积、许可、日期、赛事满分与名次,以及头文件里的增益与时间戳。项目页的数据描述段与挑战赛成绩页各自支撑一部分。

第二层:论文摘要与文献。 关于这类数据在文献中的常见用法(特征筛选、方法对照、外部小测),来自检索到的综述与摘要;关于两路信号与任何具体导联的对应,官方未定义,本文不采用文献中的类推。

第三层:未核实。 全库每条记录的增益全貌、各组的 RR 统计量与主导频率分布、测试集逐条真值、N 组样本中实际后来终止的比例。这些都需要逐条计算或需要未公开的信息,正文涉及处均只做定性表述。

附录 C:完整参考文献

编号 文献 链接 用途
1 AF Termination Challenge Database v1.0.0 项目页 https://physionet.org/content/aftdb/1.0.0/ 全部规模、结构、边界数字的主来源
2 该库文件目录 https://physionet.org/files/aftdb/1.0.0/ RECORDS、ANNOTATORS、各记录头文件
3 挑战赛页面(含成绩与公告) https://physionet.org/content/challenge-2004/ 事件划分、满分、冠军成绩、时间线
4 Moody GB, CinC 31:101–104 (2004) http://www.cinc.org/archives/2004/pdf/101.pdf 官方指定引用(本文未读全文)
5 PhysioNet 平台综述(2026) https://doi.org/10.1038/s44360-026-00096-z 托管模式与引用规范
6 PhysioBank 注释定义 https://physionet.org/physiobank/annotations.shtml QRS 注释符号的标准含义
7 MIT-BIH 房颤库(对照) https://physionet.org/content/afdb/ §2.5 与 §6.1 对比
8 PAF 预测挑战赛库(对照) https://physionet.org/content/afpdb/ 方向相反的任务线

附录 D:任务代码

任务一:给每条记录打上组标签并做泄漏安全的划分

import re
import wfdb

def group_of(name):
    #-- 组标签只存在于文件名首字母,头文件里没有
    m = re.match(r'^([nstab])', name)
    return {'n': 'N', 's': 'S', 't': 'T',
            'a': 'A', 'b': 'B'}[m.group(1)]

def split_safe(seed=0):
    import random
    rng = random.Random(seed)
    #-- s 与 t 必须成对进出,否则同一场房颤会被切开
    units = [[f's{i:02d}', f't{i:02d}'] for i in range(1, 11)]
    units += [[f'n{i:02d}'] for i in range(1, 11)]
    rng.shuffle(units)
    cut = int(len(units) * 0.7)
    train = [r for u in units[:cut] for r in u]
    test = [r for u in units[cut:] for r in u]
    return train, test

tr, te = split_safe()
print(len(tr), len(te), sorted(set(map(group_of, tr + te))))

split_safe 把 s 与 t 绑定成一个不可拆分的单元,n 段各自独立,这样划分出来的训练集与测试集不会共享同一场房颤。用不同的 seed 重复几次,可以估计成绩的波动幅度。

任务二:一分钟窗口内的 f 波主导频率估计

import numpy as np
import wfdb

def dominant_freq(name, sub, fs=128.0, lo=3.0, hi=12.0):
    rec = wfdb.rdrecord(name, pn_dir=f'aftdb/1.0.0/{sub}', physical=True)
    x = rec.p_signal[:, 0]                     #-- 只用通道一
    x = x - np.mean(x)
    spec = np.abs(np.fft.rfft(x * np.hanning(len(x))))
    freqs = np.fft.rfftfreq(len(x), 1.0 / fs)
    band = (freqs >= lo) & (freqs <= hi)
    return float(freqs[band][np.argmax(spec[band])])

for g in 'st':
    vals = [dominant_freq(f'{g}{i:02d}', 'learning-set') for i in range(1, 11)]
    print(g.upper(), 'median peak =', round(float(np.median(vals)), 3), 'Hz')

第二段代码是最粗糙的 f 波频率估计:直接对整段做 FFT 取 3–12 Hz 内的峰值。真实使用中必须先去 QRS(常用办法是做模板减除或用带通滤波压低 QRS 能量),否则峰值会被 QRS 的谐波带偏。这里保留粗糙版本,是为了让读者看到"先跑通、再改进"的顺序——先确认频带与采样率都没问题,再考虑用更复杂的去 QRS 方法。注意 128 Hz 的采样率下,奈奎斯特频率只有 64 Hz,对 f 波分析够用,对更高频的 QRS 细节就不够了。

附录 E:如果只记五件事

序号 记住什么 为什么
1 80 段、一分钟、双通道、128 Hz、三组标签 这五个数字定义了一切用法与限制
2 是自发终止,不是电复律或药物 搞错这一点,全部结论都偏
3 终止点被放在记录之外 题目问的是"窗外会发生什么"
4 s 与 t 是同一场房颤的两半 任何划分都必须成对处理
5 只有 QRS 位置标注,且未审核 心搏类型与节律类型都不在库里

编辑说明

核验时间为 2026 年 10 月 28 日。核对方式为直接访问 PhysioNet 的 AF Termination Challenge Database 项目页、挑战赛页面与文件目录,逐项确认 RECORDS 的 80 行、ANNOTATORS 的一行内容,以及 n01、s01、t01、a01、a30、b01、b20 七条记录头文件的采样率、采样点数、增益与时间戳。2004 年原始论文全文未能取得,赛事相关信息以官方成绩页与公告为准。未覆盖的内容包括:全库每条记录的增益分布、各组的 RR 与 f 波统计量、测试集逐条真值、N 组样本的后续转归。这些要么需要逐条计算,要么根本未公开,本篇按要求只写规模、结构、边界三类数字。


相关数据集导航

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

  • afpdb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • incartdb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • twadb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • cinc-2021 — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • ltafdb — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • cinc-2011 — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • cinc-2020 — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • butppg — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • ecg-image-database — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
  • georgia-12-lead-ecg-challenge — 共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集

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

返回 AI-Ready 数据集