def nonlinear(nni=None, rpeaks=None, signal=None, sampling_rate=1000., show=False, kwargs_poincare=None, kwargs_sampen=None, kwargs_dfa=None): """Computes all time domain parameters of the HRV time domain module and returns them in a ReturnTuple object. References: [Peng1995][Willson2002] Parameters ---------- nni : array NN intervals in [ms] or [s]. rpeaks : array R-peak times in [ms] or [s]. signal : array ECG signal. sampling_rate : int, float Sampling rate used for the ECG acquisition in (Hz). show : bool, optional If True, shows DFA plot. kwargs_poincare : dict, optional Dictionary containing the kwargs for the nonlinear 'poincare()' function: .. ellipse : bool, optional If true, shows fitted ellipse in plot (default: True). .. vectors : bool, optional If true, shows SD1 and SD2 vectors in plot (default: True). .. legend : bool, optional If True, adds legend to the Poincaré plot (default: True). .. marker : character, optional NNI marker in plot (default: 'o'). kwargs_dfa : dict, optional Dictionary containing the kwargs for the nonlinear 'dfa()' function: .. short : array, 2 elements Interval limits of the short term fluctuations (default: None: [4, 16]). .. long : array, 2 elements Interval limits of the long term fluctuations (default: None: [17, 64]). .. legend : bool If True, adds legend with alpha1 and alpha2 values to the DFA plot (default: True) kwargs_sampen : dict, optional Dictionary containing the kwargs for the nonlinear 'sample_entropy()' function: .. dim : int, optional Entropy embedding dimension (default: 2). .. tolerance : int, float, optional Tolerance distance for which the vectors to be considered equal (default: std(NNI) * 0.2). Returns ------- results : biosppy.utils.ReturnTuple object All time domain results. Returned Parameters ------------------- .. SD1 in [ms] (key: 'sd1') .. SD2 in [ms] (key: 'sd2') .. SD2/SD1 [-] (key: 'sd_ratio') .. Area of the fitted ellipse in [ms^2] (key: 'ellipse_area') .. Sample Entropy [-] (key: 'sampen') .. Detrended Fluctuations Analysis [-] (short and long term fluctuations (key: 'dfa_short', 'dfa_long') Returned Figures ---------------- .. Poincaré plot (key: 'poincare_plot') Notes ----- .. Results are stored in a biosppy.utils.ReturnTuple object and need to be accessed with the respective keys as done with dictionaries (see list of parameters and keys above) .. Provide at least one type of input data (signal, nn, or rpeaks). .. Input data will be prioritized in the following order: 1. signal, 2. nn, 3. rpeaks. .. NN and R-peak series provided in [s] format will be converted to [ms] format. .. Currently only calls the poincare() function. Raises ------ TypeError If no input data for 'nn', 'rpeaks', and 'signal' provided. """ # Check input if signal is not None: rpeaks = ecg(signal=signal, sampling_rate=sampling_rate, show=False)[2] elif nni is None and rpeaks is None: raise TypeError('No input data provided. Please specify input data.') # Get NNI series nn = tools.check_input(nni, rpeaks) # Unwrap kwargs_poincare dictionary & compute Poincaré if kwargs_poincare is not None: if type(kwargs_poincare) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_poincare' must be a dictionary containing " "parameters (keys) and values for the 'poincare()' function." % type(kwargs_poincare)) # Supported kwargs available_kwargs = ['ellipse', 'vectors', 'legend', 'marker'] # Unwrwap kwargs dictionaries ellipse = kwargs_poincare[ 'ellipse'] if 'ellipse' in kwargs_poincare.keys() else True vectors = kwargs_poincare[ 'vectors'] if 'vectors' in kwargs_poincare.keys() else True legend = kwargs_poincare['legend'] if 'legend' in kwargs_poincare.keys( ) else True marker = kwargs_poincare['marker'] if 'marker' in kwargs_poincare.keys( ) else 'o' unsupported_kwargs = [] for args in kwargs_poincare.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'poincare()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Poincaré plot with custom configuration p_results = poincare(nn, show=False, ellipse=ellipse, vectors=vectors, legend=legend, marker=marker) else: # Compute Poincaré plot with default values p_results = poincare(nn, show=False) # Unwrap kwargs_sampen dictionary & compute Sample Entropy if kwargs_sampen is not None: if type(kwargs_sampen) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_sampen' must be a dictionary containing " "parameters (keys) and values for the 'sample_entropy()' function." % type(kwargs_sampen)) # Supported kwargs available_kwargs = ['dim', 'tolerance'] # Unwrwap kwargs dictionaries dim = kwargs_sampen['dim'] if 'dim' in kwargs_sampen.keys() else 2 tolerance = kwargs_sampen[ 'tolerance'] if 'tolerance' in kwargs_sampen.keys() else None unsupported_kwargs = [] for args in kwargs_sampen.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'sample_entropy()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Poincaré plot with custom configuration s_results = sample_entropy(nn, dim=dim, tolerance=tolerance) else: # Compute Poincaré plot with default values s_results = sample_entropy(nn) # Unwrap kwargs_dfa dictionary & compute Poincaré if kwargs_dfa is not None: if type(kwargs_dfa) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_dfa' must be a dictionary containing " "parameters (keys) and values for the 'dfa()' function." % type(kwargs_dfa)) # Supported kwargs available_kwargs = ['short', 'legend', 'long'] # Unwrwap kwargs dictionaries short = kwargs_dfa['short'] if 'short' in kwargs_dfa.keys() else None long = kwargs_dfa['long'] if 'long' in kwargs_dfa.keys() else None legend = kwargs_dfa['legend'] if 'legend' in kwargs_dfa.keys( ) else True unsupported_kwargs = [] for args in kwargs_dfa.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'dfa()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Poincaré plot with custom configuration d_results = dfa(nn, show=False, short=short, long=long, legend=legend) else: # Compute Poincaré plot with default values d_results = dfa(nn, show=False) # Join Results results = tools.join_tuples(p_results, s_results, d_results) # Plot if show: plt.show() # Output return results
def hrv(nni=None, rpeaks=None, signal=None, sampling_rate=1000., interval=[0, 10], plot_ecg=True, plot_tachogram=True, show=False, fbands=None, kwargs_ecg_plot={}, kwargs_tachogram=None, kwargs_time=None, kwargs_nonlinear=None, kwargs_welch=None, kwargs_lomb=None, kwargs_ar=None): """Computes all HRV parameters of the pyHRV toolkit (see list below). References: See 'references.txt' for the full list of references Parameters ---------- nni : array NN intervals in (ms) or (s). rpeaks : array R-peak times in (ms) or (s). signal : array ECG signal. sampling_rate : int, float Sampling rate used for the ECG acquisition in (Hz). plot_ecg : bool, optional If True, plots ECG signal with specified interval ('signal' must not be None). plot_tachogram : bool, optional If True, plots tachogram with specified interval. fbands : dict, optional Dictionary with frequency bands for frequency domain (tuples or list). Value format: (lower_freq_band_boundary, upper_freq_band_boundary) Keys: 'ulf' Ultra low frequency (default: none) optional 'vlf' Very low frequency (default: (0.003Hz, 0.04Hz)) 'lf' Low frequency (default: (0.04Hz - 0.15Hz)) 'hf' High frequency (default: (0.15Hz - 0.4Hz)) show : bool, optional If true, shows all plots (default: True). kwargs_ecg_plot : dict, optional **kwargs for the plot_ecg() function (see 'tools.py' module): .. rpeaks : bool, optional If True, marks R-peaks in ECG signal (default: True). .. title : str, optional Plot figure title (default: None). kwargs_tachogram : dict, optional **kwargs for the plot_tachogram() function (see 'tools.py' module): .. hr : bool, optional If True, plots series of heart rate data in [bpm] (default: True). .. title : str, optional Plot figure title (default: None). kwargs_time : dict, optional **kwargs for the time_domain() function (see 'time_domain()' function) .. threshold : int, optional Custom threshold in [ms] for the NNXX and pNNXX parameters (default: None). .. plot : bool If True, creates histogram plot using matplotlib, else uses numpy (data only, no plot) - (geometrical params). .. binsize : int, float Bin size in [ms] of the histogram bins - (geometrical params). kwargs_welch : dict, optional **kwargs for the 'welch_psd()' function: .. nfft : int, optional Number of points computed for the FFT result (default: 2**12). .. detrend : bool optional If True, detrend NNI series by subtracting the mean NNI (default: True). .. window : scipy window function, optional Window function used for PSD estimation (default: 'hamming'). kwargs_lomb : dict, optional **kwargs for the 'lomb_psd()' function: .. nfft : int, optional Number of points computed for the FFT result (default: 2**8). .. ma_size : int, optional Window size of the optional moving average filter (default: None). kwargs_ar : dict, optional **kwargs for the 'ar_psd()' function: .. nfft : int, optional Number of points computed for the entire AR result (default: 2**12). .. order : int, optional Autoregressive model order (default: 16). kwargs_nonlinear : dict, optional **kwargs for the nonlinear functions (poincare(), sample_enntropy(), dfa()): .. ellipse : bool, optional If true, shows fitted ellipse in plot (default: True). .. vectors : bool, optional If true, shows SD1 and SD2 vectors in plot (default: True). .. legend : bool, optional If True, adds legend to the Poincaré plot (default: True). .. marker : character, optional NNI marker in plot (default: 'o'). .. short : array, 2 elements Interval limits of the short term fluctuations (default: None: [4, 16]). .. long : array, 2 elements Interval limits of the long term fluctuations (default: None: [17, 64]). .. legend : bool If True, adds legend with alpha1 and alpha2 values to the DFA plot (default: True). .. dim : int, optional Entropy embedding dimension (default: 2). .. tolerance : int, float, optional Tolerance distance for which the vectors to be considered equal (default: std(NNI) * 0.2). Returns ------- results : biosppy.utils.ReturnTuple object All time domain results. Returned Parameters - Time Domain --------------------------------- .. NNI parameters (# of NNI, mean, min, max) in [count] and [ms] (keys: 'nni_counter', 'nni_mean', 'nni_min', 'nni_max') .. NNI differences (mean, min, max, standard deviation) in [ms] (keys: 'nni_diff_mean', 'nni_diff_min', 'nn_diff_max') .. HR parameters (mean, min, max, standard deviation) in [BPM] (keys: 'hr_mean', 'hr_min', 'hr_max', 'hr_std') .. SDNN in [ms] (key: 'sdnn') .. SDNN index in [ms] (key: 'sdnn_index') .. SDANN in [ms] (key: 'sdann') .. RMSSD in [ms] (key: 'rmssd') .. SDSD in [ms] (key: 'sdsd') .. nn50 in [count] & pNN50 in [%] (keys: 'nn50', 'pnn50') .. nn20 in [count] & pNN20 in [%] (keys: 'nn20', 'pnn20') .. nnXX (XX = custom threshold) if specified (keys: 'nnXX', 'pnnXX') .. Triangular Index [-] (key: 'tri_index') .. TINN in [ms] (key: 'tinn', 'tinn_n', 'tinn_m') Returned Parameters - Frequency Domain -------------------------------------- (below, X = one of the methods 'fft' or 'lomb') .. Peak frequencies of all frequency bands in [Hz] (key: 'X_peak') .. Absolute powers of all frequency bands in [ms^2] (key: 'X_abs') .. Relative powers of all frequency bands in [%] (key: 'X_rel') .. Logarithmic powers of all frequency bands [-] (key: 'X_log') .. Normalized powers of the LF and HF frequency bands [-] (key: 'X_norms') .. LF/HF ratio [-] (key: 'X_ratio') .. Total power over all frequency bands (key: 'X_total') .. Interpolation method used for NNI interpolation (FFT/Welch's method only) (key: 'fft_interpolation') .. Resampling frequency used for NNI interpolation (FFT/Welch's method only) (key: 'fft_resampling_frequency') .. Spectral window used for PSD estimation of the Welch's method (key: 'fft_spectral_window)' Returned Parameters - Nonlinear ------------------------------- .. SD1 in [ms] (key: 'sd1') .. SD2 in [ms] (key: 'sd2') .. SD2/SD1 [-] (key: 'sd_ratio') .. Area of the fitted ellipse in [ms^2] (key: 'ellipse_area') .. Sample Entropy [-] (key: 'sampen') .. Detrended Fluctuations Analysis [-] (short and long term fluctuations) (key: 'dfa_short', 'dfa_long') Returned Figures ---------------- .. ECG plot (key: 'ecg_plot') (only if ECG signal is provided) .. Tachogram (key: 'tachogram_plot') .. Poincaré plot (key: 'poincare_plot') .. NNI Histogram (key: 'nn_histogram') .. Welch PSD (key: 'fft_plot') .. Lomb PSD (key: 'lomb_plot') .. AR PSD (key: 'ar_plot') .. Poincaré (key: 'pincare_plot') Notes ----- .. Results are stored in a biosppy.utils.ReturnTuple object and need to be accessed with the respective keys as done with dictionaries (see list of parameters and keys above). .. Provide at least one type of input data (ecg_signal, nn, or rpeaks). .. Input data will be prioritized in the following order: 1. ecg_signal, 2. nn, 3. rpeaks. .. SDNN Index and SDANN: In some cases, the NN interval may start in a segment (or time interval) N and end only in the successive segment N+1. In this case, use the 'overlap' parameter to select if the first element of the segment should be dropped or not: .. If True: overlap allowed, returns all NNI but the cumulative sum of the NNI in a segment can be greater than the specified duration. .. If False: no overlap allowed, first NNI will be dropped and the cumulative sum of the NNI in a segment will always be < specified duration. Raises ------ TypeError If no input data for 'signal', 'nn' and 'rpeaks' provided. """ # Check input if signal is not None: signal, rpeaks = biosppy.signals.ecg.ecg(signal=signal, sampling_rate=sampling_rate, show=False)[1:3] elif nni is None and rpeaks is None: raise TypeError('No input data provided. Please specify input data.') nn = tools.check_input(nni, rpeaks) version = utils.ReturnTuple(('v.' + pyhrv.__version__, ), ('version', )) # COMPUTE TIME DOMAIN PARAMETERS # Check for kwargs for the 'kwargs_time' if kwargs_time is not None: if type(kwargs_time) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_time' must be a dictionary containing " "parameters (keys) and values for the 'time_domain()' function." % type(kwargs_time)) # Supported kwargs available_kwargs = ['threshold', 'binsize', 'plot'] # Unwrwap kwargs dictionary threshold = kwargs_time['threshold'] if 'threshold' in kwargs_time.keys() else None binsize = kwargs_time['binsize'] if 'binsize' in kwargs_time.keys() else 7.8125 plot = kwargs_time['plot'] if 'plot' in kwargs_time.keys() else True unsupported_kwargs = [] for args in kwargs_time.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'time_domain()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Time Domain Parameters t_results = td.time_domain(nni=nn, show=False, threshold=threshold, plot=plot) else: # Compute Welch's PSD with default values t_results = td.time_domain(nni=nn, show=False) # COMPUTE FREQUENCY DOMAIN RESULTS (kwargs are verified by the frequency_domain() function) f_results = fd.frequency_domain(nni=nn, fbands=fbands, kwargs_welch=kwargs_welch, kwargs_lomb=kwargs_lomb, kwargs_ar=kwargs_ar, show=False) # COMPUTE NONLINEAR PARAMETERS if kwargs_nonlinear is not None: if type(kwargs_nonlinear) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_nonlinear' must be a dictionary containing " "parameters (keys) and values for the 'nonlinear()' function." % type(kwargs_time)) # Supported kwargs available_kwargs = ['ellipse', 'vectors', 'legend', 'marker', 'dim', 'tolerance', 'short', 'long', 'legend'] kwargs_poincare = {} kwargs_sampen = {} kwargs_dfa = {} # Unwrwap kwargs dictionaries kwargs_poincare['ellipse'] = kwargs_nonlinear['ellipse'] if 'ellipse' in kwargs_nonlinear.keys() else True kwargs_poincare['vectors'] = kwargs_nonlinear['vectors'] if 'vectors' in kwargs_nonlinear.keys() else True kwargs_poincare['legend'] = kwargs_nonlinear['legend'] if 'legend' in kwargs_nonlinear.keys() else True kwargs_poincare['marker'] = kwargs_nonlinear['marker'] if 'marker' in kwargs_nonlinear.keys() else 'o' kwargs_sampen['dim'] = kwargs_nonlinear['dim'] if 'dim' in kwargs_nonlinear.keys() else 2 kwargs_sampen['tolerance'] = kwargs_nonlinear['tolerance'] if 'tolerance' in kwargs_nonlinear.keys() else None kwargs_dfa['short'] = kwargs_nonlinear['short'] if 'short' in kwargs_nonlinear.keys() else None kwargs_dfa['long'] = kwargs_nonlinear['long'] if 'long' in kwargs_nonlinear.keys() else None kwargs_dfa['legend'] = kwargs_nonlinear['legend'] if 'legend' in kwargs_nonlinear.keys() else True unsupported_kwargs = [] for args in kwargs_nonlinear.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'nonlinear()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) n_results = nl.nonlinear(nni=nn, show=False, kwargs_poincare=kwargs_poincare, kwargs_sampen=kwargs_sampen, kwargs_dfa=kwargs_dfa) else: n_results = nl.nonlinear(nni=nn, show=False) # Prepare output results = tools.join_tuples(t_results, f_results, n_results) # Plot ECG signal if plot_ecg and signal is not None: # COMPUTE NONLINEAR PARAMETERS if kwargs_ecg_plot is not None: if type(kwargs_ecg_plot) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_ecg_plot' must be a dictionary containing " "parameters (keys) and values for the 'plot_ecg()' function." % type(kwargs_ecg_plot)) # Supported kwargs available_kwargs = ['rpeaks', 'title'] # Unwrwap kwargs dictionaries show_rpeaks = kwargs_ecg_plot['rpeaks'] if 'rpeaks' in kwargs_ecg_plot.keys() else True title = kwargs_ecg_plot['title'] if 'title' in kwargs_ecg_plot.keys() else None unsupported_kwargs = [] for args in kwargs_ecg_plot.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'plot_ecg()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) ecg_plot = tools.plot_ecg(signal=signal, show=False, rpeaks=show_rpeaks, title=title, interval=interval, sampling_rate=sampling_rate) else: ecg_plot = tools.plot_ecg(signal=signal, sampling_rate=sampling_rate, show=False, interval=interval) results = tools.join_tuples(results, ecg_plot) # Plot Tachogram if plot_tachogram: # COMPUTE NONLINEAR PARAMETERS if kwargs_tachogram is not None: if type(kwargs_tachogram) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_tachogram' must be a dictionary containing " "parameters (keys) and values for the 'tachogram()' function." % type(kwargs_tachogram)) # Supported kwargs available_kwargs = ['hr', 'title'] # Unwrwap kwargs dictionaries hr = kwargs_tachogram['hr'] if 'hr' in kwargs_tachogram.keys() else True title = kwargs_tachogram['title'] if 'title' in kwargs_tachogram.keys() else None unsupported_kwargs = [] for args in kwargs_tachogram.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'tachogram()': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) tachogram_plot = tools.tachogram(nni=nn, sampling_rate=sampling_rate, hr=hr, interval=interval, title=title, show=False) else: tachogram_plot = tools.tachogram(nni=nn, show=False, interval=interval) results = tools.join_tuples(results, tachogram_plot) if show: plt.show() # Output return results
import numpy as np # Get Sample Data nni = np.load('./files/SampleNNISeries.npy') # Compute Poincaré res1 = poincare(nni, show=False) # Compute Triangular Index res2 = sample_entropy(nni) # Compute Detrended Fluctuation Analysis res3 = dfa(nni, show=False) # Join results results = tools.join_tuples(res1, res2, res3) # Results print("=========================") print("NONLINEAR ANALYSIS") print("=========================") print("Poincaré Plot") print("SD1: %f [ms]" % results['sd1']) print("SD2: %f [ms]" % results['sd2']) print("SD2/SD1: %f [ms]" % results['sd_ratio']) print("Area S: %f [ms]" % results['ellipse_area']) print("Sample Entropy: %f" % results['sampen']) print("DFA: %f [ms]") # Alternatively use the nonlinear() function to compute all the nonlinear parameters at once nonlinear(nni=nni)
def frequency_domain(nni=None, rpeaks=None, signal=None, sampling_rate=1000., fbands=None, show=False, show_param=True, legend=True, kwargs_welch=None, kwargs_lomb=None, kwargs_ar=None): """Computes PSDs (Welch, Lomb, and Autoregressive), the parameters of the frequency domain, and returns the results in a ReturnTuple object. References: [Electrophysiology1996], [Kuusela2014], [Kallas2012], [Boardman2002], [Lomb1976], [Scargle1982], [Laguna1995], [Umberto2017], [Welch2017], [Basak2014] Docs: https://pyhrv.readthedocs.io/en/latest/_pages/api/frequency.html#module-level-function-frequency-domain Parameters ---------- nni : array_like NN intervals in (ms) or (s) rpeaks : array_like R-peak times in (ms) or (s) signal : array_like ECG signal sampling_rate : int, float Sampling rate used for the ECG acquisition in (Hz) fbands : dict, optional Dictionary with frequency bands (tuples or list) Value format: (lower_freq_band_boundary, upper_freq_band_boundary) Keys: 'ulf' Ultra low frequency (default: none) optional 'vlf' Very low frequency (default: (0.003Hz, 0.04Hz)) 'lf' Low frequency (default: (0.04Hz - 0.15Hz)) 'hf' High frequency (default: (0.15Hz - 0.4Hz)) show : bool, optional If True, show PSD plots show_param : bool, optional If True, list all computed parameters next to the plots legend : bool, optional If True, adds legends to the PSD plots kwargs_welch : dict, optional Dictionary containing the kwargs for the 'welch_psd()' function: .. nfft : int, optional Number of points computed for the FFT result (default: 2**12) .. detrend : bool optional If True, detrend NNI series by subtracting the mean NNI (default: True) .. window : scipy window function, optional Window function used for PSD estimation (default: 'hamming') kwargs_lomb : dict, optional Dictionary containing the kwargs for the 'lomb_psd()' function: .. nfft : int, optional Number of points computed for the FFT result (default: 2**8) .. ma_size : int, optional Window size of the optional moving average filter (default: None) kwargs_ar : dict, optional Dictionary containing the kwargs for the 'ar_psd()' function: .. nfft : int, optional Number of points computed for the entire AR result (default: 2**12) .. order : int, optional Autoregressive model order (default: 16) Returns ------- results : biosppy.utils.ReturnTuple object All results of the frequency domain PSD estimation methods (see list and keys below) Returned Parameters & Keys -------------------------- (below, X = one of the methods 'fft', 'ar' or 'lomb') .. Peak frequencies of all frequency bands in [Hz] (key: 'X_peak') .. Absolute powers of all frequency bands in [ms^2] (key: 'X_abs') .. Relative powers of all frequency bands in [%] (key: 'X_rel') .. Logarithmic powers of all frequency bands in [-] (key: 'X_log') .. Normalized powers of all frequency bands in [-] (key: 'X_norms') .. LF/HF ratio in [-] (key: 'X_ratio') .. Total power over all frequency bands in [ms^2] (key: 'X_total') .. Number of PSD samples (key: 'X_nfft') .. FFT-specific: Interpolation method used for NNI interpolation (key: 'fft_interpolation') .. FFT-specific: Resampling frequency used for NNI interpolation (key: 'fft_resampling_frequency') .. FFT-specific: Window function used for PSD estimation of the Welch's method (key: 'fft_window) .. Lomb-specific: Moving average window size (key: 'lomb_ma') .. AR-specific: Model order (key: 'ar_order') .. AR-specific: Interpolation method used for NNI interpolation (key: 'ar_interpolation') .. AR-specific: Resampling frequency used for NNI interpolation (key: 'ar_resampling_frequency') Notes ----- .. The returned BioSPPy ReturnTuple object contains all frequency band parameters in parameter specific tuples of length 4 when using the ULF frequency band or of length 3 when NOT using the ULF frequency band. The structures of those tuples are shown in this example below (fft_results = ReturnTuple object returned by this function): Using ULF, VLF, LF and HF frequency bands: fft_results['fft_peak'] = (ulf_peak, vlf_peak, lf_peak, hf_peak) Using VLF, LF and HF frequency bands: fft_results['fft_peak'] = (vlf_peak, lf_peak, hf_peak) .. If 'show_param' is true, the parameters (incl. frequency band limits) will be listed next to the graph and no legend with frequency band limits will be added to the plot graph itself, i.e. the effect of 'show_param' will be used over the 'legend' effect .. 'fbands' and 'show' will be selected for all methods. Individually adding specific 'fbands' to 'kwargs_welch' or 'kwargs_lomb' will have no effect .. Select the 'nfft' individually for each method using the kwargs dictionaries of the respective method(s) """ # Check input if signal is not None: rpeaks = biosppy.ecg.ecg(signal=signal, sampling_rate=sampling_rate, show=False)[2] elif nni is None and rpeaks is None: raise TypeError('No input data provided. Please specify input data.') # Get NNI series nn = tools.check_input(nni, rpeaks) # Check for kwargs for the 'welch_psd' function and compute the PSD if kwargs_welch is not None: if type(kwargs_welch) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_welch' must be a dictionary containing " "parameters (keys) and values for the 'welch_psd' function." % type(kwargs_welch)) # Supported kwargs available_kwargs = ['fbands', 'detrend', 'show', 'show_param', 'legend', 'window', 'nfft'] # Unwrwap kwargs dictionary for Welch specific parameters detrend = kwargs_welch['detrend'] if 'detrend' in kwargs_welch.keys() else True window = kwargs_welch['window'] if 'window' in kwargs_welch.keys() else 'hamming' nfft = kwargs_welch['nfft'] if 'nfft' in kwargs_welch.keys() else 2**12 unsupported_kwargs = [] for args in kwargs_welch.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'welch_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Welch's PSD with custom parameter settings welch_results = welch_psd(nn, fbands=fbands, detrend=detrend, show=False, show_param=show_param, legend=legend, nfft=nfft, window=window) else: # Compute Welch's PSD with default values welch_results = welch_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # Check for kwargs for the 'welch_psd' function and compute the PSD if kwargs_lomb is not None: if type(kwargs_lomb) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_lomb' must be a dictionary containing " "parameters (keys) and values for the 'kwargs_lomb' function." % type(kwargs_lomb)) # Supported kwargs available_kwargs = ['fbands', 'ma_size', 'show', 'show_param', 'legend', 'nfft', ''] # Unwrwap kwargs dictionary nfft = kwargs_lomb['nfft'] if 'nfft' in kwargs_lomb.keys() else 2**8 ma_size = kwargs_lomb['ma_size'] if 'ma_size' in kwargs_lomb.keys() else None unsupported_kwargs = [] for args in kwargs_lomb.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'lomb_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Welch's PSD with custom parameter settings lomb_results = lomb_psd(nn, fbands=fbands, ma_size=ma_size, show=False, show_param=show_param, legend=legend, nfft=nfft) else: # Compute Welch's PSD with default values lomb_results = lomb_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # Check for kwargs for the 'ar_psd' function and compute the PSD if kwargs_ar is not None: if type(kwargs_ar) is not dict: raise TypeError("Expected <type 'dict'>, got %s: 'kwargs_ar' must be a dictionary containing " "parameters (keys) and values for the 'ar_psd' function." % type(kwargs_ar)) # Supported kwargs available_kwargs = ['fbands', 'show', 'order', 'show_param', 'legend', 'window', 'nfft'] # Unwrwap kwargs dictionary for Welch specific parameters nfft = kwargs_ar['nfft'] if 'nfft' in kwargs_ar.keys() else 2**12 order = kwargs_ar['order'] if 'order' in kwargs_ar.keys() else 16 unsupported_kwargs = [] for args in kwargs_ar.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn("Unknown kwargs for 'welch_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Autoregressive PSD with custom parameter settings ar_results = ar_psd(nn, fbands=fbands, order=order, show=False, show_param=show_param, legend=legend, nfft=nfft) else: # Compute Autoregressive PSD with default values ar_results = ar_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # If plots should be shown (show all plots at once) if show: plt.show() # Output return tools.join_tuples(welch_results, lomb_results, ar_results)
def time_domain(nni=None, rpeaks=None, signal=None, sampling_rate=1000., threshold=None, plot=True, show=False, binsize=7.8125): """Computes all time domain parameters of the HRV time domain module and returns them in a ReturnTuple object. Parameters ---------- nni : array NN intervals in [ms] or [s]. rpeaks : array R-peak times in [ms] or [s]. signal : array ECG signal. sampling_rate : int, float, optional Sampling rate used for the ECG acquisition in [Hz] (default: 1000.). threshold : int, optional Custom threshold in [ms] for the NNXX and pNNXX parameters (default: None). plot : bool If True, creates histogram plot using matplotlib, else uses numpy (data only, no plot) - (geometrical params). figsize : array, optional Matplotlib figure size for the histogram (width, height) (default: (6, 6)) - (geometrical params). binsize : int, float Bin size in [ms] of the histogram bins - (geometrical params). legend : bool If True, highlights D(X) marker to the plot to be added to the legends (default=True) - (geometrical params). Returns ------- results : biosppy.utils.ReturnTuple object All time domain results (see list and keys below) Returned Parameters ------------------- .. NNI parameters (# of NNI, mean, min, max) in [count] and [ms] (keys: 'nni_counter', 'nni_mean', 'nni_min', 'nni_max') .. NNI differences (mean, min, max, standard deviation) in [ms] (keys: 'nni_diff_mean', 'nni_diff_min', 'nn_diff_max') .. HR parameters (mean, min, max, standard deviation) in [BPM] (keys: 'hr_mean', 'hr_min', 'hr_max', 'hr_std') .. SDNN in [ms] (key: 'sdnn') .. SDNN index in [ms] (key: 'sdnn_index') .. SDANN in [ms] (key: 'sdann') .. RMSSD in [ms] (key: 'rmssd') .. SDSD in [ms] (key: 'sdsd') .. nn50 in [count] & pNN50 in [%] (keys: 'nn50', 'pnn50') .. nn20 in [count] & pNN20 in [%] (keys: 'nn20', 'pnn20') .. nnXX (XX = custom threshold) if specified (keys: 'nnXX', 'pnnXX') .. Triangular Index [-] (key: 'tri_index') .. TINN in [ms] (key: 'tinn', 'tinn_n', 'tinn_m') .. NNI histogram (key: 'nni_histogram') Notes ----- .. Results are stored in a biosppy.utils.ReturnTuple object and need to be accessed with the respective keys as done with dictionaries (see list of parameters and keys above). .. Only one type of input data is required (signal, nni, or rpeaks). .. Input data will be prioritized in the following order: 1. signal, 2. nni, 3. rpeaks. .. SDNN Index and SDANN: In some cases, the NN interval may start in a segment (or .. Default bin size set to recommended bin size of 1/128 (with 128Hz being the minimum recommended sampling frequency) as recommended by the HRV guidelines. .. 'show' has only effect if 'plot' is also True. .. 'legend' has only effect if 'plot' is also True. .. 'figsize' has only effect if 'plot' is also True. Raises ------ TypeError If no input data for 'nni', 'rpeaks', and 'signal' provided. """ # Check input if signal is not None: rpeaks = ecg(signal=signal, sampling_rate=sampling_rate, show=False)[2] elif nni is None and rpeaks is None: raise TypeError('No input data provided. Please specify input data.') # Get NNI series nn = tools.check_input(nni, rpeaks) # Call time domain functions & wrap results in a single biosspy.utils.ReturnTuple object results = nni_parameters(nn) results = tools.join_tuples(results, hr_parameters(nn)) results = tools.join_tuples(results, nni_differences_parameters(nn)) results = tools.join_tuples(results, sdnn(nn)) results = tools.join_tuples(results, sdnn_index(nn)) results = tools.join_tuples(results, sdann(nn)) results = tools.join_tuples(results, rmssd(nn)) results = tools.join_tuples(results, sdsd(nn)) results = tools.join_tuples(results, nn50(nn)) results = tools.join_tuples(results, nn20(nn)) # Compute custom threshold if required if threshold is not None: results = tools.join_tuples(results, nnXX(nn, threshold=int(threshold))) # Compute geometrical parameters results = tools.join_tuples( results, geometrical_parameters(nn, plot=plot, show=show, binsize=binsize)) # Output return results
def ar_psd(nni=None, rpeaks=None, fbands=None, nfft=2**12, order=16, show=True, show_param=True, legend=True, mode='normal'): """Computes a Power Spectral Density (PSD) estimation from the NNI series using the Autoregressive method and computes all frequency domain parameters from this PSD according to the specified frequency bands. References: [Electrophysiology1996], [Kuusela2014], [Kallas2012], [Boardman2002] (additionally recommended: [Miranda2012]) Docs: https://pyhrv.readthedocs.io/en/latest/_pages/api/frequency.html#autoregressive-method-ar-psd Parameters ---------- rpeaks : array R-peak locations in [ms] or [s] nni : array NN-Intervals in [ms] or [s] fbands : dict, optional Dictionary with frequency bands (2-element tuples or list) Value format: (lower_freq_band_boundary, upper_freq_band_boundary) Keys: 'ulf' Ultra low frequency (default: none) optional 'vlf' Very low frequency (default: (0.003Hz, 0.04Hz)) 'lf' Low frequency (default: (0.04Hz - 0.15Hz)) 'hf' High frequency (default: (0.15Hz - 0.4Hz))´ nfft : int, optional Number of points computed for the entire AR result (default: 2**12) order : int, optional Autoregressive model order (default: 16) show : bool, optional If true, show PSD plot (default: True) show_param : bool, optional If true, list all computed PSD parameters next to the plot (default: True) legend : bool, optional If true, add a legend with frequency bands to the plot (default: True) Returns ------- results : biosppy.utils.ReturnTuple object All results of the Autoregressive PSD estimation (see list and keys below) Returned Parameters & Keys -------------------------- .. Peak frequencies of all frequency bands in [Hz] (key: 'ar_peak') .. Absolute powers of all frequency bands in [ms^2][(key: 'ar_abs') .. Relative powers of all frequency bands [%] (key: 'ar_rel') .. Logarithmic powers of all frequency bands [-] (key: 'ar_log') .. Normalized powers of all frequency bands [-] (key: 'ar_norms') .. LF/HF ratio [-] (key: 'ar_ratio') .. Total power over all frequency bands in [ms^2] (key: 'ar_total') .. Interpolation method (key: 'ar_interpolation') .. Resampling frequency (key: 'ar_resampling_frequency') .. AR model order (key: 'ar_order') .. Number of PSD samples (key: 'ar_nfft') Notes ----- .. The returned BioSPPy ReturnTuple object contains all frequency band parameters in parameter specific tuples of length 4 when using the ULF frequency band or of length 3 when NOT using the ULF frequency band. The structures of those tuples are shown in this example below (lomb_results = ReturnTuple object returned by this function): Using ULF, VLF, LF and HF frequency bands: lomb['ar_peak'] = (ulf_peak, vlf_peak, lf_peak, hf_peak) Using VLF, LF and HF frequency bands: lomb['ar_peak'] = (vlf_peak, lf_peak, hf_peak) .. If 'show_param' is true, the parameters (incl. frequency band limits) will be listed next to the graph and no legend with frequency band limits will be added to the plot graph itself, i.e. the effect of 'show_param' will be used over the 'legend' effect. .. Only one type of input data is required. .. If both 'nni' and 'rpeaks' are provided, 'rpeaks' will be chosen over the 'nni' and the 'nni' data will be computed from the 'rpeaks'. .. NN and R-peak series provided in [s] format will be converted to [ms] format. """ # Check input nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = _check_freq_bands(fbands) # Resampling (with 4Hz) and interpolate # Because RRi are unevenly spaced we must interpolate it for accurate PSD estimation. fs = 4 t = np.cumsum(nn) t -= t[0] f_interpol = sp.interpolate.interp1d(t, nn, 'cubic') t_interpol = np.arange(t[0], t[-1], 1000./fs) nn_interpol = f_interpol(t_interpol) # Compute autoregressive PSD ar = spectrum.pyule(data=nn_interpol, order=order, NFFT=nfft, sampling=fs, scale_by_freq=False) ar() # Get frequencies and powers frequencies = np.asarray(ar.frequencies()) psd = np.asarray(ar.psd) powers = np.asarray(10 * np.log10(psd) * 10**3) # * 10**3 to compensate with ms^2 to s^2 conversion # in the upcoming steps # Define metadata meta = utils.ReturnTuple((nfft, order, fs, 'cubic'), ('ar_nfft', 'ar_order', 'ar_resampling_frequency', 'ar_interpolation')) if mode not in ['normal', 'dev', 'devplot']: warnings.warn("Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # Compute frequency parameters params, freq_i = _compute_parameters('ar', frequencies, powers, fbands) # Plot PSD figure = _plot_psd('ar', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('ar_plot', )) # Complete output return tools.join_tuples(params, figure) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = _compute_parameters('ar', frequencies, powers, fbands) # Output return tools.join_tuples(params, meta), frequencies, (powers / 10 ** 6) # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # Compute frequency parameters params, freq_i = _compute_parameters('ar', frequencies, powers, fbands) # Plot PSD figure = _plot_psd('ar', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('ar_plot', )) # Complete output return tools.join_tuples(params, figure, meta), frequencies, (powers / 10 ** 6)
def welch_psd(nni=None, rpeaks=None, fbands=None, nfft=2**12, detrend=True, window='hamming', show=True, show_param=True, legend=True, mode='normal'): """Computes a Power Spectral Density (PSD) estimation from the NNI series using the Welch’s method and computes all frequency domain parameters from this PSD according to the specified frequency bands. References: [Electrophysiology1996], [Umberto2017], [Welch2017] Docs: https://pyhrv.readthedocs.io/en/latest kwa/_pages/api/frequency.html#welch-s-method-welch-psd Parameters ---------- nni : array NN-Intervals in [ms] or [s] rpeaks : array R-peak locations in [ms] or [s] fbands : dict, optional Dictionary with frequency bands (2-element tuples or list) Value format: (lower_freq_band_boundary, upper_freq_band_boundary) Keys: 'ulf' Ultra low frequency (default: none) optional 'vlf' Very low frequency (default: (0.000Hz, 0.04Hz)) 'lf' Low frequency (default: (0.04Hz - 0.15Hz)) 'hf' High frequency (default: (0.15Hz - 0.4Hz)) nfft : int, optional Number of points computed for the FFT result (default: 2**12) detrend : bool optional If True, detrend NNI series by subtracting the mean NNI (default: True) window : scipy window function, optional Window function used for PSD estimation (default: 'hamming') show : bool, optional If true, show PSD plot (default: True) show_param : bool, optional If true, list all computed PSD parameters next to the plot (default: True) legend : bool, optional If true, add a legend with frequency bands to the plot (default: True) Returns ------- results : biosppy.utils.ReturnTuple object All results of the Welch's method's PSD estimation (see list and keys below) Returned Parameters & Keys -------------------------- .. Peak frequencies of all frequency bands in [Hz] (key: 'fft_peak') .. Absolute powers of all frequency bands in [ms^2][(key: 'fft_abs') .. Relative powers of all frequency bands [%] (key: 'fft_rel') .. Logarithmic powers of all frequency bands [-] (key: 'fft_log') .. Normalized powers of all frequency bands [-] (key: 'fft_norms') .. LF/HF ratio [-] (key: 'fft_ratio') .. Total power over all frequency bands in [ms^2] (key: 'fft_total') .. Interpolation method used for NNI interpolation (key: 'fft_interpolation') .. Resampling frequency used for NNI interpolation (key: 'fft_resampling_frequency') .. Spectral window used for PSD estimation of the Welch's method (key: 'fft_spectral_window)' Notes ----- .. The returned BioSPPy ReturnTuple object contains all frequency band parameters in parameter specific tuples of length 4 when using the ULF frequency band or of length 3 when NOT using the ULF frequency band. The structures of those tuples are shown in this example below (fft_results = ReturnTuple object returned by this function): Using ULF, VLF, LF and HF frequency bands: fft_results['fft_peak'] = (ulf_peak, vlf_peak, lf_peak, hf_peak) Using VLF, LF and HF frequency bands: fft_results['fft_peak'] = (vlf_peak, lf_peak, hf_peak) .. If 'show_param' is true, the parameters (incl. frequency band limits) will be listed next to the graph and no legend with frequency band limits will be added to the plot graph itself, i.e. the effect of 'show_param' will be used over the 'legend' effect. .. Only one type of input data is required. .. If both 'nni' and 'rpeaks' are provided, 'rpeaks' will be chosen over the 'nni' and the 'nni' data will be computed from the 'rpeaks'. .. NN and R-peak series provided in [s] format will be converted to [ms] format. """ # Check input values nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = _check_freq_bands(fbands) # Resampling (with 4Hz) and interpolate # Because RRi are unevenly spaced we must interpolate it for accurate PSD estimation. fs = 4 t = np.cumsum(nn) t -= t[0] f_interpol = sp.interpolate.interp1d(t, nn, 'cubic') t_interpol = np.arange(t[0], t[-1], 1000./fs) nn_interpol = f_interpol(t_interpol) # Subtract mean value from each sample for surpression of DC-offsets if detrend: nn_interpol = nn_interpol - np.mean(nn_interpol) # Adapt 'nperseg' according to the total duration of the NNI series (5min threshold = 300000ms) if t.max() < 300000: nperseg = nfft else: nperseg = 300 # Compute power spectral density estimation (where the magic happens) frequencies, powers = welch( x=nn_interpol, fs=fs, window=window, nperseg=nperseg, nfft=nfft, scaling='density' ) # Metadata args = (nfft, window, fs, 'cubic') names = ('fft_nfft', 'fft_window', 'fft_resampling_frequency', 'fft_interpolation',) meta = utils.ReturnTuple(args, names) if mode not in ['normal', 'dev', 'devplot']: warnings.warn("Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # Compute frequency parameters params, freq_i = _compute_parameters('fft', frequencies, powers, fbands) # Plot PSD figure = _plot_psd('fft', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('fft_plot', )) # Output return tools.join_tuples(params, figure, meta) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = _compute_parameters('fft', frequencies, powers, fbands) # Output return tools.join_tuples(params, meta), frequencies, (powers / 10 ** 6) # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # Compute frequency parameters params, freq_i = _compute_parameters('fft', frequencies, powers, fbands) # Plot PSD figure = _plot_psd('fft', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('fft_plot', )) # Output return tools.join_tuples(params, figure, meta), frequencies, (powers / 10 ** 6)
def lomb_psd( nni=None, rpeaks=None, fbands=None, nfft=2**8, ma_size=None, show=True, show_param=True, legend=True, mode='normal' ): """Computes a Power Spectral Density (PSD) estimation from the NNI series using the Lomb-Scargle Periodogram and computes all frequency domain parameters from this PSD according to the specified frequency bands. References: [Lomb1976], [Scargle1982], [Kuusela2014], [Laguna1995] Docs: https://pyhrv.readthedocs.io/en/latest/_pages/api/frequency.html#lomb-scargle-periodogram-lomb-psd Parameters ---------- rpeaks : array R-peak locations in [ms] or [s] nni : array NN-Intervals in [ms] or [s] fbands : dict, optional Dictionary with frequency bands (2-element tuples or list) Value format: (lower_freq_band_boundary, upper_freq_band_boundary) Keys: 'ulf' Ultra low frequency (default: none) optional 'vlf' Very low frequency (default: (0.003Hz, 0.04Hz)) 'lf' Low frequency (default: (0.04Hz - 0.15Hz)) 'hf' High frequency (default: (0.15Hz - 0.4Hz))´ nfft : int, optional Number of points computed for the FFT result (default: 2**8) ma_size : int, optional Window size of the optional moving average filter (default: None) show : bool, optional If true, show PSD plot (default: True) show_param : bool, optional If true, list all computed PSD parameters next to the plot (default: True) legend : bool, optional If true, add a legend with frequency bands to the plot (default: True) Returns ------- results : biosppy.utils.ReturnTuple object All results of the Lomb-Scargle PSD estimation (see list and keys below) Returned Parameters & Keys -------------------------- .. Peak frequencies of all frequency bands in [Hz] (key: 'lomb_peak') .. Absolute powers of all frequency bands in [ms^2][(key: 'lomb_abs') .. Relative powers of all frequency bands [%] (key: 'lomb_rel') .. Logarithmic powers of all frequency bands [-] (key: 'lomb_log') .. Normalized powers of all frequency bands [-] (key: 'lomb_norms') .. LF/HF ratio [-] (key: 'lomb_ratio') .. Total power over all frequency bands in [ms^2] (key: 'lomb_total') .. Number of PSD samples (key: 'lomb_nfft') .. Moving average filter order (key: 'lomb_ma') Notes ----- .. The returned BioSPPy ReturnTuple object contains all frequency band parameters in parameter specific tuples of length 4 when using the ULF frequency band or of length 3 when NOT using the ULF frequency band. The structures of those tuples are shown in this example below (lomb_results = ReturnTuple object returned by this function): Using ULF, VLF, LF and HF frequency bands: lomb['fft_peak'] = (ulf_peak, vlf_peak, lf_peak, hf_peak) Using VLF, LF and HF frequency bands: lomb['fft_peak'] = (vlf_peak, lf_peak, hf_peak) .. If 'show_param' is true, the parameters (incl. frequency band limits) will be listed next to the graph and no legend with frequency band limits will be added to the plot graph itself, i.e. the effect of 'show_param' will be used over the 'legend' effect. .. Only one type of input data is required. .. If both 'nni' and 'rpeaks' are provided, 'rpeaks' will be chosen over the 'nni' and the 'nni' data will be computed from the 'rpeaks'. .. NN and R-peak series provided in [s] format will be converted to [ms] format. """ # Check input nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = _check_freq_bands(fbands) t = np.cumsum(nn) t -= t[0] # Compute PSD according to the Lomb-Scargle method # Specify frequency grid frequencies = np.linspace(0, 0.41, nfft) # Compute angular frequencies a_frequencies = np.asarray(2 * np.pi / frequencies) powers = np.asarray(lombscargle(t, nn, a_frequencies, normalize=True)) # Fix power = inf at f=0 powers[0] = 2 # Apply moving average filter if ma_size is not None: powers = biosppy.signals.tools.smoother(powers, size=ma_size)['signal'] # Define metadata meta = utils.ReturnTuple((nfft, ma_size, ), ('lomb_nfft', 'lomb_ma')) if mode not in ['normal', 'dev', 'devplot']: warnings.warn("Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # ms^2 to s^2 powers = powers * 10 ** 6 # Compute frequency parameters params, freq_i = _compute_parameters('lomb', frequencies, powers, fbands) # Plot parameters figure = _plot_psd('lomb', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('lomb_plot', )) # Complete output return tools.join_tuples(params, figure, meta) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = _compute_parameters('lomb', frequencies, powers, fbands) # Complete output return tools.join_tuples(params, meta), frequencies, powers # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # ms^2 to s^2 powers = powers * 10**6 # Compute frequency parameters params, freq_i = _compute_parameters('lomb', frequencies, powers, fbands) # Plot parameters figure = _plot_psd('lomb', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('lomb_plot', )) # Complete output return tools.join_tuples(params, figure, meta), frequencies, powers
def frequency_domain(self, nni=None, rpeaks=None, signal=None, sampling_rate=1000., fbands=None, show=False, show_param=True, legend=True, kwargs_welch=None, kwargs_lomb=None, kwargs_ar=None): # Check input if signal is not None: rpeaks = biosppy.ecg.ecg(signal=signal, sampling_rate=sampling_rate, show=False)[2] elif nni is None and rpeaks is None: raise TypeError( 'No input data provided. Please specify input data.') # Get NNI series nn = tools.check_input(nni, rpeaks) # Check for kwargs for the 'welch_psd' function and compute the PSD if kwargs_welch is not None: if type(kwargs_welch) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_welch' must be a dictionary containing " "parameters (keys) and values for the 'welch_psd' function." % type(kwargs_welch)) # Supported kwargs available_kwargs = [ 'fbands', 'detrend', 'show', 'show_param', 'legend', 'window', 'nfft' ] # Unwrwap kwargs dictionary for Welch specific parameters detrend = kwargs_welch[ 'detrend'] if 'detrend' in kwargs_welch.keys() else True window = kwargs_welch['window'] if 'window' in kwargs_welch.keys( ) else 'hamming' nfft = kwargs_welch['nfft'] if 'nfft' in kwargs_welch.keys( ) else 2**12 unsupported_kwargs = [] for args in kwargs_welch.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'welch_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Welch's PSD with custom parameter settings welch_results = self.welch_psd(nn, fbands=fbands, detrend=detrend, show=False, show_param=show_param, legend=legend, nfft=nfft, window=window) else: # Compute Welch's PSD with default values welch_results = self.welch_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # Check for kwargs for the 'welch_psd' function and compute the PSD if kwargs_lomb is not None: if type(kwargs_lomb) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_lomb' must be a dictionary containing " "parameters (keys) and values for the 'kwargs_lomb' function." % type(kwargs_lomb)) # Supported kwargs available_kwargs = [ 'fbands', 'ma_size', 'show', 'show_param', 'legend', 'nfft', '' ] # Unwrwap kwargs dictionary nfft = kwargs_lomb['nfft'] if 'nfft' in kwargs_lomb.keys( ) else 2**8 ma_size = kwargs_lomb['ma_size'] if 'ma_size' in kwargs_lomb.keys( ) else None unsupported_kwargs = [] for args in kwargs_lomb.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'lomb_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Welch's PSD with custom parameter settings lomb_results = self.lomb_psd(nn, fbands=fbands, ma_size=ma_size, show=False, show_param=show_param, legend=legend, nfft=nfft) else: # Compute Welch's PSD with default values lomb_results = self.lomb_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # Check for kwargs for the 'ar_psd' function and compute the PSD if kwargs_ar is not None: if type(kwargs_ar) is not dict: raise TypeError( "Expected <type 'dict'>, got %s: 'kwargs_ar' must be a dictionary containing " "parameters (keys) and values for the 'ar_psd' function." % type(kwargs_ar)) # Supported kwargs available_kwargs = [ 'fbands', 'show', 'order', 'show_param', 'legend', 'window', 'nfft' ] # Unwrwap kwargs dictionary for Welch specific parameters nfft = kwargs_ar['nfft'] if 'nfft' in kwargs_ar.keys() else 2**12 order = kwargs_ar['order'] if 'order' in kwargs_ar.keys() else 16 unsupported_kwargs = [] for args in kwargs_ar.keys(): if args not in available_kwargs: unsupported_kwargs.append(args) # Throw warning if additional unsupported kwargs have been provided if unsupported_kwargs: warnings.warn( "Unknown kwargs for 'welch_psd': %s. These kwargs have no effect." % unsupported_kwargs, stacklevel=2) # Compute Autoregressive PSD with custom parameter settings ar_results = self.ar_psd(nn, fbands=fbands, order=order, show=False, show_param=show_param, legend=legend, nfft=nfft) else: # Compute Autoregressive PSD with default values ar_results = self.ar_psd(nn, show=False, fbands=fbands, legend=legend, show_param=show_param) # If plots should be shown (show all plots at once) if show: plt.show() # Output return tools.join_tuples(welch_results, lomb_results, ar_results)
def welch_psd(self, nni=None, rpeaks=None, fbands=None, nfft=2**12, detrend=True, window='hamming', show=True, show_param=True, legend=True, mode='normal'): # Check input values nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = self._check_freq_bands(fbands) # Resampling (with 4Hz) and interpolate # Because RRi are unevenly spaced we must interpolate it for accurate PSD estimation. fs = 4 t = np.cumsum(nn) t -= t[0] f_interpol = sp.interpolate.interp1d(t, nn, 'cubic') t_interpol = np.arange(t[0], t[-1], 1000. / fs) nn_interpol = f_interpol(t_interpol) # Subtract mean value from each sample for surpression of DC-offsets if detrend: nn_interpol = nn_interpol - np.mean(nn_interpol) # Adapt 'nperseg' according to the total duration of the NNI series (5min threshold = 300000ms) if t.max() < 300000: nperseg = nfft else: nperseg = 300 # Compute power spectral density estimation (where the magic happens) frequencies, powers = welch(x=nn_interpol, fs=fs, window=window, nperseg=nperseg, nfft=nfft, scaling='density') # Metadata args = (nfft, window, fs, 'cubic') names = ( 'fft_nfft', 'fft_window', 'fft_resampling_frequency', 'fft_interpolation', ) meta = utils.ReturnTuple(args, names) if mode not in ['normal', 'dev', 'devplot']: warnings.warn( "Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # Compute frequency parameters params, freq_i = self._compute_parameters('fft', frequencies, powers, fbands) # Plot PSD figure = self._plot_psd('fft', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('fft_plot', )) # Output return tools.join_tuples(params, figure, meta) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = self._compute_parameters('fft', frequencies, powers, fbands) # Output return tools.join_tuples(params, meta), frequencies, (powers / 10**6) # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # Compute frequency parameters params, freq_i = self._compute_parameters('fft', frequencies, powers, fbands) # Plot PSD figure = self._plot_psd('fft', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('fft_plot', )) # Output return tools.join_tuples(params, figure, meta), frequencies, (powers / 10**6)
def ar_psd(self, nni=None, rpeaks=None, fbands=None, nfft=2**12, order=16, show=True, show_param=True, legend=True, mode='normal'): # Check input nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = self._check_freq_bands(fbands) # Resampling (with 4Hz) and interpolate # Because RRi are unevenly spaced we must interpolate it for accurate PSD estimation. fs = 4 t = np.cumsum(nn) t -= t[0] f_interpol = sp.interpolate.interp1d(t, nn, 'cubic') t_interpol = np.arange(t[0], t[-1], 1000. / fs) nn_interpol = f_interpol(t_interpol) # Compute autoregressive PSD ar = spectrum.pyule(data=nn_interpol, order=order, NFFT=nfft, sampling=fs, scale_by_freq=False) ar() # Get frequencies and powers frequencies = np.asarray(ar.frequencies()) psd = np.asarray(ar.psd) powers = np.asarray( 10 * np.log10(psd) * 10**3) # * 10**3 to compensate with ms^2 to s^2 conversion # in the upcoming steps # Define metadata meta = utils.ReturnTuple( (nfft, order, fs, 'cubic'), ('ar_nfft', 'ar_order', 'ar_resampling_frequency', 'ar_interpolation')) if mode not in ['normal', 'dev', 'devplot']: warnings.warn( "Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # Compute frequency parameters params, freq_i = self._compute_parameters('ar', frequencies, powers, fbands) # Plot PSD figure = self._plot_psd('ar', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('ar_plot', )) # Complete output return tools.join_tuples(params, figure) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = self._compute_parameters('ar', frequencies, powers, fbands) # Output return tools.join_tuples(params, meta), frequencies, (powers / 10**6) # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # Compute frequency parameters params, freq_i = self._compute_parameters('ar', frequencies, powers, fbands) # Plot PSD figure = self._plot_psd('ar', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('ar_plot', )) # Complete output return tools.join_tuples(params, figure, meta), frequencies, (powers / 10**6)
def lomb_psd(self, nni=None, rpeaks=None, fbands=None, nfft=2**8, ma_size=None, show=True, show_param=True, legend=True, mode='normal'): # Check input nn = tools.check_input(nni, rpeaks) # Verify or set default frequency bands fbands = self._check_freq_bands(fbands) t = np.cumsum(nn) t -= t[0] # Compute PSD according to the Lomb-Scargle method # Specify frequency grid frequencies = np.linspace(0, 0.41, nfft) # Compute angular frequencies a_frequencies = np.asarray(2 * np.pi / frequencies) powers = np.asarray(lombscargle(t, nn, a_frequencies, normalize=True)) # Fix power = inf at f=0 powers[0] = 2 # Apply moving average filter if ma_size is not None: powers = biosppy.signals.tools.smoother(powers, size=ma_size)['signal'] # Define metadata meta = utils.ReturnTuple(( nfft, ma_size, ), ('lomb_nfft', 'lomb_ma')) if mode not in ['normal', 'dev', 'devplot']: warnings.warn( "Unknown mode '%s'. Will proceed with 'normal' mode." % mode, stacklevel=2) mode = 'normal' # Normal Mode: # Returns frequency parameters, PSD plot figure and no frequency & power series/arrays if mode == 'normal': # ms^2 to s^2 powers = powers * 10**6 # Compute frequency parameters params, freq_i = self._compute_parameters('lomb', frequencies, powers, fbands) # Plot parameters figure = self._plot_psd('lomb', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('lomb_plot', )) # Complete output return tools.join_tuples(params, figure, meta) # Dev Mode: # Returns frequency parameters and frequency & power series/array; does not create a plot figure nor plot the data elif mode == 'dev': # Compute frequency parameters params, _ = self._compute_parameters('lomb', frequencies, powers, fbands) # Complete output return tools.join_tuples(params, meta), frequencies, powers # Devplot Mode: # Returns frequency parameters, PSD plot figure, and frequency & power series/arrays elif mode == 'devplot': # ms^2 to s^2 powers = powers * 10**6 # Compute frequency parameters params, freq_i = self._compute_parameters('lomb', frequencies, powers, fbands) # Plot parameters figure = self._plot_psd('lomb', frequencies, powers, freq_i, params, show, show_param, legend) figure = utils.ReturnTuple((figure, ), ('lomb_plot', )) # Complete output return tools.join_tuples(params, figure, meta), frequencies, powers