Python时间序列预测:识别运动平台期
概述
运动平台期是健身过程中常见的现象——无论如何努力训练,体重、力量或耐力指标都无法继续提升。通过时间序列分析,我们可以提前预测平台期的到来,并及时调整训练计划。
本文将介绍如何使用Python的Prophet库进行运动数据的时间序列预测,帮助你:
- 识别当前是否处于平台期
- 预测未来几周的运动表现
- 生成个性化的预警信号
数据准备
1. 收集运动数据
code
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 模拟运动数据(实际应用中从健身API获取)
def generate_workout_data(days=90):
"""生成模拟的运动数据"""
dates = pd.date_range(start='2026-01-01', periods=days)
# 基础体重 + 趋势 + 周期性 + 噪声
base_weight = 75
trend = np.linspace(0, -3, days) # 90天减重3kg
weekly_pattern = 0.5 * np.sin(2 * np.pi * np.arange(days) / 7)
noise = np.random.normal(0, 0.2, days)
weight = base_weight + trend + weekly_pattern + noise
df = pd.DataFrame({
'ds': dates,
'y': weight
})
return df
# 生成数据
workout_data = generate_workout_data(90)
print(workout_data.head())
Code collapsed
2. 处理真实数据
code
# 从Apple Health或Google Fit导入数据
import json
def import_health_data(file_path):
"""从健康应用导出的JSON文件导入数据"""
with open(file_path, 'r') as f:
data = json.load(f)
records = []
for entry in data.get('records', []):
if entry.get('type') == 'weight':
records.append({
'ds': pd.to_datetime(entry['date']),
'y': float(entry['value'])
})
return pd.DataFrame(records).sort_values('ds')
Code collapsed
Prophet模型训练
基础预测
code
from prophet import Prophet
import matplotlib.pyplot as plt
# 创建Prophet模型
model = Prophet(
yearly_seasonality=False, # 数据少于一年
weekly_seasonality=True, # 每周周期(训练/休息)
daily_seasonality=False,
changepoint_prior_scale=0.05, # 控制趋势灵活性
seasonality_prior_scale=10.0 # 季节性强度
)
# 训练模型
model.fit(workout_data)
# 创建未来30天的预测
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
# 可视化预测
fig1 = model.plot(forecast)
plt.title('运动表现预测(体重变化)')
plt.xlabel('日期')
plt.ylabel('体重 (kg)')
plt.show()
Code collapsed
添加自定义季节性
code
# 添加月度周期(女性生理周期影响)
model.add_seasonality(
name='monthly',
period=30.5,
fourier_order=5
)
# 添加训练日/休息日影响
workout_data['is_training_day'] = workout_data['ds'].dt.dayofweek < 6 # 周一到周六
model.add_regressor('is_training_day')
# 重新训练
model.fit(workout_data)
forecast = model.predict(future)
Code collapsed
平台期检测算法
1. 趋势斜率分析
code
def detect_plateau_by_slope(forecast, window_days=14, threshold=0.01):
"""
通过趋势斜率检测平台期
参数:
forecast: Prophet预测结果
window_days: 检测窗口(天)
threshold: 斜率阈值
返回:
plateau_start: 平台期开始日期
is_plateau: 是否处于平台期
"""
# 获取最近window_days的预测趋势
recent_forecast = forecast.tail(window_days)
# 计算线性回归斜率
x = np.arange(len(recent_forecast))
y = recent_forecast['trend'].values
slope = np.polyfit(x, y, 1)[0]
# 判断是否平台期
is_plateau = abs(slope) < threshold
if is_plateau:
# 找到平台期开始点
trend_changes = forecast[forecast['trend'].diff().abs() > threshold]
if len(trend_changes) > 0:
plateau_start = trend_changes.iloc[-1]['ds']
else:
plateau_start = forecast.iloc[0]['ds']
else:
plateau_start = None
return {
'is_plateau': is_plateau,
'slope': slope,
'plateau_start': plateau_start,
'days_in_plateau': window_days if is_plateau else 0
}
# 检测平台期
plateau_info = detect_plateau_by_slope(forecast)
print(f"是否处于平台期: {plateau_info['is_plateau']}")
print(f"当前斜率: {plateau_info['slope']:.4f} kg/天")
Code collapsed
2. 置信区间分析
code
def detect_plateau_by_uncertainty(forecast, confidence_threshold=0.5):
"""
通过预测不确定性检测平台期
当预测的置信区间变宽时,说明模型无法确定趋势
这通常是平台期的信号
"""
recent = forecast.tail(14)
# 计算置信区间宽度
uncertainty = recent['yhat_upper'] - recent['yhat_lower']
avg_uncertainty = uncertainty.mean()
# 与历史不确定性比较
historical_uncertainty = (
forecast['yhat_upper'] - forecast['yhat_lower']
).mean()
# 不确定性显著增加 = 平台期
is_plateau = avg_uncertainty > 1.5 * historical_uncertainty
return {
'is_plateau': is_plateau,
'uncertainty_ratio': avg_uncertainty / historical_uncertainty,
'avg_uncertainty': avg_uncertainty
}
Code collapsed
预警系统
综合预警评分
code
class WorkoutPlateauDetector:
def __init__(self, forecast):
self.forecast = forecast
self.indicators = {}
def calculate_plateau_score(self):
"""计算平台期综合评分 (0-100)"""
scores = {}
# 1. 趋势斜率评分
slope_info = detect_plateau_by_slope(self.forecast)
slope_score = min(100, abs(slope_info['slope']) * 1000)
scores['trend_slope'] = 100 - slope_score
self.indicators['trend_slope'] = slope_info['slope']
# 2. 不确定性评分
uncertainty_info = detect_plateau_by_uncertainty(self.forecast)
uncertainty_score = min(100, uncertainty_info['uncertainty_ratio'] * 50)
scores['uncertainty'] = uncertainty_score
self.indicators['uncertainty_ratio'] = uncertainty_info['uncertainty_ratio']
# 3. 实际值vs预测值偏差
recent_actual = self.forecast['yhat'].tail(14).values
recent_predicted = self.forecast['yhat'].tail(14).values
mae = np.mean(np.abs(recent_actual - recent_predicted))
deviation_score = min(100, mae * 100)
scores['deviation'] = deviation_score
self.indicators['mae'] = mae
# 综合评分
plateau_score = np.mean(list(scores.values()))
return {
'plateau_score': plateau_score,
'level': self._get_plateau_level(plateau_score),
'indicators': self.indicators,
'recommendations': self._get_recommendations(plateau_score)
}
def _get_plateau_level(self, score):
if score < 30:
return '正常进步期'
elif score < 50:
return '进步放缓'
elif score < 70:
return '平台期预警'
else:
return '平台期'
def _get_recommendations(self, score):
if score < 30:
return ['保持当前训练计划', '继续监测数据']
elif score < 50:
return ['考虑增加训练强度', '调整饮食结构']
elif score < 70:
return ['建议引入新的训练方式', '检查恢复是否充分']
else:
return ['强烈建议更换训练计划', '考虑安排减周', '咨询专业教练']
# 使用预警系统
detector = WorkoutPlateauDetector(forecast)
result = detector.calculate_plateau_score()
print(f"平台期评分: {result['plateau_score']:.1f}/100")
print(f"当前状态: {result['level']}")
print(f"\n建议:")
for rec in result['recommendations']:
print(f" - {rec}")
Code collapsed
可视化仪表板
Plotly交互式图表
code
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def create_workout_dashboard(forecast, actual_data, plateau_info):
"""创建运动数据仪表板"""
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('体重趋势预测', '预测组件分解',
'平台期检测', '每周模式'),
specs=[[{"secondary_y": True}, {"secondary_y": True}],
[{"type": "indicator"} for _ in range(2)]]
)
# 1. 主趋势图
fig.add_trace(
go.Scatter(x=actual_data['ds'], y=actual_data['y'],
mode='markers', name='实际数据', marker=dict(size=4)),
row=1, col=1
)
fig.add_trace(
go.Scatter(x=forecast['ds'], y=forecast['yhat'],
mode='lines', name='预测', line=dict(color='blue')),
row=1, col=1
)
fig.add_trace(
go.Scatter(x=forecast['ds'], y=forecast['yhat_upper'],
mode='lines', line=dict(width=0), showlegend=False),
row=1, col=1
)
fig.add_trace(
go.Scatter(x=forecast['ds'], y=forecast['yhat_lower'],
mode='lines', line=dict(width=0), fill='tonexty',
name='置信区间', fillcolor='rgba(0,100,80,0.1)'),
row=1, col=1
)
# 2. 组件分解
fig.add_trace(
go.Scatter(x=forecast['ds'], y=forecast['trend'],
name='趋势', line=dict(color='red')),
row=1, col=2
)
fig.add_trace(
go.Scatter(x=forecast['ds'], y=forecast['weekly'],
name='每周周期', line=dict(color='green')),
row=1, col=2
)
# 3. 平台期指示器
fig.add_trace(
go.Indicator(
mode="gauge+number",
value=plateau_info['plateau_score'],
domain={'x': [0, 1], 'y': [0, 1]},
title={'text': "平台期风险"},
gauge={
'axis': {'range': [None, 100]},
'bar': {'color': "darkblue"},
'steps': [
{'range': [0, 30], 'color': "lightgreen"},
{'range': [30, 50], 'color': "yellow"},
{'range': [50, 70], 'color': "orange"},
{'range': [70, 100], 'color': "red"}
],
'threshold': {
'line': {'color': "red", 'width': 4},
'thickness': 0.75,
'value': 70
}
}
),
row=2, col=1
)
fig.update_layout(
height=800,
showlegend=True,
title_text="运动表现分析仪表板"
)
return fig
# 创建仪表板
dashboard = create_workout_dashboard(forecast, workout_data, result)
dashboard.show()
Code collapsed
关键要点
- Prophet适合运动数据预测:自动处理趋势和季节性
- 多指标综合判断:斜率、不确定性、偏差
- 提前预警是关键:提前1-2周识别平台期
- 可视化辅助决策:交互式图表更直观
- 结合专业建议:数据仅供参考,需配合教练指导
常见问题
需要多少数据才能预测?
建议至少60天的连续数据。数据越多,预测越准确。
平台期一定是坏事吗?
不一定。平台期可能意味着:
- 身体适应了当前训练
- 需要休息恢复
- 是突破前的积累期
如何打破平台期?
- 改变训练刺激:新的训练方式/强度
- 调整营养:增加/减少热量摄入
- 优化恢复:睡眠、压力管理
- 安排减周:主动恢复后再突破
参考资料
- Facebook Prophet官方文档
- 运动科学杂志:平台期研究
- 《NSCA体能训练指南》
发布日期:2026年3月8日 最后更新:2026年3月8日