initial commit
This commit is contained in:
commit
99daa55e68
4 changed files with 227 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "gc_wrapper"]
|
||||
path = gc_wrapper
|
||||
url = https://github.com/rb83421/GoldenCheetah_Python_Chart_Wrapper.git
|
||||
1
gc_wrapper
Submodule
1
gc_wrapper
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7b81e28aeab748a47d9ea0bc8fd5d7b45e88ac74
|
||||
221
hrv_trends.py
Normal file
221
hrv_trends.py
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
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 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'])
|
||||
|
||||
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')
|
||||
|
||||
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='lightgray', ))
|
||||
|
||||
hrv_cv_trace = Scatter(x=hrv_data['date'],
|
||||
y=hrv_data[HRV_CV],
|
||||
name='CV',
|
||||
mode='lines',
|
||||
line=dict(color='black'))
|
||||
|
||||
# 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='none',
|
||||
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',
|
||||
'Stress',
|
||||
'HRV vs. HR',
|
||||
'RPE',
|
||||
],
|
||||
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.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)
|
||||
Loading…
Reference in a new issue