信息速览
OSV 的全称是 Oxygen Saturation Variability,即氧饱和度变异性。它不是呼吸机测试记录,也不是海洋表面流速或光学应变数据,而是一份关于「血氧读数会不会自己抖动」的小型生理参考集:伦敦大学学院医学部的 Amar S. Bhogal 与 Ali R. Mani 记录了 36 名健康成人每人一小时的静坐血氧,2017 年 9 月 27 日经 PhysioNet 以 1.0.0 版发布。同一课题组围绕这批记录写成了一篇 Frontiers in Physiology 论文,标题直白地说:脉搏血氧信号的熵携带了关于机体氧合的信息。
它之所以值得单独立传,不是因为体量,而是因为它把一个被长期忽略的量摆到了台面上。临床上脉搏血氧仪几乎只用来看那个数够不够高,很少有人关心它在一小时里怎么起伏。这份数据证明这种起伏本身有结构:它像分形,比心率变异性更规则,均值越高的健康人样本熵越低,而复杂度随年龄下降。
因此它的正确位置是「锚定参考」而非「训练语料」。全部记录未压缩合计不到 1 MB,每人只有一条 1 Hz 的整数百分比曲线,没有患者组、没有原始波形、没有一个标注文件。一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 官方全名 | Pattern Analysis of Oxygen Saturation Variability |
| 中文名 | 氧饱和度变异性模式分析数据集 |
| 缩写含义 | OSV = Oxygen Saturation Variability,与呼吸机无关 |
| 贡献者 | Amar S. Bhogal、Ali R. Mani(伦敦大学学院医学部) |
| 发布方 | PhysioNet,Database Open Access 档 |
| 版本与日期 | 1.0.0,2017-09-27 |
| DOI | 10.13026/C2TH47 |
| 受试者 | 36 名健康成人,年轻组 20 人、年长组 16 人 |
| 记录时长 | 每人一小时连续记录 |
| 通道数 | 每记录 1 个,通道名 SpO2 |
| 采样率 | 1 Hz(原始采集 1 kHz,降采样一千倍) |
| 数据组织 | 36 个记录 × 3 个同名文件,另附元数据、清单、脚本、校验和 |
| 体积 | 未压缩 969.2 KB,压缩包 992.2 KB |
| 许可 | Open Data Commons Attribution License v1.0 |
| 关联论文 | Frontiers in Physiology 8:555,2018 年被引文献 |
| 一句话定位 | 血氧变异性研究的公开锚定参考集,体量小、用途窄、血缘清楚 |
§1 它要解决的具体麻烦
1.1 血氧仪吐出的那个数,从来没有人认真看它怎么动
病房里的脉搏血氧仪是一个被当作开关使用的仪器:数字高于阈值就放心,低于阈值就报警。至于这个数在过去一小时里是纹丝不动,还是在九十七与九十九之间反复跳跃,临床上没有人记录,也没有人分析。这种忽略有其道理——单个百分点的波动看起来像噪声,而噪声通常不值得研究。
Bhogal 与 Mani 的出发点是把这种「噪声」当成信号。他们问了一个很朴素的问题:如果一名健康人静坐着不动,血氧读数的起伏有没有规律?如果有,这个规律能不能用现成的复杂度工具刻画出来?如果还能刻画,那么它会不会随年龄变化?
这份数据集就是回答这三个问题的原始材料。它不提供答案,只提供 36 条一小时长的曲线,让后来者可以重跑同样的分析、换用别的指标、或者拿它当对照。§4.1 的案例会具体展示一条曲线长什么样,以及为什么它值得被反复分析。
1.2 三条常见的错误期待
围绕这个缩写的误读相当集中,值得先一次性排除。它们大多来自名字的歧义,而不是来自数据本身的问题。
| 期待 | 来源 | 实际情况 |
|---|---|---|
| OSV 是开源呼吸机(Open Source Ventilator)的硬件测试数据 | 缩写巧合,疫情期间该词高频出现 | 无关。全库没有任何呼吸机、通气参数或设备标定记录 |
| OSV 是海洋表面流速(Ocean Surface Velocity)遥感产品 | 地球科学领域的同名缩写 | 无关。数据来自人体手指,采样率 1 Hz,与遥感无关 |
| OSV 是某种光学应变矢量化(Optical Strain Vectorization)算法 | 工程领域同名缩写 | 无关。没有任何应变、位移或材料力学量 |
还有一类期待不属于缩写歧义,但同样常见:以为它是一个「血氧数据库」,可以拿来做低氧血症检测或睡眠呼吸暂停筛查。这类任务需要患者组与事件标注,而这份数据两者都没有,全部受试者都是健康成人,也没有逐段的事件标记。§1.3 会把这类需求与数据的实际能力逐条对起来。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 验证一套血氧变异性指标(样本熵、DFA、MSE)能否跑通 | 36 条一小时曲线,格式标准,工具链成熟 | 够,这是设计初衷 |
| 拿健康人基线去对照自己的患者队列 | 一份公开、可复现的健康人参照值 | 基本够,但组内只有 36 人,参照区间较粗 |
| 训练一个从血氧序列判别疾病的模型 | 无患者组、无标签、无事件 | 不够,样本量与标注都不支持 |
| 研究呼吸对血氧的调制 | 只有 1 Hz 的血氧,呼吸带通道未发布 | 不够,采样率与通道都不支持 |
| 研究脉搏血氧仪本身的测量误差 | 单一设备、单一导出路径,无同步参考仪器 | 不够,没有误差评估所需的对照 |
| 做信号处理与熵分析的教学演示 | 数据小、许可宽松、样例干净 | 够,且是最省事的选项之一 |
| 做可穿戴设备的算法回归测试 | 一段干净的静坐基线,可当「已知良好」样本 | 部分够,缺运动与低灌注场景 |
| 复现原论文的图表 | 原始曲线齐全,分析脚本给了 MATLAB 版 | 够,前提是接受整数百分比的精度 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 刚写完一个熵分析函数,想找个真实数据试跑 | 提供 36 条长度一致、格式规范的序列,能立刻验证输入输出 | 先拿一条记录跑通,再批量跑全部记录比对分布 |
| 手上有患者血氧数据,不确定变异性指标是否可信 | 给出同设备、同采样率下的健康人参照,可作为对照臂 | 注意采样率必须对齐到 1 Hz,否则熵值不可比 |
| 在写综述,需要一个「血氧变异性存在」的引证 | 提供可引用的公开资源与配套论文 | 引用资源 DOI 与论文两处,不要只引其一 |
| 想评估某款手环的血氧波动是否合理 | 提供一段临床级设备的静坐基线 | 只可作定性对照,手环的采样与滤波差异会淹没差异 |
| 需要一份极小的 PhysioNet 样例来教 WFDB | 单文件体积小、字段完整、含头文件注释 | 用它讲 record、signal、annotation 三层结构 |
| 正在做健康老龄化的多组学整合 | 提供年龄跨度 19 至 70 岁的血氧复杂度对照 | 只能作为一个弱表型,权重不宜过高 |
| 想复现论文里「复杂度随年龄下降」的结论 | 原始数据与脚本都在,可完整重跑 | 注意原文的年轻组与年长组划分界线需要从论文核对 |
| 需要一个能塞进移动端的血氧异常检测器训练集 | 数据量太小,类别单一 | 转向含标注的睡眠或ICU数据库,把本库当外部验证 |
1.5 一句话判断
这是一个血缘清楚、许可宽松、体量微小、边界明确的锚定参考集:它能让你确认自己的血氧变异性分析没有跑偏,但不能替你训练任何东西。
1.6 不适合谁:四条边界
第一条边界是规模。36 名受试者、每人一小时,总计三十六小时左右的观测。这个量级对统计推断勉强够用,对任何需要学习参数分布的模型都不够。把深度学习模型喂给它,得到的只会是对训练集的记忆。
第二条边界是通道。发布版本每记录只有一个通道,即血氧饱和度。采集时明明接了三路输入——血氧、相邻手指的脉搏压力、脐部的呼吸带——但后两路没有随数据发布。这意味着任何需要呼吸相位或脉搏波形的研究都无法在原始层面开展,只能在 1 Hz 的血氧序列上做间接推断。
第三条边界是人群。全部受试者都是健康成人,按论文口径分为年轻组与年长组,没有患者、没有慢性病分层、没有低氧状态。它是一个「正常」的样本,任何关于异常的判断都必须另找对照。
第四条边界是采样。1 Hz 的整数百分比序列已经抹掉了高频内容。血氧信号的呼吸调制通常落在每分钟十余次的量级,1 Hz 勉强能覆盖其基频,但逐拍细节、脉搏波形形态、灌注指数等信息全部丢失。想研究这些,必须回到 1 kHz 原始记录,而原始记录并未公开。
§2 数据构成与规格
2.1 一次下载、四个层次的组成
这份数据的组织方式比它的体量更值得说清楚,因为它把「记录」「元数据」「清单」「辅助脚本」四类东西放在了同一层目录里,没有任何子目录。下载压缩包解压后,会看到三十六个记录各自对应三件同名文件,外加四件公共文件。
第一层是数据实体,由 WFDB 头文件与二进制数据文件构成。头文件是纯文本,第一行声明记录名、通道数、采样率与采样点数,第二行声明数据文件名、存储格式、增益与基线、单位与通道名,之后是若干以井号开头的注释行,重复记录性别、吸烟状态、BMI 与年龄。二进制文件存的是 16 位整数样本,按增益换算成百分比。
第二层是纯文本副本,与二进制文件同名但扩展名为 .txt,内容是 LabChart 导出的原始文本,用空格分隔的一长串数值。它与 WFDB 数据在数值上一致,只是没有经过增益换算,读出来是整数百分比本身。
第三层是元数据表,只有一张,六列,三十六行,把性别、吸烟状态、BMI、年龄与记录名放在一起,行首另有一个录入序号。
第四层是公共文件:记录清单、校验和、以及一个 MATLAB 分析脚本。记录清单是纯文本,每行一个记录名,共三十六行,用于批量读取。校验和用于确认下载完整。脚本是作者留下的分析入口,说明了他们当时用什么方式读取数据。
2.2 字段与记录的完整字典
先把数据侧的字段说全。WFDB 头文件的字段虽少,但每一行都有明确含义,误读其中任何一处都会导致后续分析出错。
| 字段 | 出现位置 | 取值示例 | 含义与注意事项 |
|---|---|---|---|
| 记录名 | 头文件第一行首项、文件名 | 010217A | 采集日期加当日字母序号,与受试者一一对应 |
| 通道数 | 头文件第一行第二项 | 1 | 恒为 1,不要按多通道读取 |
| 采样率 | 头文件第一行第三项 | 1 | 单位赫兹,即每秒一个样本 |
| 采样点数 | 头文件第一行第四项 | 3587 | 各记录不完全相同,满一小时者约三千六百点 |
| 数据文件名 | 头文件第二行首项 | 010217A.dat | 与头文件同目录,扩展名固定 |
| 存储格式 | 头文件第二行第二项 | 16 | 16 位整数,按字交错 |
| 增益与基线 | 头文件第二行第三项 | 21140.0(-2072777)/% | 斜杠后为物理单位,括号内为基线值 |
| 通道名 | 头文件第二行末项 | SpO2 | 全库唯一通道,无第二路 |
| 性别注释 | 头文件井号行 | Gender: F | 与元数据表一致,可用于交叉校验 |
| 吸烟注释 | 头文件井号行 | Smoking Status: Non-Smoker | 与元数据表一致 |
| BMI 注释 | 头文件井号行 | BMI: 20.5 | 一位小数 |
| 年龄注释 | 头文件井号行 | Age: 23 | 整数岁 |
再看元数据侧。这张表是唯一把「人」与「文件」连起来的地方,列名与取值如下。
| 列名 | 类型 | 取值 | 说明 |
|---|---|---|---|
| Participant No | 整数 | 1 至 36 | 录入顺序,不是文件名字母序号 |
| Record Name | 字符串 | 010217A 等 | 与数据文件名去扩展名后一致,是唯一连接键 |
| Gender | 字符 | M 或 F | 二元,无其他取值 |
| Smoking Status | 字符串 | Non-Smoker、Ex-Smoker、Smoker | 存在大小写与空格不统一,需先归一 |
| BMI | 浮点 | 17.9 至 28.4 | 一位小数,按体重身高计算 |
| Age | 整数 | 19 至 70 | 整数岁,无出生日期 |
吸烟状态这一列需要特别处理。全列出现了四种写法:标准写法两种,另有两种大小写变体。若不先做去空格与小写化,按原样分组会把同一类人拆成两组。这是拿到数据后第一步就该做的清洗,与分析方法无关,纯粹是字符串规范问题。
2.3 一条记录的完整解剖
以记录 010217A 为例,把它从文件头到最后一个采样点拆开看。下面这段是头文件的原文,共七行,前两行是机器读的,后五行是给人读的注释。
#-- 以下为该头文件的实际内容,井号行是原作者写入的注释
010217A 1 1 3587
010217A.dat 16 21140.0(-2072777)/% 16 0 11627 16884 0 SpO2
#-- Gender: F
#-- Smoking Status: Non-Smoker
#-- BMI: 20.5
#-- Age: 23
第一行四个数字依次是记录名、通道数、采样率、采样点数。三千五百八十七个采样点,按每秒一个算,约五十九分四十七秒,略短于一小时——这是取舍一小时的片段时按整点切齐后的自然结果,不是丢数据。
第二行描述信号本身。十六位整数存储,增益两万一千一百四十,基线负二百零七万二千七百七十七,物理单位是百分号。换句话说,存进文件的每个整数都要减去基线再除以增益,才得到我们看到的血氧百分比。基线是一个很大的负数,这是 16 位有符号整数的常见做法,用来把百分比的小数位变成整数位。
后四行注释是元数据的镜像。头文件里存一份、元数据表里存一份,两处应当一致;若不一致,以头文件为准,因为它与数据文件同批生成。
再看数据本身。文本副本的开头一长串几乎都是同一个值,偶尔掉到九十七点五、九十六点五,然后回到高值。这种「长时间贴着高值、偶发小幅下探」的形态,正是样本熵偏低的原因:序列大部分时间可预测。
#-- 读取单条记录并打印前后的形态
import wfdb
import numpy as np
rec = wfdb.rdrecord("010217A", pn_dir="osv/1.0.0")
sig = rec.p_signal[:, 0]
print("通道名:", rec.sig_name)
print("采样率:", rec.fs, "采样点数:", rec.sig_len)
print("首十个值:", np.round(sig[:10], 1))
print("末十个值:", np.round(sig[-10:], 1))
print("取值集合的前八个:", np.unique(sig)[:8])
运行后会发现一个值得注意的现象:数值并不是连续的小数,而是落在若干个离散档位上,间距约零点五到零点七个百分点。这是把 1 kHz 原始浮点序列降采样一千倍后的结果——降采样不是取平均,而是抽样,因此保留下来的仍是原始仪器输出的量化档位。理解这一点,才能正确解释为什么样本熵算出来的绝对值与连续信号不可直接比较。
2.4 记录清单与人群构成
记录清单共三十六行,按采集日期排序,同一天有多人的用字母区分。按日期看,采集集中在 2016 年 11 月底到 2017 年 3 月中旬之间,其中单日人数最多的一天出现了五条记录。
| 观察维度 | 结构 | 对使用的影响 |
|---|---|---|
| 记录总数 | 36 | 与受试者数相等,无一人多次测量 |
| 单日记录数 | 一至五条不等 | 同日受试者可能共享环境条件,不宜视为完全独立 |
| 采集跨度 | 2016 年 11 月至 2017 年 3 月 | 季节跨度短,无法讨论季节性 |
| 每记录文件数 | 3 | 头文件、数据、文本副本,三者缺一不可 |
| 公共文件数 | 4 | 清单、校验和、脚本、元数据表 |
| 目录层级 | 0 | 全部文件平铺,无子目录 |
人群构成按元数据表可直接读出。女性十九人、男性十七人,性别接近平衡。吸烟状态以从不吸烟者为主,戒烟者与现吸烟者合计不到十人。年龄跨十九至七十岁,但分布并不均匀:三十岁以下二十人,与之互补的三十岁及以上十六人,与论文里年轻组二十人、年长组十六人的人数完全吻合——这说明分组界线就落在三十岁附近。
| 分组维度 | 人数 | 备注 |
|---|---|---|
| 女性 | 19 | 略多于男性 |
| 男性 | 17 | |
| 从不吸烟 | 28 | 含两种大小写写法 |
| 戒烟 | 5 | |
| 现吸烟 | 3 | |
| 三十岁以下 | 20 | 对应论文年轻组 |
| 三十岁及以上 | 16 | 对应论文年长组 |
| 六十岁及以上 | 4 | 年长组的高龄端 |
这张表只回答「有多少」,不回答「血氧是多少」。后者属于拿到数据后自行统计的范畴,且不同记录的均值差异本身就受到手指位置、环境温度等未记录因素的影响。
2.5 与同类资源的字段差异
把这份数据和 PhysioNet 上其他血氧相关资源放在一起比较字段,能更清楚地看出它的定位。下表只比较结构,不比较数据质量。
| 资源 | 通道 | 采样率 | 受试者数 | 标注 | 许可 |
|---|---|---|---|---|---|
| OSV | 仅 SpO₂ | 1 Hz | 36 | 无 | ODC-By 1.0 |
| 常规多参数监护波形库 | 心电、血氧、呼吸等多路 | 数十至数百赫兹 | 数百至数千 | 事件或临床变量 | 多为 ODC-By 或 CC BY |
| 睡眠呼吸暂停心电库 | 心电为主,部分含血氧 | 一百赫兹以上 | 数十 | 逐段呼吸事件 | ODC-By |
| 可穿戴血氧公开集 | 血氧加运动 | 变动大 | 数十至数百 | 部分含活动标签 | 各异 |
差异的要点有三个。其一是通道极简:其他库通常把血氧作为多路之一,OSV 把血氧单独拎出来,反而让「只看血氧能读出什么」这个问题变得干净。其二是采样极低:1 Hz 在同类资源里属于下限,几乎只保留了慢变成分。其三是完全没有标注:既没有事件,也没有临床结局,唯一的结构信息是元数据里的年龄、性别、BMI 与吸烟状态。
§3 它是什么:一份被刻意做小的生理参考
3.1 定位:锚定参考,不是训练集
数据集大致可以分成几类:训练集、评测基准、锚定参考、教学样例。OSV 属于后两类。它的价值不在于「覆盖了多少情况」,而在于「在一个非常窄的条件下,给出了一条被反复核对过的基线」。
锚定参考的作用是让不同实验室的结论可比。假如甲实验室报告某患者群体的样本熵是零点八,乙实验室报告零点六,这两个数字能不能直接比?只有在双方用同一套算法跑过同一批健康人数据、得到的基线一致时,比较才有意义。OSV 就是为了提供这个共同基线而存在的。
这也解释了一个看起来矛盾的现象:一份只有 36 人的数据,被引用的次数却远多于它的体量所暗示的。原因不是它信息量大,而是它便宜——下载不到 1 MB,许可只要署名,格式标准到几乎所有工具都能读。
3.2 名字里的三个词各自意味着什么
Oxygen Saturation 是血氧饱和度,指的是血液中血红蛋白被氧结合的比例,脉搏血氧仪通过红光与红外光的吸收比来估计它。Variability 是变异性,指这个比例随时间起伏的程度与形态。Pattern Analysis 是模式分析,指的是不满足于均值与标准差,而要用熵、分形维数这类工具刻画起伏的结构。
三个词合起来,研究对象的层级就很清楚了:它不是「血氧是多少」,而是「血氧怎么变」;不是「变多少」,而是「变的方式有没有结构」。这个区分是理解整份数据的前提。§4.2 会具体解释三种结构指标各自在问什么。
3.3 它为何被放进 PhysioNet
PhysioNet 的收录标准通常是「有临床价值、有复用价值、有明确血缘」。OSV 满足后两条毫无争议,第一条则取决于你如何理解临床价值。它没有任何临床终点,受试者全是健康人,短期内不可能直接改变诊疗。
但它满足一个更基础的临床研究需求:把一个从未被系统描述过的生理量,用一套可复现的方法描述出来,并把原始数据公开。这类工作在新指标刚被提出时格外重要——如果没有公开数据,后续研究只能各自采集、各自定义,结论永远无法合并。PhysioNet 收录它,本质上是承认了「血氧变异性」这个研究对象值得有一个公共起点。
§4 它凭什么可信:方法与证据
4.1 案例:一条静坐一小时的血氧曲线
取记录 010217A,一名二十三岁女性,BMI 二十点五,从不吸烟。把这条曲线画出来,会看到三个层次的结构,值得逐一说明,因为后续所有分析都建立在这三个层次上。
第一个层次是长时段的平台。曲线在大部分时间里贴着九十七到九十九这一段,形态近乎水平。这不是因为血氧恒定不变,而是因为百分比的显示精度只有零点几,微小的生理波动被量化吞掉了。
第二个层次是偶发的下探。每隔一段时间,曲线会掉到九十六点五附近,停留数十秒后回升。这些下探的幅度很小,远不到临床上会被注意的程度,但它们的出现频率与持续时间构成了变异性的主要成分。
第三个层次是下探内部的阶梯。仔细观察会发现,回升不是一步到位,而是经过九十七点一、九十七点五、九十七点六、九十八点一这样几级台阶。这些台阶的间距与脉搏血氧仪的量化档位一致,提示它们部分是仪器分辨率造成的,部分是真实生理变化被离散化后的投影。
这三个层次合起来,解释了为什么这份数据适合做熵分析:它有可测的结构,结构又足够微弱,不会让任何指标轻易饱和。§7.3 会说明这种结构也带来了一个麻烦——量化档位会让某些熵指标在小样本下出现系统性偏移。
4.2 三种结构指标各自在问什么
原论文用了三种工具,它们问的问题并不相同,混用会导致结论误读。
| 指标 | 全称 | 问的问题 | 对什么敏感 |
|---|---|---|---|
| SampEn | 样本熵 | 序列有多难被预测 | 短程规律性,值越低越规则 |
| DFA | 去趋势波动分析 | 起伏在不同时间尺度上是否自相似 | 长程相关性,标度指数是否接近某个特征值 |
| MSE | 多尺度熵 | 复杂度在不同时间尺度上如何分配 | 多尺度上的信息量,常用于比较年龄或疾病 |
样本熵的回答是「这份曲线比心率变异性更规则」。DFA 的回答是「它有分形样的自相似结构」。MSE 的回答是「复杂度随尺度变化,且这种变化在年长者身上减弱」。三者合起来支撑了论文的核心论断:血氧变异性不是随机噪声,而是受调控的生理信号。
值得强调的是,这三个指标都对采样率与序列长度敏感。§3.1 所说的「锚定」正是指这一点:只有在同样的 1 Hz、同样的时长、同样的算法参数下,数值才可比。
4.3 年龄效应的证据强度
论文报告的最有分量的发现是复杂度随年龄下降,显著性水平在百分之五以下。这个结论的证据强度需要分两面看。
支持的一面是:数据本身干净,年龄跨度覆盖十九到七十岁,年轻组与年长组人数均衡,分析脚本公开,任何人可以重跑。这在生理信号研究里已经算是相当透明的证据链。
需要谨慎的一面是:样本量小,年龄分组是二分而非连续,未控制吸烟、BMI、手指温度、环境湿度等混杂因素。元数据里虽然记了吸烟与 BMI,但论文并未报告分组后是否做过校正。因此「年龄导致复杂度下降」这个因果表述,严格说只能退到「年龄与复杂度存在关联,且关联方向与预期一致」。
更稳妥的用法是把这条结论当作假设而非定论,在自己的数据上检验。如果自己的队列里看不到同样的方向,不能直接判为矛盾,只能说明条件不同。
4.4 配套论文的位置
论文发表在 Frontiers in Physiology 第八卷,编号 555,标题强调「熵携带信息」。这个标题的选择很能说明作者对自己工作的定位:他们不主张血氧变异性已经可以直接用于诊断,只主张它含有可提取的信息。
这种克制是这份数据能被长期引用的原因之一。若论文当年宣称「血氧熵可诊断心衰」,几年内就会被后续研究的阴性结果推翻;而「携带信息」是一个几乎不可能被证伪、又确实有指导意义的表述。§7.5 会从生态角度再谈这一点。
§5 怎么拿到与怎么读
5.1 三条取数路径
取数方式有三种,按使用场景选择。若只是看看,用网页上的波形查看器最省事;若要批量分析,用命令行同步;若在集群上工作且不允许外网,用对象存储同步。
#-- 路径一:整包下载(压缩包不到 1 MB,适合一次性取回)
wget -c https://physionet.org/static/published-projects/osv/osv-1.0.0.zip
#-- 路径二:递归抓取逐文件目录(适合只取部分记录)
wget -r -N -c -np https://physionet.org/files/osv/1.0.0/
#-- 路径三:匿名同步对象存储(适合集群内批量作业)
aws s3 sync --no-sign-request s3://physionet-open/osv/1.0.0/ ./osv-1.0.0/
三条路径都不需要账号。第三条要求本机装有 AWS 命令行工具,但不需要任何凭证,因为该桶开放匿名读取。
5.2 用 WFDB 读一条记录
WFDB 是 PhysioNet 的通用格式,Python 与 MATLAB 都有成熟实现。读取时可以直接指向远端项目,不必先下载。
#-- 从远端直接读一条记录,不落盘
import wfdb
import numpy as np
rec = wfdb.rdrecord("010217A", pn_dir="osv/1.0.0")
sig = rec.p_signal[:, 0]
fs = rec.fs
#-- 打印基本信息
print(rec.record_name, rec.sig_name, rec.sig_len, fs)
print("均值 %.2f 最小值 %.2f 最大值 %.2f" % (sig.mean(), sig.min(), sig.max()))
#-- 按分钟切段,得到一个粗略的形态概览
n_per_min = int(fs * 60)
seg = sig[: len(sig) // n_per_min * n_per_min].reshape(-1, n_per_min)
print("完整分钟数:", seg.shape[0])
print("每分钟最小值:", np.round(seg.min(axis=1), 1))
最后一行打印的是每分钟的最小值。它不用于统计推断,只用于肉眼确认「下探发生在哪些时段」,这是理解 §4.1 的案例最直接的方式。
5.3 用纯文本副本读一条记录
如果环境里没有 WFDB 库,纯文本副本可以救急。它是空格分隔的整数序列,一行也没有,全部数值连在一起。
#-- 无 WFDB 依赖时的最小读取
import numpy as np
with open("010217A.txt", encoding="utf-8") as f:
raw = f.read()
sig = np.array(raw.split(), dtype=float)
print("点数:", sig.size, "首值:", sig[0], "末值:", sig[-1])
print("不同取值个数:", np.unique(sig).size)
这个读法的坑在于分隔符。文件用空格分隔,但不同记录的导出可能有细微差别,稳妥做法是先按空白切分再转数值,而不是假定固定列宽。
5.4 批量读取全部记录
批量处理时,记录清单是最好的入口。它保证你能拿到全部记录名,且不会因为目录里混进杂项文件而出现意外。
#-- 按 RECORDS 清单批量读取,跳过元数据表
import wfdb
names = [ln.strip() for ln in open("RECORDS", encoding="utf-8") if ln.strip()]
print("记录数:", len(names))
ok, bad = [], []
for n in names:
try:
rec = wfdb.rdrecord(n, pn_dir="osv/1.0.0")
ok.append((n, rec.sig_len))
except Exception as e:
bad.append((n, str(e)))
print("成功:", len(ok), "失败:", len(bad))
for n, msg in bad[:3]:
print("失败样例:", n, msg)
记录清单的三十六个名字一定要与元数据表的记录名列做一次集合比对。§2.2 提到元数据行的序号与文件名字母序号不一致,若误用序号去匹配文件,会把年龄错配到别人身上。
5.5 把元数据接上去
元数据表是 CSV,读进来后需要先做规范化,再与记录名合并。规范化包括去首尾空格、统一大小写。
#-- 规范化吸烟状态并与记录名合并
import csv
import numpy as np
rows = list(csv.DictReader(open("participants.csv", encoding="utf-8")))
for r in rows:
r["record"] = r["Record Name"].strip()
r["smoke"] = r["Smoking Status"].strip().lower().replace(" ", "-")
r["age"] = int(r["Age"])
r["bmi"] = float(r["BMI"])
print("元数据行数:", len(rows))
groups = {}
for r in rows:
groups.setdefault(r["smoke"], 0)
groups[r["smoke"]] += 1
print(groups)
#-- 用记录名与数据侧对齐
import wfdb
sample = rows[0]["record"]
rec = wfdb.rdrecord(sample, pn_dir="osv/1.0.0")
print("对齐成功:", sample, rec.sig_name)
规范化后吸烟状态只剩三类,与 §2.4 的表一致。若不做这一步,会看到五类,多出来的是同一类的大小写变体。
5.6 原作者的脚本与它的局限
压缩包里附带一个 MATLAB 脚本,体积很小。它的作用是演示作者当时如何读取并整理数据,而不是提供完整的熵分析流程。真正的熵计算、DFA 与 MSE 参数选择,需要回到论文的方法部分核对,脚本里没有。
如果只用 Python,这个脚本可以完全忽略,直接用 WFDB 库读取即可;但如果要完全复现论文的数值,应当逐参数对照论文与脚本,因为不同的嵌入维数、容限系数、尺度选择,会让样本熵的绝对值相差可观。
§6 与库内词条的关系
6.1 关系图谱
这台库里的许多生理信号词条都涉及血氧,但绝大多数是「血氧作为一路信号」存在,而 OSV 是「血氧作为唯一对象」存在。这个区别决定了它与其他词条的关系不是替代,而是补充。
| 库内词条 | 与本篇的关系 | 分工 |
|---|---|---|
| barcelona-sicu-signals | 同为多天、多通道、床旁收集,但那是 ICU 波形,本篇是健康人的单通道 | 它是「患者的多路信号」,本篇是「健康人的单路信号」 |
| multimodal-surgery-anesthesia | 术中生理特征与麻醉给药,含血氧相关特征但非研究对象 | 它研究围术期伤害性刺激,本篇研究静息氧合波动 |
| 各类 ICU 数据库(eicu-crd、nwicu 等) | 提供大规模患者队列,可作外部验证 | 它们回答「患者怎么了」,本篇回答「健康人本该怎样」 |
需要特别说明的是,本篇与呼吸机、机械通气没有关系。虽然 barcelona-sicu-signals 与 multimodal-surgery-anesthesia 都涉及机械通气场景下的监护数据,OSV 却不含任何通气参数、气道压力或呼吸机设置。若把 OSV 误用作通气相关研究的材料,会得到完全无效的结论。反过来,若研究的是自主呼吸健康人的氧合调控,ICU 通气队列也不是合适的对照。
| 与本篇容易混淆的概念 | 区别 | 检索时的区分词 |
|---|---|---|
| 呼吸机(Open Source Ventilator) | 硬件设备,与本篇无关 | 加「通气血氧」去查,不要用缩写 |
| 脉搏血氧波形(plethysmogram) | 高频体积描记波形,本篇不含 | 用 pleth 或 PLETH 检索 |
| 血氧饱和度时间序列 | 本篇正是这一类 | 用 saturation 或 SpO2 检索 |
| 睡眠呼吸暂停 | 需事件标注与患者队列,本篇没有 | 用 apnea 检索 |
6.2 检索路径
在库内找与血氧变异性相关的内容,有三条路径可用。第一条是按生理信号大类直接浏览,能一次看到多通道监护与单通道记录的区别。第二条是按关联论文的关键词检索,样本熵、去趋势波动分析、多尺度熵这三个词命中率最高。第三条是按许可类型筛选,开放署名类许可的记录通常血缘说明更完整,适合作为方法学参考。
| 想找什么 | 推荐路径 | 预期结果 |
|---|---|---|
| 健康人基线 | 生理信号大类下的短时记录 | 本篇与少数运动、睡眠基线库 |
| 患者血氧时序 | ICU 或睡眠相关词条 | 多通道、带标注、采样率高 |
| 熵分析方法 | 按论文关键词检索 | 本篇及若干心率变异性库 |
| 可穿戴血氧 | 可穿戴相关词条 | 采样不稳定,需另行核对 |
§7 质量评估与局限性
7.0 阅读本节前的一个前置校正
在给这份数据打分之前,必须先纠正一个常见的错位:不要用「训练语料的丰度」去评价它。用规模、多样性、标注覆盖率这些指标衡量,它样样不及格,这样的评价没有信息量,因为它本来就不打算做训练集。
正确的参照系是「参考标准的可靠性」。一个 pH 标准液的瓶子只有几百毫升,也不会有人因为它装得少而给它差评;评价它的是标称值的准确度与稳定性。这份数据也该这样看:它给出的是一个明确的、可复现的、公开的血氧变异性基线,评分的重点应当是这条基线清不清楚、能不能被复现、在什么条件下会失效。若沿用训练集的标尺,就会把它的优点(小而干净)误判成缺点,也会把它的真实缺点(未公开的原始通道)漏掉。
7.1 五维评分
按 DAIMS 框架的五个维度打分,每项满分二十分,总分一百分。评分针对的是「作为参考标准」这一用途,而非「作为训练集」。
| 维度 | 得分 | 依据 |
|---|---|---|
| 数据获取与发布 | 19 | 三条取数路径全部免凭证,压缩包不足 1 MB,格式为标准 WFDB |
| 数据血缘与溯源 | 17 | 采集设备、软件、流程、日期全部写明,但原始 1 kHz 文件未公开 |
| 数据质量与完整性 | 16 | 记录完整、无缺失通道,但采样率低且量化档位明显 |
| 数据语义与文档 | 15 | 字段少而清楚,无本体映射,吸烟状态存在写法不统一 |
| 数据许可与合规 | 19 | 开放署名许可,允许商业使用,仅需署名 |
五个维度的分数都不低,总分接近九十分。这个分数反映的是「拿到手就能用、用了不惹麻烦」,而不是「内容有多丰富」。若换成训练集标尺,同样的五维会大幅下滑,尤其是数据质量与语义两项。
7.2 三维对照
把上表换算成更直观的三组对照,能看出分数的分布并不均匀。
| 对照项 | 强的一侧 | 弱的一侧 |
|---|---|---|
| 获取难易 | 免注册、免申请、几十秒下完 | 无 API,只能整包或递归抓取 |
| 复现成本 | 脚本与数据同包,参数在论文里写清 | 文本副本与二进制副本并存,易读错单位 |
| 用途广度 | 方法验证、教学、外部参照都能用 | 任何需要标注或多通道的任务都用不上 |
7.3 已知的、可枚举的偏差
第一项偏差是量化。数值落在离散档位上,档位间距约零点五到零点七个百分点。对样本熵这类计数重复模式的指标,量化会让「相同的值」出现得过于频繁,从而人为抬高规律性。因此论文报告的样本熵绝对值,不能直接套到连续采样的数据上比较。
第二项偏差是采样率。1 Hz 对血氧的慢变成分够用,对呼吸调制偏紧。呼吸频率若接近每分钟二十次,即零点三三赫兹,1 Hz 采样虽不至于混叠,但每个呼吸周期只有约三个样本,波形形状无法还原。
第三项偏差是人群。全部为健康志愿者,且多为三十岁以下的在校人群,年长者虽占十六人,但整体仍偏年轻、偏瘦、偏不吸烟。任何关于「老年人血氧变异」的推断都要考虑这一层。
第四项偏差是条件。测量在静坐、手指静止、室温环境下完成,没有记录环境温度、手指位置、是否进食。这些因素对末梢灌注有直接影响,进而影响读数波动。
第五项偏差是未发布的通道。脉搏压力与呼吸带两路信号在采集时存在,但没有随数据公开。这使得「血氧波动是否由呼吸驱动」这个本该可以直接回答的问题,只能间接推断。§4.1 的案例里那些下探,究竟对应呼吸相位还是运动伪迹,仅凭单通道无法判定。
7.4 外部验证矩阵
这份数据是否被独立复用,可以看三个方向。
| 验证方向 | 情况 | 说明 |
|---|---|---|
| 同课题组内部复用 | 有 | 论文本身即基于此数据,另有后续分析 |
| 独立课题组复现 | 有限 | 体量小,多数引用是方法对比而非独立重跑 |
| 作为方法基准 | 较多 | 熵分析工具箱常拿它做演示数据 |
| 作为临床证据 | 无 | 未见于任何诊断或预后模型的训练与验证 |
| 作为教学材料 | 有 | 因体量小、格式标准,常被用于 WFDB 入门 |
需要说明的是,「独立复现有限」并不等于数据有问题。更可能的原因是它的用途太窄:能用到血氧变异性分析的人本来就不多,而这些人手里往往有自己的队列,不需要借用一份三十六人的健康样本。
7.5 它为何能持续:一个不会被证伪的问题
一份三十六个文件的小数据集,能在发布近十年后仍被检索到、仍被引用,原因不在于它提供了多少结论,而在于它提出的问题足够基础。
「健康人的血氧读数会不会自己抖动、抖动有没有结构」这个问题,不依赖于任何特定的临床场景、任何特定的设备型号、任何特定的疾病定义。只要脉搏血氧仪还在用,这个问题就还在。它的答案可能会被修正——比如某个指标被发现对量化更敏感、某个年龄效应在更大样本里变弱——但问题本身不会消失。
这正是它区别于众多「一次性」数据集的地方。许多数据集绑定在一个具体的疾病或一次具体的采集活动上,活动结束、疾病被更好的方法覆盖,数据就沉底了。OSV 绑定的是一个生理学常识层面的疑问,因此它总能找到新的使用者:今天是熵分析,明天可能是新的复杂度度量,后天可能是设备厂商的回归测试。
§8 生态位与历史
8.1 历史沿革
这条线索可以按三个时间点来梳理。
第一个节点是采集期,从 2016 年 11 月底延续到 2017 年 3 月中旬。四个多月里陆续记录了三十六人次,每次一小时,全部在同一间实验室、同一套设备上完成。
第二个节点是发表。论文在 2017 年发表在 Frontiers in Physiology,同年 9 月 27 日数据以 1.0.0 版上线 PhysioNet。数据与论文几乎同步公开,这在当年的生理信号研究里并不普遍。
第三个节点是后续。1.0.0 之后没有发布新版本,也没有补充通道或延长记录。数据文件的时间戳显示部分文本副本在 2018 年 7 月有过更新,但版本号未变,推测是格式修正而非内容变更。
| 时间 | 事件 | 状态 |
|---|---|---|
| 2016-11 至 2017-03 | 三十六人次静坐记录采集 | 完成 |
| 2017 年 | 论文发表于 Frontiers in Physiology | 完成 |
| 2017-09-27 | 1.0.0 版上线 PhysioNet | 完成 |
| 2018-07 | 部分文本副本时间戳更新 | 内容未变 |
| 之后 | 无新版本 | 维持 |
8.2 在血氧研究谱系中的位置
把血氧相关研究铺开,会看到一条从「测准一个数」到「读懂一条曲线」的演进线。最早期的研究关注标定与误差,即脉搏血氧仪在低灌注、深色皮肤、运动状态下的读数偏差。中期的研究关注事件检测,即从血氧曲线里找出呼吸暂停或低氧事件。OSV 属于较晚的一支,关注的是「在没有任何事件的情况下,曲线本身有没有结构」。
这个定位决定了它的邻域。它不与误差研究竞争,因为不涉及参考仪器;不与事件检测竞争,因为没有事件;它只与「变异性分析」这一小支对话。§6.1 的关系图谱也是按这条线索画的。
8.3 与心率变异性研究的关系
心率变异性研究比血氧变异性研究早得多,方法工具箱也成熟得多。OSV 的论文明确借用了这套工具箱:样本熵、DFA、MSE 都不是为血氧发明的,而是从心率变异性研究里搬过来的。
这种借用带来两个后果。好处是方法现成,不需要重新论证工具的有效性。坏处是参数选择的默认值都来自心率变异性场景,而这些默认值未必适合 1 Hz 的血氧序列。心率变异性通常在数百赫兹采样、数分钟到数小时的时长上分析,血氧只有 1 Hz,尺度对齐需要重新考虑。
8.4 生态位总结
| 维度 | 它的位置 |
|---|---|
| 规模谱 | 最小端,三十六人一小时 |
| 通道谱 | 最少端,单通道 |
| 采样谱 | 最低端,1 Hz |
| 人群谱 | 健康端,无患者 |
| 用途谱 | 参考与教学,非训练 |
| 许可谱 | 最宽松端之一,仅需署名 |
这个位置有一个好处:几乎没有竞品。任何想要「干净、单通道、1 Hz、健康人、许可宽松」组合的人,绕不开它。也有一个坏处:需求本身小众,使用者数量天然有限。
§9 使用指引
9.1 三种角色的路径
第一类是方法研究者。你的目标是验证一个新指标在真实生理数据上的表现。路径是:先读论文的方法部分厘清参数,再用 §5.2 的代码读入数据,拿两三条记录试跑,确认输出量级合理,最后批量跑全部记录,把结果与论文报告的数值对照。若量级差一个数量级,多半是单位或采样率弄错了。
第二类是临床研究者。你有一批患者血氧数据,想判断某个变异性指标是否有临床意义。路径是:先用本库建立健康基线的参照区间,再把自己的数据按同样的采样率与时长处理,最后比较。关键的一步是对齐——若你的数据是每秒四次采样,必须先降到 1 Hz 再算,否则熵值不可比。
第三类是教学者。你需要一份小到能塞进课堂练习、又真实到能反映工程细节的数据。路径是:用本库讲 WFDB 的三层结构,用 §2.3 的头文件讲增益与基线,用 §4.1 的三个层次讲「真实信号里哪些是生理、哪些是仪器」。这份数据在课堂上最大的价值,恰恰是它的量化档位——它让学生看到仪器如何塑造数据。
9.2 一条最短的可用流程
从零到得到一组可比指标,最短路径如下。
#-- 第一步:取数
wget -c https://physionet.org/static/published-projects/osv/osv-1.0.0.zip
unzip -q osv-1.0.0.zip -d osv
#-- 第二步:装依赖
python3 -m pip install --quiet wfdb numpy
#-- 第三步:读一条记录确认通路
python3 -c "import wfdb; r=wfdb.rdrecord('osv/010217A'); print(r.sig_name, r.fs, r.sig_len)"
三步走完,若看到通道名、采样率、采样点数三行输出,通路就通了。接下来按 §5.4 批量读取,按 §5.5 接元数据。指标计算不建议用第三方的「一键熵」函数,因为不同实现的参数默认值差异很大,最好按论文方法自己写一遍,或至少在报告里写清参数。
9.3 引用方式
引用这份数据需要两处引用,缺一不可。第一处是 PhysioNet 资源页,用于指向数据本身及其版本。第二处是配套论文,用于指向方法学依据。只引论文不引资源,使用者无法定位数据版本;只引资源不引论文,则无从知道分析方法。
| 引用对象 | 用途 | 关键标识 |
|---|---|---|
| PhysioNet 资源页 | 指向数据与版本 | DOI 10.13026/C2TH47 |
| Bhogal 与 Mani 论文 | 指向方法与结论 | Frontiers in Physiology 第八卷,编号 555 |
| 许可条款 | 说明再分发条件 | ODC-By 1.0 |
在软件包里再分发时,应保留许可声明与资源链接,并在文档里写明「数据未做修改」或「做了何种修改」。ODC-By 允许修改,但要求标注。
§10 AI 使用声明
本词条的撰写过程与数据本身的 AI 适用性,分两件事说明。
| 项目 | 内容 |
|---|---|
| 撰写方式 | 由 AI 辅助检索官方页面与文件内容,人工核对后成文 |
| 事实来源 | PhysioNet 项目主页、头文件、记录清单、元数据表 |
| 未核实的部分 | 论文全文中的具体参数设置,仅依据摘要与页面转述 |
| 数据是否可作训练语料 | 技术上可以,但规模与多样性不支持有效训练 |
| 数据是否适合评测 AI 模型 | 适合作为熵分析的回归测试样例,不适合作为判别任务基准 |
| 许可对 AI 训练的影响 | ODC-By 1.0 未禁止训练,但要求署名 |
| 生成内容的风险 | 年龄组界线、吸烟分组等细节以官方页面为准,避免二次转述引入错误 |
| 建议的复核方式 | 使用前重跑 §5.2 的读取代码,确认字段与本文描述一致 |
常见误读(避坑清单)
| 常见说法 | 实际 | 证据 |
|---|---|---|
| OSV 是开源呼吸机数据 | 是氧饱和度变异性 | 官方标题为 Pattern Analysis of Oxygen Saturation Variability |
| 每记录有三路信号 | 发布版本只有 SpO2 一路 | 头文件第二行仅声明一个信号 |
| 原始采样率是 1 Hz | 原始采集 1 kHz,发布为 1 Hz | 页面测量设置写明 1 kHz,导出时降采样一千倍 |
| 数据可以训练深度模型 | 三十六人一小时,不足以训练 | 记录清单共三十六行 |
| 含患者与低氧样本 | 全部为健康成人 | 页面与研究人群描述均称健康个体 |
| 元数据序号等于文件名字母序号 | 两者不一致,只能用记录名关联 | 元数据表第一行序号与记录名字母不同 |
| 吸烟状态只有三类 | 原始写法有五类,需先归一 | 元数据表中存在大小写变体 |
| 有呼吸带通道可分析呼吸调制 | 呼吸带信号未发布 | 文件目录只有血氧通道的实体文件 |
| 采样点是连续小数 | 落在离散量化档位上 | 文本副本的取值集合间距约零点五 |
| 引用只需引论文 | 需同时引资源页与论文 | PhysioNet 引用页要求两处 |
FAQ
Q1:OSV 到底是不是呼吸机数据集?
不是。OSV 是 Oxygen Saturation Variability 的缩写,指氧饱和度变异性。内容为三十六名健康成人的静坐血氧曲线,不含任何呼吸机、通气参数或设备标定数据。
Q2:为什么每个人只有一小时?
研究目标是比较不同人的变异模式,而不是追踪个体在长时间内的变化。一小时足够计算样本熵、DFA 与 MSE 的稳定估计,也更便于在实验室条件下控制坐姿与手指静止。
Q3:1 Hz 会不会太低?
对慢变的血氧变异性分析够用,对呼吸波形与脉搏形态不够。原论文的分析对象就是降采样后的 1 Hz 序列,因此复现论文不需要更高采样率。
Q4:那三路信号还能拿到吗?
官方页面与文件目录均未提供。若确需呼吸带或脉搏压力信号,只能联系贡献者询问,或另找含多通道的公开资源。
Q5:能不能用它评估手环的血氧精度?
不合适。这份数据没有参考仪器,无法构成精度评估的基准;且手环的采样与滤波与临床设备差异很大,二者的波动不可直接比较。
Q6:元数据里的序号为什么和文件名对不上?
序号是录入顺序,文件名是采集日期加当日字母序号,两者本就独立生成。唯一的连接键是记录名列。
Q7:吸烟状态为什么有四类写法?
人工录入时的规范不统一,出现了大小写与空格差异。清洗时应去空格并统一大小写,归并后得到从不吸烟、戒烟、现吸烟三类。
Q8:年龄组怎么分?
论文按年轻组与年长组二分,人数分别为二十与十六。从元数据可推出分界落在三十岁附近,但具体界线应以论文表述为准。
Q9:可以用它做睡眠呼吸暂停检测吗?
不能直接做。数据没有事件标注,受试者也非睡眠状态,更没有患者组。要研究呼吸暂停,应使用专用的睡眠数据库。
Q10:分析脚本能直接用吗?
可以用作读取示例,但它不包含完整的熵分析流程。真正的参数设置需回到论文方法部分核对。
Q11:数据会不会更新?
1.0.0 版之后未见新版本。发布近十年保持原样,短期内更新的可能性不大。
Q12:商用需要付费吗?
不需要。ODC-By 1.0 允许商业使用,条件是署名并保留许可声明。
术语表
| 术语 | 英文 | 含义 |
|---|---|---|
| 血氧饱和度 | oxygen saturation | 血红蛋白被氧结合的比例,脉搏血氧仪以百分比显示 |
| 血氧变异性 | oxygen saturation variability | 血氧读数随时间起伏的程度与形态 |
| 脉搏血氧仪 | pulse oximeter | 通过红光与红外光吸收比估计血氧的指夹式设备 |
| 样本熵 | sample entropy | 衡量序列不可预测性的指标,值越低越规则 |
| 去趋势波动分析 | detrended fluctuation analysis | 衡量序列在不同尺度上自相似性的方法 |
| 多尺度熵 | multiscale entropy | 在多个时间尺度上计算熵以刻画复杂度的方法 |
| 分形样模式 | fractal-like pattern | 在不同时间尺度上呈现相似起伏形态的结构 |
| 降采样 | downsampling | 按固定间隔抽取样本以降低采样率 |
| 量化档位 | quantization level | 仪器输出可分辨的最小数值台阶 |
| 增益 | gain | WFDB 头文件中把整数样本换算为物理量的系数 |
| 基线 | baseline | WFDB 头文件中整数样本的零点偏移 |
| WFDB | Waveform Database | PhysioNet 使用的生理信号存储格式 |
| 记录 | record | WFDB 中一段信号及其头文件的集合 |
| 记录清单 | RECORDS | 纯文本文件,每行一个记录名 |
| 锚定参考 | anchor reference | 用于校准不同研究之间可比性的公共基线 |
| 灌注 | perfusion | 指端血流状况,影响血氧读数稳定性 |
| 低灌注 | hypoperfusion | 末梢血流不足,常见于低温或休克 |
| 无创监测 | non-invasive monitoring | 不经体腔或血管插入的监测方式 |
| 自主调控 | autonomic regulation | 循环与呼吸的自主神经调节 |
| 解耦 | uncoupling | 原本协同的两个系统失去同步 |
| 开放署名许可 | ODC-By 1.0 | 允许再利用但要求署名的数据许可 |
| DAIMS | Data Attribution and Information Management Standard | 数据属性与信息管理标准,本库用于五维评估 |
| 采样率 | sampling rate | 每秒采集的样本数 |
| 通道 | channel | 一路独立的测量信号 |
| 血氧事件 | desaturation event | 血氧短时下降超过阈值的片段,本库未标注 |
附录 A:核心数字核对表
| 项目 | 值 | 来源 | 层级 |
|---|---|---|---|
| 记录数 | 36 | 记录清单 | 官方文件 |
| 受试者数 | 36 | 官方摘要 | 官方页面 |
| 每记录时长 | 1 小时 | 官方摘要 | 官方页面 |
| 通道数 | 1 | 头文件 | 官方文件 |
| 采样率 | 1 Hz | 头文件 | 官方文件 |
| 采样点数 | 约三千六百 | 头文件 | 官方文件 |
| 未压缩体积 | 969.2 KB | 官方页面 | 官方页面 |
| 压缩包体积 | 992.2 KB | 官方页面 | 官方页面 |
| 版本 | 1.0.0 | 官方页面 | 官方页面 |
| 发布日期 | 2017-09-27 | 官方页面 | 官方页面 |
| 许可 | ODC-By 1.0 | 官方页面 | 官方页面 |
| DOI | 10.13026/C2TH47 | 官方页面 | 官方页面 |
| 年轻组人数 | 20 | 官方摘要 | 官方页面 |
| 年长组人数 | 16 | 官方摘要 | 官方页面 |
| 年龄跨度 | 19 至 70 | 元数据表 | 官方文件 |
附录 B:数字的来源分层
| 层级 | 定义 | 本词条中的典型项 |
|---|---|---|
| 官方页面 | PhysioNet 项目主页与其数据说明 | 受试者数、时长、体积、许可、DOI、年龄组人数 |
| 官方文件 | 数据集内可直接读取的文件 | 通道名、采样率、采样点数、元数据各列 |
| 论文摘要 | 论文摘要与页面转述的论文结论 | 显著性水平、三种指标方向性结论 |
| 未核实 | 未取得全文或需额外权限 | 论文的具体参数设置、分组界线的精确值 |
附录 C:完整参考文献
| 编号 | 文献 |
|---|---|
| 1 | Bhogal AS, Mani AR. Pattern Analysis of Oxygen Saturation Variability in Healthy Individuals: Entropy of Pulse Oximetry Signals Carries Information about Mean Oxygen Saturation. Frontiers in Physiology, 8:555. |
| 2 | PhysioNet. Pattern Analysis of Oxygen Saturation Variability (version 1.0.0). DOI 10.13026/C2TH47. |
| 3 | Open Data Commons Attribution License v1.0. |
| 4 | Goldberger A, et al. PhysioBank, PhysioToolkit, and PhysioNet: components of a new research resource for complex physiologic signals. Circulation. |
| 5 | Silva I, Moody G. An open-source toolbox for analysing and processing PhysioNet databases in MATLAB and Octave. |
| 6 | Richman JS, Moorman JR. Physiological time-series analysis using approximate entropy and sample entropy. Am J Physiol Heart Circ Physiol. |
| 7 | Peng CK, et al. Quantification of scaling exponents and crossover phenomena in nonstationary heartbeat time series. Chaos. |
| 8 | Costa M, Goldberger AL, Peng CK. Multiscale entropy analysis of complex physiologic time series. Phys Rev Lett. |
附录 D:任务代码
下面这段代码把前面几节串成一条完整流程:取数、读取、接元数据、算一个简单指标。指标只用来说明流程,不是论文指标。
#-- 端到端最小流程:读全部记录,按年龄组汇总一个简单指标
import csv
import numpy as np
import wfdb
BASE = "osv" #-- 解压后的本地目录
rows = list(csv.DictReader(open(f"{BASE}/participants.csv", encoding="utf-8")))
meta = {r["Record Name"].strip(): r for r in rows}
result = []
for r in rows:
name = r["Record Name"].strip()
rec = wfdb.rdrecord(f"{BASE}/{name}")
sig = rec.p_signal[:, 0]
#-- 相邻样本的变化率,作为「抖动强度」的粗代理
diff = np.abs(np.diff(sig)).mean()
age = int(r["Age"])
grp = "young" if age < 30 else "old"
result.append((name, grp, age, diff))
for g in ("young", "old"):
vals = [x[3] for x in result if x[1] == g]
print(g, "人数", len(vals), "抖动均值的平均", round(float(np.mean(vals)), 4))
#-- 记录清单与元数据表必须一一对应
rec_names = [ln.strip() for ln in open(f"{BASE}/RECORDS", encoding="utf-8") if ln.strip()]
print("清单与元数据一致:", set(rec_names) == set(meta.keys()))
最后一行输出应当为真。若为假,说明清单与元数据表存在单边记录,需要先排查再分析。
附录 E:如果只记五件事
- OSV 是氧饱和度变异性,与呼吸机、遥感、光学应变都无关。
- 三十六名健康成人,每人一小时静坐,只有 1 Hz 的 SpO2 单通道。
- 原始 1 kHz 的三路采集只公开了血氧一路,呼吸带与脉搏压力缺失。
- 用途是锚定参考与教学,不是训练集;不到 1 MB,ODC-By 1.0 许可。
- 量化档位与低采样率是两个必须先理解、再使用的技术特征。
编辑说明
本篇的事实核对时间为 2026 年 10 月 28 日,核对对象为 PhysioNet 项目主页 1.0.0 版说明、记录清单、单条记录头文件与参与者元数据表。论文全文未逐段核对,涉及论文方法的表述以官方页面转述与摘要为限,已在需要处注明。未覆盖的内容包括:论文中熵计算的具体参数、降采样前原始序列的形态、受试者的招募方式与伦理批号。页脚内容不作为正文事实的一部分。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- sicdb — 共享标签:生理信号 / 电子健康记录 / 时序数据
- alott — 共享标签:生理信号 / 电子健康记录 / 时序数据
- ob1db — 共享标签:生理信号 / 电子健康记录 / 时序数据
- mimic3wdb — 共享标签:生理信号 / 电子健康记录
- mimic3wdb-matched — 共享标签:生理信号 / 电子健康记录
- mimic4wdb — 共享标签:生理信号 / 电子健康记录
- uci-ctg — 共享标签:生理信号 / 电子健康记录 / 时序数据
- mmgdb — 共享标签:生理信号 / 时序数据
- ctu-uhb-ctg — 共享标签:生理信号 / 电子健康记录 / 时序数据
- hirid — 共享标签:电子健康记录 / 时序数据
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

