信息速览
心源性猝死最残酷的地方不是它致命,而是它几乎从不被完整记录。一个人在院外倒下,从窦性心律走到心室颤动往往只花几分钟;等急救人员到达、电极贴上、监护仪开机,记录下来的大多已经是心电静止,或者心肺复苏按压留下的大片伪迹。真正有科学价值的那段窗口——最后一次正常心搏之后,室性早搏怎样逐渐增多、怎样成对成串、怎样演变为室性心动过速、怎样蜕变为心室扑动与颤动、最后怎样停下来——在临床记录里几乎是空白。要拿到这一段,只可能靠一件事:患者倒下的那一刻,身上恰好戴着 Holter。
SDDB 就是一批"恰好"的带子。SDDB 猝死长程心电库(Sudden Cardiac Death Holter Database)由 PhysioNet 于 2004 年 7 月 2 日发布,版本 1.0.0,主体是 23 段完整的双通道长程心电记录,单段从 3 小时 56 分到 25 小时 8 分不等,采样率 250 Hz。全部 23 名患者都在记录期间发生了持续性室性心动过速,其中多数发生了真正的心脏骤停。这些数据主要采集于 1980 年代的波士顿地区医院,由当时在 MIT 的 Scott Greenwald 为室性心律失常研究而汇编。
它的代价与它的价值同样极端:只有 23 段、两通道、患者信息大量缺失、审校过的心搏标注只覆盖其中 12 段。它不是一个"规模够用"的训练集,而是一批在伦理与技术上都不可能重新采集的原始素材。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 全名 | Sudden Cardiac Death Holter Database |
| 发布方 | PhysioNet(记录由 Scott Greenwald 在 MIT 期间采集并汇编) |
| 首次发布 | 2004 年 7 月 2 日,版本 1.0.0 |
| 最近改动 | 仅一个版本,无后续修订记录 |
| 记录数 | 23 段,编号 30–52 |
| 患者数 | 23 名(官方未给出统一人口学统计) |
| 单段时长 | 3:56:00(记录 41)至 25:08:30(记录 51) |
| 通道数 | 2(头文件未标注导联名,两通道均标记为 ECG) |
| 采样率与分辨率 | 250 Hz,格式 212 打包存储,12 位 ADC 分辨率 |
| 增益 | 多数记录 800 ADC 单位/毫伏;记录 39 与 47 为 200 |
| 原始来源 | 1980 年代波士顿地区医院的 Holter 记录 |
| 底层心律构成 | 18 例窦性(含 4 例间歇起搏)、1 例持续起搏、4 例心房颤动 |
| 标注规模 | 23 份未审校算法标注(.ari)+ 12 份审校参考标注(.atr) |
| 标注生成方式 | 算法自动初标;其中 12 段经人工逐搏审校 |
| 文件格式 | WFDB:.hea 头文件 / .dat 信号 / .ari 与 .atr 注释 |
| 总体积 | 未压缩 1.1 GB,共 110 个文件 |
| 许可 | Open Data Commons Attribution License v1.0(ODC-BY 1.0),无需注册 |
| DOI | 10.13026/C2W306 |
| 典型用途 | 室颤检测与预警、室性心律失常演进分析、除颤算法评估、猝死前驱信号研究 |
§1 它要解决的具体麻烦
1.1 猝死研究的证据缺口:临床记录不到那段
心电数据的公开生态里,绝大多数库回答的是"如何识别已经发生的异常"。MIT-BIH 心律失常库回答的是心搏形态怎么分;房颤类库回答的是心房活动怎么判;ST 类库回答的是缺血怎么定。这些任务的共同前提是:异常已经出现在那段被公开的记录里,研究者只要学会认出它。
猝死研究面对的是另一个问题:异常还没发生,而你需要提前几分钟知道它要发生。这个问题在数据层面几乎无解,因为能用来监督训练的"正样本"必须是真实的、连续的、从稳定状态一路走到致死性心律失常的整段过程,而这类过程在临床上绝大多数没有被记录。院外骤停的患者身上没有电极;院内监护的患者往往已经在干预之下,心电形态被药物、起搏、按压改写。
于是出现了一个近乎悖论的处境:最能推动预警算法进步的数据,恰恰是最不可能被前瞻性采集的数据。SDDB 的存在依赖于一次纯粹的历史巧合——一批 1980 年代做的 Holter 检查,恰好碰上了患者在这次检查期间发生致死性事件。这不是设计出来的队列,是筛出来的幸存样本。
1.2 三条常见的错误期待
第一条,期待它有清晰的临床标签。官方页面明确说:患者信息有限,有时完全缺失,包括用药方案与剂量。它给的是一张 23 行的临床信息表,性别、年龄、病史、用药、底层心律五列,其中"Unknown"反复出现。想用它做"某种药物是否延长了室颤前驱期"这类研究,从一开始就没有数据支撑。
第二条,期待它是"室颤检测的标准基准库"。它确实常被拿来测室颤检测器,但它不适合当唯一基准:样本只有 23 段,且底线心律不齐(18 例窦性、4 例房颤、1 例起搏),不同底层心律下的检测难度差异极大。用它报一个总准确率,等于把异质的人群混成一个数字。
第三条,期待标注是完整的参考标准。官方在发布公告里写得很清楚:该库包含一套未经审校的心搏标注文件,以及一套不完整的审校标注文件,并公开邀请研究者参与补全。也就是说,23 段里只有 12 段有人工审校过的标注,剩下的 11 段只有算法初标。把 .ari 当金标准用,是把算法的错误当成监督信号。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 致死性室性心律失常的完整演进过程 | 23 段连续长程记录,多数录到心脏骤停 | 够,这是它唯一无可替代的地方 |
| 室颤起始时刻的粗定位 | 20 段在头文件里带 #vfon 注释,给出室颤起始的已历时刻 |
够做对齐,不够做逐搏界标 |
| 心搏级别的形态分类 | 12 段有审校标注,23 段有算法标注 | 够做评测,不够做大规模训练 |
| 室颤检测算法的外部测试 | 真实发生的室颤,非模拟、非叠加 | 够,且是少数能提供真实终末事件的库 |
| 除颤决策算法的评估 | 长时程背景 + 真实终末事件 | 可做,但通道数只有 2,看不到导联间关系 |
| 猝死风险的前瞻性预测建模 | 有终末事件,但无随访、无对照组 | 不够,23 段全是"发生了"的病例,没有"没发生"的 |
| 药物与心律关系的分析 | 用药列大量标注为 Unknown | 不够,官方明确说明剂量信息缺失 |
| 导联间差异或十二导联形态学研究 | 两通道,且无导联名 | 完全不够 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 你在做室颤检测,但现有测试集的室颤是人工标注的短片段 | 提供真实发生、连续演化到室颤的长记录 | 按 #vfon 前后切窗,分别报告预警提前量与误报率 |
| 你在做可植入或体外除颤器的放电决策逻辑 | 长程背景里混着起搏、房颤、室早、室速多种心律 | 把 40、42、49 三段(官方注明无室颤)当阴性对照 |
| 你想研究室颤前的自主神经或心率动力学前兆 | 单段最长超过 25 小时,室颤前有数十小时稳定期 | 用审校过的 12 段做,避免算法标注引入虚假心搏 |
| 你在写综述,需要说明猝死数据的稀缺程度 | 它是"这类数据全球公开量极小"的直接证据 | 引用官方页面的规模与限制描述,不要夸大 |
| 你想复现 1980 年代的室颤检测器结果 | 官方参考文献即 Greenwald 1986 年硕士论文 | 注意该论文用的是同批记录的半小时摘录,不是全集 |
| 你需要一批"噪声真实、基线漂移真实"的长程数据 | 1980 年代模拟磁带转录,噪声特性与现代数字记录不同 | 做鲁棒性评测是好材料,做绝对性能基准要注明来源年代 |
1.5 一句话判断
它不适合当"更大更好的训练集",而适合当"唯一能回答某个问题的证据":人从活着的心律走到致死性心律失常,中间那几十分钟到底长什么样。
1.6 不适合谁:四条边界
不适合做统计意义上的风险建模。 23 段全部是发生了事件的病例,库内没有对照组。任何基于它的"预测"都是在没有负样本的情况下拟合,得不出可外推的风险估计。
不适合当心搏分类的主粮仓。 12 段审校标注的体量,对现代深度学习模型来说是几分钟就能记住的规模。它在这个任务上的角色是外部测试,不是训练来源。
不适合做药物、剂量、干预相关的任何分析。 官方原话是患者信息有限、有时完全缺失,包括用药方案与剂量。临床信息表里"Unknown"占了相当比例,这是官方承认的缺陷,不是数据整理疏漏。
不适合做导联相关的形态学研究。 只有两个通道,头文件里两个信号的描述字段都是 ECG,没有给出导联名。任何需要知道"这是哪个导联"的分析都无从下手。
§2 数据构成与规格
2.1 一段记录由四个文件组成
数据库采用 WFDB(Waveform Database)格式,这是 PhysioNet 自 1980 年代沿用至今的物理信号存储规范。每一段记录对应同名的一组文件:.hea 是纯文本头文件,声明采样率、信号条数、增益、初始值、校验和,并附带采集来源与室颤起始时刻的注释;.dat 是二进制信号本体,按采样点交错存放两个通道的整数值;.ari 是未经审校的算法心搏标注;.atr 是经人工审校的参考心搏标注,只有 12 段有。
目录里还有一个容易被忽略的 .hea-,23 段各一份。它是头文件的早期版本副本,正文与现行 .hea 只差最后一列:现行版两个信号的描述字段写的是 ECG,而 .hea- 里写的是 record 30, signal 0 与 record 30, signal 1。这个差别本身就在说明一件事——官方从未为这两个通道确认过导联身份。附带清单还有 RECORDS(23 行记录名)、ANNOTATORS(两行,说明两种注释扩展名的角色)、SHA256SUMS.txt(校验和)、vf-30.png(记录 30 的波形图)与两个 index 页面文件。
2.2 字段与信息的完整字典
需要强调:这里的"字段"不是表格列,而是 WFDB 头文件的关键字行、信号行与注释行。
| 信息项 | 所在位置 | 取值形态 | 含义 | 使用注意 |
|---|---|---|---|---|
| 记录名 | .hea 第 1 行首字段 |
30–52 |
记录标识 | 纯数字,字面量就是文件名前缀 |
| 信号条数 | .hea 第 1 行第 2 字段 |
2 |
通道数 | 全部 23 段均为 2 |
| 采样率 | .hea 第 1 行第 3 字段 |
250 |
每秒采样点数 | 全部 23 段一致 |
| 采样点数 | .hea 第 1 行第 4 字段 |
如 22099250 |
记录总长度 | 除以 250 即秒数 |
| 起始时刻 | .hea 第 1 行第 5 字段 |
如 12:00:00 |
记录开始挂钟时间 | 多数为 12:00:00,少数是真实时刻 |
| 文件名 | .hea 信号行第 1 字段 |
30.dat |
信号存放位置 | 两通道共用同一个 .dat |
| 存储格式 | .hea 信号行第 2 字段 |
212 |
两通道 12 位打包 | 不是 16 位,也不是浮点 |
| 增益 | .hea 信号行第 3 字段 |
800 或 200 |
ADC 单位/毫伏 | 记录 39、47 为 200,其余为 800 |
| 位分辨率 | .hea 信号行第 4 字段 |
12 |
ADC 位数 | 与格式 212 配套 |
| ADC 零值 | .hea 信号行第 5 字段 |
0 |
零电平对应的整数值 | 换算时先减零值 |
| 初始值 | .hea 信号行第 6 字段 |
如 51 |
首采样点整数值 | 用于校验读取是否对齐 |
| 校验和 | .hea 信号行第 7 字段 |
如 -24065 |
采样点累加和 | 用于校验 .dat 完整性 |
| 块大小 | .hea 信号行第 8 字段 |
0 |
每帧字节数 | 由 wfdb 按格式自动计算 |
| 信号描述 | .hea 信号行第 9 字段 |
ECG |
通道含义 | 两通道相同,无导联名 |
| 采集来源 | .hea 注释行 |
#Produced by xform… |
转录工具与起始偏移 | xform 与 xform_new 两种 |
| 室颤起始 | .hea 注释行 |
#vfon: 07:54:33 |
室颤起始的已历时刻 | 40、42、49 三段无此行 |
| 心搏位置 | .ari / .atr |
采样点序号 | 事件发生时刻 | 除以 250 得秒数 |
| 心搏类型 | .ari / .atr |
类型码 | 如 N、V、F |
沿用 PhysioBank 标准定义 |
| 注释角色 | ANNOTATORS |
两行文本 | ari 未审校 / atr 参考 |
决定该不该当金标准 |
心搏类型码沿用 PhysioBank 的标准定义。本库涉及的心律以室性为主,最常出现的几类如下:
| 类型码 | 含义 | 映射到 AAMI 五分类 |
|---|---|---|
N |
正常心搏 | N |
L |
左束支阻滞心搏 | N |
R |
右束支阻滞心搏 | N |
A |
房性早搏 | S |
a |
伴差异性传导的房性早搏 | S |
J |
交界性早搏 | S |
S |
室上性早搏或异位搏动 | S |
V |
室性早搏 | V |
r |
R-on-T 室性早搏 | V |
F |
室性融合波 | F |
f |
起搏与正常心搏融合 | F |
/ |
起搏心搏 | Q |
Q |
无法分类的心搏 | Q |
! |
心室颤动波 | 通常单列,不进入五分类 |
最后一行值得单独说。室颤期间的心搏无法按常规方式逐搏定义,PhysioBank 用 ! 标记颤动波。如果你的流程把 ! 直接丢进 AAMI 五分类,会把一整段室颤压成若干"未知心搏",从而丢掉最关键的标签。
2.3 一条记录的完整解剖
以记录 30 为例。头文件第一行:
30 2 250 22099250 12:00:00
含义依次是:记录名 30,2 个通道,250 Hz,共 22099250 个采样点,起始挂钟时刻 12:00:00。22099250 ÷ 250 = 88397 秒,即 24 小时 33 分 17 秒——与官方表格里记录 30 的时长一栏完全一致。这种一致性是全库的普遍情况:头文件给出的采样点数除以 250,就是官方表格里的时长。
接下来两行信号声明:
30.dat 212 800 12 0 51 -24065 0 ECG
30.dat 212 800 12 0 145 21051 0 ECG
从右往左读:描述 ECG;块大小 0;校验和 -24065;初始值 51;ADC 零值 0;位分辨率 12;增益 800;存储格式 212;文件名 30.dat。第二通道除初始值 145 与校验和 21051 外完全相同。增益 800 意味着 800 个整数值对应 1 毫伏,约 1.25 微伏/单位。
最后两行注释给出它的身份与关键事件:
#Produced by xform_new from record 30, beginning at 26:35.000
#vfon: 07:54:33
第一行说明这段信号是用 xform_new 从原始磁带的第 26 分 35 秒开始转录的——也就是说,公开的记录起点不是原始 Holter 的起点,前面那段被截掉了。第二行是本库最重要的元数据:室颤起始于记录开始后的 7 小时 54 分 33 秒。换算成采样点,就是第 7132500 个采样点附近。
读取信号与标注的标准写法:
import wfdb
#-- 远程直读,无需先下载;pn_dir 指向 PhysioNet 上的库名
rec = wfdb.rdrecord('30', pn_dir='sddb')
ari = wfdb.rdann('30', 'ari', pn_dir='sddb') #-- 未审校算法标注,23 段全有
atr = wfdb.rdann('30', 'atr', pn_dir='sddb') #-- 审校参考标注,仅 12 段有
print(rec.fs, rec.sig_len, rec.sig_name) #-- 250 22099250 ['ECG', 'ECG']
print(rec.p_signal.shape) #-- (22099250, 2) 已换算成毫伏
print(rec.comments) #-- 采集来源与 #vfon 注释
print(len(ari.sample), len(atr.sample))
p_signal 是已按增益与零值换算过的浮点毫伏值;需要原始整数用 d_signal。注意 rec.sig_name 返回的是 ['ECG', 'ECG'],不是导联名——这一点在写论文的"数据"一节时必须如实说明。
2.4 记录与标注的映射:23 对 12
这是本库最容易出事的一处结构。23 段记录各有 .ari,但只有 12 段另有 .atr:30、31、32、34、35、36、41、45、46、49、51、52。剩下 11 段(33、37、38、39、40、42、43、44、47、48、50)没有审校标注。
| 标注扩展名 | 段数 | 覆盖的记录 | 官方定义 | 可否当金标准 |
|---|---|---|---|---|
.ari |
23 | 全部 30–52 | unaudited beat annotations | 不可,算法初标未过人工 |
.atr |
12 | 30、31、32、34、35、36、41、45、46、49、51、52 | reference beat annotations | 可,但仍需注意室颤段的心搏定义 |
| 无 | 11 | 33、37、38、39、40、42、43、44、47、48、50 | 无 | 需自行标注或只用信号 |
ANNOTATORS 文件只有两行,把这件事写得很直白:ari 是 unaudited beat annotations,atr 是 reference beat annotations。官方在发布公告里的措辞是"一套未经审校的心搏标注文件,以及一套不完整的审校标注文件",并公开征集研究者参与补全。
这个结构决定了两种完全不同的用法。做心搏分类评测,只应该用那 12 段;做室颤检测或长时程演化分析,23 段都可以用,因为这类任务依赖的是信号本身与室颤起始时刻,而不是逐搏标签。混淆这两者是本库最常见的误用。
2.5 与同类资源的字段差异
| 对比项 | SDDB | MIT-BIH 恶性室性心律失常库 | MIT-BIH 心律失常库 | 长期 ST 库 | INCART |
|---|---|---|---|---|---|
| 记录数 | 23 | 22 | 48 | 86 | 75 |
| 单段时长 | 3:56–25:08 | 约 30 分钟 | 约 30 分钟 | 21–24 小时 | 30 分钟 |
| 通道数 | 2 | 2 | 2 | 2–3 | 12 |
| 采样率 | 250 Hz | 250 Hz | 360 Hz | 250 Hz | 257 Hz |
| 终末事件 | 持续性室速,多数心脏骤停 | 恶性室性心律失常 | 各类心律失常 | 短暂心肌缺血 | 冠心病相关心律失常 |
| 心搏标注 | 23 段算法标注 + 12 段审校标注 | 有 | 有 | 有 | 有 |
| 标注审校 | 不完整,官方征集补全 | 完整 | 完整 | 完整 | 自动加人工修正 |
| 临床信息 | 23 行表,大量 Unknown | 有限 | 有限 | 详细临床摘要 | 逐患者诊断 |
| 许可 | ODC-BY 1.0 | ODC-BY 1.0 | ODC-BY 1.0 | ODC-BY 1.0 | ODC-BY 1.0 |
这张表要说清的只有一件事:SDDB 在"终末事件真实性"这一列上唯一性地突出,在"规模"“标注完整度”"临床信息"三列上都不占优。它的定位不是替代任何一个库,而是补上别人补不了的那一行——真实发生的死亡。
2.6 两个通道分别测的是什么
官方没有给出导联名,这一点要反复强调。从 .hea- 的旧版描述 record 30, signal 0 与 record 30, signal 1 可以看出,官方自己也只把它们当作"第 0 通道"与"第 1 通道"。但双通道 Holter 的常规配置仍有规律可言,理解这些规律有助于判断某段信号能不能用于某个分析:
| 通道 | 常规 Holter 配置下的可能角色 | 常见失真来源 | 对分析的影响 |
|---|---|---|---|
| 通道 0 | 多为模拟 V1 或 V5 位置的胸导联 | 电极贴合度、呼吸起伏 | 幅度随体位变化大,绝对幅度不可靠 |
| 通道 1 | 多为模拟 II 或 V5 位置的第二路 | 肌电干扰、上肢活动 | QRS 主波方向可能与通道 0 相反 |
| 两通道共同 | 共享同一时间轴与同一放大器 | 磁带转录引入的抖晃 | 时间轴误差是相关的,不能靠互相校正消除 |
上表是双通道 Holter 的通用配置推断,不是官方声明。它的价值在于提示两件事:第一,不要假设其中一个是 II 导联;第二,两通道来自同一次放大与同一盘磁带,噪声在时间上相关,做"用一路去噪另一路"的实验时这个相关性会让结果虚高。
§3 它是谁的数据:一批 1980 年代的 Boston 磁带
3.1 贡献者与机构
官方页面没有列出作者栏,发布公告与参考文献共同指向同一个人:Scott D. Greenwald。他在 MIT 电气工程与计算机科学系做硕士期间,为研究心室颤动检测器而采集并汇编了这批 Holter 记录,1986 年的硕士论文《Development and analysis of a ventricular fibrillation detector》是官方指定的引用文献。论文全文托管在 MIT 的 DSpace(handle 1721.1/92988)。
数据后来由 PhysioNet 承接托管,2004 年 7 月 2 日以 “Sudden Cardiac Death Holter Database” 之名正式发布。官方页面把它标注为 Open Access,访问政策是"任何人都可以访问文件,只要遵守指定许可的条款"。从捐献方式看,它是典型的 PhysioNet 早期模式:一位研究者把已经完成的历史资料交给平台,平台负责格式规范化与长期托管,没有基金持续投入,因此也几乎没有后续更新。
3.2 时间线
| 时间 | 事件 | 意义 |
|---|---|---|
| 1980 年代 | 波士顿地区医院采集 Holter 记录 | 原始采集,模拟磁带 |
| 1985 | Greenwald 等发表心律失常检测器性能置信限研究 | 方法学基础 |
| 1986 | Greenwald 硕士论文完成,成为官方指定引用 | 数据集的学术出处 |
| 1988 | Goldberger 等基于本批数据发表心率振荡与分岔研究 | 首次用于非线性动力学分析,基于 16 例窦性心律者 |
| 1989 | 半小时摘录以 MIT-BIH 恶性室性心律失常库之名开放 | 子集先行公开 |
| 1989 | Courtemanche 等的复杂心律失常建模研究引用记录 47 | 个案层面的使用 |
| 2004-07-02 | 完整 23 段在 PhysioNet 发布,版本 1.0.0 | 全集首次公开 |
| 2004 | 官方同期发布征集公告,邀请补全标注与扩充记录 | 标注不完整的官方说明 |
| 2008 | 被列为 PhysioNet/CinC 2008 T 波电交替挑战赛的候选来源库 | 进入挑战赛视野 |
最后一行属于使用侧的观察而非官方版本变更。它的意义在于说明:SDDB 长期处于"被引用但不被维护"的状态,二十年没有出过版本 1.0.1。
3.3 采集场景:为室性心律失常研究而汇编
官方原话是:这些记录主要采集于 1980 年代的波士顿地区医院,后来被汇编为室性心律失常研究的一部分。这句话包含两个关键限定。第一是"主要"——不是全部,说明来源机构不止一家,采集规范可能不一致。第二是"汇编"——不是为这项研究专门采集的,而是从既有临床 Holter 里挑出来的。
由此可以推出它的人群画像:记录期间发生持续性室性心动过速,多数发生心脏骤停;底层心律 18 例窦性(其中 4 例间歇起搏)、1 例持续起搏、4 例心房颤动。临床信息表还给出了部分患者的病史,包括心力衰竭、冠状动脉旁路移植、二尖瓣置换、心脏手术、急性髓系白血病、肥厚型心肌病伴猝死家族史、冠心病、陈旧性心肌梗死合并室速史等,最年轻的一例 17 岁,最年长的一例 89 岁。相当比例的性别、年龄、病史、用药四列为 Unknown。
官方对这批数据的自我判断相当克制:由于是回顾性汇编,存在重要局限;患者信息有限,有时完全缺失,包括用药方案与剂量;这些病例未必能代表自发性猝死事件,因为受试者群体很可能高度异质。这段话应该被原文引用,而不是被改写成更乐观的表述。
3.4 数据来源的三个层次
理解这份数据要先分清三层:原始 Holter 磁带(1980 年代,未公开,且多半已不存在)、转录后的双通道信号(公开的基本单位,起点是转录偏移点而非原始起点)、心搏标注(公开的监督信号,两层质量)。
第一层不可得,意味着转录时丢失的信息无法追回,也无法验证。第二层的起点偏移值得注意——记录 30 的头文件写着 beginning at 26:35.000,说明公开信号从原始磁带的第 26 分 35 秒开始;记录 47 甚至是 beginning at 44:10.000。这意味着不同记录的起点对齐到挂钟时间的口径并不统一,做跨记录的时刻对比时要小心。第三层的两层质量已在 §2.4 说明。
§4 它最独特的东西:把死亡录了下来
4.1 为什么这件事不可复制
SDDB 的稀缺性不是"采得精细",而是"不可能再采"。要复现它,你需要一批患者恰好在做 24 小时 Holter 的当天发生致死性心律失常,还需要这些记录被完整保存、转录、捐赠。任何前瞻性研究都设计不出这样的队列——伦理上不能等着患者死,技术上也无法预知谁会在哪一天出事。
于是它的稀缺性来自一次历史窗口的巧合:1980 年代 Holter 检查刚开始普及,模拟磁带是唯一载体,而记录下来的东西在几十年后被数字化并公开。这个窗口已经关闭。现代的数字 Holter 理论上能采到同样的事件,但数据分散在各家医院的商业系统里,公开意愿与合规成本都完全不同。
所以它的价值判断标准也和别的库不一样。别的库可以问"够不够大"“标注够不够准”;它只能问"还有没有第二份"。答案目前是没有。
4.2 与恶性室性心律失常库的关系:全集与半小时摘录
官方在发布公告里明确说:这些记录中,半小时摘录自 1989 年起就以 MIT-BIH Malignant Ventricular Ectopy Database 之名向研究者开放,且这个库继续存在。站内对应的 VFDB 词条就是这份摘录集,DOI 为 10.13026/C22P44。
| 对比项 | SDDB(本库) | MIT-BIH 恶性室性心律失常库(VFDB) |
|---|---|---|
| 关系 | 全集 | 从同批记录中切出的半小时摘录 |
| 记录数 | 23 段 | 22 段 |
| 单段时长 | 3:56–25:08 | 约 30 分钟 |
| 覆盖的事件 | 完整演进过程,含室颤前数十小时 | 围绕事件的半小时窗口 |
| 适合的任务 | 长时程前兆分析、预警提前量评估 | 事件片段的分类与检测 |
| 不适合的任务 | 需要对照组的统计推断 | 室颤前的长程动力学分析 |
| DOI | 10.13026/C2W306 | 10.13026/C22P44 |
这个关系带来一个实用提醒:如果一篇论文声称"使用了 MIT-BIH 恶性室性心律失常库",它用的是半小时窗口;如果它要研究室颤之前几十小时发生了什么,必须用 SDDB。反过来,如果你的模型只需要围绕事件的一段固定长度输入,VFDB 更省事,也不必处理 1.1 GB 的体积。
还有一处需要留意:SDDB 23 段,VFDB 22 段,两者并不一一对应。官方没有逐条给出对应关系,因此不能假定 VFDB 的每一段都能在 SDDB 里找到母记录。
4.3 案例:记录 30 的二十四小时与第七小时五十四分
取记录 30。它是全库信息最完整的一段之一:24 小时 33 分 17 秒,起始挂钟时刻 12:00:00,转录自原始磁带第 26 分 35 秒之后,室颤起始于记录开始后 7 小时 54 分 33 秒,同时拥有未审校标注 .ari 与审校标注 .atr。
把这三个数字放在一起,就能算出一件很关键的事:这段记录里,室颤之前有将近八小时的连续稳定期,室颤之后还有十六个多小时的余下记录。这是 SDDB 相对任何摘录集最本质的优势——它给了你"之前"和"之后",而不只是"之中"。
用 §2.3 的代码读出信号后,取室颤起始前后的固定窗口做对照:
import wfdb
import numpy as np
rec = wfdb.rdrecord('30', pn_dir='sddb')
fs = rec.fs
#-- 从头文件注释里解析室颤起始时刻(时:分:秒 -> 秒)
vfon = None
for c in rec.comments:
if c.lower().startswith('vfon'):
h, m, s = [int(x) for x in c.split(':')[1:]]
vfon = h * 3600 + m * 60 + s
print('室颤起始秒:', vfon)
def window(center_sec, half=10):
#-- 取中心前后各十秒的片段,返回两通道毫伏值
lo = max(0, int((center_sec - half) * fs))
hi = min(rec.sig_len, int((center_sec + half) * fs))
return rec.p_signal[lo:hi]
before = window(vfon - 600) #-- 室颤前十分钟处
after = window(vfon + 60) #-- 室颤后一分钟处
print(before.shape, after.shape)
print('前:', round(float(np.ptp(before[:, 0])), 3), 'mV 峰峰值')
print('后:', round(float(np.ptp(after[:, 0])), 3), 'mV 峰峰值')
这段代码的用途不是下结论,而是建立一个可复现的观察点。官方只给了室颤起始的已历时刻,没有给结束时刻,也没有给转复或终止方式的标注,所以"之后"的解读必须自己看波形,不能靠注释。
把这类观察整理成表,就成了后续讨论的支点:
| 观察位置 | 头文件能给的 | 头文件给不了的 |
|---|---|---|
| 室颤起始之前 | 已历时刻(#vfon)、连续信号、心搏标注 |
前驱期的定义、何时算"预警成功" |
| 室颤起始瞬间 | 一个时刻点,精确到秒 | 起始判定的依据与判读者 |
| 室颤持续期间 | 连续信号 | 室颤的终止时刻、是否自行终止 |
| 室颤之后 | 连续信号(本段还有十六小时) | 结局、是否复苏成功、临床处理 |
这张表最有用的是最后一列。它说明:用 SDDB 做研究时,绝大部分"标签"要自己定义,而定义的口径必须写进方法节,否则结果不可复现。
4.4 标注的两层:算法初标与人工审校
ANNOTATORS 用两行把层次说清楚了。.ari 是 unaudited beat annotations,23 段全有,由算法生成后没有过人工。.atr 是 reference beat annotations,只有 12 段有,是审校过的参考标注。
官方对标注难度的说明也很直接:标注本库的任务特别困难,因为所包含的心律非常复杂。这句话的实际含义是——在一段走向室颤的记录里,心搏形态会从规整的窦性,经过频发的室性早搏、成对室早、短阵室速、多形性室速,一直到形态完全失去周期性。算法在这一整条链条上的表现并不均匀:在规整段很准,在形态剧烈变形的段落会系统性失效。
这对使用者是实打实的约束。如果你在 .ari 上统计室性早搏的时序模式,那么算法在"快要室颤"那一段的表现最差,而你恰恰最关心那一段。这不是可以通过增加样本量解决的问题,因为它是一个与状态相关的偏差,样本越多,偏差越稳定。
| 用途 | 用 .ari 的风险 |
用 .atr 的风险 |
建议 |
|---|---|---|---|
| 心搏分类评测 | 算法错误被当作金标准 | 只有 12 段,样本小 | 只用 .atr,并在论文里写明段数 |
| 室性早搏负荷统计 | 高,形态变形段漏标 | 中,室颤段心搏定义本身就模糊 | 限定在室颤起始之前、形态尚规整的窗口 |
| 室颤检测与预警 | 低,因为不依赖心搏标签 | 低 | 主要用信号与 #vfon,标注只做辅助 |
| 心率变异性 | 高,RR 序列直接受影响 | 中,同样受室颤段影响 | 先自行检测 R 峰再做对齐 |
| 长时程节律趋势 | 中,趋势级结论对个别漏标不敏感 | 低 | 两者都可用,但不要混用 |
4.5 边界条件:什么情况下它不再适用
| 条件 | 是否适用 | 判断依据 |
|---|---|---|
| 研究室颤前的长程前兆 | 适用,且几乎无可替代 | 单段最长超过 25 小时,多数含数十小时稳定期 |
| 评估除颤或预警算法的提前量 | 适用 | 20 段带室颤起始时刻,可定义预警窗口 |
| 做心搏形态分类的外部测试 | 适用,但只能用 12 段 | 审校标注只覆盖 12 段 |
| 作为阴性对照 | 部分适用 | 40、42、49 三段官方注明无室颤,可作对照 |
| 做猝死风险的统计预测 | 不适用 | 全库均为发生事件者,无对照组 |
| 做药物或剂量相关分析 | 不适用 | 官方说明用药方案与剂量信息缺失 |
| 做导联间差异或十二导联研究 | 不适用 | 两通道且无导联名 |
| 做现代数字 Holter 的绝对性能基准 | 谨慎 | 1980 年代模拟磁带转录,噪声谱与现代设备不同 |
| 做多中心泛化验证 | 不适用 | 来源集中在波士顿地区医院 |
| 做儿科或青少年心电模型 | 不适用 | 已知年龄最小 17 岁,且多数年龄未知 |
这张表最有用的两行是第一行与倒数第三行。第一行是它的立身之本;倒数第三行提醒的是一件常被忽略的事:从它上面得到的绝对性能指标,不能直接外推到今天的数字记录设备,因为噪声来源完全不同——模拟磁带带来的是抖晃与磁带噪声,现代设备带来的是量化噪声与运动伪迹。
§5 怎么拿到:从目录树到可训练张量
5.1 目录结构与下载面
PhysioNet 的文件目录是扁平的:根目录下直接放 23 组文件,加上几个附属清单,没有按患者或日期分的子目录。全库未压缩 1.1 GB,共 110 个文件,官方提供了整库 ZIP(1.1 GB)。
| 路径 | 内容 | 数量 | 大小量级 |
|---|---|---|---|
/files/sddb/1.0.0/RECORDS |
23 行记录名 | 1 | 数十字节 |
/files/sddb/1.0.0/ANNOTATORS |
注释扩展名与角色 | 1 | 两行 |
/files/sddb/1.0.0/SHA256SUMS.txt |
全部文件的校验和 | 1 | 数千字节 |
/files/sddb/1.0.0/vf-30.png |
记录 30 的波形图 | 1 | 数十 KB |
/files/sddb/1.0.0/NN.hea |
头文件 | 23 | 约 200 字节 |
/files/sddb/1.0.0/NN.hea- |
旧版头文件副本 | 23 | 约 250 字节 |
/files/sddb/1.0.0/NN.dat |
双通道信号 | 23 | 十数 MB 至 60 余 MB |
/files/sddb/1.0.0/NN.ari |
未审校心搏标注 | 23 | 数十 KB 至 250 余 KB |
/files/sddb/1.0.0/NN.atr |
审校心搏标注 | 12 | 数十 KB 至 260 余 KB |
单段体积差异很大:记录 30 的信号文件是 63.2 MB,而记录 41(3 小时 56 分)只有十数 MB。规划磁盘时按整库 1.1 GB 准备最省心;只想做方法验证的话,先拿记录 30 一段就足够跑通全流程。
5.2 批量下载
#-- 整库 ZIP,一次拿全 1.1 GB
wget -O sddb.zip https://physionet.org/content/sddb/get-zip/1.0.0/
#-- 或者用官方命令行工具
physionet download sddb --version 1.0.0
#-- 或者用 S3 免签名同步
aws s3 sync --no-sign-request s3://physionet-open/sddb/1.0.0/ ./sddb
#-- 只取清单,先确认 23 条记录名
curl -sL https://physionet.org/files/sddb/1.0.0/RECORDS | tr -s '\n' ' '
四条路径里,ZIP 最省事但一次性落盘 1.1 GB;S3 同步支持断点续传,适合网络不稳的环境;physionet 命令行工具会顺带校验。只做流程验证时,建议先只下记录 30 的四个文件,跑通后再决定要不要全量。
5.3 用 wfdb 直接流式读取
import wfdb
from collections import Counter
names = wfdb.get_record_list('sddb')
print(len(names)) #-- 23
#-- 逐段读标注,注意 atr 不是每段都有
for name in names[:5]:
ari = wfdb.rdann(name, 'ari', pn_dir='sddb')
try:
atr = wfdb.rdann(name, 'atr', pn_dir='sddb')
n_atr = len(atr.sample)
except FileNotFoundError:
n_atr = 0 #-- 11 段没有审校标注
print(name, len(ari.sample), n_atr)
get_record_list 从 RECORDS 解析,不需要本地磁盘,代价是每段重新走网络。全库 1.1 GB,全部走网络读取会很慢,正式实验建议先落盘。
5.4 转成通用的表结构
下游若是 PyTorch 或 TensorFlow 的常规管线,通常需要切成定长片段。切窗时建议以室颤起始时刻为锚点,而不是从头等距切:
import wfdb
import numpy as np
import pandas as pd
FS = 250
WIN = 10 * FS #-- 十秒一窗
def vfon_sec(name):
#-- 从头文件注释里取室颤起始秒;无则 None
h = wfdb.rdheader(name, pn_dir='sddb')
for c in h.comments:
if c.lower().startswith('vfon'):
hh, mm, ss = [int(x) for x in c.split(':')[1:]]
return hh * 3600 + mm * 60 + ss
return None
rows = []
for name in ['30', '31', '32']:
rec = wfdb.rdrecord(name, pn_dir='sddb')
vf = vfon_sec(name)
if vf is None:
continue
ari = wfdb.rdann(name, 'ari', pn_dir='sddb')
for w in range(0, rec.sig_len // WIN):
lo, hi = w * WIN, (w + 1) * WIN
t_end = hi / FS #-- 该窗结束时的已历秒数
#-- 以室颤起始为界:之前记 0,之后记 1,并留出灰区
label = 0 if t_end < vf - 60 else (1 if t_end >= vf else -1)
if label < 0:
continue #-- 起始前后一分钟为灰区,丢弃
rows.append({'record': name, 'win': w, 'label': label,
'ch0': rec.p_signal[lo:hi, 0].astype('float32'),
'ch1': rec.p_signal[lo:hi, 1].astype('float32')})
df = pd.DataFrame(rows)
print(df.groupby(['record', 'label']).size())
这段代码只用三段演示口径。上正式实验时有四处必须改:一是灰区宽度要写进方法节,它直接决定正负样本边界;二是要不要把室颤之后的十几小时全部标为正样本,需要明确——多数研究只取室颤前的一段与室颤后的一小段;三是 label 在这里是逐窗的,与心搏级标注不同源,不要混用;四是只用了 .ari,如果是心搏级任务必须换到审校过的 12 段。
5.5 注释读取与 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',
'e': 'Q', 'j': 'Q', 'n': 'Q', 'E': 'Q', '/': 'Q', 'Q': 'Q'}
ann = wfdb.rdann('30', 'atr', pn_dir='sddb') #-- 审校标注,12 段之一
sym = [s for s in ann.symbol if s in AAMI]
print(len(ann.symbol), len(sym), len(ann.symbol) - len(sym))
最后打印的差值是被映射表排除的类型数。这个值值得看一眼:它通常包含 !(室颤波)以及各类非心搏注释。如果差值很大,说明你取的窗口落在了室颤段里,而不是稳定段里。
映射表不是本库独有的,它来自心电算法评价的通行分组:束支阻滞与正常心搏并作 N,室上性与房性并作 S,室性与 R-on-T 并作 V,融合波作 F,其余作 Q。做跨库对比时必须用同一张表,否则数字不可比。
5.6 五个最容易出错的地方
| 位置 | 错误写法 | 后果 | 正确做法 |
|---|---|---|---|
| 取标注 | 默认每段都有 .atr |
11 段会抛文件不存在错误 | 先检查 RECORDS 里是否有对应 .atr,或捕获异常 |
| 取标注 | 把 .ari 当参考标注用 |
算法错误进入监督信号 | 只有 .atr 是 reference beat annotations |
| 幅度换算 | 直接用整数喂模型 | 39、47 两段增益是 200,其余是 800,幅度不可比 | 用 p_signal 或按 .hea 的增益归一 |
| 时间定位 | 把 #vfon 当作记录内的绝对采样点 |
单位错,差 250 倍 | 先按 时:分:秒 解析成秒,再乘 250 |
| 通道命名 | 假设通道 0 是 II 导联 | 论文里写出没有依据的导联结论 | 头文件描述字段只有 ECG,如实写"双通道,导联未标注" |
§6 与库内条目的关系
6.1 关系图谱
SDDB 在心电类内容里的位置,可以用"时长 × 事件严重度"两个轴摆出来:
| 条目 | 时长 | 事件性质 | 与 SDDB 的关系 |
|---|---|---|---|
| MIT-BIH 恶性室性心律失常库 | 约 30 分钟 | 恶性室性心律失常 | 同源:它是本库半小时摘录的版本 |
| MIT-BIH 心律失常库 | 约 30 分钟 | 各类心律失常 | 对照:同为短片段,无终末事件 |
| 长期 ST 库 | 21–24 小时 | 短暂心肌缺血 | 对照:同为长时程,事件性质不同 |
| INCART | 30 分钟 | 冠心病相关心律失常 | 对照:十二导联,但无终末事件 |
| 噪声压力测试库 | 数分钟至数十分钟 | 纯噪声 | 工具:叠加到本库做鲁棒性评测 |
| 欧洲 ST-T 库 | 约 2 小时 | 缺血性 ST 改变 | 对照:同为长程,事件为缺血而非致死性心律失常 |
| 本库 | 3:56–25:08 | 持续性室速、心脏骤停 | 唯一以真实致死性事件为终点 |
这条关系线的意义在于:如果你要论证一个预警算法有效,只用短片段库说服力有限,因为短片段库没有"之前",只有"之中";SDDB 提供的是事件之前的连续时间轴,而这正是预警任务的全部前提。
6.2 检索路径
| 你要找的东西 | 检索词 | 会落到哪里 |
|---|---|---|
| 同批记录的半小时摘录 | VFDB、Malignant Ventricular Ectopy |
§4.2 的关系表 |
| 心搏类型码的标准定义 | PhysioBank annotation、MIT-BIH |
§2.2 的类型码表,多个库通用 |
| 长时程缺血对照 | Long-Term ST、European ST-T |
§6.1 表 |
| 噪声叠加的来源 | NSTDB、noise stress |
§4.5 的边界表与 §6.1 |
| 十二导联长时程对照 | INCART |
§2.5 与 §6.1 |
| 除颤与可电击节律识别 | shockable rhythm、AED |
§1.4 的处境表 |
| 挑战赛来源 | CinC 2008、T-wave alternans |
§8.2 |
6.3 与其他库的分工判据
选数据集时最实际的问题不是"哪个最好",而是"哪个负责哪一段"。下面把常见的心电任务逐项拆开,看 SDDB 该站在哪一格:
| 任务 | 主粮仓该用什么 | SDDB 的位置 | 原因 |
|---|---|---|---|
| 心搏形态分类 | 大体量短片段库 | 外部测试(仅 12 段) | 审校标注不完整,规模太小 |
| 房颤检测 | 长时程房颤库 | 辅助 | 本库只有 4 例房颤,且都是终末事件病例 |
| 室颤与可电击节律识别 | 混合多库 | 主力之一 | 唯一提供真实终末事件的公开来源 |
| 室颤前兆与预警 | 无可替代 | 主力 | 需要事件前的连续时间轴 |
| 长时程心率动力学 | 长时程大库 | 辅助 | 只有 23 段,但事件真实 |
| 噪声鲁棒性 | 噪声库加任一长时程库 | 主力之一 | 模拟磁带噪声真实且与现代设备不同 |
| 缺血与 ST 分析 | 长期 ST 库、欧洲 ST-T 库 | 不适用 | 本库事件性质不同,无 ST 标注 |
| 十二导联形态学 | 十二导联长时程库 | 不适用 | 只有两个通道 |
这张表的前四行是大多数人的真实处境:他们已经有主粮仓,缺的是一个不撒谎的裁判。被高估的用法是第五行及之后的所有行——把 SDDB 当通用长时程库用的人不少,但真正需要它的场景集中在"致死性事件的演进"这一格。
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
先纠正一个常见的误判:把"样本少、标注不全"当成"质量差"。SDDB 的 23 段不是采集能力不足,而是这类事件本来就不可前瞻性采集(§4.1)。评价它的时候不能套用"越大越好"的尺子,而应该问:在它能回答的那个问题上,还有没有第二份数据?
这个校正会改变五维评分的读法。规模与标注完整度两项它拿不到高分,但在"任务唯一性"上它几乎无可替代。把这两件事混在一起看,就会得出"不值得用"的错误结论。
7.1 五维评分
按 DAIMS 的常用维度打分,每项 1–5 分,分数针对"作为致死性心律失常研究数据"这个用途:
| 维度 | 分数 | 理由 |
|---|---|---|
| 数据规模 | 2 | 23 段、23 名患者,长时程但样本数少 |
| 标注质量 | 2 | 23 段算法标注未审校,仅 12 段有审校标注 |
| 任务适配 | 5 | 唯一公开提供真实致死性事件演进过程的长程库 |
| 文档完整 | 4 | 官方把规模、限制、征集意图写得很清楚,但无逐条临床摘要 |
| 可获取性 | 4 | 免注册、ODC-BY、有整包下载,但 1.1 GB 体积偏大 |
五项合计 17 分(满分 25)。这个分数结构说明它是明显的"偏科生":任务适配拉满,规模与标注拖后腿。用它的人必须清楚自己要的是哪一科。
7.2 标注质量的两层
第一层是类型对不对。.atr 经过人工审校,是 reference beat annotations;.ari 是算法输出,官方标为 unaudited。两者不应该混用,也不应该互相补充——把审校段的 .atr 和未审校段的 .ari 拼起来训练,等于在同一个模型里混了两种标注标准。
第二层是心搏定义在室颤段是否成立。室颤期间心电活动失去周期性,"心搏"这个概念本身就模糊。PhysioBank 用 ! 标记颤动波,但室颤的起止边界、颤动波是否逐条标出,官方页面没有说明。因此任何涉及室颤段内部的心搏级统计都要非常谨慎。
| 用途 | 建议用哪套标注 | 风险 |
|---|---|---|
| 心搏分类评测 | .atr,12 段 |
样本小,结论不稳 |
| 室性早搏时序分析 | .atr,限定在室颤前的规整段 |
形态变形段算法漏标 |
| 室颤检测与预警 | 不用标注,只用信号与 #vfon |
标签口径需自建并公开 |
| 心率变异性 | 自行检测 R 峰 | 室颤段与频发早搏段会污染 RR 序列 |
| 长时程趋势 | 两者皆可,但不要混用 | 跨段标准不一致 |
7.3 已知的、可枚举的偏差
| 偏差 | 表现 | 影响 |
|---|---|---|
| 选择偏差 | 全部为发生致死性事件的病例,无对照组 | 无法做统计意义上的风险预测 |
| 回顾性汇编 | 官方明确说明患者信息有限、有时完全缺失 | 用药、剂量、既往史大量未知 |
| 时代偏差 | 1980 年代模拟磁带转录 | 噪声谱与现代数字记录不同 |
| 地域集中 | 波士顿地区医院 | 人群与设备单一,不代表其他人群 |
| 起点偏移 | 各记录的转录起点不同(0 至 44 分不等) | 跨记录的时刻对齐需谨慎 |
| 标注不完整 | 11 段无审校标注 | 心搏级任务只能用 12 段 |
| 增益不一致 | 39、47 两段为 200,其余为 800 | 整数幅度跨段不可比 |
| 通道语义缺失 | 两通道描述均为 ECG |
无法做导联相关分析 |
7.4 外部验证矩阵
SDDB 最常被引用的方式,是当"真实终末事件"的来源。这类用法可以从公开论文的摘要里看出轮廓。
| 验证角色 | 做法 | 讲得通的地方 | 风险 |
|---|---|---|---|
| 室颤检测器的测试集 | 在别的库上训练,本库上测灵敏度 | 事件真实,非人工构造 | 样本只有 23 段,置信区间宽 |
| 除颤决策算法评估 | 用长程背景评估误放电 | 背景心律复杂,接近真实难度 | 无临床结局,无法评估决策的临床后果 |
| 去噪算法的压力测试 | 叠加噪声后看检测性能 | 本底噪声真实 | 1980 年代噪声谱与现代不同 |
| 综述中的稀缺性论据 | 引用其规模说明数据缺口 | 官方页面自述限制 | 不应被改写成"代表性队列" |
值得注意的是第二行。除颤决策不只是"识别出可电击节律",还包括"不该放电时不放电"。SDDB 里有起搏、房颤、频发室早等大量"不该放电"的心律,这确实是好的阴性材料;但它没有给出任何临床处理与结局,所以算法做出"放电"判断之后会发生什么,无法用它评估。
反过来也要防止把外测成绩当作绝对标准。23 段里每一段的时长差异巨大(最短 3 小时 56 分,最长 25 小时 8 分),按段平均会把一段 25 小时的记录和一段 4 小时的记录等权处理。合理的做法是显式声明是按段还是按时间加权,并在结果里分别报告。
7.5 它为何能持续:稀缺的不是数据量,是那件事本身
它没有更新、没有扩版、标注至今不完整,却仍然被引用,原因只有一个:它占着一个别人没法再占的坑——真实发生的死亡被连续记录了下来。官方在 2004 年的发布公告里公开征集更多记录与标注贡献,二十多年过去仍是版本 1.0.0,这本身就说明补充这类数据有多难。只要这个前提不变,它作为证据的价值就不会被新库替代。
§8 生态位与历史
8.1 历史沿革
SDDB 的诞生路径与后来大多数捐赠型数据库不同。它不是一个机构决定公开一批数据,而是一位研究者的个人资料在近二十年后被平台承接。1986 年 Greenwald 的硕士论文完成,1989 年这批记录的半小时摘录以恶性室性心律失常库之名开放,2004 年 PhysioNet 才把完整 23 段作为独立数据集发布。
这条路径解释了它身上几个"不像现代数据集"的特征:没有统一的采集协议文档,没有伦理审查编号的公开说明,没有逐条临床摘要,标注也不完整。它是历史资料的数字化,不是按现代标准建设的数据资产。把这些当成缺陷是对的,但把它们当成"可以修好的缺陷"就错了——原始磁带与纸质病历大概率已经不存在。
8.2 与挑战赛的关系
PhysioNet/CinC 2008 的 T 波电交替挑战赛把本库列为候选来源数据库之一。该挑战赛页面对本库的描述是:23 段完整的双通道 Holter 记录,每段 7 至 25 小时,来自记录期间发生持续性室性心动过速与心脏骤停的患者;信号以 250 Hz 数字化;其中 12 段经过逐搏精心标注,其余有算法生成的心搏标注;没有临床对照信息。
这段描述与官方表格有一处需要说明的差异:挑战赛页面写"7 至 25 小时",而官方记录表里最短的一段是记录 41 的 3:56:00。两处都以官方发布为准,最短时长应按官方表格的 3 小时 56 分理解;挑战赛页面的表述属于当时的概略描述。挑战赛页面另外确认了两件事——250 Hz 与"12 段精心标注",这两点与现行文件目录完全一致。
需要强调的是:本库只是候选来源之一,挑战赛最终采用的记录与公开库并不等同。想研究室颤演进,必须用原始库,不能把挑战赛数据当成它的替身。
8.3 生态位:证据而不是粮仓
| 角色 | 主力粮仓 | 证据 |
|---|---|---|
| 典型用法 | 大规模训练、预训练 | 特定问题的唯一可用材料 |
| 需要的前提 | 数据量大、标签细 | 事件罕见且不可复现 |
| 代表库 | 心律失常库、短片段大库十二导联库 | 本库 |
| 失败模式 | 过拟合、标签噪声累积 | 样本少导致结论不稳、标签口径自建 |
SDDB 坐在右边这一列。把证据当粮仓用,会立刻遇到规模瓶颈;把粮仓当证据用,会立刻遇到"问题根本没被回答"的尴尬。两者不能互换。
8.4 谁在用它,用来做什么
从公开文献的摘要看,使用者集中在三类:做致死性心律失常检测的信号处理组(把它当真实事件的测试集)、做除颤与监护设备的工程组(把它当长程复杂背景的评测材料)、做心电脑电去噪的组(把它当真实噪声来源)。近年也有综述类文章把它列为心电算法评估的常用公开资源之一。第一类最少,但正是它最不可替代的位置。
§9 使用指引
9.1 三种角色的路径
| 角色 | 第一天 | 第一周 | 注意事项 |
|---|---|---|---|
| 猝死机制研究者 | 读官方页面与临床信息表,跑通 §5.3 的读取 | 按 #vfon 建立事件前后窗口,定义前兆口径 |
口径必须写进方法节(§4.3) |
| 设备工程师 | 用记录 30 跑通全流程 | 把 40、42、49 三段作阴性对照,评估误报 | 报告按段与按时间分别给(§7.4) |
| 算法研究者 | 只用审校过的 12 段做评测 | 建立与短片段库的跨库对照 | 不要混用 .ari 与 .atr(§5.5) |
9.2 一条最短的可用流程
#-- 1. 取记录清单,确认 23 条
curl -sL https://physionet.org/files/sddb/1.0.0/RECORDS -o RECORDS
tr -s '\n' ' ' < RECORDS
#-- 2. 确认哪些段有审校标注(.atr),只有 12 段
for r in $(cat RECORDS); do
code=$(curl -s -o /dev/null -w '%{http_code}' \
"https://physionet.org/files/sddb/1.0.0/${r}.atr")
[ "$code" = "200" ] && echo "$r atr" || echo "$r -"
done > atr_availability.txt
#-- 3. 抽出全部头文件里的室颤起始时刻
for r in $(cat RECORDS); do
echo -n "$r "
curl -sL "https://physionet.org/files/sddb/1.0.0/${r}.hea" \
| grep '^#vfon' || echo "(no VF onset)"
done > vfon.txt
import wfdb
#-- 4. 只取有审校标注的段,读信号与参考标注
names = wfdb.get_record_list('sddb')
for name in names:
h = wfdb.rdheader(name, pn_dir='sddb')
print(name, h.fs, h.sig_len, h.comments)
上面 bash 段的第二步会发起 23 次请求。想省事可以直接看官方记录表里"Audited Annotations"一列的空格——那 11 个空格就是没有 .atr 的段。第三步同理:官方表格里 40、42、49 三行的 VF Onset 列写的不是时间,而是 (paced, no VF) 与 (no VF)。
9.3 引用方式
PhysioNet 的惯例是引用数据集本身加平台:
| 引用对象 | 内容 |
|---|---|
| 数据集 | Sudden Cardiac Death Holter Database, version 1.0.0. PhysioNet. 2004. doi:10.13026/C2W306 |
| 原始出处 | Greenwald SD. Development and analysis of a ventricular fibrillation detector. M.S. thesis, MIT Dept. of Electrical Engineering and Computer Science, 1986. https://dspace.mit.edu/handle/1721.1/92988 |
| 平台 | PhysioNet 平台综述论文(2026 年发表于 Nature Health),按官方页面的 Cite 按钮生成 |
| 许可 | Open Data Commons Attribution License v1.0,署名要求以官方许可全文为准 |
写论文时建议同时注明四点:使用的版本号(1.0.0);用的是 .ari 还是 .atr,以及涉及哪几段;室颤起始时刻是从头文件 #vfon 注释解析的;标签口径(灰区宽度、正负样本定义)是自己定义的。
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 文本生成 | 本文由 AI 辅助撰写,编辑把关结构与事实 |
| 数字来源 | 规模、结构、边界三类数字来自 PhysioNet 官方页面与官方文件目录 |
| 文件核验 | RECORDS、ANNOTATORS、23 份 .hea 头文件、30.hea-、39.hea- 经直接访问确认 |
| 不一致说明 | 挑战赛页面写"7 至 25 小时",官方记录表最短为 3:56:00,已在 §8.2 说明并以官方表为准 |
| 论文信息 | 仅参考公开摘要与官方参考文献列表,未读全文 |
| 未核验内容 | 各心搏类型的具体条数、.atr 的逐条审校范围、室颤终止时刻、23 段与 VFDB 22 段的对应关系 |
| 图像 | 无配图;封面为生成式提示词,不含真实心电图 |
| 免责 | 本文不构成临床建议;数据使用须遵守 ODC-BY 1.0 署名要求 |
常见误读(避坑清单)
| 说法 | 实际 | 证据 |
|---|---|---|
| 「23 段都有审校标注」 | 只有 12 段有 .atr,其余 11 段只有未审校的 .ari |
ANNOTATORS 两行定义;官方公告写"不完整的审校标注集" |
「.ari 是参考标注」 |
ari 官方定义为 unaudited beat annotations |
ANNOTATORS 文件内容 |
| 「两通道是 II 导联和 V1」 | 头文件描述字段两个通道都是 ECG,无导联名 |
23 份 .hea 的信号行第 9 字段 |
| 「增益都是 800」 | 记录 39 与 47 的增益是 200 | 39.hea、47.hea 信号行第 3 字段 |
| 「它是 25 小时一段的标准长程库」 | 单段 3:56:00 至 25:08:30,差异很大 | 官方记录表的 Signal Duration 列 |
| 「23 段都发生了室颤」 | 40、42、49 三段官方注明无室颤(其中两段为起搏心律) | 官方记录表的 VF Onset 列 |
| 「它有完整的临床信息」 | 官方明确说明患者信息有限、有时完全缺失,含用药与剂量 | 官方 Data Description 段 |
| 「可以拿它做猝死风险预测」 | 全库均为发生事件者,无对照组 | 官方描述"全部患者有持续性室性心动过速,多数心脏骤停" |
| 「记录起点就是 Holter 起点」 | 转录起点各不相同,记录 47 从原始磁带第 44 分 10 秒开始 | 47.hea 的 #Produced by 注释 |
| 「它和 VFDB 一一对应」 | SDDB 23 段,VFDB 22 段,官方未给出逐条对应关系 | 两库官方记录表规模不同 |
FAQ
Q1:SDDB 到底有多少段记录?有多少段有审校标注?
23 段,编号 30 至 52,一段一名患者。审校标注(.atr)只有 12 段:30、31、32、34、35、36、41、45、46、49、51、52。全部 23 段都另有未审校的算法标注(.ari)。
Q2:.ari 和 .atr 到底能不能混用?
不能。ANNOTATORS 文件把两者分别定义为 unaudited beat annotations 与 reference beat annotations。混用等于在同一个模型里掺入两种标注标准,评测结果失去意义。
Q3:室颤起始时刻在哪里找?
在头文件的注释行里,格式是 #vfon: 07:54:33,含义是记录开始后的已历时刻。40、42、49 三段没有这一行,官方表格对应位置写的是 (no VF) 与 (paced, no VF)。
Q4:两个通道分别是什么导联?
官方没有给。头文件信号行的描述字段两个通道都是 ECG。论文里应如实写"双通道,导联未标注",不要凭猜测写成具体导联。
Q5:为什么记录 39 和 47 的波形看起来幅度不一样?
这两段的增益是 200 ADC 单位/毫伏,其余 21 段是 800。直接用整数值比较幅度会差四倍。用 wfdb 的 p_signal 或按头文件增益归一后再比较。
Q6:它能用来训练一个室颤检测模型吗?
可以,但要清楚限制:23 段、无对照组、标签口径自建。更合适的定位是外部测试与验证,而不是训练集。做训练时建议按段划分而非随机切窗,避免同一段的相邻窗落在两侧。
Q7:它和 MIT-BIH 恶性室性心律失常库是什么关系?
后者是从同批记录切出的半小时摘录,1989 年就已开放,至今仍然存在。SDDB 是全集,包含事件之前的数十小时。两者记录数不同(23 对 22),官方未给出逐条对应关系。
Q8:体积有多大,能一次下完吗?
未压缩 1.1 GB,共 110 个文件,官方提供整库 ZIP。只想跑通流程的话,先下记录 30 的四个文件即可,它同时有 .ari 与 .atr,信息最完整。
Q9:患者信息有多全?
不全,这是官方承认的局限。临床信息表有性别、年龄、病史、用药、底层心律五列,相当比例标为 Unknown;官方明确说包括用药方案与剂量在内的信息有时完全缺失。
Q10:底层心律是怎么分布的?
官方写明:18 例窦性心律(其中 4 例间歇起搏)、1 例持续起搏、4 例心房颤动,合计 23 例。全部患者都有持续性室性心动过速,多数发生了心脏骤停。
Q11:许可允许商业使用吗?
ODC-BY 1.0 允许商业使用,条件是署名。引用时要写清数据集名称、版本与来源,具体条款以官方许可全文为准。
Q12:为什么二十多年只有一个版本?官方有扩版计划吗?
官方在 2004 年的发布公告里公开征集更多记录与标注贡献,但至今仍是版本 1.0.0,页面没有给出扩版时间表。从数据性质看,这类事件不可能前瞻性采集(§4.1),扩充只能依赖其他机构捐赠历史资料。
术语表
| 术语 | 解释 |
|---|---|
| Holter | 便携式长时程心电监护,患者随身携带记录仪生活,通常记录 24 小时以上 |
| 心源性猝死 | 因心脏原因导致的突然死亡,多由持续性室性心动过速或心室颤动引发 |
| 室性心动过速 | 起源于心室的快速心律,可分为单形性与多形性 |
| 心室扑动 | 介于室速与室颤之间的极快而相对规整的室性心律 |
| 心室颤动 | 心室电活动完全失去组织,无法有效泵血,是猝死最常见的直接原因 |
| 心脏骤停 | 心脏有效搏出停止的临床状态 |
| 持续性室性心动过速 | 持续存在而非短暂发作的室速 |
| R-on-T | 室性早搏落在前一心搏 T 波上,易诱发室颤 |
| 起搏心律 | 由人工起搏器驱动的心律 |
| 心房颤动 | 心房电活动紊乱,心室率绝对不规则 |
| 逐搏标注 | 对每一次心搏标出位置与类型 |
| WFDB | PhysioNet 使用的波形数据库格式,含头文件、信号、注释三类文件 |
.hea |
纯文本头文件,声明采样率、增益、采样点数与注释 |
.hea- |
头文件的早期版本副本,描述字段写作 record N, signal M |
.dat |
二进制信号文件,本库用 212 格式打包两路 12 位数据 |
.ari |
未经人工审校的算法心搏标注 |
.atr |
经人工审校的参考心搏标注 |
| 格式 212 | WFDB 的双通道 12 位打包存储格式 |
| 增益 | 每毫伏对应的 ADC 整数值,换算物理量时使用 |
| 校验和 | 采样点累加和,用于检测文件是否完整 |
| AAMI 五分类 | 心电算法评价的通行分组:N、S、V、F、Q |
| 可电击节律 | 除颤器应当放电的心律,主要指室颤与无脉性室速 |
| 阴性对照 | 不含目标事件的记录,用于评估误报 |
| ODC-BY | 开放数据共享署名许可 |
| PhysioNet | 由 MIT 等机构维护的生物医学信号数据平台 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 记录数 | 23(编号 30–52) | 官方页面;RECORDS 逐行 | 官方页面 |
| 患者数 | 23 | 官方页面"23 complete Holter recordings" | 官方页面 |
| 审校标注段数 | 12 | 官方记录表 Audited Annotations 列;目录文件计数 | 官方页面与官方文件 |
| 未审校标注段数 | 23 | 官方记录表 Unaudited Annotations 列 | 官方页面 |
| 单段时长范围 | 3:56:00(记录 41)–25:08:30(记录 51) | 官方记录表 Signal Duration 列 | 官方页面 |
| 通道数 | 2 | 全部 23 份 .hea 第 1 行第 2 字段 |
官方文件 |
| 采样率 | 250 Hz | 全部 23 份 .hea 第 1 行第 3 字段 |
官方文件 |
| 存储格式 | 212,12 位 | 全部 23 份 .hea 信号行第 2、4 字段 |
官方文件 |
| 增益 | 800(21 段);200(记录 39、47) | 全部 23 份 .hea 信号行第 3 字段 |
官方文件 |
| 室颤起始时刻覆盖 | 20 段带 #vfon,3 段(40、42、49)无 |
全部 23 份 .hea 注释行;官方记录表 |
官方文件 |
| 底层心律构成 | 18 窦性(4 间歇起搏)、1 持续起搏、4 房颤 | 官方 Data Description 段 | 官方页面 |
| 文件总数 | 110 | 官方文件清单 | 官方页面 |
| 总体积 | 未压缩 1.1 GB | 官方页面 Files 段 | 官方页面 |
| 许可 | ODC-BY 1.0 | 官方页面 Access 栏与许可页 | 官方页面 |
| DOI | 10.13026/C2W306 | 官方页面 Discovery 栏 | 官方页面 |
| 发布日期 | 2004-07-02 | 官方页面版本列表 | 官方页面 |
| 引用文献 | Greenwald 1986 硕士论文 | 官方页面 Citation 栏 | 官方页面 |
| 关联库 DOI | 10.13026/C22P44(半小时摘录集) | 官方发布公告 | 官方页面 |
附录 B:数字的来源分层
第一层:官方页面与官方文件。 记录数、患者数、时长范围、通道数、采样率、存储格式、增益、室颤起始时刻的覆盖范围、底层心律构成、文件总数、总体积、许可、DOI、发布日期。这些数字来自项目主页的数据描述段与记录表,以及 23 份头文件、RECORDS、ANNOTATORS 的直接访问。
第二层:其他官方页面。 CinC 2008 挑战赛页面对本库的描述(250 Hz、12 段精心标注、无临床对照信息)。它属于 PhysioNet 站内页面,但描述口径与现行记录表略有出入,已在 §8.2 说明。
第三层:未核实。 各心搏类型的具体条数、.atr 的逐条审校范围与审校者、室颤的终止时刻、23 段与 VFDB 22 段之间的记录对应关系、两名通道的具体解剖位置。这些需要下载全量并逐条比对,正文中凡涉及处均只做定性表述。
附录 C:完整参考文献
| 编号 | 文献 | 链接 | 用途 |
|---|---|---|---|
| 1 | Sudden Cardiac Death Holter Database v1.0.0 | https://physionet.org/content/sddb/1.0.0/ | 全部规模、结构、边界数据的来源 |
| 2 | 该库的 PhysioNet 文件目录 | https://physionet.org/files/sddb/1.0.0/ | RECORDS、ANNOTATORS、23 份头文件 |
| 3 | Greenwald SD. Development and analysis of a ventricular fibrillation detector. M.S. thesis, MIT EECS, 1986 | https://dspace.mit.edu/handle/1721.1/92988 | 官方指定引用文献 |
| 4 | Greenwald SD, Albrecht P, Moody GB, Mark RG. Computers in Cardiology 1985;12:383-386 | 官方参考文献列表 | 检测器性能置信限方法 |
| 5 | Goldberger AL, Rigney DR, Mietus J, Antman EM, Greenwald S. Experientia 1988;44:983-987 | 官方参考文献列表 | 基于 16 例窦性心律者的非线性动力学分析 |
| 6 | Courtemanche M, Glass L, Rosengarten MD, Goldberger AL. Am J Physiol 1989;257:H693-H706 | 官方参考文献列表 | 记录 47 的个案建模 |
| 7 | PhysioNet/CinC Challenge 2008(T 波电交替)来源页 | https://physionet.org/content/challenge-2008/1.0.0/ | §8.2 的候选来源描述 |
| 8 | 该库的 ODC-BY 1.0 许可全文 | https://physionet.org/content/sddb/view-license/1.0.0/ | 许可条款 |
| 9 | MIT-BIH 恶性室性心律失常库(同源摘录集) | https://physionet.org/content/vfdb/ | §4.2 的关系说明 |
| 10 | PhysioNet 平台综述(2026,Nature Health) | https://doi.org/10.1038/s44360-026-00096-z | 托管模式与引用规范 |
附录 D:任务代码
任务一:按室颤起始时刻建立事件前后的对照窗口
import wfdb
import numpy as np
FS = 250
PRE, POST = 3600, 600 #-- 室颤前一小时 / 后十分钟
def vfon_sec(name):
h = wfdb.rdheader(name, pn_dir='sddb')
for c in h.comments:
if c.lower().startswith('vfon'):
hh, mm, ss = [int(x) for x in c.split(':')[1:]]
return hh * 3600 + mm * 60 + ss
return None
def segments(name):
rec = wfdb.rdrecord(name, pn_dir='sddb', physical=True)
vf = vfon_sec(name)
if vf is None:
return None
lo = int(max(0, vf - PRE) * FS)
hi = int(min(rec.sig_len / FS, vf + POST) * FS)
return rec.p_signal[lo:hi], vf - lo / FS
for name in ['30', '31', '32']:
seg, rel = segments(name)
print(name, seg.shape, '室颤位于片段内第', round(rel), '秒')
任务二:检查标注覆盖并只在审校段上做心搏统计
import wfdb
from collections import Counter
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',
'e': 'Q', 'j': 'Q', 'n': 'Q', 'E': 'Q', '/': 'Q', 'Q': 'Q'}
#-- 先用官方记录表确认这 12 段有 .atr,不要靠 try/except 猜
AUDITED = ['30', '31', '32', '34', '35', '36',
'41', '45', '46', '49', '51', '52']
for name in AUDITED:
ann = wfdb.rdann(name, 'atr', pn_dir='sddb')
mapped = Counter(AAMI.get(s, 'other') for s in ann.symbol)
total = sum(mapped.values())
kept = total - mapped['other']
print(name, total, dict(mapped), '排除非心搏注释', mapped['other'])
第二段代码里 AUDITED 这个列表是从官方记录表的 Audited Annotations 列直接抄下来的,不是靠遍历试错得到的。这一点很重要:如果靠 try/except 去猜哪些段有 .atr,一旦官方补全了标注,脚本行为会静默改变。
附录 E:如果只记五件事
| 序号 | 记住什么 | 为什么 |
|---|---|---|
| 1 | 23 段长程双通道 250 Hz 心电,录到真实致死性事件 | 这是它唯一无可替代的地方 |
| 2 | 只有 12 段有审校标注,其余 11 段是算法初标 | 心搏级任务只能用那 12 段 |
| 3 | 两通道导联未标注,描述字段都是 ECG |
任何导联相关分析都无从下手 |
| 4 | 39、47 两段增益是 200,其余是 800 | 幅度跨段不可比,必须换算 |
| 5 | 无对照组,无随访,临床信息大量缺失 | 不能做风险预测,只能做事件演进分析 |
编辑说明
本文的核验时间为 2026 年 10 月 29 日。核对方式为直接访问 PhysioNet 项目页、版本页、许可页与文件目录,逐一确认 RECORDS、ANNOTATORS、23 份 .hea 头文件、30.hea-、39.hea- 的可访问性与内容;挑战赛相关描述取自 PhysioNet 站内 CinC 2008 来源页。文中所有数字限于规模、结构、边界三类,未自行计算任何分布性统计。未覆盖的内容包括:各心搏类型的具体条数、.atr 的逐条审校者与审校范围、室颤的终止时刻、23 段与 VFDB 22 段的记录对应关系、两个通道的解剖位置。这些需要下载全量后逐条比对,本文按要求只写可追到官方页或官方文件的数字。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- vfdb,共享标签:生理信号 / 心电信号 / 心血管疾病 / 急危重症
- incartdb,共享标签:生理信号 / 心电信号 / 心血管疾病 / 挑战赛数据集
- nstdb,共享标签:生理信号 / 心电信号 / 元数据
- edb,共享标签:生理信号 / 心电信号 / 心血管疾病
- cdb,共享标签:生理信号 / 心电信号 / 心血管疾病
- ptbdb,共享标签:生理信号 / 心电信号 / 心血管疾病
- svdb,共享标签:生理信号 / 心电信号 / 心血管疾病
- twadb,共享标签:生理信号 / 心电信号 / 心血管疾病
- cinc-2020,共享标签:生理信号 / 心电信号 / 心血管疾病
- ltafdb,共享标签:生理信号 / 心电信号 / 心血管疾病
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

