信息速览
正常窦性心律 RR 间期库这个名字,前半段听起来像一份健康人名单,后半段听起来像一份现成的间期表。两处都需要修正。它是 PhysioNet 在 2003 年 3 月 3 日以 1.0.0 版发布的一份纯注释数据,提交者是圣路易斯华盛顿大学医学院的 Phyllis Stein,合作者是纽约哥伦比亚-长老会医学中心的 Rochelle Goldsmith,DOI 为 10.13026/C2S881,按 ODC-BY 1.0 开放。
它确实收录了 54 名正常窦性心律受试者的长时程心电,但只留下每一次心跳发生的时刻,原始波形被官方明确标为不可用。所以整包只有 11.1 MB——不是压缩得好,是本来就没有那条曲线。头文件里信号数写 0、样本数写 0,这两个零是全库最重要的一行元数据。
因此它的价值与限制是同一件事的两面:你拿到的是全库最干净的心搏时间序列,代价是你永远无法回头看那个心搏长什么样。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 官方全名 | Normal Sinus Rhythm RR Interval Database |
| 常用缩写 | NSR2DB、nsr2db |
| 中文名 | 正常窦性心律 RR 间期库 |
| 提交者 | Phyllis Stein(华盛顿大学医学院,圣路易斯) |
| 合作者 | Rochelle Goldsmith(哥伦比亚-长老会医学中心,纽约) |
| 发布方与版本 | PhysioNet,2003 年 3 月 3 日,v1.0.0(唯一版本) |
| DOI | 10.13026/C2S881(v1.0.0);最新版 10.13026/r43m-7x19 |
| 受试者 | 54 名,30 男 24 女 |
| 年龄范围 | 男性 28.5 至 76 岁,女性 58 至 73 岁 |
| 记录数 | 54 条,记录名 nsr001 至 nsr054 |
| 每条内容 | 仅心搏时刻与注释码,无波形 |
| 采样与量化 | 原始记录按每秒 128 个样本数字化,波形不存 |
| 文件构成 | 每条一对 .hea 与 .ecg,另有三个清单文件 |
| 整包体量 | 未压缩 11.1 MB |
| 许可 | Open Data Commons Attribution License v1.0 |
§1 它要解决的具体麻烦
1.1 心率变异性研究的第一道工序,恰好被这份数据跳过了
做心率变异性分析的人,开工第一件事不是算指标,而是把波形变成一串时刻。这一步包括滤波、QRS 检测、阈值调参、漏检补、误检删。每一步都会留下痕迹:滤波器截止频率换了,RR 序列的极短间期就变;检测器阈值调高了,漏掉的搏动会在序列里留下一个双倍长的间期,直接把 SDNN 抬上去。
所以同一个公开波形库,交给两个实验室,能算出两套对不上的 HRV 结果。差异不在后面的统计,而在前面那道工序。这在方法学上是一个长期存在的麻烦:你要比较的是人群或病理差异,实际比较到的可能只是两个 QRS 检测器。
nsr2db 的定位就是把这道工序抽走。它交付的是已经由自动检测器定位、再经人工复核与校正的心搏时刻。你拿到手就是时间戳序列,差分即得 RR。省下的不只是代码量,还有一层争议:指标差异不能再归因于各自的检测器。
这解释了它为什么没有波形也能成立。它不是在提供一份心电记录,而是在提供一份关于心电记录的判定结果——每一次心跳在哪里。判定一旦落成时刻,波形对下游就是冗余,冗余到可以整包丢掉,只花 11.1 MB。
也解释了它为什么不适合所有人。任何需要看波形形态的问题,在这里都没有入口。它的取舍非常干脆:为了让节律分析有一个不含检测器变量的起点,代价是放弃全部形态信息。
1.2 三条常见的错误期待
| 常见期待 | 实际情况 | 后果 |
|---|---|---|
| 以为是 nsrdb 的新版或子集 | 两者人群不同、机构不同、互不包含 | 按记录名对齐会全部错位,拼接出并不存在的 72 人队列 |
| 以为下载包解压后有波形 | 原始记录官方标为不可用,文件里只有注释 | 数据管线在第一步就断,误以为是下载不完整 |
| 以为是 54 名健康志愿者 | 受试者来自若干 HRV 研究队列,入组条件是正常窦性心律 | 把研究入组条件当成健康筛查结论,正常值范围算窄 |
三条期待有一个共同来源:名字里的 RR Interval 听起来像一份已经算好的表,Normal 听起来像一种人的属性。实际前者是一堆时刻,后者是一条入组标准。
| 字面 | 读起来像 | 实际指涉 |
|---|---|---|
| Normal | 健康人 | 研究入组时的节律判定 |
| RR Interval | 现成的间期数值表 | 心搏时刻,间期需自行差分 |
| Database | 一批同源记录 | 若干研究队列的汇合 |
| 54 | 一个完整队列 | 四项研究积累的总数 |
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 一份可直接差分的长时程 RR 序列 | 经人工校正的心搏时刻 | 够,且省掉检测环节 |
| 跨昼夜的心率慢变趋势 | 数十小时量级的连续时刻 | 够,但每条确切跨度要自己读 |
| 与波形形态有关的任何特征 | 无 | 不够,且无替代路径 |
| 一群人的 HRV 正常值范围 | 54 人,年龄性别分层极不均 | 不够,只能做描述不能定范围 |
| 一个不含检测器变量的评测基准 | 时刻本身即金标准 | 够,但只覆盖节律层 |
| 异位搏动的形态学证据 | 只有注释码,没有波形 | 不够,只能看到间期后果 |
| 可直接商用的开放数据 | ODC-BY 1.0,署名即可 | 够 |
| 逐人的临床背景 | 只有年龄与性别 | 不够,无病史无用药 |
这张表里最容易被忽略的是第三行和第六行。它们不是「暂时缺」,而是这份数据的定义使然:既然交付物是时刻,形态就必然不在。凡是需求落在形态一侧的项目,无论怎么加工都补不回来。
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 手上有一批 Holter 波形,想验证自己的 QRS 检测器 | 提供人工校正后的时刻作为参照 | 在同一批人上跑检测器,比对逐搏差异 |
| 要做 HRV 与预后或年龄的关联,但样本采集成本高 | 54 条长时程序列可立即开工 | 先确认年龄性别分布能否支撑你的分层 |
| 想把正常节律做成一个阴性类,与房颤库配对 | 提供无形态依赖的阴性侧时刻 | 与房颤库的 RR 序列统一到同一差分口径 |
| 在教 HRV 指标怎么算,需要一份干净数据 | 体量小、无波形、装进课堂笔记本毫无压力 | 直接从时刻讲到频域,不必先讲滤波 |
| 论文审稿要求排除检测器带来的偏倚 | 时刻由他人校正,可作独立参照 | 在方法段写明参照来源与版本 DOI |
| 想做心电图像或波形分类 | 帮不上 | 改选带波形的库,别在此处耗时 |
第一行与第五行是这份数据最硬的用途:它把「你的检测器准不准」变成一个可以逐搏核对的问题。第四行是它被低估的用途:教学场景里,波形往往是干扰项,学员把大量时间花在调滤波上,反而没学会指标本身的含义。
1.5 一句话判断
它不是心电库,是一份关于五十四段长时程心电「每一次心跳发生在第几秒」的判定书;判定之外的一切,官方都没有交付。
1.6 不适合谁:四条边界
第一条,不适合任何形态学任务。没有波形,ST 段、QRS 宽度、T 波电轴全部无从谈起。哪怕只想画一张「正常心搏长什么样」的示意图,也得换库。
第二条,不适合当作人群参考。54 人里 30 名男性跨 28.5 至 76 岁,24 名女性全在 58 至 73 岁。这个构成不是抽样设计的结果,而是几个研究队列拼起来的结果。按性别年龄分层之后,每格只剩个位数。
第三条,不适合需要逐条时长的严谨复现。官方页面没有给出每条记录的小时数,头文件里样本数写 0,也读不出时长。要写「共多少小时」,必须先读注释时间戳再自己算,且必须说明这是推算而非官方口径。
第四条,不适合与 nsrdb 拼接成一个大库。两者人群不重叠,记录名体系不同,注释器不同,连「正常」的判定流程都出自不同机构。把它们当同一队列的两个视图,会得到一个既非 18 也非 54 的虚构样本量。
§2 数据构成与规格
2.1 一个扁平目录,三类文件,没有任何层次
解压之后没有子目录。所有文件平铺在同一层,共 111 个:54 组记录文件各两个,加三个清单。这个结构简单到可以用一条命令看完,也简单到没有任何元数据文档——所有关于受试者的信息,只存在于每个头文件的一行注释里。
| 文件 | 数量 | 作用 |
|---|---|---|
| nsrXXX.hea | 54 | 头文件,声明记录参数与受试者年龄性别 |
| nsrXXX.ecg | 54 | 注释文件,存心搏时刻与注释码 |
| RECORDS | 1 | 记录清单,每行一个记录名 |
| ANNOTATORS | 1 | 注释器清单,说明注释来源 |
| SHA256SUMS.txt | 1 | 校验和清单,2019 年补入 |
| 文件类型 | 大小量级 | 是否含波形 | 解析方式 |
|---|---|---|---|
| .hea | 33 至 35 字节 | 否 | 纯文本,逐行读 |
| .ecg | 150 至 267 KB | 否 | WFDB 注释二进制 |
| RECORDS | 378 字节 | 否 | 纯文本,一行一名 |
| ANNOTATORS | 58 字节 | 否 | 制表符分隔两列 |
111 个文件里最容易被误解的是 .ecg。这个后缀在心电语境里几乎总是指波形采样文件,但在这里它是注释文件的扩展名。头文件第一行的信号数为 0,等于官方在文件内部写了一句「本记录没有信号」。任何按后缀猜内容的流程,都会在这里出错。
2.2 字段与记录的完整字典
整个库的字段少得反常。一条记录真正的字段只有七个,其中四个来自头文件第一行,两个来自注释行,一个来自注释文件本身。
| 字段 | 位置 | 取值示例 | 类型 | 含义 | 是否逐条变化 |
|---|---|---|---|---|---|
| 记录名 | 头文件第 1 列 | nsr001 | 字符串 | 记录标识,nsr 加三位序号 | 是 |
| 信号数 | 头文件第 2 列 | 0 | 整数 | 信号通道数,全库为 0 | 否 |
| 采样率 | 头文件第 3 列 | 128 | 整数 | 每秒样本数,仅用于时刻刻度 | 否 |
| 样本数 | 头文件第 4 列 | 0 | 整数 | 采样点总数,全库为 0 | 否 |
| 年龄 | 注释行 | 64 | 小数或整数 | 受试者年龄,单位岁 | 是 |
| 性别 | 注释行 | F | 字符 | M 或 F | 是 |
| 心搏时刻 | 注释文件 | 采样点序号 | 整数序列 | 每次心搏的位置 | 是 |
| 头文件字段 | 常见取值 | 为什么是这个值 | 使用注意 |
|---|---|---|---|
| 信号数 | 0 | 该记录没有信号通道 | 为 0 时同名文件必为注释 |
| 采样率 | 128 | 沿用原始数字化参数,给注释定刻度 | 单位是样本每秒,不是赫兹带宽 |
| 样本数 | 0 | 未声明信号长度 | 不能用来算时长 |
| 注释行 | Age 与 Sex | 研究者顺手写下的受试者属性 | 全库唯一的个体信息 |
这七个字段里,只有年龄和性别与「人」有关,而这两个值没有单位说明、没有采集时点、也没有与记录日期对齐。它们是研究表格里的两列,被顺手抄进了头文件,不是临床变量。
2.3 一条记录的完整解剖
以 nsr001 为例。整个头文件只有两行,第一行四个字段,第二行一句注释:
#-- 一个头文件只有两行,三十三个字节
cat nsr2db/nsr001.hea
| 行 | 字面内容 | 读法 |
|---|---|---|
| 第 1 行 | nsr001 0 128 0 | 记录名、信号数、采样率、样本数 |
| 第 2 行 | Age: 64 Sex: F(行首有一个注释符) | 年龄 64 岁,女性 |
第一行四个字段依次读作:记录名 nsr001,信号数 0,采样率 128,样本数 0。第二行是注释,给出年龄与性别。全文三十三个字节,比一行邮件主题还短。它同时也是这条记录的全部元数据——没有诊断、没有用药、没有记录日期、没有设备型号、没有导联位置。
注释文件里则是一串时刻。用 WFDB 的读取接口打开,得到的不是采样点数组,而是样本序号与注释码:
#-- 读一条记录的注释,确认它没有信号
import wfdb
rec = wfdb.rdheader("nsr001") #-- 只读头文件
print(rec.n_sig, rec.fs, rec.sig_len) #-- 期望输出 0 128 0
ann = wfdb.rdann("nsr001", "ecg") #-- 注释器名为 ecg
print(ann.sample[:5]) #-- 前五次心搏的样本序号
print(ann.symbol[:5]) #-- 对应注释码
这段代码跑通之后,两件事同时被确认:信号通道为零,注释可以正常读出。若把第二行换成波形读取函数,得到的是空数组而不是报错,这一点常让初学者误以为文件损坏,实际是读取方式与内容不匹配。
| 解剖层次 | 内容 | 可否再分 |
|---|---|---|
| 文件层 | 两个文件,一对一同名 | 否 |
| 头文件层 | 一行参数,一行年龄性别 | 否 |
| 注释层 | 心搏时刻序列加注释码 | 可按码筛选 |
| 信号层 | 不存在 | — |
2.4 五十四条记录的体量与长度
注释文件的体量从 151.7 KB 到 266.9 KB,跨度不到两倍。这个窄区间本身是一条信息:如果各条时长差异很大,体量应该拉开几倍。窄,说明五十四条的时长量级相近,都在一昼夜上下。
| 观测项 | 官方值 | 说明 |
|---|---|---|
| 记录条数 | 54 | RECORDS 逐行列出 |
| 头文件大小 | 33 字节(nsr047 为 35) | 差异来自年龄写成 28.5 |
| 注释文件最小 | 151.7 KB | nsr033 |
| 注释文件最大 | 266.9 KB | nsr020 |
| 整包未压缩 | 11.1 MB | 含清单与校验和 |
| 每条时长 | 官方未给出 | 头文件样本数为 0,读不出 |
| 常见推算方式 | 可行性 | 会得到什么 |
|---|---|---|
| 用样本数除以采样率 | 不可行 | 样本数为 0,除出零 |
| 用文件体量除以平均心率 | 粗略可行 | 只能判断量级,误差大 |
| 读最后一个注释的时间戳 | 可行 | 得到该条记录的实际跨度 |
| 引用参考文献的「24 小时」 | 可行但需说明 | 是研究设计口径,不是逐条核对的结果 |
第四行是唯一诚实的做法:说清「约二十四小时」来自研究设计与注释跨度,而不是官方逐条标注。把这句写进方法段,比含糊地写「均为 24 小时」要稳。
2.5 与同类资源的规格差异
同样是长时程正常心律,波形版与时刻版的规格差异不是量的差别,是交付物的差别:
| 规格项 | nsr2db | nsrdb | chf2db | ltafdb |
|---|---|---|---|---|
| 记录数 | 54 | 18 | 29 | 84 |
| 是否含波形 | 否 | 是 | 否 | 是 |
| 采样率 | 128(仅刻度) | 128 | 128(仅刻度) | 128 |
| 受试者状态 | 正常窦性心律 | 正常窦性心律 | 充血性心衰 | 阵发性或持续性房颤 |
| 注释器 | ecg | atr | ecg | atr |
| 整包体量 | 11.1 MB | 582.7 MB | 十余 MB | 数 GB |
| 体量比 | 约五十分之一 | 基准 | — | — |
最后一行的对比最能说明问题:同样是一昼夜量级的心电,只保留时刻之后,体积降到波形版的五十分之一左右。这个比例不是压缩算法的功劳,而是被丢掉的那部分信息——每一次心跳的完整形态——本来占了绝大部分。
§3 它的身份:一份从 HRV 研究里长出来的时刻表
3.1 谁做的、为什么做
两位提交者都不是心电设备工程师,而是心率变异性研究者。Stein 在圣路易斯华盛顿大学医学院,Goldsmith 在纽约哥伦比亚-长老会医学中心,两人的合作横跨二十余年,产出集中在同一个问题上:二十四小时心电里的 RR 波动,能不能反映自主神经的状态。
| 角色 | 姓名 | 机构 | 在库中的角色 |
|---|---|---|---|
| 提交者 | Phyllis Stein | 华盛顿大学医学院,圣路易斯 | 数据集署名作者 |
| 合作者 | Rochelle Goldsmith | 哥伦比亚-长老会医学中心,纽约 | 数据来源研究的主要合作者 |
| 发布平台 | PhysioNet | MIT 计算生理实验室 | 托管、版本化、长期保存 |
| 资助 | NIBIB、NHLBI、NIH | 美国国立卫生研究院 | 平台级资助 |
这个组合决定了数据的形状。心电工程师会把波形存好,因为波形是他们的产品;HRV 研究者只关心时刻,因为指标全在时刻上算。当这批人决定公开数据时,他们自然地只公开了自己用得到的那一层。
3.2 五十四个人是谁
官方给出的只有两行:30 名男性 28.5 至 76 岁,24 名女性 58 至 73 岁。八个数字里藏着明显的结构。
| 组别 | 人数 | 年龄下限 | 年龄上限 | 跨度 | 观察 |
|---|---|---|---|---|---|
| 男性 | 30 | 28.5 | 76 | 47.5 岁 | 覆盖青年到老年 |
| 女性 | 24 | 58 | 73 | 15 岁 | 全部为老年 |
| 全体 | 54 | 28.5 | 76 | 47.5 岁 | 女性拉高下限 |
女性一个人都不在五十八岁以下,这不是抽样误差能解释的。如果是从门诊连续入组,年轻女性总会占到一定比例。全缺,说明这批记录来自若干项已有研究,而其中提供女性记录的,是一项以老年人为对象的研究。
男性一侧跨到二十八岁半,且出现小数,说明年龄是从研究表格里原样搬来的——某些研究记录了半岁精度,某些没有。这个小数后来在文件层面留下了痕迹:nsr047 的头文件比其他条多两个字节,因为 28.5 比两位整数多占了字符。
3.3 官方列出的四篇文献,指向四条来源
页面列出四篇参考文献,主题全部是心率变异性。它们不是「用了这个库的论文」,而是「这批数据的来源研究」。官方没有逐条说明哪条记录来自哪篇,但四篇的主题与人群分布对得上。
| 文献 | 年份 | 主题 | 与人群构成的对应(推断) |
|---|---|---|---|
| Goldsmith 等,JACC | 1992 | 耐力训练与未训练年轻男性的二十四小时副交感活性 | 对应 28 至 40 岁男性段 |
| Bigger 等,Circulation | 1995 | 健康中年人与冠心病、心梗者的 RR 变异性比较 | 对应中年健康对照 |
| Stein 等,Am Heart J | 1999 | 运动训练对健康老年人 HRV 的影响 | 对应 58 至 73 岁段 |
| Mietus 等,Heart | 2002 | pNNx 指标的再评估 | 方法学,未必贡献记录 |
| 来源研究 | 是否可核到记录 | 官方是否说明 | 使用影响 |
|---|---|---|---|
| Goldsmith 1992 | 否 | 未说明 | 无法只取该研究子集 |
| Bigger 1995 | 否 | 未说明 | 无法做病例对照 |
| Stein 1999 | 否 | 未说明 | 无法区分训练组与对照组 |
| Mietus 2002 | 否 | 未说明 | 仅作方法学参考 |
这张表的最后一行是全库最实质的缺口:五十四条记录被打包成一个整体,但它们的原始研究设计各不相同。有人是训练干预的对照,有人是冠心病的健康参照。如果不能按研究分层,任何组间比较都失去前提。
3.4 名字里的三层信息
名字由四个词组成,每个词都在限定范围,也都在制造误解。
| 词 | 限定什么 | 容易误读成 | 实际含义 |
|---|---|---|---|
| Normal | 节律判定 | 健康人群 | 入组时判为正常窦性心律 |
| Sinus Rhythm | 节律类型 | 心电图正常 | 起搏点在窦房结 |
| RR Interval | 交付物 | 现成间期数值 | 心搏时刻,需差分 |
| Database | 汇集方式 | 单一队列 | 多项研究的汇合 |
第三层最值得强调。库名叫 RR 间期,文件里却没有一个 RR 值。它存的是每一次心搏的绝对时刻,间期是相邻时刻之差。这个设计不是偷懒:存绝对时刻可以无损还原间期,还能还原任意时间窗内的搏动计数、瞬时心率、以及节律的绝对相位;存间期则会丢掉起点信息,一旦某处出错也无法定位。
3.5 记录名 n s r 加三位序号说明了什么
记录名从 nsr001 排到 nsr054,连续无缺号。这种命名方式说明两件事:记录是在一次整理中批量命名的,且编号与原始研究编号之间没有保留映射。
| 命名特征 | 含义 | 使用后果 |
|---|---|---|
| 三位补零 | 上限定在三位,容量 999 | 扩容空间充足,但从未扩容 |
| 连续无缺号 | 一次成批命名 | 编号不携带来源信息 |
| 前缀 nsr | 与 nsrdb 的五位数字名不冲突 | 两库不会误撞名,但也无从对齐 |
| 无分组标记 | 未按研究或性别分段 | 无法用名字筛子集 |
对比 nsrdb 的五位数字记录名,可以看出两个库在整理方式上的差别:那个库沿用了当年实验室的记录编号,这个库重新编了号。重新编号的好处是整齐,代价是丢掉了与原始研究的对应线索。
§4 它最独特的东西:把波形删掉之后还剩下什么
4.1 删掉波形不是减法,是换了一种数据结构
一般的数据精简是压缩,删掉波形是换问题。压缩之后你还能还原,删掉之后你不能。所以这份数据的每一个使用场景,都必须先回答一句:我的问题是否只依赖时刻。
| 被删掉的信息 | 是否影响节律分析 | 是否影响形态分析 | 能否从别处补 |
|---|---|---|---|
| 波形振幅 | 否 | 是 | 否 |
| QRS 宽度 | 否 | 是 | 否 |
| ST 段水平 | 否 | 是 | 否 |
| 心搏时刻 | — | — | 就是它本身 |
| 注释码 | 否 | 部分 | 部分可由间期推断 |
第三列与第四列决定了它的使用边界:只要问题落在「什么时候跳」,它够用且优越;一旦落到「跳成什么样」,它连补救的余地都没有。
4.2 案例:nsr047 的三十五字节头文件
全库五十四个头文件里,五十三个是三十三字节,只有 nsr047 是三十五字节。多出来的两个字节,来自一个年龄:28.5。
#-- 唯一一条三十五字节的头文件
cat nsr2db/nsr047.hea
| 项 | nsr047 的值 | 与其余五十三条的差别 |
|---|---|---|
| 记录名 | nsr047 | 序号落在后段 |
| 信号数 | 0 | 相同 |
| 采样率 | 128 | 相同 |
| 年龄 | 28.5 | 全库最年轻,且带半岁 |
| 性别 | M | 男性 |
| 文件体量 | 35 字节 | 其余为 33 字节 |
这条记录是三十名男性中最年轻的一位,二十八岁半。他的头文件比其他条多两个字符,因为「28.5」比两位整数多占两个字符位置。文件大小列表里那处不起眼的 35 B,就是这半个年岁留下的痕迹。
§4.2 的案例值得停一下,因为它同时展示了三件事。其一,年龄确实是从原始研究表格原样搬来的,否则不会保留半岁精度;其二,头文件是纯文本拼出来的,没有任何字段对齐或填充,所以字符数会随内容浮动;其三,全库的元数据精度取决于当年那份表格,而不是发布时的整理。
这个案例还能反向提醒一件事:既然半岁都被保留,那么「年龄」在研究里可能是一个协变量,而不是一个描述性标签。使用者若把年龄当连续变量做回归,28.5 这样的值说明它本来就打算被这样用。
把这条记录与 nsr001(64 岁女性)并排看,能直观感到人群的跨度:
| 记录 | 年龄 | 性别 | 头文件体量 | 在同组中的位置 |
|---|---|---|---|---|
| nsr047 | 28.5 | M | 35 B | 男性最年轻 |
| nsr054 | 35 | M | 33 B | 男性偏年轻 |
| nsr001 | 64 | F | 33 B | 女性中段 |
| nsr033 | 65 | M | 33 B | 男性偏老年 |
这四条是抽查所得,只用于说明头文件格式,不构成分布。真正的年龄分布要读全部五十四行才能算,而那是拿到数据之后的一步。
4.3 时刻的精度由采样率锁死
注释里的时刻是样本序号,除以采样率才是秒。采样率为 128,所以时间的量化步长是 1/128 秒,约 7.8 毫秒。这个数字决定了所有下游指标的精度上限。
| 采样率 | 量化步长 | 对 RR 的影响 | 适用指标 |
|---|---|---|---|
| 128 Hz | 约 7.8 毫秒 | 每个时刻独立取整 | SDNN、RMSSD、频域均可用 |
| 250 Hz | 4 毫秒 | 误差减半 | 同上,更稳 |
| 360 Hz | 约 2.8 毫秒 | 更细 | 短程指标更稳 |
| 1000 Hz | 1 毫秒 | 接近连续 | 高频分析 |
关键在于误差不是单向的。相邻两个时刻各自取整,差值可能在正负一个步长之间摆动。一个真实 RR 为 800 毫秒的间期,记录成 800 毫秒、792 毫秒或 808 毫秒都属正常。对以毫秒为单位的指标,这一步长会带来随机抖动;对以 50 毫秒为阈值的 pNN50,抖动会改变临界样本的归属。
| 指标 | 对量化步长的敏感度 | 说明 |
|---|---|---|
| 平均心率 | 低 | 长时程平均会抵消抖动 |
| SDNN | 低 | 抖动相对总体标准差很小 |
| RMSSD | 中 | 逐差放大了抖动 |
| pNN50 | 中高 | 阈值附近的样本会翻转 |
| DFA 标度指数 | 中 | 短尺度受影响明显 |
| 样本熵 | 中高 | 匹配容差与步长同量级时敏感 |
正确的处理不是假装步长不存在,而是在方法段写明:时刻量化为 1/128 秒,短程指标解释时考虑此限制。若一定要与 250 Hz 以上的数据比较,可先对 RR 序列做轻度平滑,再报告两套结果。
4.4 它能支撑的五类实验
| 实验类型 | 输入 | 输出 | 是否需要波形 | 常见陷阱 |
|---|---|---|---|---|
| 时域 HRV 描述 | RR 序列 | SDNN、RMSSD、pNN50 | 否 | 未剔除异位搏动 |
| 频域 HRV 分析 | 重采样后的 RR | VLF、LF、HF 功率 | 否 | 未去趋势,VLF 不可靠 |
| 心率动力学 | RR 序列 | 复杂度、分形指标 | 否 | 序列过短 |
| QRS 检测器评测 | 检测结果与参照时刻 | 灵敏度、阳性预测值 | 否 | 容差窗设置不一致 |
| RR 序列生成模型 | 全部序列 | 合成序列 | 否 | 只学均值不学长程相关 |
| 实验类型 | 本库适配度 | 原因 |
|---|---|---|
| 时域描述 | 高 | 时刻干净,直接可用 |
| 频域分析 | 中高 | 需自行重采样与去趋势 |
| 心率动力学 | 中高 | 时长足够,但步长限制短尺度 |
| 检测器评测 | 高 | 人工校正过的时刻正合适 |
| 生成模型 | 中 | 54 条偏少,易过拟合 |
五类实验有一个共同前提:先把异位搏动与伪差造成的异常间期处理掉。库里的注释码能帮上一部分忙,但没有波形,你无法判断一个短间期是房性早搏还是检测错误。
4.5 边界:什么时候它不再适用
| 场景 | 是否适用 | 原因 | 替代方案 |
|---|---|---|---|
| 计算二十四小时 HRV | 适用 | 正是设计目标 | — |
| 训练心电形态分类器 | 不适用 | 无波形 | 带波形的 Holter 库 |
| 研究房颤的 RR 特征 | 部分适用 | 全是正常窦性心律 | 房颤长时程库 |
| 做人群正常值 | 不适用 | 54 人,分层极不均 | 人群队列 |
| 复现某篇论文的检测器 | 适用 | 时刻可作参照 | — |
| 研究睡眠期心率下降 | 适用 | 覆盖昼夜 | 需自带睡眠分期则不行 |
最后一行值得展开。这份数据覆盖一昼夜,却没有任何时间标签说明哪一段是睡眠。你可以从心率本身推断夜间段,但那是推断,不是标注。要做睡眠分期与心率的联合分析,必须另找带睡眠标注的数据。
4.6 与「只保留 RR 的库」这一类的关系
PhysioNet 上有一小类库走同一条路线:只存心搏时刻。它们的共同理由是原始波形已经不在,或从未数字化。理解这一类,比记住单个库更有用。
| 库 | 人群 | 记录数 | 走这条路线的原因 |
|---|---|---|---|
| nsr2db | 正常窦性心律 | 54 | 原始记录未随库公开 |
| chf2db | 充血性心衰 | 29 | 原始记录未随库公开 |
| stdb | 运动试验中的 ST 改变 | 28 | 早期库,波形与注释并存但以注释为主 |
| 本类共性 | — | — | 波形缺失,时刻完整 |
这条路线有一个共同的方法学后果:所有形态学问题被永久排除,所有节律问题被永久简化。选择这类数据,本质上是在承认自己的研究问题只关心时间轴。
§5 怎么拿到:从 11.1 MB 到一条可用的 RR 序列
5.1 三条取数路径
| 路径 | 命令形态 | 适合谁 | 注意 |
|---|---|---|---|
| 整包 ZIP | 浏览器下载 | 一次性使用、教学 | 解压后平铺,无子目录 |
| 递归镜像 | wget 递归 | 需要与官方目录同构 | 会带上清单与校验和 |
| 对象存储 | S3 同步 | 集群内批量、可重复 | 需匿名访问参数 |
#-- 三条路径任选其一;第二条最适合脚本化
wget -r -N -c -np https://physionet.org/files/nsr2db/1.0.0/
aws s3 sync --no-sign-request s3://physionet-open/nsr2db/1.0.0/ ./nsr2db
第一条路径的坑是解压位置:压缩包内没有顶层目录,直接解压会把一百多个文件倒进当前目录。第二条路径的坑是递归参数:少了 -np 会顺着链接爬向站内其他项目。
5.2 目录与体积
| 项 | 值 | 说明 |
|---|---|---|
| 文件总数 | 111 | 54 组记录加三个清单 |
| 目录层级 | 1 | 全部平铺 |
| 单条记录 | 约 150 至 270 KB | 头文件加注释 |
| 清单与校验 | 约 9 KB | RECORDS、ANNOTATORS、SHA256SUMS |
| 整包 | 11.1 MB | 未压缩 |
体积小带来的一个实际好处是:整库可以进内存,也可以进版本库。把五十四条记录全读进来,得到的对象在内存里只占几十 MB,做全量交叉验证不需要分批。
5.3 先把头文件读出来,确认三个零
在写任何分析代码之前,先确认这批文件的自我描述。这一步只需要几行,但能避免后面所有的误判。
#-- 一次性核对全库的记录名、信号数与采样率
import wfdb
from pathlib import Path
names = Path("nsr2db/RECORDS").read_text().split()
rows = []
for n in names:
h = wfdb.rdheader(f"nsr2db/{n}")
rows.append((n, h.n_sig, h.fs, h.sig_len))
print(len(rows)) #-- 期望 54
print(sorted({r[1] for r in rows})) #-- 期望 [0],全库无信号
print(sorted({r[2] for r in rows})) #-- 期望 [128]
输出里应当只有 0 和 128 两个值。若看到信号数不是 0,说明文件被别的内容替换过;若采样率不是 128,说明混入了别的库。这一步同时也是最省事的完整性检查。
顺带还能核对一件小事:文件体量的集合里会出现一个异类。§4.2 的案例里那条三十五字节的头文件,会在这张核对表里第一个被认出来,因为其余记录都是三十三字节。若批量读取时把年龄一律解析成整数,这个异类就会消失——不是文件变了,是解析把它们抹平了。
5.4 建索引:把两个文件拼成一张表
库本身没有索引表,索引要自己建。最小可用的索引只需四列:记录名、年龄、性别、心搏数。
#-- 建一张全库索引,年龄性别从头文件注释里取
import re
import wfdb
import pandas as pd
rows = []
for n in open("nsr2db/RECORDS").read().split():
h = wfdb.rdheader(f"nsr2db/{n}")
ann = wfdb.rdann(f"nsr2db/{n}", "ecg")
comment = " ".join(h.comments)
age = float(re.search(r"Age:\s*([\d.]+)", comment).group(1))
sex = re.search(r"Sex:\s*([MF])", comment).group(1)
rows.append({"record": n, "age": age, "sex": sex, "beats": len(ann.sample)})
idx = pd.DataFrame(rows)
print(idx.groupby("sex")["age"].agg(["count", "min", "max"]))
最后一行打印的是按性别分组的计数与年龄上下限,可以直接与官方页面给出的两组数字对照。若对不上,说明注释行格式有变体,需要放宽正则。
| 索引列 | 来源 | 类型 | 用途 |
|---|---|---|---|
| record | RECORDS | 字符串 | 主键 |
| age | 头文件注释 | 浮点 | 分层、协变量 |
| sex | 头文件注释 | 类别 | 分层 |
| beats | 注释文件 | 整数 | 质量筛查、权重 |
布尔值与字符串混在一起是这一步唯一的陷阱:性别只有 M 与 F 两个取值,不要按数值处理;年龄含小数,读成整数会丢精度,也会让 28.5 变成 28。
5.5 从时刻到 RR:差分、清洗、切窗
拿到时刻之后,间期是一行差分。麻烦在清洗。异位搏动会制造一个短间期紧跟一个长间期,长度之和约等于两个正常间期。这类组合会同时抬高 SDNN 与 RMSSD。
#-- 时刻转 RR,并按相邻比值剔除明显的异位组合
import numpy as np
import wfdb
ann = wfdb.rdann("nsr2db/nsr001", "ecg")
rr = np.diff(ann.sample) / 128.0 * 1000.0 #-- 毫秒
rr = rr[(rr > 300) & (rr < 2000)] #-- 先掐掉明显不合理的值
med = np.median(rr)
keep = (rr > 0.7 * med) & (rr < 1.3 * med) #-- 再用中位数比值筛一遍
rr_clean = rr[keep]
print(len(rr), len(rr_clean), rr_clean.mean())
两段筛选的顺序有讲究:先用绝对上下限去掉检测错误与漏检造成的极端值,再用相对中位数的比例去掉异位组合。反过来做,中位数会被极端值污染。
| 清洗步骤 | 判据 | 去掉什么 | 风险 |
|---|---|---|---|
| 绝对范围 | 300 至 2000 毫秒 | 漏检、误检 | 阈值过窄会删真值 |
| 相对范围 | 中位数 0.7 至 1.3 倍 | 早搏组合 | 心动过速者会被误删 |
| 注释码筛 | 只看正常搏动码 | 异位搏动 | 需确认码表 |
| 分段剔除 | 按窗内变异系数 | 噪声段 | 会缩短有效时长 |
5.6 按记录切分的评测协议
任何涉及模型的实验,切分单位必须是记录,不能是心搏。同一记录内相邻心搏高度相关,按心搏随机切分会让训练集与测试集共享同一段生理状态,指标虚高。
#-- 按记录做五折,保证同一受试者只出现在一折
import numpy as np
records = sorted(open("nsr2db/RECORDS").read().split())
rng = np.random.default_rng(0)
order = rng.permutation(len(records))
folds = np.array_split(order, 5)
for i, test_idx in enumerate(folds):
test = [records[j] for j in test_idx]
train = [r for r in records if r not in test]
print(i, len(train), len(test))
| 切分单位 | 是否泄漏 | 后果 |
|---|---|---|
| 心搏 | 严重泄漏 | 指标虚高,无法复现 |
| 时间窗 | 轻度泄漏 | 相邻窗共享状态 |
| 记录 | 无泄漏 | 推荐 |
| 受试者 | 无泄漏 | 本库一人一条,等价于按记录 |
本库一人一条记录,所以按记录切分与按受试者切分等价。这一点省掉了去重工作,但它也意味着:折数不可能超过五十四,五折之后每折只有十条左右。
5.7 六个最容易出错的地方
| 出错点 | 表现 | 原因 | 处理 |
|---|---|---|---|
| 按后缀认定波形 | 读出空数组 | .ecg 在此为注释 | 先读头文件信号数 |
| 以为包不完整 | 体积只有 11 MB | 本来就没有波形 | 核对官方大小 |
| 用样本数算时长 | 得到 0 | 头文件样本数为 0 | 读最后时间戳 |
| 年龄读成整数 | 28.5 变 28 | 解析时强转 | 按浮点读 |
| 与 nsrdb 拼接 | 记录对不上 | 两库人群不同 | 分别使用 |
| 按心搏切分 | 指标异常高 | 同记录泄漏 | 按记录切分 |
§6 与库内词条的关系
6.1 关系图谱
| 库内词条 | 关系 | 差异要点 |
|---|---|---|
| nsrdb | 最容易被混淆的近邻 | 18 人带波形,与本库人群不同源 |
| chf2db | 同一交付形态的阳性侧 | 心衰患者,同样只有时刻 |
| ltafdb | 房颤长时程波形库 | 有波形,节律为房颤 |
| mit-bih-afdb | 房颤短时程库 | 十小时量级,有波形 |
| chfdb | 心衰波形库 | 十五例,有波形 |
| stdb | 早期注释为主的心电库 | 运动试验,ST 改变 |
| ltstdb | 长时程 ST 库 | 有波形,缺血事件为主 |
| ecg-arrhythmia | 十二导联大库 | 数万人,形态完整 |
这张表的读法是按交付形态分两列:左边一半是「只有时刻」,右边一半是「有波形」。跨列组合通常没有意义,同列组合才有。
| 组合方式 | 是否可行 | 说明 |
|---|---|---|
| nsr2db 加 chf2db | 可行 | 正常与心衰的 RR 对照 |
| nsr2db 加 ltafdb | 可行但需转换 | 需从波形抽取时刻 |
| nsr2db 加 nsrdb | 不可行 | 人群不同,不能拼队列 |
| nsr2db 加 stdb | 谨慎 | 后者以运动试验为主,节律负担不同 |
6.2 与 nsrdb 的区隔:两种交付形态,两批人
这是本篇最需要说清的一处。两个库的名字只差一个字符,发布方相同,主题相同,连采样率都相同,很容易被当成同一份数据的两个版本。事实不是。
先说官方怎么定位它们。nsr2db 的说明页写着,若需要此类数据且包含数字化心电信号,请参见 MIT-BIH 正常窦性心律库。这句话的意思是:两者是同一类数据的两种交付形态——一个给波形,一个给时刻。这是类型层面的关系,不是人群层面的关系。
再说人群。官方从未声明两者同源,而给出的构成直接否定了同源的可能:
| 维度 | nsrdb | nsr2db | 是否可能同源 |
|---|---|---|---|
| DOI | 10.13026/C2NK5R | 10.13026/C2S881 | 不同数据集 |
| 发布 | 1999 年 8 月 3 日 | 2003 年 3 月 3 日 | 相隔近四年 |
| 记录数 | 18 | 54 | 数量不嵌套 |
| 男性 | 5 人,26 至 45 岁 | 30 人,28.5 至 76 岁 | 人数与年龄均不符 |
| 女性 | 13 人,20 至 50 岁 | 24 人,58 至 73 岁 | 年龄区间无交集 |
| 注释器 | atr | ecg | 不同来源 |
| 体量 | 582.7 MB | 11.1 MB | 差五十倍 |
第五行是决定性证据。nsrdb 的十三名女性全部在二十至五十岁之间,nsr2db 的二十四名女性全部在五十八至七十三岁之间。两个区间没有重叠,所以那十三个人一个都不可能在后者里。既然女性完全不重叠,两个库就不可能包含同一批人。
| 假说 | 能否成立 | 反证 |
|---|---|---|
| nsr2db 是 nsrdb 的超集 | 否 | 女性年龄区间不相交 |
| nsrdb 是 nsr2db 的子集 | 否 | 同上 |
| 两者是同一批人的波形版与时刻版 | 否 | 人群、机构、注释器均不同 |
| 两者是同类数据的两种形态 | 是 | 官方互相指路,但未称同源 |
所以正确的表述是:同一类交付形态的两种选择,不是同一批人的两种视图。凡是把两者按记录名对齐、或声称可以合并成七十二人的写法,都错在这一步。
| 你的目的 | 该选哪个 | 理由 |
|---|---|---|
| 只算 RR 与 HRV | nsr2db | 时刻已校正,省一道工序 |
| 需要看波形 | nsrdb | 唯一有波形的正常长时程库之一 |
| 需要更多人 | nsr2db | 五十四对十八 |
| 需要年轻女性 | nsrdb | 后者女性全为老年 |
| 需要做两库一致性研究 | 都不合适 | 人群不同,无法配对 |
6.3 与 chf2db 的分工:同一形态的阴性侧与阳性侧
chf2db 是充血性心衰的 RR 间期库,二十九条记录,同样没有波形,同样是自动检测加人工校正。它和本库几乎是镜像关系。
| 维度 | nsr2db | chf2db |
|---|---|---|
| 人群 | 正常窦性心律 | 充血性心衰,NYHA 分级 |
| 记录数 | 54 | 29 |
| 交付物 | 心搏时刻 | 心搏时刻 |
| 波形 | 无 | 无 |
| 典型用法 | 阴性侧 | 阳性侧 |
两个库合起来可以做一个最朴素的二分类:用 RR 序列区分正常与心衰。这个任务不需要波形,也不需要对齐采样,唯一要做的是统一差分口径与清洗规则。但要注意两边人数都不多,任何深度模型都会迅速过拟合。
6.4 与房颤库的分工:节律规则的边界
房颤检测的经典特征是 RR 间期的不规则性。要用本库做对照,得先明白一件事:正常窦性心律也有不规则,只不过不规则有呼吸性来源,且与心率同步。
| 对照维度 | 正常窦性心律 | 房颤 |
|---|---|---|
| RR 变异来源 | 呼吸、压力反射、昼夜 | 房室结随机传导 |
| 变异是否有节律 | 有,与呼吸同频 | 无 |
| 长程相关性 | 保留 | 削弱 |
| 本库能否提供 | 能 | 不能 |
这意味着一个只靠 RR 变异系数做判别的模型,在本库与房颤库之间可能表现很好,却在真实场景里失效——因为真实场景中最难区分的是「窦性心律不齐」与「慢心室率房颤」,而后者在短窗内变异并不大。
6.5 组合使用的三条路线
| 路线 | 组成 | 任务 | 注意事项 |
|---|---|---|---|
| 阴性对照 | nsr2db 加任意房颤库 | 节律分类 | 统一差分与清洗口径 |
| 心衰筛查 | nsr2db 加 chf2db | 二分类 | 样本少,只用线性模型 |
| 检测器评测 | nsr2db 加自采波形 | 逐搏比对 | 容差窗统一到毫秒 |
第一条路线最常用。它的隐含假设是「正常」与「房颤」在 RR 统计上有可分性,这个假设在长窗上成立,在三十秒短窗上常常不成立。
6.6 检索路径
| 你想找的 | 站内路径 | 关键词 |
|---|---|---|
| 本库 | 生理信号 → 心电信号 | NSR2DB、RR 间期、逐搏 |
| 易混库 | 同上 | NSRDB、正常窦性心律 |
| 同时刻形态 | 同上 | RR 间期库、chf2db |
| 同人群波形版 | 同上 | 长时程、Holter、正常 |
| 房颤对照 | 同上 | 房颤、AFDB、LTAFDB |
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
打分之前要先纠正一个常见的误判:拿波形库的尺子量它。按那把尺子,它必然在完整性上失分——没有信号、没有导联、没有标定、没有采样点。这些都对,但都不是缺陷,因为它们本来就不在交付清单里。
它的设计目标只有一个:给心率变异性研究提供一批不带检测器变量的长时程节律。按这个目标衡量,评价的落点变成三件事——时刻是否可信、覆盖是否够长、体量是否轻到可以全量处理。三项都成立,短板就只剩人群单薄,而这正是这类二手整理付不起的成本。
7.1 五维评分
用数据交换标准(DAIMS)常被追问的五个维度——可发现、可访问、可互操作、可复用、可追溯——来衡量,长短分布得很清楚。
| 维度 | 评分 | 依据 |
|---|---|---|
| 可发现 | 高 | 有 DOI、有平台收录、有稳定引用锚点 |
| 可访问 | 高 | 无需登录,整包十一兆,许可宽松 |
| 可互操作 | 中高 | WFDB 格式通用,但信号层为空 |
| 可复用 | 中高 | 时刻可直接用,形态类需求全灭 |
| 可追溯 | 中 | 可追到研究队列,追不到逐条来源 |
| 维度 | 常见失分场景 | 补救 |
|---|---|---|
| 可发现 | 与 nsrdb 名字混淆 | 记 DOI 末段 |
| 可访问 | 以为包不完整 | 核对官方体量 |
| 可互操作 | 用波形接口打开 | 改注释接口 |
| 可复用 | 想做形态任务 | 换库,别硬改 |
| 可追溯 | 想按研究分层 | 只能整体使用 |
DAIMS 关心的是数据在交换与复用时会不会掉信息。这份数据在可发现与可访问上表现稳定,在可互操作上有一个特殊的空洞:格式是标准的,内容却是最薄的一层。标准格式保证了工具能读,薄内容决定了读出来只有时刻。
7.2 分数背后的取舍
| 维度 | 加分项 | 减分项 | 净效果 |
|---|---|---|---|
| 可互操作 | 注释格式全生态通用 | 信号数为零 | 能读,但只能读时刻 |
| 可复用 | 许可允许商用与再分发 | 无波形,任务面窄 | 适合节律任务 |
| 可追溯 | 有提交者、机构、DOI | 无逐条研究归属 | 可引用,不可分层 |
| 可发现 | 平台收录、名字直白 | 与近邻库仅差一字符 | 搜得到,易搜错 |
| 可访问 | 体量极小 | 无 | 最省事的一项 |
第三行最能说明这类整理数据的处境:它知道自己来自哪些研究,却说不清哪一条来自哪一项。机构级可追溯、个体级不可追溯,这个组合在方法学研究里够用,在流行病学里不够。
7.3 已知的、可枚举的偏差
| 偏差 | 表现 | 影响 | 应对 |
|---|---|---|---|
| 规模偏差 | 仅 54 人 | 无法做组间比较 | 只做单组描述 |
| 性别偏差 | 30 男 24 女,但女性年龄集中 | 年轻女性完全缺失 | 不外推到青年女性 |
| 年龄偏差 | 女性全在 58 至 73 岁 | 性别与年龄混淆 | 分层时报告格内人数 |
| 来源偏差 | 多项研究汇合 | 入组标准不统一 | 不假定同一判读口径 |
| 形态缺失 | 无波形 | 无法核实注释 | 只信时刻,不推形态 |
| 量化偏差 | 步长约 7.8 毫秒 | 短程指标抖动 | 方法段声明 |
| 时长不明 | 官方未给逐条时长 | 无法精确报总小时 | 读时间戳后自算 |
| 分组缺失 | 无研究标签 | 无法做病例对照 | 只能整体使用 |
前四条是设计取向,改不了;后四条是使用层面的坑,可以写进流程。前者进方法段的限制说明,后者进清洗步骤。
§4.2 的案例在这里再出现一次:nsr047 保留着 28.5 这样的半岁精度,说明年龄是从研究表格原样搬来的。这既是可追溯性的证据,也是来源混杂的证据——不同研究记录的精度不同,年龄这一列因此不是同质的。
7.4 外部验证矩阵
| 验证方式 | 是否可行 | 说明 |
|---|---|---|
| 与现代 Holter 对照心率 | 可行 | 比平均值与昼夜趋势 |
| 与十二导联库对照形态 | 不可行 | 无波形 |
| 重复测量检验 | 不可行 | 每人一条 |
| 专家重新判读 | 不可行 | 无波形可读 |
| 与 nsrdb 对照心率 | 部分可行 | 人群不同,只能比统计特征 |
| 与可穿戴设备对照 | 可行 | 需解决采样与佩戴差异 |
第四行是这份数据最实质的不可逆点。带波形的库可以请现代医师重做判读,得到一份跨越三十年的判读差异研究;这份数据连重判的入口都没有,注释一旦有系统偏差,就永久留在数据里。
| 重判设计要素 | 本库能否满足 | 说明 |
|---|---|---|
| 原始波形 | 不满足 | 未公开 |
| 既往判读结论 | 部分满足 | 只有总体描述 |
| 判读人员盲法 | 不适用 | 无可读材料 |
| 一致性统计 | 不满足 | 无第二读者 |
7.5 它为何能持续:省掉的那道工序一直没有更好的替代
二十多年来,QRS 检测的算法换了几代,但「谁来保证这批时刻是对的」这个问题没有变。公开的波形库不少,公开的、经人工校正的长时程时刻却很少。原因不复杂:波形是采集的副产品,校正过的时刻是人力投入的产物,后者更难攒。
它能一直有人用,靠三点。体量小到没有下载门槛,十一兆可以随论文附件一起走;格式标准,三十年不断代;定位干净,从不声称自己有波形,也就不存在「名不副实」的失望。对一个只想算 RR 的人来说,这三点加起来就够了。
7.6 与替代方案的取舍
| 替代方案 | 优势 | 劣势 | 何时选它 |
|---|---|---|---|
| nsrdb | 有波形,可重判 | 只有 18 人 | 需要形态或重判 |
| chf2db | 同一形态,阳性侧 | 人群为心衰 | 做病例对照 |
| 自采 Holter | 人群与设备可控 | 成本高,需伦理审批 | 有明确临床问题 |
| 公开波形库加自建检测 | 灵活,可控 | 检测器偏差进结果 | 需要特定采样率 |
| 合成 RR 序列 | 标签精确、量可调 | 缺真实长程相关 | 预训练或增广 |
选择的关键是一句自问:我需要的是「干净的时刻」还是「那条曲线」。要时刻,本库是成本最低的起点;要曲线,它连补救的余地都没有。
§8 生态位与历史
8.1 时间沿革
| 时间 | 事件 | 意义 |
|---|---|---|
| 1992 | Goldsmith 等发表训练与副交感活性研究 | 最早的来源队列 |
| 1995 | Bigger 等发表健康中年与冠心病对照研究 | 中年对照来源 |
| 1996 | 心率变异性测量标准发表 | 指标口径统一,需求上升 |
| 1999 | Stein 等发表老年人运动训练研究 | 老年段来源 |
| 1999 | PhysioNet 上线 | 有了长期托管平台 |
| 2002 | Mietus 等发表 pNNx 再评估 | 方法学需求明确 |
| 2003 | 本库以 1.0.0 版发布 | 时刻版正式成形 |
| 2019 | 补入校验和清单 | 平台开始做完整性校验 |
这张表里最值得注意的是 1996 年那一行。心率变异性测量的标准发布之后,指标定义统一了,各实验室开始能横向比较结果,随之而来的是对「同一批时刻」的需求。本库正好在这个需求成形几年后发布。
8.2 在心律谱系里的位置
| 谱系维度 | 位置 | 代表库 |
|---|---|---|
| 按交付形态 | 时刻侧 | nsr2db、chf2db |
| 按节律 | 正常侧 | nsr2db、nsrdb |
| 按时长 | 长时程 | nsr2db、nsrdb、ltafdb |
| 按人数 | 中小规模 | 数十人量级 |
三个维度交叉之后,它的位置很清楚:长时程、正常节律、只有时刻、数十人。四个条件同时满足的公开资源,屈指可数。
8.3 谁在用它,用来做什么
| 使用者 | 典型用途 | 为什么选它 |
|---|---|---|
| HRV 方法学研究者 | 比较不同指标对量化的敏感度 | 时刻干净,可作基准 |
| 节律算法团队 | 检测器的逐搏参照 | 人工校正过 |
| 生理信号教学 | 从时刻讲到频域 | 体量小,无波形干扰 |
| 数据平台建设者 | 演示注释型数据的接入 | 结构最简单 |
第三类使用者常被忽略。教学场景里波形往往是噪声源:学员把时间花在调滤波与画图上,指标本身的含义反而没讲透。只给时刻,反而逼着人面对指标定义。
8.4 二十三年里它剩下什么
它没有变成主流基准,也没有被淘汰。它停在了一个安静的位置:需要它的人知道它在,不需要的人从名字上看不出它和 nsrdb 的区别。
| 资产 | 是否仍然有效 | 说明 |
|---|---|---|
| 时刻数据 | 有效 | 格式与工具都还在 |
| DOI | 有效 | 可稳定引用 |
| 许可 | 有效 | ODC-BY 仍宽松 |
| 年龄性别 | 有效 | 但精度不一 |
| 来源研究对应 | 从未建立 | 永久缺口 |
§9 使用指引
9.1 三种角色的路径
| 角色 | 第一步 | 第二步 | 第三步 | 时间投入 |
|---|---|---|---|---|
| 只想算指标的人 | 下载整包 | 读注释转 RR | 清洗后算 SDNN | 半小时 |
| 做算法评测的人 | 读头文件确认无信号 | 建索引 | 按记录交叉验证 | 半天 |
| 做数据平台的人 | 解析头文件与清单 | 抽字段入表 | 建立版本与校验 | 一天 |
第一种角色的路径最短,也最容易踩坑:跳过清洗直接算指标,得到的 SDNN 往往偏高,原因是异位组合没有被剔除。
9.2 一条最短的可用流程
#-- 从下载到一条 RR 序列,四步
wget -r -N -c -np https://physionet.org/files/nsr2db/1.0.0/
python3 -c "import wfdb; h=wfdb.rdheader('physionet.org/files/nsr2db/1.0.0/nsr001'); print(h.n_sig, h.fs)"
python3 -c "import wfdb; a=wfdb.rdann('physionet.org/files/nsr2db/1.0.0/nsr001','ecg'); print(len(a.sample))"
python3 -c "import wfdb, numpy as np; a=wfdb.rdann('physionet.org/files/nsr2db/1.0.0/nsr001','ecg'); rr=np.diff(a.sample)/128*1000; print(rr.mean())"
四步的输出分别是:信号数 0 与采样率 128、该条记录的心搏数、平均 RR 毫秒数。第三步的数字同时也是质量的第一个信号——若某条记录的心搏数明显偏离同批记录,值得单独检查。
9.3 引用方式
Stein P. Normal Sinus Rhythm RR Interval Database (version 1.0.0).
PhysioNet. 2003. https://doi.org/10.13026/C2S881
| 引用要素 | 必填 | 说明 |
|---|---|---|
| 数据集名 | 是 | 用官方全名,不用缩写 |
| 版本号 | 是 | 1.0.0,本库只有一个版本 |
| 平台 | 是 | PhysioNet |
| 年份 | 是 | 2003 |
| DOI | 是 | 指向具体版本 |
| 许可 | 建议 | 方法段或数据可用性声明中注明 |
版本号在这个库里格外重要:虽然只有 1.0.0,但平台同时给出了「最新版」DOI,两者不同。引用具体版本可以避免日后解析到别的内容。
9.4 复现一份最小报告的四步检查
| 检查项 | 通过标准 | 常见失败 |
|---|---|---|
| 记录数 | 五十四 | 只下了部分文件 |
| 信号数 | 全为 0 | 误用了别的库 |
| 年龄解析 | 二十八点五能读出 | 强转整数丢精度 |
| 切分单位 | 记录级 | 按心搏切分 |
9.5 三类人群的最短路径
| 人群 | 最短路径 | 需要绕开的坑 |
|---|---|---|
| 临床研究者 | 直接取 RR 做组内描述 | 不要报人群正常值 |
| 算法工程师 | 建索引后按记录交叉验证 | 不要按心搏切分 |
| 教学者 | 用一条记录讲完全部指标 | 不要先讲滤波 |
9.6 复现性清单:别人能不能照你的描述重建
| 描述项 | 应写清 | 不写的后果 |
|---|---|---|
| 版本与 DOI | 1.0.0 与对应 DOI | 无法定位 |
| 记录范围 | 用了哪几条 | 无法复算 |
| 清洗阈值 | 绝对与相对范围 | 指标不可比 |
| 量化步长 | 约 7.8 毫秒 | 短程指标被高估精度 |
| 切分方式 | 记录级,折数 | 指标不可信 |
| 指标定义 | 用哪套标准 | 与文献对不上 |
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 数据性质 | 真实临床研究来源的长时程心电,仅保留逐搏时刻 |
| 是否含合成数据 | 否 |
| 是否含个人身份信息 | 否,仅年龄与性别 |
| 标注方式 | 自动检测加人工复核校正 |
| 适合的模型用途 | 节律分类、序列建模、检测器评测 |
| 不适合的模型用途 | 形态学分类、心电图像任务 |
| 已知风险 | 人数少、人群偏老年女性、无波形可核 |
| 合规提示 | 商用需署名,医学用途不构成临床结论 |
| 使用层级 | 建议 | 理由 |
|---|---|---|
| 研究 | 可用 | 许可允许,来源可引 |
| 教学 | 可用 | 体量小,结构清晰 |
| 产品原型 | 谨慎 | 人群不代表目标用户 |
| 临床决策 | 不可用 | 无波形,无病史 |
常见误读(避坑清单)
| 说法 | 实际 | 证据 |
|---|---|---|
| 「nsr2db 是 nsrdb 的第二版」 | 两个独立数据集,DOI 不同 | 官方 DOI 分别为 C2S881 与 C2NK5R |
| 「下载包不完整,缺波形」 | 官方明确原始记录不可用 | 说明页写明 not available |
| 「54 人是 18 人的扩充」 | 女性年龄区间无交集 | 20 至 50 与 58 至 73 |
| 「.ecg 是波形文件」 | 是注释二进制 | 头文件信号数为 0 |
| 「可以合并成 72 人队列」 | 两库人群不同源 | 机构、注释器、年龄均不同 |
| 「Normal 指健康人」 | 指入组时的节律判定 | 页面只写正常窦性心律 |
| 「每条都是精确 24 小时」 | 官方未给逐条时长 | 头文件样本数为 0 |
| 「有波形可以重做判读」 | 无波形,重判不可能 | 文件里只有时刻 |
| 「54 人够做人群参考」 | 女性全为老年 | 分层后每格个位数 |
FAQ
Q1:nsr2db 和 nsrdb 到底什么关系?
同一类交付形态的两种选择,不是同一批人的两种视图。官方在 nsr2db 页面指路 nsrdb,说的是「若需要带波形的此类数据请移步」,这是类型层面的指路。人群层面两者不重叠:女性年龄区间一个在二十至五十岁,一个在五十八至七十三岁。
Q2:为什么整个包只有十一兆?
因为没有波形。五十四条约一昼夜的记录,如果带波形,按 128 Hz 双通道估算会在数百兆量级。这里只存心搏时刻,一条记录两百 KB 上下,全库自然就小了。
Q3:头文件里两个零是什么意思?
第一个零是信号通道数,第二个零是采样点总数。两个零合起来声明:本记录没有信号。同名的 .ecg 文件因此只能按注释解析,不能按波形解析。
Q4:能直接算 HRV 指标吗?
能。时刻差分即得 RR,时域与频域指标都可以算。要注意时刻量化为采样间隔,约 7.8 毫秒,pNN50 这类阈值敏感的指标会受影响,方法段应写明。
Q5:库名叫 RR 间期,为什么存的是时刻?
时刻是无损的,间期是它的差分。存时刻可以还原任意窗口的搏动计数、瞬时心率与绝对相位;存间期会丢掉起点,出错也无法定位。
Q6:54 人能做什么、不能做什么?
能做单组描述、能做方法学验证、能当阴性对照。不能做人群正常值、不能按性别年龄细分、不能支撑任何分层推断。
Q7:为什么女性都在五十八岁以上?
因为提供女性记录的那项研究以老年人为对象。官方没有逐条说明来源,但从四篇参考文献的主题与人群分布可以推断,全库是多项 HRV 研究的汇合,不是一次连续入组。
Q8:有异位搏动吗?
有。正常窦性心律不等于每一次搏动都规整,早搏与传导异常都可能出现。注释码能标出一部分,但没有波形,无法区分是房性早搏还是检测错误。
Q9:可以商用吗?
可以。ODC-BY 1.0 允许商用与再分发,条件是署名:数据集名、版本、DOI、许可。它不要求衍生作品同样开放。
Q10:怎么引用?
引用具体版本的 DOI,即 10.13026/C2S881,并写明版本号 1.0.0 与平台 PhysioNet。平台另有一个「最新版」DOI,两者不同,引用时应指向所用版本。
Q11:能用来训练深度学习模型吗?
可以做序列模型,输入是 RR 或心搏间隔序列。样本只有五十四条,必须按记录切分,且应把模型规模压到与样本量相称。任何以波形为输入的模型都用不了。
Q12:和 chf2db 能一起用吗?
能,这是同形态的阴性侧与阳性侧。两边都只有时刻,统一差分与清洗口径即可做二分类。但两边人数都不多,结论应限于「可分」而非「可泛化」。
术语表
| 术语 | 英文 | 含义 |
|---|---|---|
| 逐搏标注 | beat annotation | 对每一次心搏位置的标注 |
| 注释文件 | annotation file | 存时刻与注释码的二进制文件 |
| 注释器 | annotator | 注释来源的标识,本库为 ecg |
| 头文件 | header file | 声明记录参数的文本文件 |
| 信号数 | number of signals | 通道数,本库全为 0 |
| 采样率 | sampling frequency | 每秒样本数,本库为 128 |
| 量化步长 | quantization step | 时刻的最小间隔,约 7.8 毫秒 |
| RR 间期 | RR interval | 相邻两次心搏的时间差 |
| 瞬时心率 | instantaneous heart rate | 由单个 RR 换算的心率 |
| 心率变异性 | heart rate variability | RR 序列的波动程度 |
| SDNN | standard deviation of NN | 全部正常间期的标准差 |
| RMSSD | root mean square of successive differences | 相邻间期差值的均方根 |
| pNN50 | proportion of NN50 | 相邻差值超过 50 毫秒的占比 |
| 频域指标 | frequency domain measures | VLF、LF、HF 三个频带功率 |
| 异位搏动 | ectopic beat | 非窦房结起源的心搏 |
| 漏检 | missed detection | 检测器未识别出的心搏 |
| 误检 | false detection | 被错误标记为非心搏的信号 |
| 长时程心电 | long-term ECG | 数小时至一昼夜的连续记录 |
| 动态心电 | Holter | 便携式长时程心电记录 |
| 波形 | waveform | 电压随时间的连续曲线 |
| 校验和 | checksum | 用于验证文件完整性的摘要 |
| 数据交换标准 | DAIMS | 衡量数据可发现、可访问、可互操作、可复用、可追溯的框架 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 记录数 | 54 | 说明页与 RECORDS | 官方页面 |
| 受试者数 | 54 | 说明页 | 官方页面 |
| 男性人数 | 30 | 说明页 | 官方页面 |
| 男性年龄 | 28.5 至 76 | 说明页 | 官方页面 |
| 女性人数 | 24 | 说明页 | 官方页面 |
| 女性年龄 | 58 至 73 | 说明页 | 官方页面 |
| 采样率 | 128 | 说明页与头文件 | 官方页面 |
| 整包体量 | 11.1 MB | 文件列表 | 官方页面 |
| 头文件大小 | 33 字节 | 文件列表 | 官方页面 |
| 最大头文件 | 35 字节(nsr047) | 文件列表与逐个读取 | 官方页面 |
| 注释文件范围 | 151.7 至 266.9 KB | 文件列表 | 官方页面 |
| 版本与日期 | 1.0.0,2003-03-03 | 说明页 | 官方页面 |
| DOI | 10.13026/C2S881 | 说明页 | 官方页面 |
| 许可 | ODC-BY 1.0 | 说明页 | 官方页面 |
附录 B:数字的来源分层
| 层级 | 数字 | 可信度 | 说明 |
|---|---|---|---|
| 官方页面 | 54 条、30 男 24 女、两个年龄区间、128 Hz、11.1 MB | 高 | 可直接引用 |
| 官方文件 | 头文件 33 与 35 字节、信号数 0、注释器 ecg | 高 | 读取即得 |
| 论文摘要 | 约二十四小时、HRV 指标口径 | 中 | 需注明是研究设计口径 |
| 未核实 | 每条记录的确切时长、逐条研究归属 | 低 | 官方未给,须自算或推断 |
附录 C:完整参考文献
| 序号 | 文献 | 用途 |
|---|---|---|
| 1 | Goldsmith RL, Bigger JT, Steinman RC, et al. Comparison of 24-hour parasympathetic activity in endurance-trained and untrained young men. J Am Coll Cardiol. 1992;20:552-558. | 来源研究之一 |
| 2 | Bigger JT, Fleiss JL, Steinman RC, et al. RR variability in healthy, middle-age persons compared with patients with chronic coronary heart disease or recent acute myocardial infarction. Circulation. 1995;91:1936-1943. | 来源研究之一 |
| 3 | Stein PK, Ehsani AA, Domitrovich PP, et al. The effect of exercise training on heart rate variability in healthy older adults. Am Heart J. 1999;138:567-576. | 来源研究之一 |
| 4 | Mietus JE, Peng C-K, Henry I, et al. The pNNx files: re-examining a widely used heart rate variability measure. Heart. 2002;88:378-380. | 方法学参考 |
| 5 | Stein P. Normal Sinus Rhythm RR Interval Database (version 1.0.0). PhysioNet. 2003. DOI 10.13026/C2S881. | 数据集本体 |
附录 D:任务代码
#-- 全库批处理:每条记录输出心搏数、RR 均值、RR 标准差
import numpy as np
import wfdb
REFS = ["nsr001", "nsr002", "nsr003"] #-- 示例三条,可换成全量 RECORDS
for name in REFS:
ann = wfdb.rdann(f"nsr2db/{name}", "ecg")
rr = np.diff(ann.sample) / 128.0 * 1000.0
rr = rr[(rr > 300) & (rr < 2000)]
print(name, len(ann.sample), round(rr.mean(), 1), round(rr.std(), 1))
-- 若已把索引导入数据库,可用一句查出按性别的计数与年龄跨度
SELECT sex, COUNT(*) AS n, MIN(age) AS age_min, MAX(age) AS age_max
FROM nsr2db_index
GROUP BY sex
ORDER BY sex;
附录 E:如果只记五件事
| 序 | 要点 |
|---|---|
| 1 | 它是纯时刻库:54 人、54 条,只有心搏时刻,没有波形 |
| 2 | 与 nsrdb 不是同一批人:女性年龄区间完全不重叠 |
| 3 | 头文件信号数写 0,同名 .ecg 是注释而非波形 |
| 4 | 时刻量化步长约 7.8 毫秒,短程 HRV 指标要声明这一点 |
| 5 | ODC-BY 1.0,署名即可商用,DOI 用 10.13026/C2S881 |
编辑说明
本篇的核对时间与口径记录于此。数据事实取自 PhysioNet 项目页与 1.0.0 版文件页,另逐个读取 RECORDS、ANNOTATORS 与四个头文件(nsr001、nsr033、nsr047、nsr054)用于确认字段格式,头文件体量取自官方文件列表。与 nsrdb 的区隔依据两库官方页面的人群描述与 DOI,不作同一队列的假定。未覆盖的内容包括:每条记录的确切时长、逐条记录所属的原始研究、注释码的完整分布;这三项官方未给出,前者可由时间戳推算,后两者无法从公开材料确定。文中凡属推断均已标注,凡属推算均已说明口径。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- wctecgdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- ahadb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- chfdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- crisdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- cudb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- ltdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- ltstdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- iafdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- mvtdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
- nsrdb — 共享标签:生理信号 / 心电信号 / 心血管疾病
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

