Anyone using HRV? (Heart Rate Variability)

Good point. I agree with you, this plot probably comes from a series in which the same visualization was used for different variables (including some with a strong relationship), hence the potential confusion. Appreciate your feedback!

2 Likes

Marco - could you please elaborate a little bit more on SWC/normal values. Is this calculated from all rMSSD in the entire time series or only from the 7 days making up the moving average for a given day?

Edit: just noticed in the chart that your normal values are for 30 days. Is this correct? So you take the standard deviation for the last 30 days? And construct the green band around the mean for the 30days for each day?

Edit2: and the 0.5 or 0.75 std is only to one side of the 30d mean or is it 0.5/0.75 for both sides, e.g. 0.25/0.375 for each side to the mean?

1 Like

Hi! We currently use 2 months, so 60 days as I think this is a better trade-off between short and long term stressors (acute/chronic). The baseline is then 7 days.

Thanks.

And the 0.5 or 0.75 std is only to one side of the 60d mean or is it 0.5/0.75 for both sides?

I’d say 0.75 on each side off the top of my head (this was configurable in the past and might come back), I’d stress as usual to focus on the big picture, this is a simple way to spot deviations from what is expected to be your normal considering your past data, different numbers will do, then over the years we found the current ones to be working rather well. Hope this helps!

Thanks, have this put together quickly in Golden Cheetah. I use the HRV4Training app on Android.

I guess I should stop doing business trips. Have to tell my boss that it interfers with my training.

Would be interesting to see last year. Haven’t used the app yet. Similar volume but this year I’m a little bit “wiser” and take more care of recovery. E.g. do only hard stuff when feeling right.

1 Like

one last point and you can take us out of business haha: I would use ln rMSSD as data is not normally distributed, similarly to what you see in research when they show the SWC and HRV (as ln rMSSD you get something that is pretty much half the Recovery Points in HRV4T, you could just use those).

Do you mind pushing that to the user charts database for GC or sharing it otherwise?

Sorry, but I use my own build of GC. The HRV chart is based on completely different set up, e.g. I’ve put my data on a cloud and programmed the access myself.


Today race day. And as usual my HRV has plummeted, the app telling me to “limit intensity” today. Don’t know :slight_smile:

Now with ln(rmssd):

now if I’m honest, sorry, in the end it does not really tell me anything new to how I simply feel. And since I base my training on how I feel, this is probably the reason why I don’t cross the bottom line.

One more question about this (I threw something like this together in an R Chart for GC, which I am happy to share once I’ve made it a little prettier): Did you use 0.75 std of the moving 60 day averages to construct the band or of the raw values of the past 60 days?

I’ve added both to the latest version +/- 0.7 std and +/- 0.5 std. I’m using a 30d average for the “normal-mean”.

Have to move this out of GC at some point. Would be easy to do. Grab the csv from HRV4Training via dropbox and construct the chart. GC is super slow and cumbersome on my computers. If only I had the time …

Race went super well on the week-end despite being told by the app to limit intensity. Podium :slight_smile:
Must say, looking at my HRV it’s really mainly work and pre-race worries which drive my (daily) HRV down.

and a zoom in

I might be a bit dense (I am in the humanities!) but what I am doing right now is I get a dataframe from GC with two columns: date and RecoveryScore (from HRV4Training), then I add the following columns:

60DayMean, StandardDeviation (calculated for the last 60 values of the 60DayMean), and the 7DayBaseLine.

So I have a different sd value for every day.

Does that sound about right?

I’ve never really understood what this recovery score is exactly. I’m sure there is a detailed explanation on the website but I would appreciate an “executive’s summary” :slight_smile:

I use RMSSD.

  1. Grab date and RMSSD
  2. calculate ln(RMSSD)
    3a) calculate rolling means for 7 and 30 (or in your case 60)d
    3b) calculate rolling std for 30d from ln(RMSSD)-series [so here’s a discrepancy, do we take std of the means or of the raw series, e.g. ln(RMSSD) ???]
  3. plot

This is at least my understanding.

1 Like

Actually, I misread my own code. I did do a rolling std from the raw values rather than the means. Thanks for your help anyway! I might migrate to ln(RMSSD) after I finished this, I just decided to skip that step because of @marco_alt’s comments and because it’s in the dataset anyway.

Python code snippet for charting it outside of GC. This access the HRV4Training export *.csv file. In my case I export it to Dropbox. This path would need changing.

7d rolling mean and 60d-mean +/- 60d-std


import pandas as pd
import matplotlib.pyplot as plt
import datetime
import numpy as np

df = pd.read_csv(‘C:\Users\xy\Dropbox\Apps\HRV4Training\MyMeasurements_Android.csv’, index_col=False)
dates = [datetime.datetime.strptime(x, ‘%Y-%d-%m’) for x in df.date]
rmssd = df.rMSSD.replace(0, np.nan).apply(np.log)
rmssd7 = rmssd.rolling(7, min_periods=3).mean()
rmssd60 = rmssd.rolling(60, min_periods=20).mean()
rmssd60std = rmssd.rolling(60, min_periods=20).std()

plt.plot_date(x=dates, y=rmssd7, fmt=“r-”)
plt.plot_date(x=dates, y=rmssd60.add(rmssd60std.mul(0.7)), fmt=“b-.”)
plt.plot_date(x=dates, y=rmssd60.sub(rmssd60std.mul(0.7)), fmt=“b-.”)

plt.ylabel(“ln(rMSSD)”)
plt.grid(True)
plt.show()


Actually, In GC do you have it as a python chart or as custom metrics/formulas? I tried to go the formulas route at first but found that it wouldn’t give me measures for non-activity dates.

In GC as python chart with plotly charting library. However, as alluded to above, I store my data differently in GC. I don’t use the GC Python Api to access my workouts.

Going through the GC API would look similar to this I guess:

import pandas as pd

hrv = GC.seasonMeasures(group=“Hrv”)
pdhrv = pd.DataFrame(hrv)

pdhrv looks then likes this

AVNN HF HR LF PNN50 RMSSD RecoveryPoints SDNN date
177 1280.05698 0.0470521 46.8729134 0.02104777 55.2631579 71.0614217 8.36117889 73.7212174 30-04-19
178 1244.44444 0.06947699 48.2142857 0.02558803 68.9655172 92.4509499 8.80397241 60.2293285 01-05-19
179 1282.01754 0.09751767 46.8012316 0.04303748 45.9459459 71.4464508 8.37013374 50.7166672 02-05-19
180 1284.33048 0.04807177 46.7169476 0.0365463 28.9473684 56.9641254 8.00034183 39.8611033 03-05-19
181 0 0 0 0 0 0 0 0 04-05-19
1 Like

Yeah. You’d want to pass all=True so you get the full history. But other than that, that’s how I do it. :slight_smile:

UPDATE: This is now available as a GC chart in the GC chart database as “HRV Trends and Stress.” The range of days for the normal “band” and the type of Stress (BikeStress, TSS, Duration) can be configured via variables in the beginning of the code.

1 Like

For me this is a common effect, actually the only time I manage to drive ln(rMSSD) below/out of the green band: effect and after-effects of a business trip to the US from the EU. And this time I tried so many counter measures to dampen the effect, still, HRV taking a massive dive.