本文目录导读:

这是一个关于使用Python统计和分析慢跑恢复时间数据(即运动后心率、配速等指标恢复到正常水平所需的时间)的案例,这类数据通常来自运动手表或心率带,可以用来评估训练强度、疲劳程度以及有氧基础。
以下是一个完整的Python案例分析,包含数据模拟、预处理、指标计算、可视化和统计报告。
场景设定
你有一份来自智能手表的数据,包含每次慢跑的时间戳、心率(HR)、配速(Pace),我们想统计运动结束后,心率恢复到静息心率+20%以内所需的时间。
完整代码实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
# -------------------- 1. 模拟数据(替换为你的实际数据) --------------------
np.random.seed(42)
num_points = 1000
time_range = pd.date_range(start='2023-10-01 06:00:00', periods=num_points, freq='1s')
# 模拟跑步活动:前400秒跑步,之后恢复
heart_rate = []
pace = []
for t in range(num_points):
if t < 400: # 跑步阶段
hr_base = 140 + 20 * np.sin(t / 50) + np.random.normal(0, 3)
pace_base = 5.5 + 0.5 * np.sin(t / 30) + np.random.normal(0, 0.2)
else: # 恢复阶段
# 指数衰减恢复模拟
hr_base = 120 * np.exp(- (t - 400) / 200) + 65 + np.random.normal(0, 2)
pace_base = 7.0 + np.random.normal(0.5, 0.3) # 恢复后慢走
heart_rate.append(max(60, min(190, hr_base)))
pace.append(max(4.0, min(10.0, pace_base)))
# 构建DataFrame
df = pd.DataFrame({
'timestamp': time_range,
'heart_rate': heart_rate,
'pace_min_per_km': pace
})
# 设定静息心率为65 bpm,恢复标准为静息心率+20%(即78 bpm)
resting_hr = 65
recovery_threshold = resting_hr * 1.20 # 78
print("原始数据预览(前10行):")
print(df.head())
print(f"\n数据形状: {df.shape}")
print(f"恢复阈值: {recovery_threshold:.1f} bpm")
# -------------------- 2. 数据预处理与活动分段 --------------------
# 假设跑步开始时间就是数据开始时间,这里简单识别活动结束点
# 在实际中,你需要gps或加速度计检测运动状态,这里简化:
# 认为配速突降的时刻为跑步结束
pace_diff = df['pace_min_per_km'].diff()
# 当配速突然变慢超过0.5 min/km时,认为是运动结束点
end_of_run_idx = df[pace_diff > 0.5].index[0] if any(pace_diff > 0.5) else 400
print(f"\n检测到的跑步结束索引: {end_of_run_idx}(对应时间: {df.loc[end_of_run_idx, 'timestamp']})")
# 截取恢复期数据(从运动结束到数据末尾)
recovery_data = df.iloc[end_of_run_idx:].copy()
recovery_data['time_from_end'] = (recovery_data['timestamp'] - recovery_data['timestamp'].iloc[0]).dt.total_seconds()
# -------------------- 3. 计算恢复时间 --------------------
# 恢复时间 = 心率首次降到阈值以下的时间点
recovery_seconds = None
for i, row in recovery_data.iterrows():
if row['heart_rate'] <= recovery_threshold:
recovery_idx = i
recovery_seconds = row['time_from_end']
print(f"\n心率于 {recovery_seconds:.0f} 秒后降至阈值以下")
print(f"第{recovery_idx}行数据: 心率={row['heart_rate']:.1f}, 时间戳={row['timestamp']}")
break
if recovery_seconds is None:
print("警告:在数据范围内心率未能恢复到阈值以下")
# 更精确的计算(线性插值)
# 找到心率穿越阈值的精确时刻
from scipy.interpolate import interp1d
if recovery_seconds is not None:
# 取阈值附近的两个点进行线性插值
cross_idx = recovery_data[recovery_data['heart_rate'] <= recovery_threshold].index[0]
prev_idx = recovery_data.index.get_loc(cross_idx) - 1
prev_row = recovery_data.iloc[prev_idx]
cross_row = recovery_data.loc[cross_idx]
# 线性插值
t_prev, t_cross = prev_row['time_from_end'], cross_row['time_from_end']
hr_prev, hr_cross = prev_row['heart_rate'], cross_row['heart_rate']
if hr_prev != hr_cross:
t_interp = t_prev + (recovery_threshold - hr_prev) * (t_cross - t_prev) / (hr_cross - hr_prev)
else:
t_interp = t_prev
print(f"\n精确恢复时间(线性插值): {t_interp:.1f} 秒 ({t_interp/60:.2f} 分钟)")
# -------------------- 4. 统计指标计算 --------------------
print("\n--------------- 统计报告 ---------------")
print(f"跑步阶段最大心率: {df.iloc[:end_of_run_idx]['heart_rate'].max():.1f} bpm")
print(f"跑步阶段平均心率: {df.iloc[:end_of_run_idx]['heart_rate'].mean():.1f} bpm")
print(f"恢复阶段平均心率: {recovery_data['heart_rate'].mean():.1f} bpm")
print(f"恢复至阈值心率所需时间: {recovery_seconds:.0f} 秒 ({recovery_seconds/60:.1f} 分钟)" if recovery_seconds else "未恢复")
# 计算心率恢复率(%HRR恢复情况)
peak_hr = df.iloc[:end_of_run_idx]['heart_rate'].max()
hr_reserve = peak_hr - resting_hr
hr_60s = recovery_data[recovery_data['time_from_end'] <= 60]['heart_rate'].iloc[-1] if len(recovery_data) > 60 else recovery_data['heart_rate'].iloc[-1]
hrr_60s = (peak_hr - hr_60s) / hr_reserve * 100
print(f"1分钟心率恢复率: {hrr_60s:.1f}% HRR")
# 心率恢复斜率(前120秒的线性回归斜率)
from scipy import stats
early_recovery = recovery_data[recovery_data['time_from_end'] <= 120]
if len(early_recovery) > 2:
slope, intercept, r_value, p_value, std_err = stats.linregress(early_recovery['time_from_end'], early_recovery['heart_rate'])
print(f"前120秒心率下降斜率: {slope:.3f} bpm/秒 ({slope*60:.2f} bpm/分钟)")
# -------------------- 5. 可视化分析 --------------------
fig, axes = plt.subplots(3, 1, figsize=(12, 9), sharex=True)
# 子图1: 心率曲线
axes[0].plot(df['timestamp'], df['heart_rate'], 'b-', alpha=0.7, label='心率')
axes[0].axhline(y=recovery_threshold, color='r', linestyle='--', label=f'恢复阈值 ({recovery_threshold:.0f} bpm)')
axes[0].axvline(x=df.loc[end_of_run_idx, 'timestamp'], color='gray', linestyle=':', label='运动结束')
if recovery_seconds:
axes[0].axvline(x=df.loc[cross_idx, 'timestamp'], color='green', linestyle='-.', label=f'恢复点 ({t_interp:.0f}s)')
axes[0].set_ylabel('心率 (bpm)')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
axes[0].set_title('慢跑心率恢复曲线')
# 子图2: 配速曲线
axes[1].plot(df['timestamp'], df['pace_min_per_km'], 'g-', alpha=0.7)
axes[1].axvline(x=df.loc[end_of_run_idx, 'timestamp'], color='gray', linestyle=':')
axes[1].set_ylabel('配速 (min/km)')
axes[1].invert_yaxis() # 配速越小越快
axes[1].set_title('配速变化')
axes[1].grid(True, alpha=0.3)
# 子图3: 恢复期心率的指数拟合
axes[2].plot(recovery_data['time_from_end'], recovery_data['heart_rate'], 'b.', alpha=0.5, markersize=2)
axes[2].axhline(y=recovery_threshold, color='r', linestyle='--')
axes[2].axvline(x=t_interp if recovery_seconds else 0, color='green', linestyle='-.', label=f'恢复点 ({t_interp:.0f}s)' if recovery_seconds else '')
axes[2].set_xlabel('恢复时间 (秒)')
axes[2].set_ylabel('心率 (bpm)')
axes[2].set_title(f'恢复期心率衰减 (红色虚线: 阈值{recovery_threshold:.0f} bpm)')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# -------------------- 6. 多日趋势分析(示例) --------------------
# 假设你有5天的数据
print("\n--------------- 多日趋势(模拟) ---------------")
days = 5
daily_recovery_times = [120 + np.random.normal(0, 20) for _ in range(days)] # 秒
dates = pd.date_range('2023-10-01', periods=days)
plt.figure(figsize=(8, 4))
plt.plot(dates, daily_recovery_times, 'bo-', linewidth=2)
plt.xlabel('日期')
plt.ylabel('恢复时间 (秒)')'每日恢复时间趋势')
plt.grid(True, alpha=0.3)
plt.show()
print(f"平均恢复时间: {np.mean(daily_recovery_times):.0f} 秒 ({np.mean(daily_recovery_times)/60:.1f} 分钟)")
print(f"恢复时间变异系数: {np.std(daily_recovery_times)/np.mean(daily_recovery_times)*100:.1f}%")
关键输出解读
运行上述代码后,你将得到:
- 恢复时间:精确到0.1秒的恢复时长。
- 心率恢复率 (HRR):通常1分钟下降越多,心肺功能越好(健康标准>12bpm)。
- 恢复斜率:>0.5 bpm/秒通常表示恢复良好。
- 可视化图表:直观看到心率何时穿越阈值。
如何应用到真实数据?
- 替换数据源:将
df = pd.read_csv('your_watch_data.csv')放在代码开头。 - 数据清洗:处理缺失值、异常心率(如>220或<30)、运动起止点检测(通常使用GPS速度或加速度计)。
- 个性化阈值:将
resting_hr改为你实际的静息心率。
扩展建议
- 与训练负荷关联:计算恢复时间与训练时长、平均心率的相关系数。
- 疲劳指数:如果恢复时间连续几天显著增加(比如增加20%以上),可能表示过度训练。
- 自动化报告:使用
schedule库每天定时运行,发送邮件通知或生成PDF报告。
这个案例涵盖了数据科学在运动医学中的一个典型应用——从原始时间序列中提取有临床意义的指标。