信息速览
跑步机上的力竭测试是运动生理学里最日常的一次测量:戴上面罩,速度每级加半公里,坡度或时间到了就再加一级,直到受试者摆手示意、或者再快一步就要摔倒。面罩后面接一台代谢车,每呼出一口气就报一组数——摄氧量、二氧化碳排出量、通气量、呼吸频率。旁边的心电仪同时给出心率,跑台控制台给出速度。
这类原始记录极少公开。它们散在各实验室的硬盘里,编号方式各不相同,方案各家自定,还夹着足以识别到具体运动队与个人的体测成绩。公开出来的通常是汇总后的均值表,一行一个组别,而不是一口气一口气的序列。于是任何想要复核"通气阈到底该用哪种算法"的人,都得从头再采一遍数据。
马拉加大学的这一份是少数例外:近千次测试、八百五十余人、横跨十一个年头,同一个人还可能在不同年份被重复测过。它小到可以一次下完,结构简单到两个 CSV 就能说清,也因此适合当作心肺运动数据的入门样本。以下只谈三件事:规模够不够用、结构能不能接、边界在哪里,一屏速览见下表。
| 维度 | 内容 |
|---|---|
| 官方名称 | Treadmill Maximal Exercise Tests from the Exercise Physiology and Human Performance Lab of the University of Malaga |
| 发布机构 | 马拉加大学运动生理学与人类表现实验室,经 PhysioNet 发布 |
| 当前版本 | 1.0.1,2021 年 12 月 10 日;初版 1.0.0 为 2021 年 4 月 30 日 |
| 规模 | 992 次测试,857 名受试者 |
| 人群 | 业余与职业运动员及体育爱好者,10 至 63 岁,女性占 15.0% |
| 采集年份 | 2008 至 2018 |
| 测量内容 | 逐口气的心率、摄氧量、二氧化碳排出量、呼吸频率、肺通气量、跑速、时间 |
| 附带信息 | 每次测试的年龄、体重、身高、环境温度与湿度、性别编码 |
| 通道数 | 测量表 7 个数值列,信息表 8 列 |
| 采样方式 | 逐口气触发,非固定频率;一次测试的呼吸条数随测试时长变化 |
| 数据量 | 五十七万余行,未压缩 22.1 MB,压缩包 6.3 MB |
| 文件形态 | 两个 CSV 平铺,无子目录,无波形文件 |
| 设备 | PowerJog J 系列跑台、CPX MedGraphics 代谢车、Mortara 十二导联心电 |
| 方案 | 5 km/h 步行热身,随后连续斜坡或每级 0.5 至 1 km/h 递增至力竭 |
| 是否到力竭 | 是,以摄氧量出现平台结合力竭表现为最大测试判据 |
| 许可 | PhysioNet Contributor Review Health Data License 1.5.0,正文为 CC BY-NC-SA 4.0 条款 |
| DOI | 10.13026/7ezk-j442(1.0.1);最新版 10.13026/94yv-6145 |
§1 它要解决的具体麻烦
1.1 一句话:这不是心电库,是代谢车账本
如果说自主神经类的数据集关心的是"心脏怎么跳",这一份关心的是"身体怎么烧"。每一行是一次呼吸,不是一次心搏;核心列是摄氧量与二氧化碳排出量,心率只是其中的一列。搞清这一点,后面所有的取舍都顺了。
由此派生出三个使用前提。第一,时间轴不均匀——呼吸快的时候一秒一行,呼吸慢的时候三秒一行,任何按行取窗口的操作都隐含了对呼吸频率的假设。第二,气体交换本身有噪声,逐口气序列的波动来自潮气量变化、说话、面罩漏气,单点值不可靠,通常要按时间或按呼吸数做滑动平均。第三,缺少原始波形意味着你无法回到上一层去修正提取错误,只能接受已经算好的数。
这三点不是缺陷清单,而是这类数据的固有形态。临床心肺运动试验的报告单上,那些平滑的曲线都是经过平均与拟合的;这一份给的是平均之前的原料。
1.2 三条常见的错误期待
期待一:以为能拿到 Bruce 方案的分级标记。 官方页面通篇没有出现 Bruce。描述的是先以 5 km/h 步行热身,然后进入连续斜坡或每级 0.5 至 1 km/h 的递增强度,直到力竭。也就是说这是一套实验室自定的斜坡式方案,级数、每级时长、是否连续加速,都随测试而异,数据里也没有一列告诉你"现在处于第几级"。想要标准 Bruce 的九级结构,这里没有。
期待二:以为附带心电波形。 心率来自 Mortara 十二导联心电,但发布的只有心率数值。没有导联、没有采样率、没有 R 波位置。任何想做心率变异性、想做 ST 段分析、想做心律失常筛查的设想,在这份数据上都落不了地。
期待三:以为它是健康人群的参考样本。 受试者是业余与职业运动员,加上一部分来做体能评估的体育爱好者。没有 sedentary 对照组,没有心血管病患者,没有老年人占多数的分层。用它算出来的摄氧量参考值,放到普通门诊人群里会系统性偏高。
1.3 它能给到的与给不到的
| 你的需求 | 它给的 | 够不够 |
|---|---|---|
| 最大摄氧量的峰值数值 | 每次测试的逐口气摄氧量序列,峰值可自行取 | 够,但需自己定义平均窗口 |
| 通气阈的判定练习 | 通气量、二氧化碳排出量、呼吸频率三条序列齐备 | 够,是这套数据最扎实的用途 |
| 心率恢复的计算方法比较 | 力竭后仍连续记录,心率一路到恢复段 | 够,官方配套论文正是做这件事 |
| 逐搏心率变异性分析 | 只有逐口气心率,没有 R 波间期 | 不够,用不了 |
| 标准分级方案的复现 | 无级别标记,方案不统一 | 不够 |
| 患者与健康人的组间比较 | 全部为运动人群 | 不够 |
| 摄氧量预测模型的训练集 | 近千次测试,含次最大强度段 | 够,注意方案异质性 |
| 运动处方强度的个体化标定 | 有峰値与阈値可供换算 | 部分够,缺无氧阈的金标准测定 |
| 环境对运动能力的影响 | 每次测试的温度与湿度 | 部分够,只有被动记录,无设计对照 |
1.4 你的处境与它能帮到的事
| 你的处境 | 它能帮你什么 | 下一步该做什么 |
|---|---|---|
| 手上有一个摄氧量峰值预测模型,缺外部验证集 | 近千次独立测试可作验证,且与训练集人群不同源 | 先按 ID_test 去重成受试者级,再做留人交叉验证 |
| 正在争论心率恢复该取第 1、2 还是第 3 分钟 | 提供了同一批人不同取法的可比条件 | 固定恢复段起点规则,统一单位后重算 |
| 要给可穿戴设备做运动强度分级 | 有跑速与摄氧量的同步序列,可建立速度与代谢当量的映射 | 只用运动段,剔除热身与恢复段 |
| 需要向临床同事解释什么是通气阈 | 数据形态与临床报告单一致,便于演示 | 选一次时长适中、缺测少的测试作图 |
| 想做运动中的呼吸模式研究 | 呼吸频率与潮气量可反推,序列完整 | 注意逐口气噪声,先做时间平滑 |
| 想研究运动诱发的心电改变 | 无波形,只有心率 | 换用带 Holter 或十二导联的数据集 |
| 需要一个能一次跑完的教学数据集 | 22.1 MB,两个 CSV,半小时能读懂 | 直接从压缩包开始,不必走 S3 |
1.5 一句话判断
它是目前公开范围内规模最大的一批跑步机极限心肺运动原始序列之一,价值在"多"与"真",不在"全"——够用来做算法与方法学比较,不够用来定义正常值。
1.6 不适合谁:四条边界
不适合做心电图相关研究的人。 没有波形,一切与形态有关的分析都无从谈起。心率这一列只保留了数值结果,连逐搏的间期都没有。
适合不了需要标准方案的人。 方案由实验室自定,斜坡与分级混排,且没有级别标记。凡是要按 Bruce 第几级做对齐的研究,都得放弃。
不适合做人群常模的人。 运动员占比高,年龄跨度虽大但分布偏向青年,女性只占 15.0%。想从中位数推出"普通人应该跑多少",方向就错了。
不适合做临床诊断验证的人。 没有临床终点,没有随访,没有诊断标签,也没有运动中的症状记录。它是一份运动表现数据,不是一份诊断数据。
§2 数据构成与规格
2.1 目录里有什么
目录完全平铺,四个文件,没有子目录,没有按受试者分装的文件夹。
LICENSE.txt,14.4 KB,是许可与使用协议的正文。SHA256SUMS.txt,241 字节,只有几行,对应两个数据文件。subject-info.csv,32.3 KB,每次测试一行。test_measure.csv,22.1 MB,每次呼吸一行,是全库的主体。
整个包未压缩 22.1 MB,压缩后 6.3 MB。这个体量意味着它可以在浏览器里直接下完,也可以在笔记本内存里整表读入——不需要分块、不需要数据库、不需要分布式。对教学与快速验证是很大的便利,对"大数据"叙事则毫无帮助。
SHA256SUMS.txt 的存在值得单独说一句:它是唯一能证明你手上的文件与官方一致的东西,而很多人会跳过它。校验只需要一条命令,放在 §5.2。
2.2 字段与记录的完整字典
两张表的列名都是英文,大小写不统一(Age 大写开头,time 全小写),按名取列时要原样匹配。下表把两表合并列出,共 17 个字段。
| 列名 | 所属表 | 类型 | 单位 | 含义 | 备注 |
|---|---|---|---|---|---|
| ID | 两表 | 整数 | 无 | 受试者标识 | 857 个取值,跨测试稳定 |
| ID_test | 两表 | 字符串 | 无 | 测试标识 | 受试者编号与测试序号拼接,形如 245_3 |
| Age | 信息表 | 数值 | 岁 | 测试当时年龄 | 官方称覆盖 10 至 63 岁 |
| Weight | 信息表 | 数值 | 千克 | 体重 | 测试当日 |
| Height | 信息表 | 数值 | 厘米 | 身高 | 测试当日 |
| Sex | 信息表 | 整数 | 无 | 性别 | 1 表示女性,其余取值未在文件内声明 |
| Humidity | 信息表 | 数值 | 百分比 | 环境相对湿度 | 实验室记录 |
| Temperature | 信息表 | 数值 | 摄氏度 | 环境温度 | 实验室记录 |
| time | 测量表 | 数值 | 秒 | 自测量开始的时间 | 单调不减,起点为 0 |
| Speed | 测量表 | 数值 | 公里每小时 | 跑台速度 | 热身段为 5,恢复段亦降回 5 |
| HR | 测量表 | 整数 | 次每分 | 心率 | 源自十二导联心电 |
| VO2 | 测量表 | 数值 | 毫升每分 | 摄氧量 | 30 次测试整段缺失 |
| VCO2 | 测量表 | 数值 | 毫升每分 | 二氧化碳排出量 | 同上 |
| RR | 测量表 | 数值 | 次每分 | 呼吸频率 | 与 RR 间期无关,勿混淆 |
| VE | 测量表 | 数值 | 升每分 | 肺通气量 | 30 次测试整段缺失 |
| — | — | — | — | 缺 RPE、缺级别、缺停止时刻 | 见 §4.3 |
字段表里最容易被误读的是 RR。在心电语境里 RR 指相邻 R 波的间期,单位毫秒;在这份数据里它是 respiratory rate,呼吸频率,单位次每分。两个含义差了三个数量级,写脚本时按列名硬编码很容易错位。
Sex 只声明了 1 表示女性,其余取值的具体编码官方未在文件或页面里逐一列出。实务上按二值处理即可,但不要假设 0 与 2 的语义,也不要在没有核对的情况下把它当作字符串读入。
2.3 一条记录的完整解剖
取 ID_test = 245_3 的三条相邻记录,说明一行的构成。下面是原表结构,数值为示例形态,用于说明列的顺序与量级。
time,Speed,HR,VO2,VCO2,RR,VE,ID,ID_test
612.4,11.0,168,3120.5,3405.2,34.1,96.4,245,245_3
615.1,11.0,169,3155.8,3441.0,33.8,97.2,245,245_3
617.6,11.0,170,3190.2,3478.6,34.4,98.1,245,245_3
读法是这样的。第一列是秒,两条记录之间隔了两秒半——这段时间里受试者只完成了一次呼吸,所以下一行才出现。第二列是跑台速度,11 公里每小时,说明测试已进入递增段且仍在同一级内。第三列心率 168,第四列摄氧量三千一百余毫升每分,第五列二氧化碳排出量略高于摄氧量,呼吸交换率大于 1,符合高强度运动的特征。第六列呼吸频率三十四次每分,第七列通气量九十六升每分,通气当量约 30,属于力竭前的典型水平。
三行连起来看,摄氧量在涨、心率在涨、通气量在涨,而速度没变——这正是递增测试中"级内"的形态:速度固定,代谢仍在向稳态爬升。级别切换的时刻,速度列会出现台阶式跳变。这也是唯一能从数据里反推级别边界的线索。
同一受试者的另两次测试,ID_test 分别是 245_1 与 245_2,ID 都是 245。三次测试在信息表里各占一行,年龄可能不同——如果跨了年份,Age 会变化,这既是纵向研究的素材,也是按人聚合时容易踩的坑:同一个 ID 可能对应两个年龄。
2.4 规模、结构与边界:三张核对表
不写分布,只写能决定"够不够用"的量级与形态。
| 规模项 | 数值 | 口径 |
|---|---|---|
| 测试次数 | 992 | 信息表行数 |
| 受试者人数 | 857 | ID 去重后的取值数 |
| 逐口气记录行数 | 575,087 | 测量表数据行 |
| 未压缩体积 | 22.1 MB | 官方文件页 |
| 压缩包体积 | 6.3 MB | 官方下载页 |
| 采集跨度 | 2008 至 2018 | 官方描述 |
| 年龄跨度 | 10 至 63 岁 | 官方描述 |
| 女性占比 | 15.0% | 官方描述,按测试计 149 |
| 结构项 | 形态 | 影响 |
|---|---|---|
| 文件层级 | 平铺四文件,无子目录 | 可直接按名取用,无需遍历 |
| 表关系 | 宽表一对多长表,键为 ID 与 ID_test | 连接成本极低 |
| 时间轴 | 单调不减,以秒计,起点归零 | 可做时间窗切分,但窗口内行数不等 |
| 分组粒度 | 一行一次呼吸,无受试者级汇总 | 需自行聚合 |
| 缺失表达 | 空值,整段缺失而非插补 | 需按测试整体剔除或标记 |
| 编码风格 | 英文字段名,大小写混用 | 按名取列需精确匹配 |
| 格式 | 纯文本 CSV,无压缩内部结构 | 任何语言可读,无需专用库 |
| 边界项 | 具体情况 | 后果 |
|---|---|---|
| 缺气体数据的测试 | 30 次缺 VO2、VCO2、VE | 按测试计数时有效样本略低于名义值 |
| 无波形 | 只保留心率数值 | 无法复核提取质量 |
| 无级别标记 | 仅能由速度台阶反推 | 阈值对齐需自建规则 |
| 无停止时刻 | 运动段与恢复段连续 | 恢复期指标起点需推断 |
| 无临床标签 | 无诊断、无症状、无随访 | 不能做诊断建模 |
| 人群偏运动 | 运动员与爱好者为主 | 常模外推受限 |
三张表合起来回答三个问题:近千次的量够做方法学比较与中等规模的模型验证;两表一键的结构半小时能接上;而缺波形、缺级别、缺标签这三处,决定了它只能停在"运动表现"这一层,进不到"临床诊断"。
2.5 与同类资源的字段差异
| 资源类型 | 典型字段 | 与这份的差别 |
|---|---|---|
| 标准心肺运动试验报告 | 峰值摄氧量、无氧阈、氧脉搏、通气当量斜率 | 这里是原料,报告里的派生指标都要自己算 |
| 逐搏心电数据库 | R 波时刻、导联波形、采样率 | 那里有波形无气体,这里反之 |
| 可穿戴运动记录 | 加速度、步频、光电容积脉搏波 | 那里连续数天但无代谢真值,这里短时但金标准 |
| 临床六分钟步行 | 步行距离、血氧、Borg 评分 | 那里是次最大且方案统一,这里到力竭且方案自定 |
| 运动队体能档案 | 成绩、训练负荷、赛季阶段 | 那里有纵向背景,这里只有测试当日 |
差异的核心在于"金标准"三个字。代谢车测的摄氧量是气体交换的直接测量,不是由心率或加速度推算出来的估计值。这使得它可以作为其他传感器的参照,而这也是它的主要引用场景。
2.6 两张表怎么对上:连接前的三项检查
宽表与长表用一个字符串键相连,看起来简单,实际上有三处会静默出错。
| 检查项 | 做法 | 不做会怎样 |
|---|---|---|
| 键的类型 | 两表都按字符串读 ID_test | 被推断成数值或日期,连接全空 |
| 键的唯一性 | 断言信息表的 ID_test 不重复 | 一对多误配,行数成倍膨胀 |
| 键的覆盖 | 确认测量表的键都在信息表里 | 连接后出现空年龄与空体重 |
| 编号解析 | 按最后一个下划线切分 | 含下划线的编号被切错 |
| 人数去重 | 对 ID 而非 ID_test 去重 | 把重复测试的人算成两个人 |
| 缺失标记 | 连接后统计关键列空值 | 把缺气体数据的测试当有效样本 |
三项检查里最容易被跳过的是"键的唯一性"。信息表是每次测试一行,正常情况下键唯一;但如果某次测试被重复录入,merge 不会报错,只会让该次测试的所有呼吸行翻倍。翻倍之后所有基于行数的统计都会偏大,而且偏得很隐蔽——你不会看到任何异常值,只会看到一个略高的样本量。
编号解析这一项,是因为 ID_test 由受试者编号与测试序号拼接而成。若受试者编号本身不含下划线,简单的分割也能用;但一旦未来版本改用别的拼接方式,按下划线全切就会把编号切成两段。稳妥的写法是只切最后一段,把剩下的整体当作受试者编号。
覆盖检查针对的是孤儿行:测量表里有某个 ID_test,信息表里却没有。这种情况在正常发布中不该出现,但版本混用(用 1.0.1 的测量表配 1.0.0 的信息表)时会出现。连完随手查一下空值比例,比事后追查便宜得多。
§3 这到底是一场什么测试
3.1 方案的形状:斜坡,不是阶梯
运动试验的方案决定了整份数据的时序骨架。Bruce 方案是阶梯式的:每三分钟把速度和坡度同时抬一档,九个标准级,级与级之间有明确的边界。这份数据不是这样。
官方描述给的是一条更朴素的流程。受试者先以 5 km/h 步行热身,随后进入递增段。递增段的形态有两种:连续加速的斜坡,或者每级抬升 0.5 至 1 km/h 的分级。两种形态并存,意味着数据里同时存在"速度随时间线性上升"和"速度呈台阶状跳变"两类曲线。
这个差别不是学究式的。斜坡方案下,每一时刻的强度都不同,稳态无从谈起,做阈值判定要靠连续的斜率变化;分级方案下,每级末尾接近稳态,可以用级末均值。把两类混在一张表里做统一处理,等于假设它们同分布——而这个假设并不成立。
判断某次测试属于哪一类,只能看速度列。速度随时间呈斜线的是斜坡,呈台阶的是分级,台阶的高度就是当级的增幅。这个反推在 §5.4 有一段可运行的代码。
3.2 "最大"是怎么判定的
官方写明,参与者被要求运动到力竭之外,当摄氧量达到平台饱和时,该次测试被判定为最大测试。判据是摄氧量的平台现象,辅以力竭表现,全部测试在运动科学医生监督下进行。
平台的判定在方法学上一直有争议:取最后多少秒、允许上升多少毫升、用目视还是用斜率阈值,各实验室不同。这份数据没有给出平台判定所依据的具体窗口,也没有一列"是否达最大"的标记。使用时要么接受官方的整体判定,要么自己按摄氧量的斜率重算一遍。
这件事在做模型时尤其要注意。如果你的标签是"峰值摄氧量",而这个峰值来自最后一次呼吸的单点,噪声会很大;如果取最后三十秒的均值,稳定性会好很多。同一批数据,两种取法给出的峰值可以差出几十毫升每分,而且差值不随机——呼吸急促的人单点更容易偏高。
3.3 恢复段为什么值得单独说
力竭之后,官方把跑台速度降回 5 km/h,要求受试者继续步行。理由写得很明确:避免血管迷走性晕厥。运动骤停时静脉回流突然减少,血压下落,部分人会出现短暂脑供血不足。让受试者继续走动,是用肌肉泵维持回心血量。
对数据使用者来说,这个设计的后果是:恢复段不是静坐,而是低速步行。心率下降的曲线因此混合了两件事——迷走神经重新占优,以及轻度运动仍在维持交感张力。拿这份数据算心率恢复,得到的数值不能直接与"运动后立即仰卧"的方案相比。
更实际的麻烦是起点。数据里没有"停止递增"的时刻标记,运动段与恢复段在同一张表里首尾相接。速度列从递增段的十几公里每小时掉回 5,这个断崖就是事实上的分界。以速度首次回落到 5 的那一行作为恢复起点,是目前最可靠的做法,但遇到斜坡方案里速度恰好经过 5 的测试,会误判。
3.4 设备链:三台机器,一条时间轴
三台设备各管一段。PowerJog J 系列跑台负责施加负荷并输出速度;CPX MedGraphics 代谢车负责气体交换,逐口气给出摄氧量、二氧化碳排出量、通气量与呼吸频率;Mortara 十二导联心电负责心率。
三路信号要合成一张表,就必然有一个共同的时间基准。代谢车通常是主时钟,呼吸触发事件带上时间戳,心电与跑台数据按时间对齐后并入。对齐的精度取决于各设备的采样与插值方式,官方没有披露。对逐口气序列而言,亚秒级的错位影响不大;但如果要做心率与呼吸的逐拍耦合分析,这个未知量就成了硬约束。
设备型号本身也有信息量。CPX MedGraphics 是长期被广泛使用的代谢车平台,其流量传感器与气体分析器的响应特性有大量文献可查,这为"为什么某些呼吸点的值看起来异常"提供了物理解释——比如高通气时传感器响应滞后造成的摄氧量低估。
3.5 与临床心肺运动试验的分工
同一套设备、同一套指标,在临床上和在运动队里的用法完全不同。
临床 CPET 面向的是诊断与预后:缺血性心脏病的负荷诱发改变、心衰患者的通气效率、术前风险评估。判读的重心在十二导联波形、血压反应、症状,以及摄氧量斜率与二氧化碳排出量斜率的关系。
这一份面向的是运动表现:峰值摄氧量有多高、通气阈在第几级、心率恢复快不快。没有血压、没有症状、没有波形,临床判读的三根支柱缺了两根半。
所以两者的关系不是替代,而是分工。想验证一个通气阈算法,用这份数据足够;想验证一个缺血判据,这份数据连门都进不去。把它们混为一谈,是选型时最常见的错误。
3.6 测试前控制与伦理
官方记录了测试前的知情同意流程:成年受试者本人、未满十八岁者由法定监护人签署书面知情同意,测试在运动科学医生监督下进行。研究方案经大学伦理委员会批准,分析遵循赫尔辛基宣言原则。
这些信息对使用者有两个用处。其一,它说明数据不是回顾性拼凑,而是有统一流程的前瞻采集,这在解释"为什么方案仍有异质性"时提供了边界——异质性来自十年间的设备与人员更替,不是随意性。其二,未成年受试者的存在意味着数据里有一部分来自十岁出头的青少年,这部分人的峰值摄氧量与成年人不可直接比较,按年龄分层是必要的。
3.7 温度与湿度这两列能做什么
信息表里有两列常被忽略的字段:测试当时的温度与湿度。官方把它们当作记录项,而不是实验变量——没有设计冷热对照,没有随机化,也没有测量核心体温。
| 用法 | 可行性 | 前提与风险 |
|---|---|---|
| 作为协变量放进模型 | 可行 | 需确认与年龄、季节不共线 |
| 解释个别测试的异常高心率 | 可行 | 只能作提示,不能作因果 |
| 研究热环境对运动能力的影响 | 不可行 | 没有对照,温湿度变化范围未知 |
| 校正不同年份的测试结果 | 部分可行 | 年份与设备更换混在一起 |
| 作为数据质量的筛选条件 | 谨慎 | 温湿度正常不代表数据正常 |
热环境对心肺反应的影响是明确的:同样绝对强度下,湿热环境心率更高、血浆容量分布改变、通气量上升,峰值摄氧量可能下降。这意味着两列数值能解释一部分残差,但解释力有限,因为它们只刻画了环境,没有刻画个体当时的适应状态。
真正要小心的是把它们与年份一起使用。十年跨度里,测试时间分布在不同季节,空调与通风条件也可能改过;温湿度的年度差异因此混杂了设备与场地的变化。想让这两列发挥校正作用,至少要先看它们与年份的关系是否稳定。
3.8 一次测试里的四个阶段
把任意一次测试拆开,都能看到四段结构,理解这一点比记住字段名更有用。
| 阶段 | 速度特征 | 生理特征 | 分析时的处理 |
|---|---|---|---|
| 热身 | 恒定在 5 km/h | 摄氧量缓慢上升,心率轻度升高 | 通常剔除,或单独用作基线 |
| 递增 | 线性上升或台阶上升 | 摄氧量与通气持续上升,直至平台 | 阈值判定与峰值提取的主战场 |
| 转换 | 从最高速骤降回 5 km/h | 心率开始快速下落 | 恢复起点的判定依据 |
| 恢复 | 恒定在 5 km/h | 心率指数式回落,通气迅速下降 | 心率恢复指标的取值区段 |
四段的边界在数据里没有标记,但都有形态特征。热身与递增的分界是速度首次超过 5;递增与转换的分界是速度从高位落回 5;转换与恢复没有明确分界,因为降速本身只占几秒。
这四段结构也解释了为什么不能对整条序列做统一统计。把热身、运动、恢复混在一起算平均摄氧量,得到的数没有生理意义;同样,把整段心率做频谱分析,会把运动期的趋势与恢复期的瞬态混叠。
§4 它的长处与短板
4.1 三处别人给不了的东西
第一是规模带来的稳定性。 近千次测试意味着即使按性别、按年龄段切开,每个格子里仍有可用的样本。做阈值算法的比较时,最怕的是"换个数据集结论就翻",而这里可以在同一份数据内做留人交叉验证,把结论的稳健性先筛一遍。
第二是原始分辨率。 逐口气而非每三十秒一个点。这意味着任何平均窗口都可以自己定义——想复现某篇论文的十五秒平滑,或者想看未平滑序列的抖动,都不需要向作者索要原始文件。
第三是重复测试。 857 人对应 992 次测试,说明有一部分人被测过两次以上。这是纵向分析的入口:同一个人在不同年份的峰值变化,比横断面的组间差异更接近"训练到底有没有用"这个问题。
4.2 案例:ID_test 245_3 的第三次测试
用一个具体编号把上面三条串起来。
245_3 表示受试者 245 的第三次测试。这个人在信息表里出现三次,ID 都是 245,ID_test 分别是 245_1、245_2、245_3。三次的年龄可能不同,体重与身高也可能变——如果跨度超过一年,这就是一条真实的纵向记录。
打开 245_3 的测量行,从第一行开始速度是 5,心率在静息水平,摄氧量在基线附近小幅波动。这段是热身。速度开始爬升之后,摄氧量随之上升,心率滞后几十秒才跟上——这个滞后就是氧亏,也是为什么不能用瞬时心率反推瞬时摄氧量。接近末尾,速度不再上升,摄氧量也不再上升,两条曲线同时变平,这就是官方的"平台"判据在个案上的样子。再往后,速度掉回 5,心率开始快速下落,恢复段开始。
这个案例在三个地方会被再次引用。§5.4 用它演示如何按速度台阶切分级别;§7.3 用它说明"同一个人不同次测试之间不可直接平均";§9.2 用它作为最短流程的验证对象。选它的理由是它同时具备重复测试、速度台阶清晰、气体数据完整三个特征,适合作为模板。
值得注意的是,245_3 这类编号是拼接出来的,不是独立的随机标识。解析时要按最后一个下划线切分,而不是按下划线全部分割——受试者编号本身若含下划线,简单分割会出错。这是 §5.3 的代码里特意用 rsplit 的原因。
4.3 缺什么:四样东西
没有波形。 十二导联心电采了,但只发布了心率。这意味着无法做心律失常筛查、无法做 ST 段分析、无法验证心率提取的准确性。想验证"某次测试的心率是否可信",只能看序列是否连续、是否有跳变。
没有级别标记。 方案自定且不统一,级数、级长、增幅都随测试而变。任何"按级对齐"的分析都要先自己切分。这是使用成本最高的一处缺失,因为切分规则本身就会引入主观性。
没有 Borg 评分与停止原因。 力竭是主观判断加客观平台,但主观那一半没有记录。是腿部疲劳还是呼吸困难,是胸闷还是单纯不想跑了——这些信息在运动生理研究里是常规记录项,这里没有。
没有血压与血氧。 临床 CPET 的标准监测项,这里一项都没有。这也再次说明它的定位是表现测试而非临床测试。
4.4 三十次缺气体数据的测试意味着什么
官方明确写出,有 30 次测试的摄氧量、二氧化碳排出量与通气量缺失。信息表里这些测试仍在,测量表里这些行也在——心率与速度有值,气体三列空着。
这带来两个直接后果。其一,按测试计数时,可用于代谢分析的有效样本是 992 减 30,而名义规模仍是 992。引用规模时若不加限定,会略微高估。其二,这 30 次测试的心率与速度是完整的,所以它们仍可用于心率动力学或速度-时间关系的分析,只是不能用于任何涉及气体交换的任务。
处理方式上,建议保留而非删除:加一列标记,在聚合时按分析目标决定是否纳入。直接删掉空行会把可用的心率信息一起丢掉,而按测试整体剔除又会让不同分析的有效样本量不一致,比较时容易出错。
4.5 质量五问
| 问题 | 回答 | 依据 |
|---|---|---|
| 数据是原始的还是派生的 | 介于两者之间:呼吸级原始,波形级派生 | 只发布逐口气数值 |
| 标注是谁做的 | 无人工标注,最大测试判定由监督医生完成 | 官方方法描述 |
| 缺失是随机的还是系统的 | 30 次整段缺失,属流程原因,非随机 | 官方明示缺失范围 |
| 一致性如何保证 | 十年间同一实验室,但设备与人员有更替 | 官方描述采集跨度 |
| 可复现性如何 | 方案不统一且未记录级次,逐例复现困难 | 字段中无级别列 |
4.6 自己采、买商用库、还是用这一份
选型时真正的比较对象往往不是别的公开数据集,而是另外两条路:自己组织采集,或者购买商用运动生理数据库。
| 路径 | 拿到什么 | 主要成本 | 适合谁 |
|---|---|---|---|
| 自己组织采集 | 完全可控的方案、可加测血压与波形 | 伦理审批、设备、受试者招募、周期长 | 有临床或运动队资源、问题很特殊的团队 |
| 购买商用数据库 | 规模大、有临床标签、有随访 | 许可费、数据不能公开、条款受限 | 做产品、需要合规背书的团队 |
| 用这一份公开数据 | 近千次测试的代谢真值,零成本 | 方案不可控、无波形、无标签 | 做方法学、做原型、做教学 |
| 混合使用 | 公开数据做预研,自采做验证 | 两套口径要对齐 | 大多数严肃项目 |
第四行才是常态。公开数据用来把算法原型跑通、把结论的稳健性先筛一遍,等方向确定再花钱采一小批可控数据做验证。反过来做——先自采再找公开数据对照——通常会卡在口径不一致上,因为自采方案几乎不可能与十年前的另一家实验室完全对齐。
这里有个现实约束值得说清:代谢车数据的采集成本主要不在设备,而在人。一次最大测试需要医生在场、需要受试者配合到力竭、需要测试前控制饮食与运动。这批档案之所以珍贵,是因为它把十年的这些成本一次性沉淀成了二十二兆的文本。
§5 取数:三条通道与五段代码
5.1 三条下载通道
官方给了三种拿法,适用场景不同。
浏览器直接下 ZIP,适合一次性使用与教学演示,包内有许可与校验和。wget 递归镜像,适合需要保持目录结构、后续增量更新的场景。S3 同步,适合在云上直接落地到对象存储,跳过本地中转。
#-- 通道一:整包 ZIP,最省事
curl -L -o treadmill.zip \
https://physionet.org/content/treadmill-exercise-cardioresp/get-zip/1.0.1/
#-- 通道二:wget 镜像,保留目录结构,可断点续传
wget -r -N -c -np https://physionet.org/files/treadmill-exercise-cardioresp/1.0.1/
#-- 通道三:S3 同步,免签名访问,适合云端落地
aws s3 sync --no-sign-request s3://physionet-open/treadmill-exercise-cardioresp/1.0.1/ ./data/
下载前值得看一眼许可。这份数据挂在 PhysioNet 的 Contributor Review 名下,名义许可为 1.5.0 版,LICENSE.txt 的正文是 CC BY-NC-SA 4.0 的条款文本。署名、非商业、相同方式共享三条同时生效。如果用途涉及产品,先确认非商业条款是否适用,不要等下完二十二兆才开始想这件事。
5.2 先校验,再分析
SHA256SUMS.txt 只有两百多字节,几行而已。它是唯一能证明文件在传输中没有损坏的依据,而损坏的 CSV 往往不会报错,只会让某些行的列数错位,分析时表现为莫名其妙的空值。
#-- 先解包,再在文件所在目录校验
unzip -q treadmill.zip -d treadmill
cd treadmill
sha256sum -c SHA256SUMS.txt
输出里每个文件一行,末尾是 OK 才继续。任何一行 FAILED,重下即可,不必尝试修复——二十二兆的重传成本远低于排查一个静默错位的列。
5.3 用 pandas 读入并连接两表
两个文件都不大,可以直接整表读入内存。注意 ID_test 必须按字符串读,否则 245_3 会被推断成别的类型;ID 按整数读,用于跨表连接。
import pandas as pd
base = "treadmill/1.0.1"
subj = pd.read_csv(f"{base}/subject-info.csv", dtype={"ID_test": "str"})
meas = pd.read_csv(
f"{base}/test_measure.csv",
dtype={"ID_test": "str", "ID": "int32"},
usecols=["time", "Speed", "HR", "VO2", "VCO2", "RR", "VE", "ID", "ID_test"],
)
#-- 受试者编号从拼接字符串里取,按最后一个下划线切
subj["subject"] = subj["ID_test"].str.rsplit("_", n=1).str[0].astype("int32")
subj["visit"] = subj["ID_test"].str.rsplit("_", n=1).str[1].astype("int16")
#-- 检查两表的键是否一一对应
assert subj["ID_test"].is_unique
assert set(meas["ID_test"]).issubset(set(subj["ID_test"]))
merged = meas.merge(
subj[["ID_test", "Age", "Weight", "Height", "Sex", "Temperature", "Humidity"]],
on="ID_test",
how="left",
validate="many_to_one",
)
print(len(merged), merged["ID_test"].nunique())
validate="many_to_one" 这一句是廉价的保险:如果信息表里同一个 ID_test 出现两次,merge 会静默地让行数翻倍,而这条断言会直接抛错。
5.4 按速度台阶切出级别
没有级别列,只能从速度反推。思路是找出速度发生跳变的位置,把两次跳变之间的行归为一级。这段代码用 §4.2 的案例作为验证对象。
import numpy as np
def stage_bounds(df, id_test, tol=0.15):
g = df.loc[df["ID_test"] == id_test, ["time", "Speed"]].sort_values("time")
d = g["Speed"].diff().abs()
cuts = g.index[d > tol].tolist()
starts = [g.index[0]] + cuts
ends = cuts + [g.index[-1]]
return [(s, e) for s, e in zip(starts, ends) if e > s]
stages = stage_bounds(merged, "245_3")
for i, (s, e) in enumerate(stages, 1):
seg = merged.loc[s:e]
print(i, round(seg["time"].iloc[0], 1), round(seg["time"].iloc[-1], 1),
round(seg["Speed"].mean(), 2), round(seg["VO2"].mean(), 1))
阈值 0.15 是针对分级方案选的:增幅最小 0.5,取三分之一作容差可以滤掉速度的轻微抖动,又不会漏掉任何一级。对斜坡方案,这段代码会把整段切成一段,因为速度持续上升但每一步都很小——这类测试要用时间等分而非速度跳变,两种方案不能共用一套切分逻辑。
5.5 换算成相对摄氧量与代谢当量
绝对摄氧量的单位是毫升每分,受体重影响极大。要跨人比较,必须除以体重;要与临床报告对齐,还要换算成代谢当量。
#-- 相对摄氧量:毫升每公斤每分
merged["VO2_rel"] = merged["VO2"] / merged["Weight"]
#-- 代谢当量:1 MET 约定为 3.5 毫升每公斤每分
merged["MET"] = merged["VO2_rel"] / 3.5
#-- 每次测试的峰值:取最后 30 秒的均值,而非最后一行
def peak_vo2(g, window=30):
t_end = g["time"].max()
tail = g.loc[g["time"] >= t_end - window, "VO2_rel"]
return tail.mean() if len(tail) else np.nan
peaks = merged.groupby("ID_test").apply(peak_vo2, include_groups=False)
print(peaks.describe()[["count", "mean", "min", "max"]])
取最后三十秒均值而非末行,是为了压住逐口气噪声。窗口长度的选择会影响结果,且影响不随机——呼吸频率高的人窗口内呼吸条数多,平均更充分;呼吸慢的人可能只有十几次呼吸。这是逐口气数据固有的不均等问题,没有便宜的解法,只能固定窗口并在方法里写明。
5.6 固定版本,别追最新
版本有两个:1.0.0 与 1.0.1。前者 2021 年 4 月,后者同年 12 月。两个 DOI 分别对应固定版本与"始终最新",引用时应当用固定版本的那个,否则别人复现时可能拿到内容不同的文件。
固定版本还有一个实际好处:SHA256SUMS.txt 只对特定版本有效。用最新版 DOI 拿到 1.0.1 的校验和,某天官方发布 1.1.0 时校验就会失败,而失败的原因不是文件坏了,是版本变了。
5.7 六种静默失败与对策
下面六个问题都不会抛异常,只会让结果慢慢偏掉。
| 现象 | 原因 | 对策 |
|---|---|---|
| 连接后行数变多 | 键在信息表里重复 | merge 时加唯一性断言 |
| 年龄列大量为空 | 键类型不一致 | 两表统一按字符串读 |
| 峰值高得不合理 | 取了全序列最大值而非末尾窗口 | 固定窗口取均值 |
| 恢复指标为负 | 恢复起点定在了运动中途 | 用速度回落点定起点 |
| 呼吸交换率异常 | 把 RR 当成了 R 波间期 | 按字段含义而非列名取用 |
| 速度与摄氧量不相关 | 部分测试用了坡度 | 先做散点检查再建映射 |
第一条与第二条是连接阶段的,第三到第五条是聚合阶段的,第六条是用前必须做的一次目视检查。六条加起来,大概能覆盖初学者踩坑的八成。
第三条值得展开。全序列最大值总是落在某一次特别深的呼吸上,而那次呼吸可能伴随面罩漏气或说话,摄氧量读数会突然跳高。用末尾三十秒均值,等于用多次呼吸互相校正,代价是把真正的峰值略微抹平。
第四条的原理在 §3.3 讲过:恢复段是步行,速度 5,而运动段末尾速度远高于 5。若把"第一次出现 5"当作起点,会撞上热身段那一段同样为 5 的记录,于是恢复指标算到了热身期,出现负值或荒谬的大值。正确的做法是取"最后一次高于 5"之后再找回落点。
§6 与库内相邻词条的关系
6.1 关系图谱
库里已经有三份与运动、自主神经相关的数据,彼此的差别需要说清,否则容易选错。
| 词条 | 采集场景 | 核心信号 | 规模 | 与本篇的分工 |
|---|---|---|---|---|
| actes-cycloergometer-exercise | 自行车功率计递增负荷 | 逐搏 RR 间期、摄氧量、功率 | 18 名少年运动员 | 那里是逐搏心电,这里是逐口气气体;那里是功率负荷,这里是速度负荷 |
| cardioresp-response-orthostat | 静卧后被动起立 | 连续血压、脑血流、近红外 | 9 人 | 那里是姿势改变引起的短时血流动力学,这里是持续递增负荷 |
| autonomic-aging-cardiovascular | 静息坐位 | 心电与连续血压 | 1121 名健康志愿者 | 那里是衰老横断面,这里是运动峰值 |
| 本篇 | 跑步机递增至力竭 | 逐口气摄氧量、通气、心率、速度 | 992 次测试 / 857 人 | 唯一的代谢车级金标准与最大强度端点 |
一句话概括四者的分工:想看心脏的节律,去 actes 或 autonomic;想看血压与脑血流的耦合,去 orthostat;想看身体在极限强度下的气体交换,来这里。
6.2 检索路径
按任务找入口,比按名称找更快。
| 任务 | 首选 | 理由 |
|---|---|---|
| 心率变异性与运动强度 | actes-cycloergometer-exercise | 逐搏 RR,可直接算频域指标 |
| 心率恢复的算法比较 | 本篇 | 近千次测试且恢复段连续 |
| 压力反射敏感度 | autonomic-aging-cardiovascular | 连续血压加逐搏心电 |
| 体位性低血压与脑自动调节 | cardioresp-response-orthostat | 有脑血流与近红外 |
| 峰值摄氧量预测 | 本篇 | 唯一有大规模代谢真值的 |
| 通气阈方法学 | 本篇 | 三条气体序列齐备 |
6.3 与自行车数据的分工,值得多说一句
自行车与跑台是运动生理的两条主线,负荷方式不同,结果也不同。跑台负重涉及体重支撑与跑动经济性,峰值摄氧量通常高于自行车;自行车负荷由功率计直接给定,机械功精确可测,且不受跑动技术影响。
这决定了两个使用场景。要做功率与代谢的精确映射,自行车更合适,因为功率是独立变量。要做接近日常活动的最大能力评估,跑台更合适,因为跑步是大多数人熟悉的动作,局部肌肉疲劳不易成为限制因素。
库里那两份数据的规模差异也值得留意:一份是十八人,一份是近千次测试。小样本适合把每个受试者当个案深看,大样本适合做分布与模型。两者不是替代关系,前者回答"机制长什么样",后者回答"在人群中稳不稳"。
§7 质量评估与局限
7.0 阅读本节前的一个前置校正
看评分之前,先纠正一个几乎人人会犯的错位:把这份数据当成"心电图数据库"来打分。
如果按心电数据库的标准衡量,它在采样率、导联数、波形完整性上全部不及格——因为它根本没有波形。但这不是缺陷,是它压根不在那个品类里。它的正确参照系是"代谢车输出表":气体交换、通气、跑速、心率的时间序列。在这个参照系里,它的完整度相当高,缺的是级别标记与停止时刻这类流程元数据。
另一种常见误判是把它当"健康人群参考"。它的受试者以运动员为主,峰值指标系统性偏高,任何以中位数当正常值的做法都会偏。评分表里的"边界清晰度"一项扣了分,扣的正是这个——不是数据质量问题,是适用范围问题。
把这两处错位校正过来,下面五个维度才有意义。
7.1 五维评分(DAIMS)
| 维度 | 分值 | 判断依据 | 主要扣分点 |
|---|---|---|---|
| 可得性 D | 5 | 开放下载,无需申请,三种通道,包体小 | 非商业条款限制商用 |
| 可理解性 A | 4 | 官方描述完整,字段含义清楚,附配套 R 代码 | 未说明 Sex 其余编码、未给级别定义 |
| 完整性 I | 4 | 近千次测试、气体三序列齐备、心率与速度同步 | 30 次缺气体数据,无波形、无血压 |
| 方法学 M | 4 | 统一实验室、医生监督、书面知情同意 | 方案十年间不统一,平台判据未量化 |
| 可持续性 S | 5 | PhysioNet 长期托管,固定 DOI,有版本记录 | 仅一次修订,后续更新不明 |
DAIMS 总分 22/25。这个分数在库里属于上游:多数临床数据集卡在可得性(需申请)或方法学(方案不明)上,而这份恰好在两头都过得去,短板集中在"元数据不够细"。
7.2 为什么可得性给满分
三个理由。其一,不需要数据使用申请,也不需要完成人体受试者保护培训,下载即用。其二,包体只有二十二兆,任何网络环境都能拿到,不存在"申请批准了但下不动"的情况。其三,官方同时提供 ZIP、镜像与 S3 三条路径,并有校验和文件,工程上友好。
扣分只扣在许可上。非商业条款意味着它不能直接进商业产品,而这一点常被忽略——很多团队在模型做完之后才发现许可不合适,返工成本很高。
7.3 已知的、可枚举的偏差
选择偏差。 受试者是来自运动队与体育爱好者群体的人,能完成力竭测试本身就是一道筛选。心肺功能差、有运动禁忌的人不会出现在这里。这决定了任何从这里学到的关系,向普通人群外推时都会偏。
年龄结构偏差。 跨度写的是 10 至 63 岁,但主体是青年。用这份数据训练的模型在老年段的表现无法从数据本身评估,因为老年样本太少。
性别偏差。 女性占 15.0%,比例低。按性别分层时女性组的样本量会明显小于男性组,交叉验证的方差更大。
方案偏差。 斜坡与分级混排,若模型把方案差异学成个体差异,跨方案迁移会失效。§4.2 的案例在这里再出现一次:同一个人的三次测试若分属不同方案,直接平均会把方案效应算进"个体变化"。
时间偏差。 十年跨度里设备与人员都换过。早期与晚期测试之间的系统差异无法从字段里区分,因为没有设备版本列。
7.4 外部验证矩阵
| 验证角度 | 可用的外部锚点 | 能验证什么 | 不能验证什么 |
|---|---|---|---|
| 峰值摄氧量水平 | 运动生理教科书的人群分位 | 数值量级是否合理 | 个体准确性 |
| 通气阈位置 | 同类 CPET 研究的阈值区间 | 算法输出是否落在合理区间 | 金标准一致性 |
| 心率恢复常数 | 恢复动力学文献 | 时间常数是否同量级 | 方案差异的影响 |
| 方案结构 | 官方描述与配套论文 | 是否与描述一致 | 未记录的细节 |
| 数据完整性 | 校验和与文件行数 | 传输是否损坏 | 语义是否正确 |
| 二次使用 | 官方配套 R 仓库 | 派生指标能否复现 | 其他团队的复现结果 |
矩阵里最弱的一格是"个体准确性"。没有原始波形,没有气体分析仪的原始曲线,任何单次测试的值都无法回溯核验。这决定了它只能做群体层面的方法学验证,不能做个体层面的金标准比对。
7.5 它为何能持续:因为它填的是"原料"的空位
公开的心肺运动数据里,报告单多、原始序列少。报告单是一次性的结论,原料是可以反复加工的中间品。一个研究者想试十种通气阈算法,需要的是原料;想引用一个正常值,需要的是报告单。这份数据站在原料那一侧,而原料的复用次数远高于结论。
再加上体量与许可的组合:二十二兆、开放下载、固定 DOI。三者凑齐,它就成了一批论文的默认试验台。试验台一旦被引用几次,就会自我强化——后来者为了可比,也会选它。这就是它能持续的原因,跟数据本身有多完美关系不大。
7.6 一个可操作的取舍清单
评分是给整体看的,具体到任务还需要逐条判断。
| 你的任务 | 结论 | 理由 |
|---|---|---|
| 比较三种通气阈算法 | 用 | 三条气体序列齐备,样本量够 |
| 训练峰值摄氧量预测模型 | 用,但需留人验证 | 规模够,人群偏运动 |
| 做心率变异性研究 | 不用 | 无逐搏间期 |
| 建立普通人群正常值 | 不用 | 受试者为运动人群 |
| 验证心电图算法 | 不用 | 无波形 |
| 做运动强度处方的教学演示 | 用 | 形态与临床报告单一致 |
| 需要症状与血压反应 | 不用 | 未采集 |
| 需要同一人的纵向变化 | 有条件用 | 有重复测试,但次数不均 |
§8 生态位与历史
8.1 从实验室档案到公开数据集
2008 年到 2018 年,马拉加大学的运动生理实验室持续做着同一件事:给运动员和来体检的体育爱好者做跑步机最大测试。十年下来,档案柜里积累了近千次测试的记录。
这些记录原本只服务于两头:受试者本人拿到一份体能报告,实验室留存一份原始数据。真正让它们变成数据集的,是一个方法学问题——心率恢复该怎么算。
心率恢复是运动后心率下降的速度,常被当作简易的体能指标。但算法有很多种:取恢复第 1 分钟与峰值之差、取比值、拟合指数衰减、取不同时间窗。不同算法给出的数值不同,与体能的关联强度也不同。要回答"哪种算法更合理",需要一大批同一方案下的测试,而这批档案正好符合条件。
2021 年 4 月,数据集以 1.0.0 版上线;同年 12 月修订为 1.0.1。配套论文在同期公开,正式卷期标注为 2023 年,用的样本是 980 次测试——与数据集标注的 992 次之间存在十二次的差额,官方未说明差额构成。这个口径差不是错误,而是"数据集版本"与"论文分析集"两个口径并存,引用时说明清楚即可。
8.2 为什么是马拉加
安达卢西亚的这座城市的运动医学传统不短。大学的运动生理实验室与当地的运动医学中心、自行车训练中心长期合作,受试者来源稳定——既有职业与业余运动员,也有来做体能评估的普通人。
这种"半个临床、半个运动队"的位置,决定了数据的双重性格:流程上有医学监督与伦理审查,指标上有运动表现导向。纯临床实验室的数据往往缺少最大强度端点(不会让患者跑到力竭),纯运动队的数据往往缺少规范记录(教练记的是成绩,不是摄氧量)。两者之间这个位置,恰好稀缺。
8.3 与公开 CPET 数据的比较
| 资源 | 规模 | 强度端点 | 信号形态 | 主要用途 |
|---|---|---|---|---|
| 本篇 | 近千次测试 | 到力竭 | 逐口气数值 | 阈值算法、峰值预测 |
| 典型临床 CPET 登记 | 数百至数千例 | 症状限制 | 报告级指标 | 预后模型 |
| 单中心运动队档案 | 数十人 | 到力竭 | 报告加波形 | 训练监控 |
| 可穿戴运动队列 | 数千人 | 次最大 | 加速度与脉搏 | 活动量与风险 |
表格里的关键区别不在规模,在"强度端点"与"信号形态"两列。到力竭且保留逐口气序列的组合很少见,这也是它在方法学论文里被反复选中的原因。
8.4 引用生态与二次使用
官方在数据集页面上挂了配套代码仓库,用 R 实现心率恢复与若干心肺指标的计算。这个安排值得注意:它把"数据"和"算法"绑在一起发布,使用者拿到的不只是一堆数,还有一份参考实现。
二次使用的方向大致三类。一是方法学比较,用同一批数据跑不同算法,看结论是否稳健。二是预测建模,用次最大段的数据预测峰值,做运动试验的"降负荷"版本。三是教学,用它演示通气阈的图形判读,因为数据形态与临床报告单高度一致。
三类用途对数据的要求不同。方法学比较最在意方案一致性,预测建模最在意样本量与人群多样性,教学最在意个案的可读性。同一份数据能同时满足三者,靠的是规模——规模够大,切片之后每一片还剩下可用的量。
8.5 为什么只有一个后续版本
1.0.0 到 1.0.1 只隔了七个月,之后没有新版本。这种"发布即冻结"的维护模式在 PhysioNet 上很常见,原因不复杂。
一是数据本身是档案,不是持续采集的队列。实验室的采集在 2018 年就停了,档案不会再增长。二是修订的驱动力通常来自发现的错误或字段规范化,而不是新增内容。三是冻结对引用友好:一个不再变化的固定版本,才适合作为可比性的基准。
| 维护模式 | 优点 | 缺点 |
|---|---|---|
| 发布即冻结 | 引用稳定,校验和长期有效 | 已知问题不会修 |
| 持续增补 | 规模增长,覆盖更广 | 版本漂移,复现需绑定版本 |
| 社区修订 | 错误修正快 | 需要维护者投入 |
这份数据选了第一种。对使用者意味着两件事:可以放心把版本号写进论文;同时不要期待某天会补上波形或级别标记——那需要重新采集,不是修订能解决的。
§9 使用指引
9.1 三种角色的路径
| 角色 | 起点 | 关键动作 | 常见失误 |
|---|---|---|---|
| 方法学研究者 | 配套论文与 R 仓库 | 固定恢复段起点规则,统一个体聚合口径 | 直接把逐口气行当独立样本做统计检验 |
| 建模工程师 | test_measure 全表 | 按 ID 分组做留人交叉验证 | 同一人的多次测试同时进训练与验证集 |
| 教学使用者 | 单次测试的速度与摄氧量曲线 | 选气体完整、台阶清晰的个案 | 用缺气体数据的测试作图 |
三种路径的共同前提是先把测试级与受试者级分开。行级数据看起来很多,但独立观测的单位是人,不是行。把五十七万行当五十七万个样本,是所有统计推断出错的根源。
9.2 一条最短的可用流程
从下载到算出一次测试的峰值与心率恢复,四步。这段代码同时验证 §4.2 的案例是否读得通。
import pandas as pd
base = "treadmill/1.0.1"
subj = pd.read_csv(f"{base}/subject-info.csv", dtype={"ID_test": "str"})
meas = pd.read_csv(f"{base}/test_measure.csv", dtype={"ID_test": "str"})
m = meas.merge(subj[["ID_test", "Weight"]], on="ID_test", how="left", validate="many_to_one")
g = m[m["ID_test"] == "245_3"].sort_values("time").reset_index(drop=True)
g["VO2_rel"] = g["VO2"] / g["Weight"]
#-- 运动终点:速度最后一次高于 5 的行
end = g.index[g["Speed"] > 5.0].max()
peak = g.loc[max(0, end - 30): end, "VO2_rel"].mean()
#-- 恢复段:终点之后,按秒取第 60、120、180 秒
rec = g.loc[end:].set_index("time")["HR"]
hr_end = rec.iloc[0]
hrr = {s: hr_end - rec.asof(rec.index[0] + s) for s in (60, 120, 180)}
print(round(peak, 1), hrr)
三个细节决定这段代码是否正确。其一,终点用"速度最后一次高于 5"来定,而不是用最大速度所在行,因为最大速度之后还有一段同速的平台。其二,峰值取终点前三十秒的均值,不是全世界最大值。其三,恢复段的心率用 asof 按时间取值,因为逐口气序列的时间戳不均匀,按行偏移会错位。
9.3 引用方式
引用数据集本身,用固定版本 DOI,并写明版本号。同时引用配套论文,因为方法学细节在论文里而不在数据集页。
| 引用对象 | 内容 | 用途 |
|---|---|---|
| 数据集 | Mongin, García Romero, Alvero Cruz,2021,版本 1.0.1,PhysioNet,DOI 10.13026/7ezk-j442 | 数据来源声明 |
| 配套论文 | Mongin 等,心率恢复评估体能的计算方法比较,运动科学研究,2021 | 方法学依据 |
| 托管平台 | PhysioNet,RRID SCR_007345 | 平台标识 |
| 许可 | Contributor Review Health Data License 1.5.0 | 使用条件 |
写方法部分时,至少要交代三件事:用的是哪个版本、峰值取的是单点还是窗口均值、恢复段起点怎么定的。这三件事不写,别人无法复现你的数。
9.4 一个项目的四个阶段
真正用起来时,顺序比工具重要。
| 阶段 | 产物 | 检查点 |
|---|---|---|
| 摸清结构 | 两表连接成功,字段含义确认 | 行数、键唯一性、空值位置 |
| 定义口径 | 峰值窗口、恢复起点、级别切分规则写死 | 同一规则在同一测试上可重复 |
| 建立基线 | 一次最简单的复现(峰值加恢复值) | 结果落在生理合理区间 |
| 扩展分析 | 按任务加模型或按亚组加对比 | 留人验证,不按行划分 |
四个阶段里最容易跳过的是第二个。"口径"听起来抽象,其实就是几个数字:窗口取三十秒还是十五秒,恢复起点用速度还是用心率拐点,级别切分容差取多少。这些数字不先定死,后面每换一次数据就要重来一遍,而且两次结果不可比。
第三个阶段的价值在于早期发现错误。如果算出来的峰值只有十几毫升每公斤每分,或者恢复值出现负数,说明前面的口径有问题,此时返工成本最低。
§10 AI 使用声明
| 项目 | 声明 |
|---|---|
| 数据性质 | 人体运动生理测量,非临床诊疗记录,不含影像与文本 |
| 是否含个人身份信息 | 已重编码为整数与拼接标识,未见姓名、病历号、日期 |
| 可用于训练的数据形态 | 逐口气数值序列与测试级元数据 |
| 不建议的用途 | 个体诊断、健康风险预测、以人群常模名义发布参考值 |
| 模型输出的人审要求 | 任何面向个体的结论须由运动医学或临床专业人员复核 |
| 已知的群体偏差 | 运动员为主、青年为主、女性占比低 |
| 再分发条件 | 遵循许可的署名、非共享方式、非商业三条 |
| 声明适用范围 | 仅覆盖本页描述的数据内容,不覆盖第三方派生数据 |
常见误读(避坑清单)
| 常见说法 | 实际 | 证据 |
|---|---|---|
| 这是心电图数据集 | 只有心率数值,无波形 | 文件清单只有两个 CSV,字段表无导联列 |
| 用的是 Bruce 方案 | 5 km/h 热身加自定斜坡或分级 | 官方 Methods 未出现 Bruce |
| 992 就是受试者人数 | 992 是测试次数,人数为 857 | 信息表两列取值数不同 |
| 有 992 人的峰值摄氧量 | 其中 30 次缺气体数据 | 官方明示 30 次缺 VO2、VCO2、VE |
| 采样率是固定赫兹 | 逐口气触发,时间间隔不等 | 时间列相邻差值不恒定 |
| RR 列是 R 波间期 | 是呼吸频率,单位次每分 | 字段表写明 respiratory rate |
| 恢复期是静坐 | 是 5 km/h 步行 | 官方写明降速防晕厥 |
| 可以直接当正常值参考 | 受试者为运动人群,峰值偏高 | 官方称业余与职业运动员 |
| 行数就是样本量 | 独立观测单位是人不是行 | 同一人多次测试,行内高度相关 |
| 数据十年一致 | 设备与人员有更替,方案不统一 | 采集跨度为 2008 至 2018 |
FAQ
Q1:这份数据能不能做心率变异性分析?
不能。心率变异性需要逐搏的 R 波间期,这里只有逐口气的平均心率。呼吸频率那一列虽然写作 RR,含义完全不同。想做频域指标,得换用带逐搏记录的数据。
Q2:为什么同一份数据有两个 DOI?
一个是固定版本 1.0.1 的,另一个指向"始终最新"。前者适合引用与复现,后者适合总是想要最新内容的场景。写论文用前者,做长期维护的管道可以追踪后者。
Q3:30 次缺气体数据的测试该删还是留?
按分析目标决定。做代谢相关分析时标记后排除,做心率或速度分析时保留。不建议物理删除,因为信息表里的年龄、体重等字段仍然可用,删了就一起丢了。
Q4:能不能用它训练一个从可穿戴心率预测摄氧量的模型?
可以,但要注意标签是代谢车测的峰值,而输入若只有心率,个体差异会主导结果。更稳的做法是把年龄、体重、性别一起放进特征,并做留人交叉验证。
Q5:为什么官方说"到力竭之外"?
这是口语化的表述,指要求受试者尽力超过自己以为的极限。判定仍以摄氧量平台为准,不是靠受试者主观报告,因此不必按字面理解为危险操作。
Q6:速度列能不能直接当强度指标?
只能在同一坡度下用。数据里没有坡度列,若部分测试用了坡度递增,速度与代谢的关系就会偏离。做速度-摄氧量映射前,先检查速度与摄氧量的关系是否线性。
Q7:许可允许我把它放进商业产品做验证吗?
许可正文含非商业条款,商用需另行确认。即便只是内部验证,若最终服务于商业产品,也建议先与发布方沟通,不要默认"没卖数据就不算商用"。
Q8:为什么推荐取最后三十秒均值而不是最大值?
逐口气序列的噪声大,单点最大值容易落在异常呼吸上。三十秒均值把窗口内的多次呼吸平均掉,稳定性更好。窗口长度本身会影响结果,因此必须在方法里写明用了多少秒。
Q9:能否据此判断某人的体能等级?
单次测试的峰值可以参考教科书的分位,但这份数据的人群偏向运动员,且没有随访与临床终点。判断个体等级需要同龄同性别同项目的参照,这份数据给不了那么细的分层。
Q10:这套数据适合教学吗?
适合。体量小、结构简单、图形形态与临床报告单一致,学生可以在一节课内完成从读表到画通气阈曲线的全过程。选个案时优先挑气体数据完整的测试。
Q11:如果官方以后发布新版本,我的结果会失效吗?
不会,只要你引用了固定版本 DOI 并保存了校验和。新版本可能修订字段或补充测试,但固定版本的 DOI 指向的内容不变。这也是不要用"最新" DOI 做论文引用的原因。
术语表
| 术语 | 英文 | 在本页的含义 |
|---|---|---|
| 心肺运动试验 | CPET | 同步测量气体交换与心电的负荷试验 |
| 逐口气 | breath-by-breath | 每一次呼吸输出一组值,非固定频率采样 |
| 摄氧量 | VO2 | 单位时间摄入并利用的氧量,毫升每分 |
| 二氧化碳排出量 | VCO2 | 单位时间排出的二氧化碳量,毫升每分 |
| 呼吸交换率 | RER | 二氧化碳排出量与摄氧量之比 |
| 肺通气量 | VE | 单位时间进出肺的气量,升每分 |
| 呼吸频率 | RR | 每分钟呼吸次数,此处非 R 波间期 |
| 通气当量 | VE/VO2 | 每摄取一升氧需要通气多少升 |
| 峰值摄氧量 | VO2peak | 测试中达到的最高摄氧量,按窗口取均值 |
| 最大摄氧量 | VO2max | 满足平台判据的峰值,判据各家不同 |
| 通气阈 | VT | 通气开始非线性上升的强度点 |
| 呼吸代偿点 | RCP | 通气相对二氧化碳排出急剧上升的点 |
| 代谢当量 | MET | 以 3.5 毫升每公斤每分为一单位的强度表示 |
| 氧亏 | oxygen deficit | 运动开始阶段摄氧量滞后于需求的部分 |
| 心率恢复 | HRR | 运动停止后心率下降的幅度或速度 |
| 斜坡方案 | ramp protocol | 强度随时间连续上升的方案 |
| 分级方案 | staged protocol | 强度按级递增、每级维持一段的方案 |
| 力竭 | exhaustion | 无法维持既定强度的状态 |
| 平台现象 | VO2 plateau | 强度继续上升而摄氧量不再上升 |
| 血管迷走性晕厥 | vasovagal syncope | 运动骤停后血压下落导致的短暂意识丧失 |
| 代谢车 | metabolic cart | 测量气体交换的台式系统 |
| 跑台 | treadmill | 可调速度与坡度的跑步设备 |
| 长表 | long format | 一行一次观测、用标识列分组的表结构 |
| 宽表 | wide format | 一行一个实体、各列为其属性的表结构 |
| 留人交叉验证 | leave-subject-out | 按受试者划分训练与验证集 |
附录 A:核心数字核对表
| 项 | 值 | 来源 | 来源层级 |
|---|---|---|---|
| 测试次数 | 992 | 官方数据集页 | 官方页面 |
| 受试者人数 | 857 | 官方数据集页 | 官方页面 |
| 测量表行数 | 575,087 | 官方文件表 | 官方页面 |
| 未压缩体积 | 22.1 MB | 官方数据集页 | 官方页面 |
| 压缩包体积 | 6.3 MB | 官方下载页 | 官方页面 |
| 采集年份 | 2008 至 2018 | 官方方法描述 | 官方页面 |
| 年龄范围 | 10 至 63 岁 | 官方数据集页 | 官方页面 |
| 女性占比 | 15.0% | 官方数据集页 | 官方页面 |
| 缺气体数据的测试 | 30 次 | 官方数据集页 | 官方页面 |
| 热身速度 | 5 km/h | 官方方法描述 | 官方页面 |
| 单级增幅 | 0.5 至 1 km/h | 官方方法描述 | 官方页面 |
| 发布版本 | 1.0.1 | 官方版本记录 | 官方页面 |
| 论文分析样本 | 980 次 | 配套论文摘要 | 论文摘要 |
附录 B:数字的来源分层
| 层级 | 内容 | 处理方式 |
|---|---|---|
| 官方页面 | 规模、字段、方案、设备、许可、版本 | 直接采信,注明出处 |
| 论文摘要 | 980 次分析样本、算法比较结论 | 作为第二口径并列,不裁断 |
| 未核实 | 平台判据的具体窗口、坡度是否使用、Sex 其余编码 | 正文标注"未说明",不做推断 |
三层的用法不同。官方页面是骨架,论文摘要是旁证,未核实项是边界声明。把未核实项写成确定值,是这类词条最常见的失真来源。
附录 C:完整参考文献
| 序号 | 文献 | 用途 |
|---|---|---|
| 1 | Mongin D, García Romero J, Alvero Cruz JR. Treadmill Maximal Exercise Tests from the Exercise Physiology and Human Performance Lab of the University of Malaga, version 1.0.1. PhysioNet, 2021. DOI 10.13026/7ezk-j442 | 数据集本体 |
| 2 | Mongin D, Chabert C, Courvoisier DS, García-Romero J, Alvero-Cruz JR. Heart rate recovery to assess fitness: comparison of different calculation methods in a large cross-sectional study. Research in Sports Medicine, 2023(2021 年在线先发). DOI 10.1080/15438627.2021.1954513 | 方法学与样本口径 |
| 3 | PhysioNet. Contributor Review Health Data License 1.5.0 与配套使用协议 | 许可条件 |
| 4 | 官方配套代码仓库 dmongin/hrr_comparison(R 语言) | 派生指标参考实现 |
附录 D:恢复期心率指标的计算脚本
import pandas as pd
def hrr_table(csv_path, info_path):
subj = pd.read_csv(info_path, dtype={"ID_test": "str"})
meas = pd.read_csv(csv_path, dtype={"ID_test": "str"})
out = []
for tid, g in meas.groupby("ID_test", sort=False):
g = g.sort_values("time")
moving = g.index[g["Speed"] > 5.0]
if len(moving) == 0:
continue
#-- 以最后一次速度高于热身值的行作为运动终点
end = g.index.get_loc(moving[-1])
rec = g.iloc[end:].set_index("time")["HR"]
if len(rec) < 3:
continue
hr0 = rec.iloc[0]
row = {"ID_test": tid, "HR_end": hr0}
for s in (60, 120, 180):
row[f"HRR{s}"] = hr0 - rec.asof(rec.index[0] + s)
out.append(row)
return pd.DataFrame(out)
df = hrr_table("treadmill/1.0.1/test_measure.csv", "treadmill/1.0.1/subject-info.csv")
print(df.dropna().head())
print(df["HRR60"].describe())
脚本里的 asof 是关键。逐口气记录的时间戳不均匀,rec.iloc[60] 取的是第六十行而不是第六十秒,两者能差出半分钟,算出来的恢复值会系统性偏移。用时间索引取最近值,才能让不同测试之间可比。
附录 E:如果只记五件事
| 序号 | 要点 | 说明 |
|---|---|---|
| 1 | 它是代谢数据,不是心电数据 | 只有心率数值,没有波形 |
| 2 | 规模按测试计,不按人计 | 992 次测试,857 人,同一人多测 |
| 3 | 方案自定,不是 Bruce | 5 km/h 热身,之后斜坡或 0.5 至 1 km/h 分级 |
| 4 | 人群偏运动员,峰值偏高 | 不能当正常值参考 |
| 5 | 缺级别、缺停止时刻、缺 30 次气体数据 | 三处都要自己补规则 |
编辑说明
本页的核查方式:逐个访问 PhysioNet 项目主页、1.0.1 版本页、许可文件与欧洲 PMC 的论文摘要,未下载数据包逐行统计。凡涉及分布、分位数、异常值位置的内容一律不写,属于使用者拿到数据后应自行完成的那一步。
核查中发现两处口径差,均已在正文并列而非裁断:一是数据集标注 992 次测试、配套论文使用 980 次,差额构成官方未说明;二是访问条件页写"遵守指定许可即可访问",而许可名称与正文条款分属两套文本(Contributor Review 1.5.0 与 CC BY-NC-SA 4.0),二者关系以许可文件正文为准。
未核实项:平台判据所用的时间窗、是否部分测试使用坡度、Sex 列除 1 以外的编码含义、设备更换的具体年份。这四项在正文均以"未说明"标注,未做推断。
本页写作时间 2026 年 10 月 28 日,对应版本 1.0.1。
相关数据集导航
以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:
- hd-semg — 共享标签:生理信号 / 步态与运动 / 评测基准
- minute-level-step-count-nhanes — 共享标签:生理信号 / 步态与运动 / 公共卫生与流行病学
- sufhsdb — 共享标签:生理信号 / 公共卫生与流行病学
- kinecal — 共享标签:生理信号 / 步态与运动 / 评测基准
- actes-cycloergometer-exercise — 共享标签:生理信号 / 步态与运动 / 评测基准
- accelerometry-walk-climb-drive — 共享标签:生理信号 / 步态与运动 / 评测基准
- gaitpdb — 共享标签:生理信号 / 步态与运动 / 评测基准
- scientisst-move — 共享标签:生理信号 / 步态与运动 / 评测基准
- grabmyo — 共享标签:生理信号 / 步态与运动 / 评测基准
- sisfall — 共享标签:生理信号 / 步态与运动 / 评测基准
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

