# Heart Rate Varability (HRV)¶

For a comprehensive review of the most up-to-date HRV indices, a discussion of their significance in psychology, and a step-by-step guide for HRV analysis using **NeuroKit2**, the Heart Rate Variability in Psychology: A Review of HRV Indices and an Analysis Tutorial paper is a good place to start.

NeuroKit2 is the most comprehensive software for computing HRV indices, and the list of features is available below:

Domains |
Indices |
NeuroKit |
heartpy |
HRV |
pyHRV |
---|---|---|---|---|---|

Time Domain |
CVNN CVSD MAD MHR MRRI NNI parameters ΔNNI parameters MadNN MeanNN MedianNN MCVNN pNN20 pNN50 RMSSD SDANN SDNN SDNN_index SDSD TINN |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |

Frequency Domain |
ULF VLF LF LFn LF Peak LF Relative HF HFnu HF Peak HF Relative LF/HF |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |

Non-Linear Domain |
SD1 SD2 S SD1/SD2 SampEn DFA CSI Modified CSI CVI |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ |
✔️ ✔️ |
✔️ ✔️ ✔️ ✔️ ✔️ ✔️ |

## Compute HRV features¶

This example can be referenced by citing the package.

The example shows how to use NeuroKit2 to compute heart rate variability (HRV) indices in the time-, frequency-, and non-linear domain.

```
[12]:
```

```
# Load the NeuroKit package and other useful packages
import neurokit2 as nk
import matplotlib.pyplot as plt
%matplotlib inline
```

```
[13]:
```

```
plt.rcParams['figure.figsize'] = [15, 9] # Bigger images
```

## Download Dataset¶

First, let’s download the resting rate data (sampled at 100Hz) using `nk.data()`

.

```
[14]:
```

```
data = nk.data("bio_resting_5min_100hz")
data.head() # Print first 5 rows
```

```
[14]:
```

ECG | PPG | RSP | |
---|---|---|---|

0 | 0.003766 | -0.102539 | 0.494652 |

1 | -0.017466 | -0.103760 | 0.502483 |

2 | -0.015679 | -0.107422 | 0.511102 |

3 | -0.001598 | -0.110855 | 0.518791 |

4 | 0.002483 | -0.112610 | 0.528669 |

You can see that it consists of three different signals, pertaining to ECG, PPG (an alternative determinant of heart rate as compared to ECG), and RSP (respiration). Now, let’s extract the ECG signal in the shape of a vector (i.e., a one-dimensional array), and find the peaks using ecg_peaks().

```
[15]:
```

```
# Find peaks
peaks, info = nk.ecg_peaks(data["ECG"], sampling_rate=100)
```

*Note: It is critical that you specify the correct sampling rate of your signal throughout many processing functions, as this allows NeuroKit to have a time reference.*

This produces two elements, `peaks`

which is a DataFrame of same length as the input signal in which occurences of R-peaks are marked with 1 in a list of zeros. `info`

is a dictionary of the sample points at which these R-peaks occur.

HRV is the temporal variation between consecutive heartbeats (**RR intervals**). Here, we will use `peaks`

i.e. occurrences of the heartbeat peaks, as the input argument in the following HRV functions to extract the indices.

## Time-Domain Analysis¶

First, let’s extract the time-domain indices.

```
[16]:
```

```
# Extract clean EDA and SCR features
hrv_time = nk.hrv_time(peaks, sampling_rate=100, show=True)
hrv_time
```

```
[16]:
```

HRV_RMSSD | HRV_MeanNN | HRV_SDNN | HRV_SDSD | HRV_CVNN | HRV_CVSD | HRV_MedianNN | HRV_MadNN | HRV_MCVNN | HRV_pNN50 | HRV_pNN20 | HRV_TINN | HRV_HTI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|

0 | 69.697983 | 696.395349 | 62.135891 | 69.779109 | 0.089225 | 0.100084 | 690.0 | 44.478 | 0.064461 | 14.651163 | 49.302326 | 950.0 | 4.343434 |

These features include the RMSSD (square root of the mean of the sum of successive differences between adjacent RR intervals), MeanNN (mean of RR intervals) so on and so forth. You can also visualize the distribution of R-R intervals by specifying `show=True`

in hrv_time().

## Frequency-Domain Analysis¶

Now, let’s extract the frequency domain features, which involve extracting for example the spectral power density pertaining to different frequency bands. Again, you can visualize the power across frequency bands by specifying `show=True`

in hrv_frequency().

```
[17]:
```

```
hrv_freq = nk.hrv_frequency(peaks, sampling_rate=100, show=True)
hrv_freq
```

```
[17]:
```

HRV_ULF | HRV_VLF | HRV_LF | HRV_HF | HRV_VHF | HRV_LFHF | HRV_LFn | HRV_HFn | HRV_LnHF | |
---|---|---|---|---|---|---|---|---|---|

0 | NaN | NaN | 1168.925644 | 1586.90506 | 253.936141 | 0.736607 | 0.388377 | 0.527252 | 7.369541 |

## Non-Linear Domain Analysis¶

Now, let’s compute the non-linear indices with hrv_nonlinear().

```
[18]:
```

```
hrv_non = nk.hrv_nonlinear(peaks, sampling_rate=100, show=True)
hrv_non
```

```
[18]:
```

HRV_SD1 | HRV_SD2 | HRV_SD2SD1 | HRV_CSI | HRV_CVI | HRV_CSI_Modified | HRV_SampEn | |
---|---|---|---|---|---|---|---|

0 | 49.341281 | 85.461606 | 1.732051 | 1.732051 | 4.829101 | 592.095372 | 1.259931 |

This will produce a Poincaré plot which plots each RR interval against the next successive one.

## All Domains¶

Finally, if you’d like to extract HRV indices from all three domains, you can simply input `peaks`

into hrv(), where you can specify `show=True`

to visualize the combination of plots depicting the RR intervals distribution, power spectral density for frequency domains, and the poincare scattergram.

```
[19]:
```

```
hrv_indices = nk.hrv(peaks, sampling_rate=100, show=True)
hrv_indices
```

```
[19]:
```

HRV_RMSSD | HRV_MeanNN | HRV_SDNN | HRV_SDSD | HRV_CVNN | HRV_CVSD | HRV_MedianNN | HRV_MadNN | HRV_MCVNN | HRV_pNN50 | ... | HRV_LFn | HRV_HFn | HRV_LnHF | HRV_SD1 | HRV_SD2 | HRV_SD2SD1 | HRV_CSI | HRV_CVI | HRV_CSI_Modified | HRV_SampEn | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

0 | 69.697983 | 696.395349 | 62.135891 | 69.779109 | 0.089225 | 0.100084 | 690.0 | 44.478 | 0.064461 | 14.651163 | ... | 0.388377 | 0.527252 | 7.369541 | 49.341281 | 85.461606 | 1.732051 | 1.732051 | 4.829101 | 592.095372 | 1.259931 |

1 rows × 29 columns

## Resources¶

There are several other packages more focused on HRV in which you might find a more in depth explanation and documentation. See their documentation here: