信息速览
一段二十四小时的心电图,在数据中心里通常以两种形态存在。一种是波形,几百万个采样点,承载着每一次心跳的完整形状;另一种是事件表,每次心跳只留下一个时刻和一个类型标签,形状被丢掉了。这两种形态在体积上差着三个数量级,在能回答的问题上也差着三个数量级。
CHF2DB 是第二种形态的一个典型样本,而且比一般的注释库更极端:它连"另一半"都没有。官方页面的原文写着原始心电记录不可得,包里只有二十九个头文件和二十九个注释文件,没有任何信号。它要数据集使用者做的,是接受"只有心跳时刻"这个设定,然后在这个设定里把心率变异性做到极致。
它的价值也正在这里。心衰患者的心跳节律有一种特殊的紊乱——交感神经张力持续偏高、副交感张力衰减、心跳的逐拍变异被压缩得像一条直线。这种紊乱在波形上看并不醒目,在 RR 间期序列上却一目了然。CHF2DB 提供的正是这一层信息,干净、轻量、可以直接算。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 全名 | Congestive Heart Failure RR Interval Database |
| 简称 | CHF2DB |
| 发布方 | PhysioNet(原始数据由美国纽约哥伦比亚-长老会医学中心 Rochelle Goldsmith 贡献) |
| 首次发布 | 2003 年 3 月 3 日,版本 1.0.0,此后未改版 |
| 记录数 | 29 段(chf201–chf229) |
| 受试者数 | 29 名,一名受试者对应一段记录 |
| 年龄范围 | 34–79 岁 |
| 纽约心功能分级 | I、II、III 级(无 IV 级) |
| 性别 | 8 男、2 女,其余 21 人性别未知 |
| 数字化采样率 | 128 点/秒 |
| 记录时长 | 官方未公布;头文件长度字段为 0 |
| 信号通道 | 无。信号条数声明为 0,波形未随库发布 |
| 标注内容 | 逐搏位置与心搏类型 |
| 标注生成 | 自动检测 + 人工复核修正 |
| 文件格式 | WFDB:.hea 头文件 / .ecg 注释(扩展名沿用旧习俗) |
| 未压缩体积 | 6.4 MB |
| 许可 | ODC-BY 1.0,无需注册 |
§1 它要解决的具体麻烦
1.1 心衰的"节律指纹"只在时间轴上才看得见
充血性心衰对心脏的影响不只体现在泵血能力上,更体现在自主神经系统对窦房结的控制上。健康人的心跳间隔围绕一个中心值上下摆动,摆幅大约几十毫秒,这种摆动的来源是呼吸引起的心迷走神经张力周期性变化,以及压力感受器反射的持续调节。心衰患者身上,这两条调节通路都在退化:交感张力长期偏高,副交感张力被压制,心跳间隔的逐拍波动被压扁。
这种退化在单拍心电图上几乎看不出来。一个心衰患者的 QRS 波群可以长得和健康人一模一样,窦性心律的 P 波也规规矩矩,从形态学角度它不构成任何"异常"。但只要把连续几百拍的间隔排成一列,差异立刻显现:健康人的序列像一串有弹性的珠子,心衰患者的序列像一根被拧紧的钢丝。
这就是 RR 间期数据集的立足点。它不提供波形,因为它不需要提供波形——它要回答的问题根本不在波形上。它把每一次心跳压缩成一个时刻,让分析者可以直接在这些时刻之间做算术:相邻两拍差多少、差值超过某个阈值的比例是多少、不同时间尺度上的相关性衰减得多快。
CHF2DB 把这件事做成了一个独立的库。它不假装自己是一份完整的心电数据,官方页面第一句就说这些是心搏注释文件,第二句紧接着说原始记录不可得。这种坦白本身是有信息量的:它告诉你该拿它做什么,也告诉你别拿它做什么。
1.2 三条常见的错误期待
第一条,期待它是 chfdb 的新版本。 名字里的"2"最容易引发这种联想,而库内已有的一篇 chfdb 词条恰好会强化它。两者确实都是 PhysioNet 上的心衰长程心电库,但它们不是同一份数据先后两版的关系。chfdb 来自波士顿贝斯以色列医院,15 名心功能 III–IV 级患者,约二十小时,两导联,250 Hz,波形与注释俱全;CHF2DB 来自纽约哥伦比亚-长老会医学中心,29 名心功能 I–III 级患者,128 Hz,只有注释。人群不同、机构不同、年代不同、内容层级也不同。把两者当成同一数据的不同版本,会在论文里把样本量算错(§6.1)。
第二条,期待它自带"心衰"这个二分类标签。 库名叫充血性心衰,看起来像是一个已经标好阳性的数据集。但它的"心衰"是整库层面的纳入条件,不是逐条记录的标签。全部 29 名受试者都是心衰患者,库里没有健康对照。要做病例对照,必须从别处取对照(§7.4)。
第三条,期待它有心律失常的详细分类。 注释文件里确实有不同类型的心搏符号,但它的主体用途是给心率变异性提供时间基准,而不是给心律失常分类提供训练样本。官方页面没有任何一句承诺标注覆盖了多少种心律失常、各类各有多少条。把注释里的类型符号当成心律失常标签来统计,得到的是一个未经官方背书的分布(§4.4)。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 计算时域心率变异性指标 | 逐搏时刻序列,可直接求相邻间隔 | 够,这是它的本业 |
| 计算频域心率变异性指标 | 时刻序列均匀,可重采样后做频谱 | 够,段长需先确认 |
| 分析非线性动力学指标 | 时刻序列 + 足够长的时间覆盖 | 视段长而定,官方未公布时长 |
| 心搏形态学分类 | — | 不够,无波形 |
| QRS 宽度、振幅、ST 段 | — | 不够,无波形 |
| 导联相关分析 | — | 不够,无导联声明 |
| 心律失常的多类别检测 | 注释里有类型符号,但无覆盖承诺 | 部分可做,需自建口径 |
| 心衰与健康人的对照 | 只有心衰一侧 | 不够,须外接对照 |
| 按性别分层的亚组分析 | 仅 10 人有性别记录 | 勉强可做,21 人须剔除 |
| 按心功能分级分层 | 头文件逐条记录分级 | 够,但三级样本量不均衡 |
| 深度学习端到端训练 | 29 段 | 不够,只适合做外部验证 |
| 长时程节律负荷统计 | 段长官方未公布 | 需先确认 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 你在复刻 2002 年那篇重新审视 pNN50 的论文,需要其中心衰那一侧的数据 | 库虽晚一年发布,但受试者来源与那篇论文的心衰组同为哥伦比亚-长老会,人群口径相近 | 不要假设两者是同一批人;先在文中把"可能重叠但未经官方确认"写清楚 |
| 你有一个在健康人身上训好的心率变异性异常检测器,想找外部队列 | 29 名心功能 I–III 级患者,分级跨度比多数竞赛库宽 | 先算零样本的指标分布,再决定阈值要不要重标 |
| 你在做药物对自主神经影响的二次分析 | 头文件人群与洋地黄试验队列同源,公开文献可查 | 找出注释与"用药前/用药后"的对应关系几乎不可能,谨慎使用 |
| 你的数据带宽极低,只能传事件表 | 全库 6.4 MB,几十秒就能下完 | 一次性拿全量,不要分批 |
| 你想验证"心衰让心率变异性变平"这个结论在跨队列上成立 | 提供另一个中心、另一套设备、另一段年代 | 与 chfdb 配对使用时,必须分别报告两侧的采样率与段长差异 |
| 你需要一份轻量的教学用注释数据 | 文件小、结构简单、有年龄与分级 | 适合课堂演示 RR 序列的重建,不适合形态学教学 |
1.5 一句话判断
它不是一个"小型的心电数据库",而是一份"心电图被抽走了波形之后剩下的部分"——要用它,得先接受这个残缺,然后在这个残缺里找到它唯一完整的东西:时间。
1.6 不适合谁:四条边界
不适合做任何需要波形的工作。 这一条没有任何例外。形态学分类、QRS 检测算法评测、噪声鲁棒性研究、导联融合,只要你的方法需要看一眼电压随时间怎么变,这个库就完全无用。信号条数在头文件里写的是 0,不是没写,是明确没有。
不适合当训练集的主力。 29 段、29 人,一个人一段,没有同一人的重复记录可以用来做患者内变异分析。任何需要数万条样本的模型在这里只能做微调或外部验证。
不适合做性别或分级的精细亚组。 21 人性别未记录,按性别切分只剩 10 人;纽约心功能分级虽然逐条标了,但三级分布不均匀,做分层后的统计检验时每层样本量都很小。
不适合替代有波形的同类研究。 如果你的研究问题需要同时看节律与形态——比如心衰患者的室性早搏形态是否更宽——这个库给不出答案。它只能告诉你室性早搏在什么时候发生,不能告诉你它长什么样。
§2 数据构成与规格
2.1 一个库只有两种文件
整个下载包解开之后是六十一项:二十九个头文件、二十九个注释文件,再加上记录清单、注释者说明和校验和清单三份说明性文本。没有数据文件,没有信号文件,没有索引数据库,没有任何二进制采样。
这一点值得反复强调,因为它是这个库与绝大多数心电库在结构上的第一道分水岭。用 WFDB 工具链的术语说,一个"完整"的物理记录由头文件、信号文件、注释文件三件套组成;CHF2DB 只有其中两件,而且缺的恰好是最占体积的那件。头文件里信号条数写 0,信号行一行都没有,读取器会直接告诉你这条记录没有波形。
| 文件 | 数量 | 形态 | 作用 |
|---|---|---|---|
chf201.hea … chf229.hea |
29 | 纯文本 | 声明记录名、采样率、信号条数与长度,附年龄、性别、分级 |
chf201.ecg … chf229.ecg |
29 | 二进制 | WFDB 注释序列,存心搏位置与类型 |
RECORDS |
1 | 纯文本 | 逐行列出全部记录名 |
ANNOTATORS |
1 | 纯文本 | 一行,说明注释文件名与注释来源 |
SHA256SUMS.txt |
1 | 纯文本 | 六十项文件的 SHA-256 校验和 |
HEADER.html |
1 | HTML | PhysioNet 自动生成的页面头,非数据 |
校验和清单共六十行,对应二十九加二十九再加两份说明文本,恰好把 RECORDS 与 ANNOTATORS 都算进去了。这个数字可以当作核对下载完整性的第一道关卡:解压后重新算一遍,一行不差才算拿全。
2.2 字段与事件的完整字典
CHF2DB 的"字段"分布在两层:头文件第一行是 WFDB 标准记录行,第二行是自由文本注释行;注释文件里的事件则是一串"时刻 + 符号 + 辅助字符"的三元组。下表按层列出。
| 信息项 | 所在位置 | 取值形态 | 含义 | 使用注意 |
|---|---|---|---|---|
| 记录名 | .hea 第 1 行第 1 字段 |
chf201–chf229 |
记录标识 | 与文件名前缀一致 |
| 信号条数 | .hea 第 1 行第 2 字段 |
0 |
波形通道数 | 恒为 0,不是缺省 |
| 采样率 | .hea 第 1 行第 3 字段 |
128 |
每秒采样点数 | 只用于把采样序号换算成秒 |
| 采样点数 | .hea 第 1 行第 4 字段 |
0 |
总长度 | 未声明,不能当真实长度 |
| 年龄 | .hea 第 2 行 |
34–79 |
受试者年龄 | 以 #Age: 开头 |
| 性别 | .hea 第 2 行 |
M / F / ? |
受试者性别 | ? 表示未记录,占多数 |
| 纽约心功能分级 | .hea 第 2 行 |
I / II / III |
心衰严重程度 | 以 NYHA class: 开头 |
| 事件时刻 | .ecg |
采样点序号 | 心搏发生的时刻 | 除以 128 得秒 |
| 事件类型 | .ecg |
单字符符号 | 心搏类型 | 沿用 MIT-BIH 注释符号体系 |
| 辅助字符 | .ecg |
数字 | 附加信息(如节律编号) | 与事件类型分列,勿混读 |
| 注释者标识 | .ecg 文件头 |
ecg |
注释来源 | 见 ANNOTATORS 说明 |
ANNOTATORS 只有一行,内容承认注释来自自动检测并经过修正。这句话与官方页面的"自动分析加人工复核"完全对应,是这个库关于标注质量的全部官方承诺。没有第二份注释,没有交叉复核记录,没有逐条置信度。
2.3 一条记录的完整解剖
取 chf201 走一遍。先是头文件,全文只有两行:
chf201 0 128 0
#Age: 55 Sex: M NYHA class: III
第一行四个字段依次是记录名、信号条数、采样率、采样点数。第二行是年龄、性别与分级。整条记录的元数据到这里就结束了——没有设备型号,没有录音时长,没有诊断文本,没有用药,没有随访。
接着读注释。下面这段用 wfdb 把一条记录的注释读出来,并把采样序号换算成秒:
import wfdb
import numpy as np
#-- 只读注释,不读信号
ann = wfdb.rdann('chf201', 'ecg', pn_dir='chf2db')
t_sec = ann.sample.astype(float) / 128.0 #-- 128 是头文件声明的采样率
rr_ms = np.diff(ann.sample) / 128.0 * 1000.0 #-- 相邻心搏间隔,毫秒
print('心搏数', len(ann.sample))
print('覆盖时长下限 秒', round(float(t_sec[-1]), 1))
print('RR 中位数 ms', round(float(np.median(rr_ms)), 1))
print('符号集合', sorted(set(ann.symbol)))
这段代码能得到三件事:这一条记录里有多少次心跳、最后一次心跳发生在哪些秒、相邻心跳之间隔多久。它得不到第四件事——那一拍长什么样。这就是解剖的全部:一头一尾。
把这两层拼起来看,一条记录的完整形态是这样的:
| 层次 | 内容 | 是否可获得 |
|---|---|---|
| 身份层 | 记录名 chf201 | 是 |
| 人群层 | 55 岁、男、心功能 III 级 | 是 |
| 物理层 | 128 Hz 采样,波形 | 否,已剔除 |
| 时长层 | 实际记录时长 | 否,头文件写 0 |
| 事件层 | 每次心跳的时刻 | 是 |
| 类型层 | 每次心跳被标成什么 | 是 |
| 语义层 | 什么样的跳、属于什么节律 | 部分,符号有定义但覆盖无承诺 |
上表第五行到第七行是这个库最微妙的部分。时刻是硬的:采样序号不会骗人,除以采样率就是秒。类型是软的:符号有标准定义,但每个符号在库里出现多少次、标注者判得准不准,官方一个字都没说。语义最软:符号能告诉你这一跳被标成室性早搏,但不能告诉你这一跳所在的这一段是不是室速。
2.4 记录、受试者与分级的对应
二十九条记录与二十九名受试者一一对应,编号从 201 连续到 229,没有跳号。这个编号规则是 PhysioNet 早期的习惯:用三位数区间把同类记录圈在一起。chf 数据库用 1 开头,这里用 2 开头,两者不共享编号,也说明它们在管理上是两个独立项目。
头文件里的三项人群信息构成了唯一的可分层维度:
| 维度 | 取值 | 取值个数 | 备注 |
|---|---|---|---|
| 年龄 | 34–79 | 逐条不同 | 官方给出整体范围 |
| 性别 | M、F、未知 | 3 | 未知占多数 |
| 纽约心功能分级 | I、II、III | 3 | 无 IV 级 |
这三项都写在第二行注释里,格式固定为"键值对 + 双空格分隔"。解析时最容易错的是最后一项:NYHA class: 后面跟的是罗马数字,字符串排序会把 III 排在 II 前面。要做分级分层,必须显式做映射。
2.5 与同类资源的字段差异
把 CHF2DB 放进 PhysioNet 的心电长程库序列里,它的特殊之处立刻显形。下面这张表比较的是一致的几个字段:有没有波形、采样率、有没有长度声明、人群分级、有没有用药背景。
| 资源 | 波形 | 采样率 | 长度声明 | 分级 | 用药 |
|---|---|---|---|---|---|
| CHF2DB | 无 | 128 Hz | 未声明 | I–III | 未说明 |
| BIDMC chfdb | 有,两导联 | 250 Hz | 约二十小时 | III–IV | 洋地黄试验队列 |
| MIT-BIH 心律失常库 | 有,两导联 | 360 Hz | 半小时 | 不适用 | 不适用 |
| CAST RR 间期子研究库 | 无 | 未公开 | 近二十四小时 | 不适用 | 有,用药前后配对 |
| 长期房颤库 | 有,两导联 | 128 Hz | 二十四至二十五小时 | 不适用 | 未说明 |
这张表最有意思的一行是最后两行与 CHF2DB 的对照。CAST 库也是"只有注释没有波形",但它在官方页面里把段长、用药配对、记录条数写得清清楚楚;CHF2DB 则只给了采样率。同样是残缺型资源,信息透明度差别很大。这一点在选型时要特别留意:残缺不可怕,残缺而不自知才可怕。
2.6 采样率 128 Hz 的意义
128 Hz 这个数字在心电领域是一个"够用但不宽裕"的档位。诊断级十二导联的标准采样率是五百赫兹以上,Holter 常用 128 到 400 Hz。对形态学分析,128 Hz 意味着 QRS 波群只被采样几十个点,高频切迹会丢失,但波群的整体轮廓还能保住。对心率变异性,128 Hz 意味着时间分辨率是 7.8 毫秒,这个精度对毫秒级的 RR 间期分析完全足够——因为心率变异性关心的间隔通常是几十到上千毫秒。
换句话说,128 Hz 这个档位本身就是一个信号:这个库从设计上就更偏向节律而非形态。后来的事实也印证了这一点——波形被整个剔掉了,只留下时间。即便原始波形哪天重新找到,128 Hz 也做不了精细的形态学研究。
2.7 一个必须说清楚的体积问题
官方给出的未压缩总体积是 6.4 MB。分到二十九条记录上,平均每条两百多 KB,其中注释文件占大头,头文件每个只有几十字节。这个体量的直接后果是:任何需要"下全量、做统计"的流程都可以在几十秒内完成,任何需要"分批流式读取"的工程设计在这里都是过度设计。
体量小也意味着一个常被忽略的用途:它可以被当作校验工具。如果你的流水线在处理大库时出错,先用 CHF2DB 跑一遍同样的代码,能迅速判断问题出在代码还是数据规模上(§5.5)。
§3 它是谁的数据:一位研究者的捐献
3.1 贡献者与机构
官方页面在数据描述之后紧跟一句致谢,说感谢纽约哥伦比亚-长老会医学中心的 Rochelle Goldsmith 慷慨贡献这些数据。这是全库唯一的来源声明,也是判读这个库身份的关键线索。
Rochelle Goldsmith 是心率变异性研究领域的一位长期工作者,她的署名出现在 PhysioNet 页面列出的三篇文献里,也出现在 chfdb 的多篇核心文献里。哥伦比亚-长老会医学中心是哥伦比亚大学医学院的附属医院,在二十世纪九十年代是自主神经与心衰研究的重要基地。
这个身份决定了库的人群特征:它不是从某个心电门诊随机抽的二十九个人,而是一批进入过心衰研究队列的患者,能进队列意味着他们至少能配合完成长程心电监测,这在心功能 III 级患者里并不轻松。
3.2 时间线
| 时间 | 事件 | 来源层级 |
|---|---|---|
| 1995 | 洋地黄长期治疗对自主神经功能影响的研究发表,26 名心衰患者,二十四小时 Holter | 论文摘要 |
| 1997 | 卡维地洛长期治疗提高副交感神经活性的研究发表 | 官方页面引用 |
| 2002 | 重新审视 pNN50 的论文发表,用到 43 名心衰患者 | 论文摘要 |
| 2003 年 3 月 3 日 | CHF2DB 在 PhysioNet 发布,版本 1.0.0 | 官方页面 |
| 2003 年至今 | 版本保持 1.0.0,未再改版 | 官方页面 |
时间线上有一个值得注意的空档:数据从采集到发布之间隔了将近十年。这段空档解释了为什么库的形态如此古朴——它带着九十年代中期心电研究的痕迹,比如 .ecg 这个扩展名。
3.3 采集场景与人群构成
官方给出的人群描述是:二十九名受试者,年龄三十四到七十九岁,纽约心功能分级 I、II、III 级;其中八名男性、两名女性,其余二十一人性别未记录。
这几句话里有三个边界值得展开。
第一,分级不含 IV 级。纽约心功能 IV 级意味着静息状态下就有症状,这类患者做长程 Holter 的可行性很低,所以早期队列通常止步于 III 级。这意味着这个库覆盖的是"有症状但尚可活动"的心衰人群,终点期心衰不在其中。
第二,性别未知的比例高得反常。二十九人里二十一人性别未记录,占七成以上。这不是录入疏忽的常见形态——如果只是漏了一两个字段,通常不会漏掉七成。更可能是原始研究在设计时没有把性别作为必需字段记录,或者记录已经散佚。无论哪种原因,后果是一样的:按性别分层几乎没有统计效力。
第三,年龄跨度大。三十四到七十九岁,跨度四十五年。心衰在不同年龄段的病因差异很大,年轻患者更多见于心肌病,老年患者更多见于缺血性心脏病。库里没有病因字段,所以这个跨度只能作为背景信息,无法用于病因分层。
3.4 数据来源的三个层次
理解这个库的数据血缘,需要区分三个层次,它们的信息透明度差别很大。
| 层次 | 内容 | 透明度 |
|---|---|---|
| 原始采集 | 九十年代中期的长程心电记录,设备与导联未说明 | 低,官方只有一句"以 128 Hz 数字化" |
| 中间加工 | 自动检测定位 + 人工复核 | 中,官方说明了方法但未说明复核强度 |
| 发布形态 | 头文件 + 注释文件,波形剔除 | 高,文件结构与字段完全公开 |
这个三层结构对使用者有一个实际含义:任何关于"标注质量"的判断,都只能落在第二层与第三层之间,而第二层恰好是官方描述最简略的地方。你能确认文件结构完全公开,但确认不了复核时有没有看波形、看了几遍、分歧怎么解决。
§4 它最独特的东西:被保留下来的那一半
4.1 剔除波形这件事本身就是设计
大多数数据集的问题在于缺什么,CHF2DB 的问题在于刻意丢了什么。原始记录确实存在过——官方说它们"不可得",而不是"从未采集",并且给出了 128 Hz 的数字化采样率,说明当年数字化是做了的。既然数字化了,文件就曾经存在,只是没有随库发布。
这个选择在 2003 年的语境下并不奇怪。那时候心率变异性研究正热,分析的输入几乎全是 RR 间期序列,波形只是中间产物;把波形剔除可以极大压缩体积,让研究者在拨号上网的年代也能下载。二十多年后回看,这个取舍让库的可复用性大幅收窄,但也让它变成了一份极其纯粹的"时间数据集"——没有形态的干扰,所有分析都必须回答"节律说明了什么"。
4.2 RR 间期序列的重建
从注释到可用的 RR 序列,中间有三道坎。
第一道是符号过滤。注释序列里并不只有心搏,还可能有形态学标注、节律变换标记等非心搏事件。直接对 ann.sample 求差,会把非心搏事件也算进间隔里,得到一堆荒谬的短间隔。正确做法是先按符号筛出心搏,再求差。
第二道是采样率换算。128 Hz 的时间分辨率是 7.8 毫秒,这个精度对 RR 间期完全够用,但在做二阶差分(加速度)类指标时会放大量化误差。若要算加速度指标,应先在较粗的时间粒度上平滑。
第三道是异常间隔处理。心衰患者常有频发早搏,早搏的间隔会明显短于基础周期,代偿间歇又会明显长于基础周期。这对序列是正常的生理现象,不是错误。直接剔除会把真实的生理变异也剔掉,保留又会让时域指标失真。常用的折中是按一定规则标记并单独统计,而不是简单删除。
import wfdb
import numpy as np
ann = wfdb.rdann('chf201', 'ecg', pn_dir='chf2db')
#-- 第一步:只保留心搏类符号,其余事件单独统计
BEAT = set('NLRBAaJSVrFfejnE')
beat_idx = [i for i, s in enumerate(ann.symbol) if s in BEAT]
other = sorted(set(ann.symbol) - BEAT)
print('心搏事件', len(beat_idx), '非心搏符号', other)
#-- 第二步:按顺序取心搏时刻,求间隔
t = ann.sample[beat_idx].astype(float) / 128.0
rr = np.diff(t) * 1000.0
#-- 第三步:标记异常间隔而不是直接删除
med = np.median(rr)
bad = (rr < 0.3 * med) | (rr > 2.0 * med)
print('异常间隔占比', round(float(bad.mean()), 4))
print('清洗后 RR 条数', int((~bad).sum()))
这段代码里有两个刻意的设计:非心搏符号被单独打印而不是被静默丢弃,异常间隔被标记而不是被删除。两者都是为了保住可追溯性——出了问题时,你能知道丢掉的是哪些。
4.3 案例:同一条记录在两种口径下的 pNN50
取 chf201,55 岁男性,心功能 III 级。这是库里一条典型的记录,也是 §4.3 的案例在全文反复回指的那一条。
同一段注释,两种处理口径:口径甲直接把全部相邻间隔求差,不做任何过滤;口径乙先筛出心搏、再标记异常间隔后统计。两套流程的差别看起来只是几行代码,落到 pNN50 这个指标上却可能差出好几个百分点。
import wfdb
import numpy as np
ann = wfdb.rdann('chf201', 'ecg', pn_dir='chf2db')
t = ann.sample.astype(float) / 128.0
def pnn50(rr_ms):
#-- 相邻间隔差的绝对值超过 50 毫秒的比例
d = np.abs(np.diff(rr_ms))
return float((d > 50).mean() * 100)
rr_raw = np.diff(t) * 1000.0
BEAT = set('NLRBAaJSVrFfejnE')
idx = [i for i, s in enumerate(ann.symbol) if s in BEAT]
rr_beat = np.diff(ann.sample[idx].astype(float) / 128.0) * 1000.0
med = np.median(rr_beat)
ok = ~((rr_beat < 0.3 * med) | (rr_beat > 2.0 * med))
print('口径甲 未过滤 pNN50 =', round(pnn50(rr_raw), 2), '%')
print('口径乙 仅心搏且标记 pNN50 =', round(pnn50(rr_beat[ok]), 2), '%')
这个案例的价值不在于两个数字谁高谁低,而在于它会强迫你把口径写清楚。心衰研究里 pNN50 通常是个位数,口径之间差几个百分点,足以改变"这一例的自主神经是否严重受损"的判断。§4.3 的案例在 §7.2 与 §9.2 还会各出现一次,每次强调的都是同一件事:这个库里没有"默认口径",任何指标都得自己把预处理写明白。
4.4 标注粒度:心搏级清楚,节律级缺失
这是这个库第二层特色,也是最容易出问题的地方。
心搏级的标注是相对整齐的:每一次心跳有一个时刻、一个符号。时刻精确到采样点,符号沿用标准注释体系。这一层可以放心用。
但节律级的信息是缺失的。官方页面没有说标注里包含了节律变换事件,也没有说每个符号的时间区间覆盖到哪里。这意味着"这一段是不是窦性心律"、"这一段有没有房颤"这类问题,从注释里读不出直接答案。间接推断是可能的:RR 序列的规律性、符号的分布、某些形态学标注可以给出线索。但间接推断的可靠性取决于推断方法,不属于库本身提供的语义。
这一点在跨库研究里会造成麻烦。从别的库迁过来的流水线,往往默认节律标签是现成的,到 CHF2DB 上才发现要自己推。§4.3 的案例中那条记录,两种口径的差别其实只是预处理级别的差异;如果再加上节律层自建的差异,可比较性会进一步下降。
4.5 边界条件:什么情况下它不再适用
把优点说透之后,必须列清楚不该抬出它的场景。
| 条件 | 是否适用 | 判断依据 |
|---|---|---|
| 计算时域心率变异性指标 | 适用,核心用途 | 时刻序列完整,精度足够 |
| 计算频域指标 | 适用,但需确认段长 | 段长官方未公布,需自估 |
| 非线性动力学分析 | 谨慎 | 需要极长覆盖,段长未知可能不够 |
| 形态学分类 | 不适用 | 无波形 |
| 心律失常多类别检测 | 谨慎 | 有类型符号但无覆盖承诺 |
| 心衰与健康对照 | 不适用 | 单侧人群 |
| 药物效应分析 | 不适用 | 无用药字段 |
| 随访与预后建模 | 不适用 | 无结局变量 |
| 多中心泛化 | 只算单点 | 单一中心 |
| 跨库基准的外部测试侧 | 适用,尤其是与 chfdb 配对 | 同源机构、不同人群等级 |
表格最后一行是这个库被低估的用途。chfdb 是心功能 III–IV 级、250 Hz、二十小时;CHF2DB 是 I–III 级、128 Hz、段长未知。两者来自同一个城市、同一个研究传统,但人群分级与采集参数都不同。把 chfdb 上训好的心率变异性模型放到 CHF2DB 上测,测出来的是"跨分级、跨设备、跨年代"的综合衰减,比随便找一个别的库更接近真实场景。
§5 怎么拿到:从六兆包到一张时刻表
5.1 目录结构与下载面
库的结构是平铺的,没有子目录,六十一项全在根目录下。这跟后来的 PhysioNet 项目习惯不同——新库通常按患者或记录分子目录,老库则一律平铺。平铺的好处是遍历简单,坏处是记录一多就难以人工浏览,好在这个库只有二十九条。
下载有两条路:整包 ZIP,或者按文件逐个取。整包 6.4 MB,按文件取则可以只拿你需要的那几条。对于只想做试点分析的场景,按文件取能省掉解压步骤。
5.2 批量下载
最直接的方式是用官方给出的递归下载命令。这条命令会顺着目录结构把所有文件拉下来,并且支持断点续传,对网络不稳定的环境更友好。
#-- 递归抓取整个版本目录,保留目录结构,支持断点续传
wget -r -N -c -np https://physionet.org/files/chf2db/1.0.0/ -P ./chf2db_raw
#-- 只取记录清单,先看看有哪些记录
curl -sL https://physionet.org/files/chf2db/1.0.0/RECORDS -o RECORDS
#-- 核对下载完整性:六十项逐个比对 SHA-256
cd chf2db_raw/physionet.org/files/chf2db/1.0.0
sha256sum -c SHA256SUMS.txt | grep -c OK
三条命令里第三条最容易被跳过,但它其实是唯一能证明"你拿到的是官方那份"的手段。校验和清单覆盖六十项,全部 OK 才算完整。
5.3 用 wfdb 直接远程读取
不想落盘时,可以让 wfdb 直接从 PhysioNet 拉取并解析。对一个六兆的库,这种方式几乎没有延迟感。
import wfdb
#-- 列出全部记录名
records = wfdb.get_record_list('chf2db')
print(len(records), records[:5], records[-3:])
#-- 读头文件:只有元数据,没有信号
hdr = wfdb.rdheader('chf201', pn_dir='chf2db')
print('采样率', hdr.fs, '信号条数', hdr.n_sig, '长度字段', hdr.sig_len)
print('注释行', hdr.comments)
#-- 读注释:这才是数据本体
ann = wfdb.rdann('chf201', 'ecg', pn_dir='chf2db')
print('事件数', len(ann.sample), '前三个符号', ann.symbol[:3])
第一次跑这段代码的人几乎都会惊讶于 hdr.n_sig 是 0、hdr.sig_len 也是 0。这不是读取失败,是头文件本来就写着 0。读取器忠实地把 0 传了出来。
5.4 转成通用的表结构
把注释转成分析友好的表,关键是把"一个事件一行"这件事做对。下面的写法把每条记录展开成"受试者、时刻、符号"三列,并顺带把人群信息并进去。
import wfdb
import pandas as pd
rows = []
for rec in wfdb.get_record_list('chf2db'):
hdr = wfdb.rdheader(rec, pn_dir='chf2db')
ann = wfdb.rdann(rec, 'ecg', pn_dir='chf2db')
meta = hdr.comments[0] if hdr.comments else ''
for s, sym, sub in zip(ann.sample, ann.symbol, ann.aux_note):
rows.append({'record': rec, 'sample': int(s),
'sec': s / hdr.fs, 'symbol': sym,
'aux': sub, 'meta': meta})
df = pd.DataFrame(rows)
print(df.shape, df['record'].nunique())
print(df.head(3).to_string())
生成的表很小,几万行量级。写进磁盘时用压缩格式即可,不必担心体积。转表的代价是有损的:aux_note 里可能含有节律相关的文字,转成一张扁平表后就与心搏事件混在一起了,下游过滤要格外小心。
5.5 五个最容易出错的地方
| 出错点 | 后果 | 规避方式 |
|---|---|---|
把 sig_len 当真实长度 |
算出零时长或除零 | 用注释末位推算,且标注为下界 |
| 对全部事件求差 | 混入非心搏事件,得到荒谬短间隔 | 先按符号筛选心搏 |
把 .ecg 当成信号文件 |
读取器报错或读出乱码 | .ecg 在本库是注释,不是波形 |
用 III 的字符串顺序当严重度 |
分级排序错乱 | 显式映射到数值 1/2/3 |
| 假设有健康对照 | 二分类建模时阳性率恒为 1 | 从外部库取对照 |
第一行和第三行是这个库最常见的新手坑。sig_len 为 0 是官方数据本身如此,不是 bug;.ecg 后缀在别的库里可能是信号,在这里是注释,这一点连 WFDB 的默认约定都反着来。
5.6 六兆的库能做什么、不能做什么
在 6.4 MB 这个量级上,下载与存储成本可以忽略,真正的成本在于理解它。这一小节把"能做什么"落成一张任务清单。
| 任务 | 可行性 | 前置条件 |
|---|---|---|
| 复现 pNN 类指标的队列比较 | 高 | 需外接健康对照队列 |
| 心率变异性指标的跨库一致性检验 | 高 | 需统一预处理口径 |
| 极端短序列的鲁棒性实验 | 高 | 可从长记录截短 |
| 心衰分级的指标分层比较 | 中 | 分级样本量不均衡 |
| 性别分层的差异检验 | 低 | 仅 10 人有性别 |
| 端到端深度模型训练 | 低 | 29 段不足以训练 |
| 形态学特征研究 | 零 | 无波形 |
"带前置条件"这一列比"可行性"那一列更重要。这个库几乎没有一项任务是可以直接开干的,每一条都需要外接数据或统一口径。这不是它的缺陷,而是残缺型资源的常态。
§6 与库内条目的关系
6.1 关系图谱
库内与 CHF2DB 最容易混淆的是 BIDMC 的 chfdb。两者名字里都有 chf,都在 PhysioNet,都是心衰长程心电,一个叫"充血性心衰数据库",一个叫"充血性心衰 RR 间期数据库"。区隔必须写在最前面。
| 对比项 | CHF2DB(本篇) | chfdb(BIDMC) |
|---|---|---|
| 全名 | Congestive Heart Failure RR Interval Database | BIDMC Congestive Heart Failure Database |
| 来源机构 | 纽约哥伦比亚-长老会医学中心 | 波士顿贝斯以色列医院 |
| 年代 | 数据源自九十年代中期,2003 年发布 | 数据源自八十年代后期 |
| 受试者 | 29 人 | 15 人 |
| 纽约心功能分级 | I、II、III | III、IV |
| 年龄 | 34–79 | 22–71 |
| 性别 | 8 男 2 女,21 人未知 | 11 男 4 女 |
| 波形 | 无 | 有,两导联 |
| 采样率 | 128 Hz | 250 Hz |
| 单段时长 | 官方未公布 | 约二十小时 |
| 注释来源 | 自动检测 + 人工复核 | 自动检测,未人工修正 |
| 用药背景 | 未说明 | 洋地黄试验队列 |
| 许可 | ODC-BY 1.0 | ODC-By 1.0 |
这张表里最刺眼的两行是波形与注释来源。chfdb 有波形但注释未经人工修正,CHF2DB 没有波形但注释经过人工复核——两者恰好在"数据质量"的两个维度上一正一反。选型时如果只看到"chf"这个共同前缀,很容易拿错。
第二个相近条目是 CAST RR 间期子研究库。它同样"只有注释没有波形",规模却大得多,且明确写了用药前后配对。CHF2DB 在这条轴上是一个更早、更小、更朴素的前辈。
第三个需要提及的是 MIT-BIH 心律失常库。它是注释符号体系的发源地,CHF2DB 的符号定义从那里继承。但两者的用途完全不同:MIT-BIH 的心搏分类基准依赖波形,CHF2DB 完全不涉及。
6.2 检索路径
从需求出发找这几个库,有三条常见路径:
| 你的检索词 | 会命中 | 是否是你想要的 |
|---|---|---|
| 心衰 + PhysioNet | 两个都命中 | 需按"有无波形"二次筛 |
| RR 间期 | CHF2DB、CAST | 看段长与规模决定 |
| 心率变异性 | 全部三类 | 看是否需要形态学 |
| Holter + 注释 | chfdb、长期房颤库 | CHF2DB 无波形,需排除 |
检索时最有效的一个过滤条件是"要不要波形"。要波形,CHF2DB 直接排除;不要波形只查节律,它是候选。
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
评分之前必须先纠正一个视角错位:把 CHF2DB 当成一个残缺的数据集来评,会得到一个满盘低分的结论;把它当成一份专门为节律分析设计的注释集来评,结论完全不同。本节采用后者。这不意味着要掩盖它的缺陷,而是说,评估维度应当与它的设计意图对齐。若按形态学标准去评,它在三项上是零分,这种评法没有信息量。最容易发生的误判,是拿它和 chfdb 比规模然后得出"劣化版本"的结论——那是把一个独立项目误认成另一个项目的迭代。
7.1 五维评分
按十一个 DAIMS 维度逐项打分,每项满分五分,评分针对"作为节律与心率变异性研究资源"这一用途:
| 维度 | 分数 | 理由 |
|---|---|---|
| 数据规模 | 2 | 29 段 29 人,只够做外部验证或小样本研究 |
| 标注质量 | 4 | 自动检测加人工复核,优于纯自动标注,但复核强度未公开 |
| 任务适配 | 3 | 节律分析适配度高,形态学完全不适配 |
| 文档完整 | 2 | 页面极短,段长、导联、原始设备全部缺失 |
| 可获取性 | 5 | 免注册、ODC-BY、六兆体积 |
合计十六分。分数结构与 INCART 那类库正好相反:它的可获取性是满分,文档完整是低分。这个组合提示一件事——拿得容易不等于懂得容易。
7.2 标注质量的两层
第一层是心搏定位,第二层是心搏类型。这两层的可靠性机制不同。
定位层的可靠性来自物理约束:心跳发生在哪里是一个相对客观的事实,自动检测算法在心率相对规整的记录上准确率很高。心衰患者的节律虽然有变异,但绝大多数仍以窦性为主,定位出错的空间有限。加上人工复核,这一层可以给较高信任。
类型层的可靠性来自判读者的判断。室性早搏和差异性传导在单导联上本就难以区分,而原始波形又没有保存,复核时能依据的只剩当时看到的那一段。类型层的可信度因此低于定位层。§4.3 的案例中两种口径的差别,主要落在定位层——它影响的是间隔序列,而不是类型分布;而任何按类型统计的分析,都要面对类型层这层不确定性。
7.3 已知的、可枚举的偏差
| 偏差 | 表现 | 影响 |
|---|---|---|
| 人群偏差 | 单一中心、单一研究队列 | 不能代表一般心衰人群 |
| 分级偏差 | 无 IV 级 | 终末期心衰缺位 |
| 性别偏差 | 七成以上性别未知 | 性别分层几乎不可能 |
| 年代偏差 | 九十年代中期采集 | 当时代的心衰治疗与今天差异大 |
| 时长不明 | 头文件长度字段为 0 | 无法判断能否支撑长程指标 |
| 波形缺失 | 完全没有信号 | 形态学与导联分析为零 |
| 用药不明 | 无任何药物字段 | 无法排除药物对节律的影响 |
第七行值得单独说。心衰治疗在九十年代经历了从洋地黄与利尿剂为主到神经激素拮抗剂普及的转变。β 受体阻滞剂会直接压低心率、改变心率变异性,如果这个队列里有患者在用这类药,指标的可比性会受影响。官方没有提供任何药物信息,所以这个影响无法评估,只能作为不确定性记下来。
7.4 外部验证矩阵
一个数据集的价值很大程度上取决于它能和什么配对。
| 配对对象 | 配对性质 | 用途 | 注意事项 |
|---|---|---|---|
| BIDMC chfdb | 同城不同机构、不同分级 | 心率变异性模型的跨分级外测 | 采样率与段长不同,需分别处理 |
| MIT-BIH 心律失常库 | 同符号体系 | 注释符号的兼容性检验 | 该库以形态学为主,只能借符号定义 |
| 长期房颤库 | 同为 128 Hz 长程 | 采样率一致,预处理可复用 | 人群不同,只能做节律工具链测试 |
| 健康受试者 RR 库 | 人群互补 | 构建病例对照 | 需明确健康队列的段长与年龄分布 |
| CAST RR 间期子研究库 | 同为注释型 | 注释型资源的规模对比 | 该库用药配对,CHF2DB 无 |
这张表最有价值的一行是与健康受试者的配对。PhysioNet 上有专门收集正常窦性心律长程记录的库,采样率与段长的口径与 CHF2DB 不同,但时间尺度接近。把两者并起来,就得到了一组心衰对健康人的对照数据——这正是 CHF2DB 单看时最大的缺口。
7.5 它为何能持续:残缺得很诚实
这个库能在 PhysioNet 上活着二十多年,不是因为数据多,而是因为它把话说清楚了。页面第一段就承认原始记录不可得,注释者文件里写明标注来自自动检测,头文件里把信号条数和长度都写成 0 而不是编一个数。这种诚实让使用者不会被误导——你不会用它做形态学分析,因为一开始就知道没波形。相比之下,很多残缺而信息不透明的资源,要等到使用者花掉两周时间才发现问题,那才是真正的浪费。
§8 生态位与历史
8.1 历史沿革
CHF2DB 出现在 PhysioNet 从"研究社区共享点"向"公共数据库"转型的时期。那个阶段 PhysioNet 陆续吸纳了多家医院与研究者的历史数据,把它们整理成统一的 WFDB 格式发布。Goldsmith 捐献的这批记录是其中之一。
库的编号规则也反映了这个时期的特点:chf 前缀加三位数字,与更早的 chf 库形成"同前缀不同区间"的格局。这不是版本号,是文件命名空间的划分方式。后来 PhysioNet 改用有语义的项目短名,这种写法就逐渐消失了。
8.2 与文献队列的关系
官方页面列出的三篇文献是这个库唯一的学术锚点。它们形成一个连续的研究弧:先做药物干预对自主神经的影响,再做另一类药物的效应,最后重新审视一个具体指标的阈值选择。
| 文献 | 年份 | 与本库的关系 | 可核实内容 |
|---|---|---|---|
| 洋地黄长期治疗与自主神经功能 | 1995 | 人群同源,二十六名患者 | 摘要可查,指标为 RR 与频域功率 |
| 卡维地洛长期治疗与副交感活性 | 1997 | 官方引用 | 标题与期刊可查 |
| 重新审视 pNNx 指标 | 2002 | 用到四十三名心衰患者 | 摘要可查,比较正常与心衰两组 |
需要注意,三篇文献的样本量与库的二十九人不完全对应。这不构成矛盾:一个研究队列里只有部分人留下了可用的数字化记录,是常态。但这也意味着不能假设"库里的二十九人就是论文里的那批人",只能说是同一来源下的一批。
8.3 生态位:注释型资源中的小型标本
在心电数据集谱系里,CHF2DB 占据的位置很小但清晰。它不属于挑战赛基准,不属于大规模预训练语料,也不属于临床决策研究队列。它属于"为特定分析方法提供输入"的工具型数据。
这个位置在 2003 年很重要——那时心率变异性的计算工具还不成熟,研究者需要一批干净的注释来验证算法。二十多年后,它的工具性依然存在,只是从"验证算法"变成了"校准管线"。§4.3 的案例在 §7.2 已经用过一次,用途正是校准。
8.4 谁在用它,用来做什么
从可得的公开信息看,它的使用面比它的知名度宽。常见的用途有三类。
第一类是心率变异性的方法学研究。任何提出新指标或修正旧指标的论文,都需要在多个队列上验证。CHF2DB 因为体积小、许可宽松、人群明确,常被选为验证队列之一。
第二类是教学。二十九条记录,每条一个注释文件,结构简单到可以用一节课讲完。它能让学生直观看到"心电数据不一定有波形"这件事。
第三类是作为流水线的压力测试。前面说过,六兆的体量让它适合做冒烟测试。一个处理大库的代码在 CHF2DB 上跑通,基本能排除代码级的低级错误。
§9 使用指引
9.1 三种角色的路径
| 角色 | 主要关心 | 建议路径 |
|---|---|---|
| 方法研究者 | 指标口径与可复现性 | 先读 §4.2 的三道坎,把预处理写成独立模块 |
| 数据工程师 | 管线接入与校验 | 先跑 §5.3 的读取,确认 n_sig 为 0 是预期行为 |
| 临床研究者 | 结论能否推广 | 先读 §7.3 的偏差表,决定结论能说到哪一层 |
三种角色的共同前置动作是一样的:先把头文件读出来,确认没有波形。这一步看似多余,却能省掉后面大量的误解。
9.2 一条最短的可用流程
从空目录到一张可分析的 RR 表,最短的路径是四步:取清单、读注释、筛心搏、算指标。下面这段把四步串起来,并把 §4.3 的案例的两套口径都打印出来做对比。
import wfdb
import numpy as np
BEAT = set('NLRBAaJSVrFfejnE')
def rr_of(rec):
hdr = wfdb.rdheader(rec, pn_dir='chf2db')
ann = wfdb.rdann(rec, 'ecg', pn_dir='chf2db')
idx = [i for i, s in enumerate(ann.symbol) if s in BEAT]
t = ann.sample[idx].astype(float) / hdr.fs
return hdr.comments[0], np.diff(t) * 1000.0
for rec in wfdb.get_record_list('chf2db')[:3]:
meta, rr = rr_of(rec)
#-- 只保留身心搏间隔,标出异常
med = np.median(rr)
ok = (rr > 0.3 * med) & (rr < 2.0 * med)
sdnn = float(np.std(rr[ok]))
rmssd = float(np.sqrt(np.mean(np.diff(rr[ok]) ** 2)))
print(rec, meta, 'SDNN', round(sdnn, 1), 'RMSSD', round(rmssd, 1))
这段代码每次都能跑通,因为本库的读取不依赖任何本地文件,全部走远程。如果要离线复现,把 pn_dir 换成本地目录名即可。
9.3 引用方式
引用这个库需要两层:一层是 PhysioNet 的平台引用,一层是数据集本身的 DOI。数据集的 DOI 在侧栏标出,是 10.13026/C2F598。平台引用按 PhysioNet 的建议统一使用其官方文献。
@misc{chf2db,
title = {Congestive Heart Failure RR Interval Database},
author = {Goldsmith, Rochelle L. and PhysioNet},
year = {2003},
note = {PhysioNet, version 1.0.0},
doi = {10.13026/C2F598}
}
如果论文分析用到了具体的指标口径,还应在方法部分写清楚预处理细节——这正是 §4.3 的案例反复强调的点。引用只解决"数据从哪来",口径说明才解决"结论能不能复现"。
§10 AI 使用声明
| 项 | 说明 |
|---|---|
| 写作方式 | 由 AI 辅助整理官方页面与公开文献,人工核对数字 |
| 数字来源 | 仅取官方页面、官方文件头、论文摘要三处 |
| 未做之事 | 未下载全库做分布统计,未估算标注频次 |
| 已知不确定 | 段长、导联配置、标注覆盖率均无官方口径 |
| 与官方冲突时 | 以官方页面为准 |
| 使用风险 | 文中判断基于节律分析视角,换用途需重评 |
| 更新维护 | 官方版本自 2003 年起未变,内容稳定性高 |
| 免责 | 不构成临床建议,不用于诊断用途 |
常见误读(避坑清单)
| 说法 | 实际 | 证据 |
|---|---|---|
| 这是 chfdb 的第二版 | 独立项目,机构、人群、内容都不同 | 官方页面来源声明与两库描述对比 |
| 库里有心电波形只是没打包 | 官方明写原始记录不可得 | 官方数据描述原文 |
| 每条记录二十四小时 | 官方未公布时长 | 头文件长度字段为 0 |
| 有健康人对照 | 全部二十九人均为心衰患者 | 官方人群描述 |
| 注释是纯人工标注 | 自动检测加人工复核 | 官方原文与注释者文件 |
.ecg 是信号文件 |
本库中是注释文件,扩展名沿用旧习惯 | 无信号文件,读取器按注释解析 |
| 可以按性别做亚组 | 仅十人有性别 | 头文件逐条核对 |
| 标注覆盖全部心律失常类型 | 无覆盖承诺 | 官方页面未提及 |
| 段长与 chfdb 相当 | 无从比较 | 两库段长口径不同 |
| 这是深度学习可用的训练集 | 二十九天样本,只适合外部验证 | 官方记录数 |
FAQ
它是哪个机构的数据?
由纽约哥伦比亚-长老会医学中心的 Rochelle Goldsmith 贡献给 PhysioNet,官方页面有专门致谢。这家机构是哥伦比亚大学医学院的附属医院,在九十年代是自主神经与心衰研究的重要基地。
为什么叫 RR 间期库,而不是心电库?
因为它发布的内容就是做 RR 间期分析所需的最小集:心搏时刻。波形被剔除,导联信息也随之消失。名字里的 RR 间期是对内容的准确描述,不是修辞。
29 这个数字是患者数还是记录数?
两者一致,一名受试者对应一段记录。编号从 chf201 到 chf229 连续,没有某人多段的情况。
头文件里的 0 是不是读取坏了?
不是。信号条数与采样点数在官方文件里就是 0,表示未声明。读取器只是如实传递。
能不能用注释文件重建波形?
不能。注释只记录了心搏发生的时刻与类型,没有任何电压信息。时刻之间的距离能还原心率序列,还原不了波形。
它适合做心衰严重度的分级模型吗?
不推荐作为主训练集。分级字段确实逐条存在,但只有三级、样本量小、无 IV 级,且缺少构建分级模型通常需要的其他临床变量。
和 CAST RR 间期库比,选哪个?
看你的问题需要多长的覆盖。CAST 明确写了近二十四小时与用药配对,规模大得多;CHF2DB 更小、更早、更朴素,适合作为方法验证或快速试验。
标注符号和 MIT-BIH 的那套一样吗?
符号体系同源,定义沿用 PhysioBank 的标准注释定义。但本库没有波形,符号的形态学含义在这里无法直接验证。
能不能用它算 pNN50?
能,但要先决定口径。直接把全部相邻间隔求差会把非心搏事件也算进去,得到不可用的结果。§4.3 的案例展示了两种口径的差别。
这个库有没有更新过?
没有。版本号自 2003 年 3 月 3 日发布起一直是 1.0.0,侧栏版本列表里只有这一条。
论文里引用它要注意什么?
除数据集 DOI 外,必须写清预处理口径与段长的处理方式。段长官方未公布,如果你按注释末位推算,应在文中说明这是下界。
它和这家机构的洋地黄研究是什么关系?
人群同源但不等同。那项研究招募了二十六名患者,库里是二十九人,两者不能画等号,只能说是同一来源下的两批。
术语表
| 术语 | 解释 |
|---|---|
| RR 间期 | 相邻两次心室除极之间的时间间隔,常用毫秒表示 |
| 心率变异性 | RR 间期序列的波动程度,反映自主神经调节 |
| SDNN | RR 间期序列的标准差,时域指标 |
| RMSSD | 相邻 RR 间期差值的均方根,偏副交感指标 |
| pNN50 | 相邻 RR 差值绝对值超过五十毫秒的比例 |
| 纽约心功能分级 | 按活动受限程度划分的心衰严重度,I 最轻、IV 最重 |
| 充血性心衰 | 因心脏泵血不足导致体液潴留的心衰类型 |
| 注释 | 标注了时刻与类型的二进制事件文件 |
| 注释符号 | 单字符代码,表示心搏或事件类型 |
| 辅助字符 | 跟在符号后的附加数字,含义依上下文 |
| 头文件 | 纯文本文件,声明记录参数与元数据 |
| 采样率 | 每秒采样点数,本库为 128 |
| 采样点序号 | 事件在原始数字化序列中的位置 |
| WFDB | PhysioNet 长期使用的生理信号存储规范 |
.hea |
WFDB 头文件扩展名 |
.ecg |
本库中为注释文件,非信号文件 |
| 记录清单 | RECORDS 文件,逐行列出记录名 |
| 注释者文件 | ANNOTATORS 文件,说明注释来源 |
| 校验和 | SHA-256 摘要,用于验证下载完整 |
| 自动检测 | 算法首次定位心搏 |
| 人工复核 | 人工检查并修正算法结果 |
| 节律标签 | 描述一段时间的节律类型,本库未提供 |
| 心搏类型 | 描述单次心跳的性质,本库提供 |
| ODC-BY | 开放数据共享署名许可 |
| 外部验证 | 在训练集之外的数据上评估模型 |
| 病例对照 | 以患者组与健康组比较的研究设计 |
| 冒烟测试 | 用小数据快速验证流水线是否可用 |
| 分段时长 | 单条记录覆盖的总时间,本库未公布 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 记录数 | 29 | 官方数据描述 | 官方页面 |
| 受试者数 | 29 | 官方数据描述 | 官方页面 |
| 年龄范围 | 34–79 | 官方数据描述 | 官方页面 |
| 男性数 | 8 | 官方数据描述 | 官方页面 |
| 女性数 | 2 | 官方数据描述 | 官方页面 |
| 性别未知数 | 21 | 官方数据描述推算 | 官方页面 |
| 纽约心功能分级 | I、II、III | 官方数据描述 | 官方页面 |
| 采样率 | 128 | 官方数据描述与头文件 | 官方页面 |
| 信号条数 | 0 | 头文件首行 | 官方文件 |
| 长度字段 | 0 | 头文件首行 | 官方文件 |
| 未压缩体积 | 6.4 MB | 官方文件页 | 官方页面 |
| 校验和条目 | 60 | 校验和清单 | 官方文件 |
| 首次发布日期 | 2003-03-03 | 官方侧栏 | 官方页面 |
| 版本号 | 1.0.0 | 官方侧栏 | 官方页面 |
| DOI | 10.13026/C2F598 | 官方侧栏 | 官方页面 |
| 许可 | ODC-BY 1.0 | 官方侧栏 | 官方页面 |
| 标注生成方式 | 自动检测加人工复核 | 官方数据描述 | 官方页面 |
附录 B:数字的来源分层
官方页面直接给出。 二十九条记录、二十九名受试者、三十四到七十九岁、八男两女、二十一人性别未知、纽约心功能分级 I 至 III、128 Hz、自动检测加人工复核、6.4 MB、2003 年 3 月 3 日、版本 1.0.0、DOI、ODC-BY 1.0。
官方文件里可读到。 每条记录的年龄、性别与分级,头文件的信号条数为 0 与长度字段为 0,六十一项文件的总数,注释文件与头文件一一对应。
公开文献可核实。 九十年代三项研究的题目、作者、年份与核心指标,以及队列规模与本研究库不完全对应的原因。
未核实。 单条记录的实际时长、原始记录的导联配置、采集设备与带宽、每位受试者的用药情况、标注复核的强度与人员、各类注释符号的实际覆盖情况。这些属于官方页面没有公布、也无法从文件推断的信息。
附录 C:完整参考文献
- Goldsmith RL, 等. Congestive Heart Failure RR Interval Database. PhysioNet, 版本 1.0.0, 2003. DOI: 10.13026/C2F598.
- Krum H, Bigger JT Jr, Goldsmith RL, Packer M. Effect of long-term digoxin therapy on autonomic function in patients with chronic heart failure. J Am Coll Cardiol. 1995;25:289–294. PMID: 7829779.
- Goldsmith RL, Bigger JT, Bloomfield DM, Krum H, Steinman RC, Sackner-Bernstein J, Packer M. Long-term carvedilol therapy increases parasympathetic nervous system activity in chronic congestive heart failure. Am J Cardiol. 1997;80:1101–1104.
- Mietus JE, Peng C-K, Henry I, Goldsmith RL, Goldberger AL. The pNNx files: re-examining a widely used heart rate variability measure. Heart. 2002;88:378–380. PMID: 12231596.
- Moody GB, Mark RG, Bump MA, 等. Clinical validation of the ECG-derived respiration (EDR) technique. Computers in Cardiology. 1986;13:507–510.
- Goldberger AL, Rigney DR, Mietus J, Antman EM, Greenwald S. Nonlinear dynamics in sudden cardiac death syndrome. Experientia. 1988;44:983–987.
- Peng C-K, Mietus J, Hausdorff JM, Havlin S, Stanley HE, Goldberger AL. Long-range anticorrelations and non-Gaussian behavior of the heartbeat. Phys Rev Lett. 1993;70:1343–1346.
- Peng C-K, Havlin S, Stanley HE, Goldberger AL. Quantification of scaling exponents and crossover phenomena in nonstationary heartbeat time series. Chaos. 1995;5:82–87.
- Poon C-S, Merrill CK. Decrease of cardiac chaos in congestive heart failure. Nature. 1997;389:492–495.
- Ivanov PCh, Amaral LAN, Goldberger AL, 等. Multifractality in human heart rate dynamics. Nature. 1999;399:461–465.
附录 D:任务代码
任务一:全部记录的人群信息汇总
import wfdb
import pandas as pd
rows = []
for rec in wfdb.get_record_list('chf2db'):
hdr = wfdb.rdheader(rec, pn_dir='chf2db')
line = hdr.comments[0] if hdr.comments else ''
#-- 第二行格式固定:Age: xx Sex: x NYHA class: iii
age = sex = nyha = None
for part in line.split(' '):
if part.startswith('#Age:'):
age = int(part.split(':')[1])
elif part.startswith('Sex:'):
sex = part.split(':')[1].strip()
elif part.startswith('NYHA class:'):
nyha = part.split(':')[1].strip()
rows.append({'record': rec, 'age': age, 'sex': sex, 'nyha': nyha})
info = pd.DataFrame(rows)
print(info['nyha'].value_counts(dropna=False))
print(info['sex'].value_counts(dropna=False))
任务二:心率变异性指标的批量计算
import wfdb
import numpy as np
BEAT = set('NLRBAaJSVrFfejnE')
def hrv(rec):
hdr = wfdb.rdheader(rec, pn_dir='chf2db')
ann = wfdb.rdann(rec, 'ecg', pn_dir='chf2db')
idx = [i for i, s in enumerate(ann.symbol) if s in BEAT]
rr = np.diff(ann.sample[idx].astype(float) / hdr.fs) * 1000.0
med = np.median(rr)
ok = (rr > 0.3 * med) & (rr < 2.0 * med)
rr = rr[ok]
d = np.diff(rr)
return {
'n_rr': len(rr),
'sdnn': float(np.std(rr)),
'rmssd': float(np.sqrt(np.mean(d ** 2))),
'pnn50': float((np.abs(d) > 50).mean() * 100),
}
for rec in wfdb.get_record_list('chf2db'):
print(rec, hrv(rec))
任务三:与注释时间的对齐检查
import wfdb
#-- 检查注释时刻是否单调递增、是否越界
for rec in wfdb.get_record_list('chf2db')[:5]:
ann = wfdb.rdann(rec, 'ecg', pn_dir='chf2db')
s = ann.sample
mono = bool((s[1:] > s[:-1]).all())
print(rec, '事件数', len(s), '单调', mono,
'首事件秒', round(float(s[0]) / 128.0, 2),
'末事件秒', round(float(s[-1]) / 128.0, 2))
任务四:把注释导出为通用事件表
import wfdb
import pandas as pd
rec = 'chf201'
hdr = wfdb.rdheader(rec, pn_dir='chf2db')
ann = wfdb.rdann(rec, 'ecg', pn_dir='chf2db')
df = pd.DataFrame({
'record': rec,
'sample': ann.sample,
'sec': ann.sample / hdr.fs,
'symbol': ann.symbol,
'aux': ann.aux_note,
})
df.to_parquet('chf201_events.parquet', index=False)
print(df.groupby('symbol').size().sort_values(ascending=False).head())
附录 E:如果只记五件事
- 它有心搏时刻,没有波形;信号条数在头文件里就是 0。
- 二十九人,三十四到七十九岁,纽约心功能分级 I 至 III,八男两女、二十一人性别未知。
- 它不是 chfdb 的第二版;chfdb 在波士顿、有波形,它在纽约、只留注释。
- 单条记录时长官方没公布,头文件长度字段是 0,任何时长数字都要标明是推算下界。
- 它最合适的用途是节律分析的外部验证与管线校准,而不是训练主集。
编辑说明
本文的全部数字取自 PhysioNet 官方项目页、官方文件头与校验和清单,以及 Europe PMC 可核实的论文摘要。未下载全量做分布统计,也未对注释符号的出现频次、间隔分布的百分位、字段空值率等做交叉分析——这类统计属于使用者拿到数据后自己该做的第一步,提前替使用者做完,对决策没有增量。
文中标注为「官方未公布」的项目,包括单条记录的实际时长、原始记录的导联配置与采集设备、每位受试者的用药情况与标注复核强度。这些信息在官方页面、头文件与公开文献三处均查不到,任何具体数值都是推测,因此一律不写。
一个需要向评审专家交代的判断:本文把 CHF2DB 按「节律分析资源」评估,而非按「心电数据库」评估。这个选择在 §7.0 已作说明,理由是它的设计意图与文件结构都指向后者,按形态学标准评会在三项上直接清零,失去区分度。使用者若把本文结论套到形态学任务上,会得到相反的结论——那属于用途切换,需要重新评估。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- hls-cmds — 共享标签:生理信号 / 心血管疾病
- physionet-cinc-2016 — 共享标签:生理信号 / 心血管疾病
- bpssrat — 共享标签:生理信号 / 心血管疾病
- cardioresp-response-orthostat — 共享标签:生理信号 / 心血管疾病
- cerebral-perfusion-diabetes — 共享标签:生理信号 / 心血管疾病
- wctecgdb — 共享标签:生理信号 / 心血管疾病
- ahadb — 共享标签:生理信号 / 心血管疾病
- chfdb — 共享标签:生理信号 / 心血管疾病
- crisdb — 共享标签:生理信号 / 心血管疾病
- cudb — 共享标签:生理信号 / 心血管疾病
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

