gc_plots/hrv_trends.py

291 lines
10 KiB
Python

import os
import tempfile
import pandas as pd
import plotly
from plotly.subplots import make_subplots
from plotly.graph_objs import Scatter, Layout, Bar, Figure
try:
from gc_wrapper.GC_Wrapper import GC_wrapper as GC
except ImportError:
pass
BASELINE_DAYS = 7
NORMALRANGE_DAYS = 60
TRAINING_STRESS = 'BikeStress'
HRV = 'RECOVERY_POINTS'
HR = 'HR'
HRV_BASE = 'HrvBaseline'
HR_BASE = 'HrBaseline'
HRV_NORMAL = 'HrvNormal'
HRV_STD = 'HrvStdDev'
HRV_CEILING = 'HrvNormalCeiling'
HRV_FLOOR = 'HrvNormalFloor'
HRV_CV = 'HrvCv'
HRV_VAR_BASE = 'HrvVarBase'
def plot_plotly():
lts_trace = Scatter(x=pmc_data['date'],
y=pmc_data['lts'],
name='Fitness (LTS)',
fill='tozeroy',
line=dict(color='#1f77b4'))
sts_trace = Scatter(x=pmc_data['date'],
y=pmc_data['sts'],
name='Fatigue (STS)',
fill='tozeroy',
line=dict(color='#FF6692'))
sb_trace = Scatter(x=pmc_data['date'], y=pmc_data['sb'], name='Form (TSB)')
hr_trace = Scatter(x=hrv_data['date'],
y=hrv_data[HR_BASE],
mode='lines',
name=f'{BASELINE_DAYS}-day HR Baseline')
hrv_ceiling_trace = Scatter(x=hrv_data['date'],
y=hrv_data[HRV_CEILING],
fill=None,
mode='lines',
name=f"{NORMALRANGE_DAYS}-day Top Normal",
line=dict(color='#b3f6d1', ))
hrv_floor_trace = Scatter(x=hrv_data['date'],
y=hrv_data[HRV_FLOOR],
fill='tonexty',
mode='lines',
name=f'{NORMALRANGE_DAYS}-day Bottom Normal',
line=dict(color='#b3f6d1', ))
hrv_base_trace = Scatter(x=hrv_data['date'],
y=hrv_data[HRV_BASE],
mode='lines',
name=f"{BASELINE_DAYS}-day Baseline",
line=dict(color='red', ))
hrv_trace = Bar(x=hrv_data['date'],
y=hrv_data[HRV],
name="Recovery Points",
marker=dict(color='gray', ))
hrv_cv_trace = Scatter(x=hrv_data['date'],
y=hrv_data[HRV_CV],
name='CV',
mode='lines',
line=dict(color='yellow'))
# session_rpe_trace = Bar(x=rpe['date'],
# y=rpe['Session_RPE'],
# name='RPE',
# marker=dict(color='lightgray', ))
rpe_trace = Bar(x=rpe['date'],
y=rpe['RPE'],
name='RPE',
marker=dict(color='red', ))
layout = Layout(
template='plotly_dark',
# paper_bgcolor='rgba(0,0,0,0)',
# plot_bgcolor='rgba(0,0,0,0)',
yaxis=dict(title="HRV Recovery Points", range=[5.5, 10]),
yaxis2=dict(title="Coeffient of Variation", range=[0, 20]),
yaxis3=dict(title=f'{TRAINING_STRESS}'),
yaxis4=dict(title='HRV (ln(RMSSD)'),
yaxis5=dict(title='HR (BPM)'),
legend=dict(orientation="h"))
fig = make_subplots(
4,
1,
subplot_titles=[
'HRV',
'Coefficient of Variaton',
'Stress',
'HRV vs. HR',
],
specs=[
[dict(secondary_y=False)],
[dict(secondary_y=False)],
[dict(secondary_y=False)],
[dict(secondary_y=True)],
# [dict(secondary_y=True)],
],
# horizontal_spacing=0.2,
# vertical_spacing=0.2
)
fig.update_layout(template='plotly_dark')
fig.add_trace(hrv_trace, 1, 1)
fig.add_trace(hrv_ceiling_trace, 1, 1)
fig.add_trace(hrv_floor_trace, 1, 1)
fig.add_trace(hrv_base_trace, 1, 1)
# fig.add_trace(sb_trace, 1, 1, secondary_y=True)
fig.add_trace(hrv_cv_trace, 2, 1)
fig.add_trace(sts_trace, 3, 1)
fig.add_trace(lts_trace, 3, 1)
fig.add_trace(hr_trace, 4, 1, secondary_y=True)
fig.add_trace(hrv_base_trace, 4, 1)
# fig.add_trace(session_rpe_trace, 2, 2)
# fig.add_trace(rpe_trace, 5, 1)
fig.update_layout(layout)
fig.update_layout(
dict(
yaxis=dict(showline=True, mirror=True, ticks='inside'),
yaxis2=dict(showline=True, mirror=True, ticks='inside'),
yaxis3=dict(showline=True, mirror=True, ticks='inside'),
yaxis4=dict(showline=True, mirror=True, ticks='inside'),
yaxis5=dict(showline=True, mirror=True, ticks='inside'),
yaxis6=dict(showline=True, mirror=True, ticks='inside'),
yaxis7=dict(showline=True, mirror=True, ticks='inside'),
yaxis8=dict(showline=True, mirror=True, ticks='inside'),
xaxis=dict(showline=True, mirror=True, ticks='inside'),
xaxis2=dict(showline=True, mirror=True, ticks='inside'),
xaxis3=dict(showline=True, mirror=True, ticks='inside'),
xaxis4=dict(showline=True, mirror=True, ticks='inside'),
xaxis5=dict(showline=True, mirror=True, ticks='inside'),
xaxis6=dict(showline=True, mirror=True, ticks='inside'),
xaxis7=dict(showline=True, mirror=True, ticks='inside'),
xaxis8=dict(showline=True, mirror=True, ticks='inside'),
))
fig.update_xaxes(showline=True,
linewidth=1,
linecolor='black',
mirror=True)
fig.update_yaxes(showline=True,
linewidth=1,
linecolor='black',
mirror=True)
f = plotly.offline.plot(fig,
auto_open=False,
filename=tempfile.gettempdir() + "/temp-plot.html")
GC.webpage("file://" + f)
def plot_native():
GC.setChart(type=GC.CHART_LINE,
orientation=GC_VERTICAL,
legpos=GC_ALIGN_RIGHT,
stack=False,
animate=True)
hrv_data['date_internal'] = (hrv_data['date'] -
pd.to_datetime('1900-01-01').date()).dt.days
xx = hrv_data['date_internal']
settings = dict(symbol=GC_SYMBOL_CIRCLE,
line=GC_LINE_NONE,
color='gray',
opacity=20,
xaxis='Date',
yaxis='lnRMSSD',
size=3,
opengl=False)
GC.addCurve(name='Measurement', x=xx, y=hrv_data[HRV], **settings)
settings = dict(symbol=GC_SYMBOL_NONE,
line=GC_LINE_SOLID,
color='red',
opacity=100,
xaxis='Date',
yaxis='lnRMSSD',
size=3,
opengl=False)
settings['color'] = 'green'
settings['opacity'] = 100
settings['line'] = GC_LINE_DASH
GC.addCurve(name='Normal Top', x=xx, y=hrv_data[HRV_CEILING], **settings)
GC.addCurve(name='Normal Bot', x=xx, y=hrv_data[HRV_FLOOR], **settings)
settings['color'] = 'red'
settings['line'] = GC_LINE_SOLID
GC.addCurve(name='HRV', x=xx, y=hrv_data[HRV_BASE], **settings)
settings['color'] = 'yellow'
settings['opacity'] = 100
settings['line'] = GC_LINE_SOLID
settings['yaxis'] = 'Coefficient of Variation'
settings['xaxis'] = 'Date2'
GC.addCurve(name='Variation', x=xx, y=hrv_data[HRV_CV], **settings)
GC.setAxis('Date', type=GC.AXIS_DATE)
GC.setAxis('Date2', type=GC.AXIS_DATE)
GC.setAxis('Coefficient of Variation',
type=GC.AXIS_CONTINUOUS,
min=0,
max=25)
GC.setAxis('lnRMSSD',
type=GC.AXIS_CONTINUOUS,
min=4.0,
max=10.0,
color='red')
def get_hrv_measures():
history = pd.DataFrame(GC.seasonMeasures(
all=True, group='Hrv')).query(f'{HRV} != 0.0')
selection = pd.DataFrame(GC.seasonMeasures(
all=False, group='Hrv')).query(f'{HRV} != 0.0')
return history, selection
def compute_hrv_trends(df: pd.DataFrame):
df[HRV_BASE] = df[HRV].rolling(window=BASELINE_DAYS,
min_periods=BASELINE_DAYS // 2).mean()
df[HRV_NORMAL] = df[HRV].rolling(window=NORMALRANGE_DAYS,
min_periods=NORMALRANGE_DAYS // 2).mean()
df[HRV_STD] = df[HRV].rolling(window=NORMALRANGE_DAYS,
min_periods=NORMALRANGE_DAYS // 2).std()
df[HRV_CEILING] = df[HRV_NORMAL] + 0.75 * df[HRV_STD]
df[HRV_FLOOR] = df[HRV_NORMAL] - 0.75 * df[HRV_STD]
df[HR_BASE] = df[HR].rolling(window=BASELINE_DAYS,
min_periods=BASELINE_DAYS // 2).mean()
df[HRV_VAR_BASE] = df[HRV].rolling(window=BASELINE_DAYS,
min_periods=BASELINE_DAYS // 2).std()
df[HRV_CV] = df[HRV_VAR_BASE] / df[HRV_BASE] * 100.0
return df
def get_pmc_data(t1):
df = pd.DataFrame(GC.seasonPmc(all=True, metric=TRAINING_STRESS))
tmp = pd.DataFrame(GC.seasonPmc(all=False, metric=TRAINING_STRESS))
t0 = tmp.iloc[0]['date']
return crop(df, t0, t1)
def get_stress():
df = pd.DataFrame(GC.seasonMetrics())[['date', TRAINING_STRESS]]
data = df.groupby('date')[TRAINING_STRESS].sum()
df = pd.DataFrame({'date': data.index, TRAINING_STRESS: data.values})
return df
def get_rpe():
df = pd.DataFrame(GC.seasonMetrics())[['date', 'RPE']]
# df = df.loc[df['RPE'].dtype.kind in 'iufc']
# df = df.query('RPE != 0.0')
df['RPE'] = df['RPE'].replace('', '0').astype(int)
data = df.groupby('date', as_index=False).mean()
return data
def crop(df: pd.DataFrame, t0, t1):
df = df.loc[df['date'] >= t0]
return df.loc[df['date'] <= t1]
try:
os.remove(tempfile.gettempdir() + "/hrv_trends.html")
except:
pass
history, selection = get_hrv_measures()
compute_hrv_trends(history)
t0 = selection.iloc[0]['date']
t1 = selection.iloc[-1]['date']
hrv_data = crop(history, t0, t1)
rpe = get_rpe()
stress = get_stress()
pmc_data = get_pmc_data(stress.iloc[-1]['date'])
plot_native()
plot_plotly()