Giter VIP home page Giter VIP logo

hrv's Introduction

Build Status codecov Current version at PyPI Downloads per month on PyPI Supported Python Versions Software status License: LGPLv3 DOI

Pythonic package for Heart Rate Variability Analysis

version number: 0.2.10

author: Rhenan Bartels

logo

Overview

hrv is a simple Python module that brings the most widely used techniques to extract information about cardiac autonomic functions through RRi series and Heart Rate Variability (HRV) analyses without losing the Power and Flexibility of a native Python object.

In other words, the hrv module eases the manipulation, inspection, pre-processing, visualization, and analyses of HRV-related information. Additionally, it is written with idiomatic code and tries to implement the API of a built-in object, which might make it intuitive for Python users.

For a more in-depth explanation of how hrv works, please Read the documentation

hrv's People

Contributors

dominiquemakowski avatar jrushing99 avatar leeaandrob avatar rhenanbartels avatar rubinovitz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hrv's Issues

Sd2 returning NaN

Hello and thanks so much for this wonderful library, it's been extremely helpful!

I'm trying to get non_linear feaures from a set of RRI values I extracted from the Physionet Long Term AF Database, but I am getting sd2 returning NaN and I'm unsure as to why.

Here is the rri set (in seconds):

[0.57, 1.125, 0.57, 1.133, 0.57, 1.148, 0.57, 1.156, 0.586, 1.141, 0.562, 1.148, 0.562, 1.164, 0.57, 1.148, 0.57, 1.156, 0.562, 1.148, 0.57, 1.133, 0.578, 1.141, 0.578, 1.141, 0.562, 1.148, 0.57, 1.141, 0.57, 1.141, 0.57, 1.148, 0.562, 1.148, 0.562, 1.125, 0.562, 1.133, 0.57, 1.125, 0.578, 1.133, 0.562, 1.133, 0.578, 1.141, 0.57, 1.125, 0.57, 1.133, 0.57, 1.117, 0.57, 1.125, 0.578, 1.117, 0.578]

And here is how I am trying to get the Non Linear Features:

import numpy as np
from hrv.io import read_from_text
from hrv.rri import RRi
from hrv.classical import frequency_domain
from hrv.classical import non_linear
from hrv.classical import time_domain
import os

f = open("testNan.txt", "r")
lines = f.readlines()
averageArray = []
for l in lines[1:]:
    averageArray.append(l.split()[-3])
        #end for
averageArray = list(map(float, averageArray))

rri = RRi(averageArray)
      
nonLinear = non_linear(rri)

Wondering if Nan is expected as I'd like to use sd2 as a column in pandas dataframe but I get errors when having NaN values(and I wanna avoid dropping the whole row containing NaN)

Thanks so much!

frequency_domain() method

Hi @rhenanbartels, thanks for this convenient repository.
I successfully install and import the hrv package, but I have a problem while using the frequency_domain() method.
The issue is, I consider the parameter 'fs' stands for the sampling frequency due to the lack of docstring in the method. So I put in the value of sample frequency and obtain 'nan' value back without error.
Could you please help to explain the parameter?
Thanks a lot!

Artifact removal, outlier detection and baseline wander removal

Hi,

Thanks for the hrv toolbox. I have noticed that there are numerous HRV toolboxes that perform the same function (computing time, frequency and nonlinearity parameters). But what I find missing in all of these are automated methods to remove outliers in RR intervals and also the baseline wandering. These substantially change the computed outcomes. I suggest looking into the page 11 of the document below for some ideas in this regard:
https://www.kubios.com/downloads/Kubios_HRV_Users_Guide.pdf

Also look into the paper below, how smoothness priors could be used to remove baseline wandering:
https://pdfs.semanticscholar.org/7b28/41f2b7013eccb3b75f72a1aada97ab66d551.pdf

These are relatively simple to implement but having these would make this toolbox definitely much more useful than the other very many ones that do the same operations.

Thanks
Hooman

RRI detrended

When calculating rri_detrended with the smoothness priors method: rri_detrended = smoothness_priors(rri, l=500, fs=4.0) the rri_detrended values have a spike towards the end (see 2nd graph below) for both sample files real_rri.txt and rest_rri.txt which looks off. Is this expected behaviour or a bug? Thanks for your feedback.
Screen Shot 2021-04-03 at 15 44 08

'pip install' installs very old version

Python package repository has an old version of the hrv package. Any update on when the latest release will be pushed so that 'pip install' picks up the latest version?

Appreciate the time and efforts you put in in making this package.

Thanks!

Filters

Traceback (most recent call last):
File "/home/pi/Downloads/sleep_apnea-master/pre_proc.py", line 4, in
from hrv.filters import quotient, moving_median
File "/usr/lib/python3/dist-packages/thonny/backend.py", line 305, in _custom_import
module = self._original_import(*args, **kw)
File "/home/pi/.local/lib/python3.5/site-packages/hrv/init.py", line 20, in
import hrv.classical as classical
File "/usr/lib/python3/dist-packages/thonny/backend.py", line 305, in _custom_import
module = self._original_import(*args, **kw)
File "/home/pi/.local/lib/python3.5/site-packages/hrv/classical.py", line 5, in
from spectrum import pburg
File "/usr/lib/python3/dist-packages/thonny/backend.py", line 305, in _custom_import
module = self._original_import(*args, **kw)
File "/home/pi/.local/lib/python3.5/site-packages/spectrum/init.py", line 6, in
import arma
File "/usr/lib/python3/dist-packages/thonny/backend.py", line 305, in _custom_import
module = self._original_import(*args, **kw)
ImportError: No module named 'arma'

Polar files

Hi, I saw on the docs that this takes .hrm polar files.

I have an H10 but haven't come across any way of getting the .hrm files from it yet, do you know how I can access them so I can provide your hrv tool the RRI?

Thanks

Create FrequencyDomain results class

Currently, the results of the frequency_domain function are stored in a Python built-in dictionary.

It would be a nice improvement if the results were stored in a custom dict-like class with additional useful methods, such as plot.

For instance, a plot method could depict the power spectral density (PSD) chart like the following:

freq_domain_results = frequency_domain(rri, ...)

freq_domain_results["lf"]
# 134.56
freq_domain_results.plot()

image

There is something similar already created for time varying analyses:

class TimeVarying:

All detrending techniques can produce negative RR values

Each of the three detrending techniques (polynomial, smoothed priors, and SG) can yield negative RR intervals, which is obviously problematic. Here's some code to demonstrate the problem:

from hrv.rri import RRi
from hrv.detrend import smoothness_priors, polynomial_detrend, sg_detrend

rri_vals = [ 795, 832, 827, 897, 916, 902, 870, 827, 819, 757, 749, 725,
  727, 743, 725, 722, 719, 773, 784, 832, 908, 897, 859, 835,
  859, 824, 805, 754, 760, 765, 711, 849, 528, 762, 671, 725,
 1309, 560, 962, 892, 876, 900, 838, 873, 784, 830, 584, 579,
  803, 611, 620, 703, 768, 795, 765, 644, 714, 722, 717, 706,
  754, 714, 695, 730, 708, 722, 760, 849, 867, 878, 859, 857,
  881, 892, 819, 789, 835, 876, 770, 838, 862, 884, 876, 840,
  760, 822, 892, 935, 932, 824, 830, 795, 792, 873, 916, 983,
 1013, 989, 962, 959, 937, 959, 908, 897, 889, 816, 768, 752,
  741, 692, 620, 789, 611, 671, 663, 501, 797, 1517, 708, 601,
  894, 708, 730, 711, 733, 717, 719, 711, 671, 636, 641, 649,
  595, 636, 620, 628, 703, 725, 808, 792, 851, 867, 881, 927,
  894, 884, 873, 892, 916, 862, 870, 908, 967, 964, 943, 851,
  822, 843, 916, 989, 1010, 1010, 878, 797, 913, 784, 884, 951,
 1018, 1088, 1037, 1029, 929, 876, 805, 738, 706, 652, 655, 692,
  665, 606, 676, 673, 679, 757, 725, 1983, 2207, 671, 687, 932,
  590, 776, 838, 784, 808, 795, 808, 690, 819, 746, 795, 835,
  981, 929, 956, 916, 865, 851, 770, 735, 768, 706, 762, 698,
  727, 773, 795, 854, 994, 935, 889, 814, 733, 717, 725, 1072,
 1018, 784, 660, 784, 620, 770, 743, 768, 811, 897, 892, 840,
  811, 827, 816, 884, 886, 876, 752, 851, 859, 789, 768, 760,
  800, 811, 921, 851, 819, 859, 865, 787, 849, 876, 827, 773,
  717, 760, 762, 854, 897, 997, 991, 919, 886, 843, 784, 757,
  760, 735, 730, 1355, 620, 614, 665, 676, 668, 644, 668, 733,
  770, 700, 749, 867, 897, 805, 789, 749, 703, 719, 770, 628,
  768, 412, 932, 652, 749, 760, 773, 905, 865, 876, 711, 752,
  722, 795, 803, 781, 765, 730, 711, 738, 725, 746, 811, 859,
  881, 800, 822, 584, 956, 595, 800, 749, 1506]

rri = RRi(rri_vals)

smoothed = smoothness_priors(rri)
print(f'Total smoothed values: {len(smoothed.values)}; values < 0: {len(smoothed.values[smoothed.values < 0])}')
poly_detrended = polynomial_detrend(rri)
print(f'Total polynomially detrended values: {len(poly_detrended.values)}; values < 0: {len(poly_detrended.values[poly_detrended.values < 0])}')
sg_detrended = sg_detrend(rri)
print(f'Total sg detrended values: {len(sg_detrended.values)}; values < 0: {len(sg_detrended.values[sg_detrended.values < 0])}')

Unable to install

Hi am getting following error

Collecting coverage (from hrv==0.1.3.dev0)
Exception:
Traceback (most recent call last):
File "C:\anaconda3\lib\site-packages\pip\basecommand.py", line 215, in main
status = self.run(options, args)
File "C:\anaconda3\lib\site-packages\pip\commands\install.py", line 335, in run
wb.build(autobuilding=True)
File "C:\anaconda3\lib\site-packages\pip\wheel.py", line 749, in build
self.requirement_set.prepare_files(self.finder)
File "C:\anaconda3\lib\site-packages\pip\req\req_set.py", line 380, in prepare_files
ignore_dependencies=self.ignore_dependencies))
File "C:\anaconda3\lib\site-packages\pip\req\req_set.py", line 554, in _prepare_file
require_hashes
File "C:\anaconda3\lib\site-packages\pip\req\req_install.py", line 278, in populate_link
self.link = finder.find_requirement(self, upgrade)
File "C:\anaconda3\lib\site-packages\pip\index.py", line 465, in find_requirement
all_candidates = self.find_all_candidates(req.name)
File "C:\anaconda3\lib\site-packages\pip\index.py", line 423, in find_all_candidates
for page in self._get_pages(url_locations, project_name):
File "C:\anaconda3\lib\site-packages\pip\index.py", line 568, in _get_pages
page = self._get_page(location)
File "C:\anaconda3\lib\site-packages\pip\index.py", line 683, in _get_page
return HTMLPage.get_page(link, session=self.session)
File "C:\anaconda3\lib\site-packages\pip\index.py", line 811, in get_page
inst = cls(resp.content, resp.url, resp.headers)
File "C:\anaconda3\lib\site-packages\pip\index.py", line 731, in init
namespaceHTMLElements=False,
TypeError: parse() got an unexpected keyword argument 'transport_encoding'

Information on the normal range for HRV

Hi everyone,

I wanted to know - what is the normal range of HRV? Sometimes I see the value in 2 digits and sometimes the values are 1000+. I am bit confused on what the ideal range should be. Thank you.

threshold_filter can generate negative RR values

The cubic spline interpolation used in the threshold_filter function can generate negative values. These then cause the interpolated data set to fail validation. Here's some sample code:

from hrv.rri import RRi
from hrv.filters import threshold_filter

rri_vals = [ 795, 832, 827, 897, 916, 902, 870, 827, 819, 757, 749, 725,
  727, 743, 725, 722, 719, 773, 784, 832, 908, 897, 859, 835,
  859, 824, 805, 754, 760, 765, 711, 849, 528, 762, 671, 725,
 1309, 560, 962, 892, 876, 900, 838, 873, 784, 830, 584, 579,
  803, 611, 620, 703, 768, 795, 765, 644, 714, 722, 717, 706,
  754, 714, 695, 730, 708, 722, 760, 849, 867, 878, 859, 857,
  881, 892, 819, 789, 835, 876, 770, 838, 862, 884, 876, 840,
  760, 822, 892, 935, 932, 824, 830, 795, 792, 873, 916, 983,
 1013, 989, 962, 959, 937, 959, 908, 897, 889, 816, 768, 752,
  741, 692, 620, 789, 611, 671, 663, 501, 797, 1517, 708, 601,
  894, 708, 730, 711, 733, 717, 719, 711, 671, 636, 641, 649,
  595, 636, 620, 628, 703, 725, 808, 792, 851, 867, 881, 927,
  894, 884, 873, 892, 916, 862, 870, 908, 967, 964, 943, 851,
  822, 843, 916, 989, 1010, 1010, 878, 797, 913, 784, 884, 951,
 1018, 1088, 1037, 1029, 929, 876, 805, 738, 706, 652, 655, 692,
  665, 606, 676, 673, 679, 757, 725, 1983, 2207, 671, 687, 932,
  590, 776, 838, 784, 808, 795, 808, 690, 819, 746, 795, 835,
  981, 929, 956, 916, 865, 851, 770, 735, 768, 706, 762, 698,
  727, 773, 795, 854, 994, 935, 889, 814, 733, 717, 725, 1072,
 1018, 784, 660, 784, 620, 770, 743, 768, 811, 897, 892, 840,
  811, 827, 816, 884, 886, 876, 752, 851, 859, 789, 768, 760,
  800, 811, 921, 851, 819, 859, 865, 787, 849, 876, 827, 773,
  717, 760, 762, 854, 897, 997, 991, 919, 886, 843, 784, 757,
  760, 735, 730, 1355, 620, 614, 665, 676, 668, 644, 668, 733,
  770, 700, 749, 867, 897, 805, 789, 749, 703, 719, 770, 628,
  768, 412, 932, 652, 749, 760, 773, 905, 865, 876, 711, 752,
  722, 795, 803, 781, 765, 730, 711, 738, 725, 746, 811, 859,
  881, 800, 822, 584, 956, 595, 800, 749, 1506]

rri = RRi(rri_vals)
corrected_rr_vals = threshold_filter(rri, threshold='low', local_median_size=5)

And here's the stack trace that it generates:

Traceback (most recent call last):
  File "hrv-bug2.py", line 34, in <module>
    corrected_rr_vals = threshold_filter(rri, threshold='low', local_median_size=5)
  File "/Users/home/.virtualenvs/pyhrv-kubios-A4er0XLO/lib/python3.8/site-packages/hrv/filters.py", line 228, in threshold_filter
    return RRi(cubic_spline(rri_time), rri_time)
  File "/Users/home/.virtualenvs/pyhrv-kubios-A4er0XLO/lib/python3.8/site-packages/hrv/rri.py", line 57, in __init__
    self.__rri = _validate_rri(rri)
  File "/Users/home/.virtualenvs/pyhrv-kubios-A4er0XLO/lib/python3.8/site-packages/hrv/rri.py", line 464, in _validate_rri
    raise ValueError("rri series can only have positive values")
ValueError: rri series can only have positive values

Threshold filter does not work properly for values below the threshold

Thanks for the awesome package! One small issue I spotted: the threshold_filter function seems to only filter values where the RRI is greater than $\text{local median} + \text{threshold}$, but not where it is smaller than $\text{local median} - \text{threshold}$:

if rri[j] > (np.median(rri[slice_]) + threshold):

Only for the values in the very first local window, the filtering happens in both directions:

if abs(rri[j] - np.median(rri[slice_])) > threshold:

Is hrs package used for ECG or PPG signal?

I am process the PPG signal but I got error:

  1. This is the code I am implementing
from hrv.classical import frequency_domain
from hrv.io import read_from_text
ppg = np.loadtxt('ecg.txt', delimiter=',')
results = frequency_domain(
    rri=ppg,
    fs=64.0,
    method='welch',
    interp_method='cubic',
    detrend='linear'
)
print(results)
  1. The error:
Traceback (most recent call last):

  File "/Users/yuyang01/Documents/publication/journal/workloadVR/python/hrv.py", line 51, in <module>
    detrend='linear'

  File "/Users/yuyang01/miniconda3/envs/py37/lib/python3.7/site-packages/hrv/classical.py", line 188, in frequency_domain
    rri = _interpolate_rri(rri, time, fs, interp_method)

  File "/Users/yuyang01/miniconda3/envs/py37/lib/python3.7/site-packages/hrv/utils.py", line 77, in _interpolate_rri
    return _interp_cubic_spline(rri, time, fs)

  File "/Users/yuyang01/miniconda3/envs/py37/lib/python3.7/site-packages/hrv/utils.py", line 83, in _interp_cubic_spline
    time_rri_interp = _create_interp_time(time, fs)

  File "/Users/yuyang01/miniconda3/envs/py37/lib/python3.7/site-packages/hrv/utils.py", line 97, in _create_interp_time
    return np.arange(0, time[-1] + time_resolution, time_resolution)

TypeError: 'NoneType' object is not subscriptable
  1. Here is the data:
    ppg.txt

Best regards,

"FileNotSupportedError: 'Text file not supported'" error

Hi,
I am trying to import data from Biopack software in .txt but I am still getting
"
File "C:\Users\MSI\Anaconda2\lib\site-packages\hrv\utils.py", line 85, in _identify_rri_file_type
raise FileNotSupportedError('Text file not supported')

FileNotSupportedError: 'Text file not supported''".

I have tried to save data from Biopack (default .ACQ) as .txt or as .EDF and convert to .txt, none of this worked. Could you please tell me, how should the structure(delimeter, line ending, headers) of .txt looks like?

Thank you very much!

Progress on frequency domain features

Hi,
I am looking for an HRV analysis package for Python and came across your work.
Because this is undocumented, I have a few questions:

  • Do you have approximate timeframes on when documentation might be complete?
  • From the code it looks like the functions take rri, the RR interval locations, as inputs, is this correct?

Thanks for putting this package together, it looks very promising.

Example

Hey,

I have this array of ECG signal from which I'd like to compute HRV. Do you have any example on how to use your package?

Thanks!

frequency_domain call on interpolated RRiDetrended instance fails

Here's some sample code:

from hrv.classical import frequency_domain
from hrv.detrend import smoothness_priors
from hrv.rri import RRi

rr_vals = read_from_text('rest_rri.txt')
smooth_detrended_rr_vals = smoothness_priors(rr_vals)
smooth_fd_results = frequency_domain(smooth_detrended_rr_vals, method="ar", order=16)
print(f'smooth_fd_results: {smooth_fd_results}')

And here is the resulting stack trace:

Traceback (most recent call last):
  File "hrv-bug.py", line 8, in <module>
    smooth_fd_results = frequency_domain(smooth_detrended_rr_vals, method="ar", order=16)
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/hrv/classical.py", line 207, in frequency_domain
    fxx, pxx = _calc_pburg_psd(rri=rri, fs=fs, **kwargs)
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/hrv/classical.py", line 234, in _calc_pburg_psd
    burg = pburg(data=rri, order=order, NFFT=nfft, sampling=fs)
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/spectrum/burg.py", line 129, in __init__
    super(pburg, self).__init__(data, ar_order=order,
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/spectrum/psd.py", line 804, in __init__
    super(ParametricSpectrum, self).__init__(data, sampling=sampling,
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/spectrum/psd.py", line 282, in __init__
    self.data = data
  File "/Users/home/.virtualenvs/hrv-kubios-A4er0XLO/lib/python3.8/site-packages/spectrum/psd.py", line 445, in _setData
    self.__data = data.copy()
AttributeError: 'RRiDetrended' object has no attribute 'copy'

The problem is here (or at least starts here):

rri = _interpolate_rri(rri, time, fs, interp_method)

That call to _interpolate_rri returns a numpy.ndarray, meaning that if rri was originally an RRi (or RRiDetrended) instance it isn't any more. That allows the call to data.copy() to succeed, because ndarray has a copy method. If you pass in an RRiDetrended instance where interpolated is True, line 200 is never executed and pburg doesn't get an ndarray, triggering the failure.

Review suggestions

Hi @rhenanbartels,

I was asked to review your submission to JOSS. Some suggestions from my end:

  • I think the paper can benefit from additional integration with existing literature, especially a more recent state of the field.

  • The software needs to be documented with proper docstrings in the code, I personally recommend either the Numpy or Google style. see for example docstrings here

  • Unit tests are missing, when using Numpy or Google style you can include examples in the docstring that double as unit tests.

  • Continuous integration, code coverage metrics, and an overview of the Python versions that are supported is missing too. There's many services out there for unit tests, I personally use travis-ci for CI and codecov.io for coverage results. You can add badges to the repo that update with CI and coverage results for each git push.

Although many toolboxes exist to work with PPG, ECG, or the resulting tachograms, I think hrv can play a nice role as an accessible toolbox for those already having tachograms available and need to analyse these. Congratulations on the work. Please keep me updated on the progress.

-Paul

Poincaré Plot and frequency domain analysis problem

Hi there,
Sorry to take your time and I've two questions.
First of all, my environment is Google's colab, I just use pip to install your HRV package.

  1. I want to depict the Poincaré Plot, but I received this error.
    庞加莱图

  2. When I do frequency analysis, I got this problem.
    频域

But the time domain analysis works well, so I'm really confused.
时域

Taking sampling rate into account

I just realized that your functions did not take the sampling rate into account, and use a default of 1000Hz. I think it would be easy to fix to add a sampling_rate=1000 parameter to adjust the meaning of the RR intervals. Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.