Example #1
0
def sdnn(nni=None, rpeaks=None):
    """Computation of the standard deviation of an NN interval series.

	References: [Electrophysiology1996]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#sdnn-sdnn

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	sdnn : float
		Standard deviation of NN intervals [ms].

	Notes
	-----
	..	Only one type of input data is required.
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over 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)

    # Computation of SDNN & Output
    args = [tools.std(nn)]
    names = ['sdnn']
    return utils.ReturnTuple(args, names)
    def RRI_Mean(self,
                 nni=None,
                 rpeaks=None,
                 full=True,
                 overlap=False,
                 duration=300):

        # Check input
        nn = tools.check_input(nni, rpeaks)

        # Signal segmentation into 5 min segments
        segments, seg = tools.segmentation(nn,
                                           full=full,
                                           overlap=overlap,
                                           duration=duration)
        if seg:
            rri_mean_ = statistics.mean([np.mean(x) for x in segments])

        else:
            rri_mean_ = float('nan')
            if tools.WARN:
                warnings.warn(
                    "Signal duration too short for RRI Mean computation.")

        # Output
        args = [rri_mean_]
        names = ['rri_mean']
        return utils.ReturnTuple(args, names)
    def sdann(self,
              nni=None,
              rpeaks=None,
              full=True,
              overlap=False,
              duration=300):

        # Check input
        nn = tools.check_input(nni, rpeaks)

        # Signal segmentation into 5 min segments
        segments, seg = tools.segmentation(nn,
                                           full=full,
                                           overlap=overlap,
                                           duration=duration)

        if seg:
            mean_values = [np.mean(x) for x in segments]
            sdann_ = tools.std(mean_values)
        else:
            sdann_ = float('nan')
            if tools.WARN:
                warnings.warn(
                    "Signal duration too short for SDANN computation.")

        # Output
        args = [sdann_]
        names = ['sdann']
        return utils.ReturnTuple(args, names)
Example #4
0
def sdann(nni=None, rpeaks=None, full=True, overlap=False, duration=300):
    """Computes the standard deviation of the mean NNI value of each segment (default: 300s segments).

	References: [Electrophysiology1996], [Lohninger2017]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#sdann-sdann

	Parameters
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	full : bool, optional
		If True, returns last segment, even if the cumulative sum of NNI does not reach the 300s (default: False).
	overlap : bool, optional
		If True, allow to return NNI that go from the interval of one segment to the successive segment (default: False).
	duration : int, optional
		Maximum duration duration per segment in [s] (default: 300s).

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	sdnn_index : float
		Standard deviations of the means of all NN intervals within 5 minutes intervals in [ms].

	Notes
	-----
	..	Only one type of input data is required
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over the 'rpeaks'
	..	NN and R-peak series provided in [s] format will be converted to [ms] format
	..	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.
	"""
    # Check input
    nn = tools.check_input(nni, rpeaks)

    # Signal segmentation into 5 min segments
    segments, seg = tools.segmentation(nn,
                                       full=full,
                                       overlap=overlap,
                                       duration=duration)

    if seg:
        mean_values = [np.mean(x) for x in segments]
        sdann_ = tools.std(mean_values)
    else:
        sdann_ = float('nan')
        if tools.WARN:
            warnings.warn("Signal duration too short for SDANN computation.")

    # Output
    args = [sdann_]
    names = ['sdann']
    return utils.ReturnTuple(args, names)
Example #5
0
def nnXX(nni=None, rpeaks=None, threshold=None):
    """Find number of NN interval differences greater than a specified threshold and ratio between number of intervals
	> threshold and total number of NN interval differences.

	References:	[Electrophysiology1996], [Ewing1984]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#nnxx-nnxx

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	threshold : int
		Threshold for nnXX values in [ms].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	nnXX: int
		Number of NN interval differences greater than the specified threshold [-].
	pnnXX : float
		Ratio between nnXX and total number of NN interval differences [-].

	Notes
	-----
	..	Only one type of input data is required
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over the 'rpeaks'
	..	NN and R-peak series provided in [s] format will be converted to [ms] format
	..	The ``XX`` in the ``nnXX`` and the ``pnnXX`` keys are substituted by the specified threshold (``threshold``).

		For instance, ``nnXX(nni, threshold=30)`` returns the custom ``nn30`` and ``pnn30`` parameters. Using a
		``threshold=30`` as ``nnXX(nni, threshold=35`` returns the custom ``nn35`` and ``pnn35`` parameters.

	"""
    # Check input
    nn = tools.check_input(nni, rpeaks)

    # Check threshold
    if threshold is None:
        raise TypeError(
            "No threshold specified. Please specify a [ms] threshold.")
    if threshold <= 0:
        raise ValueError(
            "Invalid value for 'threshold'. Value must not be <= 0.")

    # Count NN20
    nnd = tools.nni_diff(nn)
    nnxx = sum(i > threshold for i in nnd)
    pnnxx = nnxx / len(nnd) * 100

    # Output
    args = (nnxx, pnnxx)
    names = ('nn%i' % threshold, 'pnn%i' % threshold)
    return utils.ReturnTuple(args, names)
Example #6
0
def sample_entropy(nn=None, rpeaks=None, dim=2, tolerance=None):
    """Computes the sample entropy (sampen) of the NNI series.

	Parameters
	----------
	nn : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	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 (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	sample_entropy : float
		Sample entropy of the NNI series.

	Raises
	------
	TypeError
		If 'tolerance' is no numeric value.

	"""
    # Check input values
    nn = tools.check_input(nn, rpeaks)

    if tolerance is None:
        tolerance = np.std(nn, ddof=-1) * 0.2
    else:
        try:
            tolerance = float(tolerance)
        except:
            raise TypeError(
                'Tolerance level cannot be converted to float.'
                'Please verify that tolerance is a numeric (int or float).')

    # Compute Sample Entropy
    sampen = float(nolds.sampen(nn, dim, tolerance))

    # Output
    args = (sampen, )
    names = ('sampen', )
    return utils.ReturnTuple(args, names)
Example #7
0
def nni_differences_parameters(nni=None, rpeaks=None):
    """Computes basic statistical parameters from a series of successive NN interval differences (mean, min, max, standard deviation).

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	nni_diff_mean: float
		Mean NN interval difference [ms].
	nni_diff_min : float
		Minimum NN interval difference [ms].
	nni_diff_max : float
		Maximum NN interval difference [ms].

	Notes
	-----
	..	Only one type of input data is required.
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over 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)

    # Get NN interval differences
    nnd = tools.nni_diff(nn)

    # output
    args = (
        float(nnd.mean()),
        int(nnd.min()),
        int(nnd.max()),
    )
    names = (
        'nni_diff_mean',
        'nni_diff_min',
        'nni_diff_max',
    )
    return utils.ReturnTuple(args, names)
Example #8
0
def hr_parameters(nni=None, rpeaks=None):
    """Computes basic statistical parameters from a series of Heart Rate (HR) data (mean, min, max, standard deviation).

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	hr_mean : float
		Mean heart rate [bpm].
	hr_min : float
		Minimum heart rate value [bpm].
	hr_max : float
		Maximum heart rate value [bpm].
	hr_std : float
		Standard deviation of the HR series [bpm].

	Notes
	-----
	..	Only one type of input data is required.
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over 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)

    # Get heart rate series
    hr = tools.heart_rate(nn)

    # Output
    args = (hr.mean(), hr.min(), hr.max(), hr.std(ddof=1))
    names = ('hr_mean', 'hr_min', 'hr_max', 'hr_std')
    return utils.ReturnTuple(args, names)
Example #9
0
def rmssd(nni=None, rpeaks=None):
    """Computes root mean of squared differences of successive NN Intervals.

	References: [Electrophysiology1996], [Lohninger2017]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#rmssd-rmssd

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	rmssd : float
		RMSSD value in [ms].

	Notes
	-----
	..	Only one type of input data is required
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over 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)

    # Compute RMSSD
    nnd = tools.nni_diff(nn)
    rmssd_ = np.sum(x**2 for x in nnd)
    rmssd_ = np.sqrt(1. / nnd.size * rmssd_)

    # Output
    args = (rmssd_, )
    names = ('rmssd', )
    return utils.ReturnTuple(args, names)
Example #10
0
def nni_parameters(nni=None, rpeaks=None):
    """Computes basic statistical parameters from a series of NN intervals (# of intervals, mean, min, max).

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	nni_counter : int
		Number of NN intervals.
	nni_mean : float
		Mean NN interval [ms].
	nni_min : float
		Minimum NN interval [ms].
	nni_max : float
		Maximum NN interval [ms].

	Notes
	-----
	..	Only one type of input data is required.
	.. 	If both 'nni' and 'rpeaks' are provided, 'nni' will be chosen over 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)

    # output
    args = (int(nn.size), nn.mean(), nn.min(), nn.max())
    names = ('nni_counter', 'nni_mean', 'nni_min', 'nni_max')
    return utils.ReturnTuple(args, names)
Example #11
0
def dfa(nn=None,
        rpeaks=None,
        short=None,
        long=None,
        show=True,
        figsize=None,
        legend=True):
    """Conducts Detrended Fluctuation Analysis for short and long-term fluctuation of an NNI series.

	References: [Joshua2008][Kuusela2014][Fred2017]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/nonlinear.html#sample-entropy-sample-entropy

	Parameters
	----------
	nn : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	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]).
	show : bool
		If True, shows DFA plot (default: True)
	legend : bool
		If True, adds legend with alpha1 and alpha2 values to the DFA plot (default: True)

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	dfa_short : float
		Alpha value of the short term fluctuations
	dfa_long : float
		Alpha value of the long term fluctuations
	dfa_plot : matplotlib plot figure
		Matplotlib plot figure of the DFA

	"""
    # Check input values
    nn = tools.check_input(nn, rpeaks)

    # Check intervals
    short = tools.check_interval(short, default=(4, 16))
    long = tools.check_interval(long, default=(17, 64))

    # Create arrays
    short = range(short[0], short[1] + 1)
    long = range(long[0], long[1] + 1)

    # Prepare plot
    if figsize is None:
        figsize = (6, 6)
    fig = plt.figure(figsize=figsize)
    ax = fig.add_subplot(111)
    ax.set_title('Detrended Fluctuation Analysis (DFA)')
    ax.set_xlabel('log n [beats]')
    ax.set_ylabel('log F(n)')

    # try:
    # Compute alpha values
    try:
        alpha1, dfa_short = nolds.dfa(nn,
                                      short,
                                      debug_data=True,
                                      overlap=False)
        alpha2, dfa_long = nolds.dfa(nn, long, debug_data=True, overlap=False)
    except ValueError:
        # If DFA could not be conducted due to insufficient number of NNIs, return an empty graph and 'nan' for alpha1/2
        warnings.warn(
            "Not enough NNI samples for Detrended Fluctuations Analysis.")
        ax.axis([0, 1, 0, 1])
        ax.text(0.5,
                0.5,
                '[Insufficient number of NNI samples for DFA]',
                horizontalalignment='center',
                verticalalignment='center')
        alpha1, alpha2 = 'nan', 'nan'
    else:
        # Plot DFA results if number of NNI were sufficent to conduct DFA
        # Plot short term DFA
        vals, flucts, poly = dfa_short[0], dfa_short[1], np.polyval(
            dfa_short[2], dfa_short[0])
        label = r'$ \alpha_{1}: %0.2f$' % alpha1
        ax.plot(vals, flucts, 'bo', markersize=1)
        ax.plot(vals, poly, 'b', label=label, alpha=0.7)

        # Plot long term DFA
        vals, flucts, poly = dfa_long[0], dfa_long[1], np.polyval(
            dfa_long[2], dfa_long[0])
        label = r'$ \alpha_{2}: %0.2f$' % alpha2
        ax.plot(vals, flucts, 'go', markersize=1)
        ax.plot(vals, poly, 'g', label=label, alpha=0.7)

        # Add legend
        if legend:
            ax.legend()
        ax.grid()

    # Plot axis
    if show:
        plt.show()

    # Output
    args = (
        fig,
        alpha1,
        alpha2,
    )
    return utils.ReturnTuple(args, (
        'dfa_plot',
        'dfa_alpha1',
        'dfa_alpha2',
    ))
    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 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)
Example #14
0
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
Example #15
0
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
Example #16
0
def geometrical_parameters(nni=None,
                           rpeaks=None,
                           binsize=7.815,
                           plot=True,
                           show=True,
                           figsize=None,
                           legend=True):
    """Creates NNI histogram with specified binsize (default: 7.815ms) and computes geometrical parameters (triangular
	index, TINN, N, and M).

	References:	[Electrophysiology1996]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#geometrical-parameters-function-geometrical-parameters


	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	binsize : int, float
		Bin size of the histogram bins (default: 7.8125ms).
	plot : bool
		If True, creates histogram plot using matplotlib, else uses numpy (data only, no plot).
	show : bool, optional
		If true, shows histogram (default: True).
	figsize : array, optional
		Matplotlib figure size (width, height) (default: (6, 6)).
	legend : bool, optional
		If True, adds legend to the histogram (default: True).

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	nni_histogram : matplotlib figure object
		Histogram figure (only if input parameter 'plot' is True).
	tri_index : float
		Triangular index.
	tinn_n : float
		N value of the TINN computation.
	tinn_m : float
		M value of the TINN computation.
	tinn : float
		TINN value.

	Raises
	------
	TypeError (via 'check_input()')
		If no input data for 'rpeaks' or 'nni' provided.

	Notes
	-----
	..	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.

	"""

    # Check input
    nn = tools.check_input(nni, rpeaks)

    # Get Histogram data & plot (optional)
    if plot:
        fig, ax, D, bins = _get_histogram(nn,
                                          figsize=figsize,
                                          binsize=binsize,
                                          legend=legend,
                                          plot=plot)
    else:
        fig = None

    # Get TINN values without plot figure
    tinn_vals = tinn(nni=nn,
                     rpeaks=rpeaks,
                     binsize=binsize,
                     show=False,
                     legend=False,
                     figsize=figsize,
                     plot=False)

    # Get triangular index without plot figure
    trindex = triangular_index(nni=nn,
                               rpeaks=rpeaks,
                               binsize=binsize,
                               show=False,
                               legend=False,
                               plot=False)['tri_index']

    # Histogram plot & settings
    if plot:
        # Plot triangular interpolation
        N, M = tinn_vals['tinn_n'], tinn_vals['tinn_m']
        ax.plot([N, bins[np.argmax(D)]], [0, D.max()], 'r--', linewidth=0.8)
        ax.plot([bins[np.argmax(D)], M], [D.max(), 0], 'r--', linewidth=0.8)

        # Add Legend
        if legend:
            l1 = mpl.patches.Patch(facecolor='skyblue',
                                   label='Histogram D(NNI)')
            l2 = mpl.lines.Line2D([0, 0], [0, 0],
                                  linestyle='--',
                                  linewidth=0.8,
                                  color='r',
                                  label='Tri. Interpol.')
            l3 = mpl.patches.Patch(facecolor='g',
                                   alpha=0.0,
                                   label='D(X): %i' % D.max())
            l4 = mpl.patches.Patch(facecolor='g',
                                   alpha=0.0,
                                   label='X: %.3f$ms$' % bins[np.argmax(D)])
            l5 = mpl.patches.Patch(facecolor='white',
                                   alpha=0.0,
                                   label='N: %.3f$ms$' % tinn_vals['tinn_n'])
            l6 = mpl.patches.Patch(facecolor='white',
                                   alpha=0.0,
                                   label='M: %.3fms' % tinn_vals['tinn_m'])
            l7 = mpl.patches.Patch(facecolor='white',
                                   alpha=0.0,
                                   label='TINN: %.3fms' % tinn_vals['tinn'])
            l8 = mpl.patches.Patch(facecolor='white',
                                   alpha=0.0,
                                   label='Tri. Index: %.3f' % trindex)
            ax.legend(handles=[l1, l2, l3, l4, l5, l6, l7, l8], loc=0, ncol=1)

        # Show plot
        if show:
            plt.show()

    # Output
    args = (fig, tinn_vals['tinn_n'], tinn_vals['tinn_m'], tinn_vals['tinn'],
            trindex)
    names = ('nni_histogram', 'tinn_n', 'tinn_m', 'tinn', 'tri_index')
    return utils.ReturnTuple(args, names)
Example #17
0
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)
Example #18
0
def triangular_index(nni=None,
                     rpeaks=None,
                     binsize=7.8125,
                     plot=True,
                     show=True,
                     figsize=None,
                     legend=True):
    """Computes triangular index based on the NN intervals histogram.

	References:	[Electrophysiology1996]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#triangular-index-triangular-index

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	binsize : int, float
		Bin size of the histogram bins (default: 7.8125ms).
	plot : bool
		If True, creates histogram plot using matplotlib, else uses numpy (data only, no plot).
	show : bool, optional
		If true, shows histogram (default: True).
	figsize : array, optional
		Matplotlib figure size (width, height) (default: (6, 6)).
	legend : bool, optional
		If True, adds legend to the histogram (default: True).

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	tri_histogram : matplotlib figure object
		Histogram figure (only if input parameter 'plot' is True).
	tri_index : float
		Triangular index.

	Raises
	------
	TypeError
		If no input data for 'rpeaks' or 'nni' provided.

	Notes
	-----
	..	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.

	"""
    # Check input
    nn = tools.check_input(nni, rpeaks)

    # If histogram should be plotted
    if plot:
        # Get histogram values
        fig, ax, D, bins = _get_histogram(nn,
                                          figsize=figsize,
                                          binsize=binsize,
                                          legend=legend,
                                          plot=plot)

        # Compute Triangular index: number of nn intervals / maximum value of the distribution
        tri_index = nn.size / D.max()

        # Add legend
        if legend:
            h = mpl.patches.Patch(facecolor='skyblue')
            x = mpl.patches.Patch(facecolor='g', alpha=0.0)
            dx = mpl.patches.Patch(facecolor='g', alpha=0.0)
            tri = mpl.patches.Patch(facecolor='white', alpha=0.0)
            ax.legend([h, x, dx, tri], [
                'Histogram D(NNI)',
                'D(X): %i' % D.max(),
                'X: %.3f' % bins[np.argmax(D)],
                'TriIndex: %.3f' % tri_index
            ],
                      loc=0)

        # Show plot
        if show:
            plt.show()

        # Output
        args = (
            fig,
            tri_index,
        )
        names = (
            'tri_histogram',
            'tri_index',
        )

    # If histogram should not be plotted
    else:
        D, bins = _get_histogram(nn,
                                 figsize=figsize,
                                 binsize=binsize,
                                 legend=legend,
                                 plot=plot)

        # Compute Triangular index: number of nn intervals / maximum value of the distribution
        tri_index = nn.size / D.max()

        # Output
        args = (tri_index, )
        names = ('tri_index', )

    return utils.ReturnTuple(args, names)
Example #19
0
def tinn(nni=None,
         rpeaks=None,
         binsize=7.8125,
         plot=True,
         show=True,
         figsize=None,
         legend=True):
    """Computes TINN based on the NN intervals histogram.

	References:	[Electrophysiology1996]
	Docs:		https://pyhrv.readthedocs.io/en/latest/_pages/api/time.html#tinn-tinn

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	binsize : int, float
		Bin size of the histogram bins (default: 7.8125ms).
	plot : bool
		If True, creates histogram plot using matplotlib, else uses numpy (data only, no plot).
	show : bool, optional
		If true, shows histogram (default: True).
	figsize : array, optional
		Matplotlib figure size (width, height) (default: (6, 6)).
	legend : bool, optional
		If True, adds legend to the histogram (default: True).

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	tinn_histogram : matplotlib figure object
		Histogram figure (only if input parameter 'plot' is True).
	tinn_n : float
		N value of the TINN computation.
	tinn_m : float
		M value of the TINN computation.
	tinn : float
		TINN value.

	Raises
	------
	TypeError (via 'check_input()')
		If no input data for 'rpeaks' or 'nni' provided.

	Notes
	-----
	..	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.
	.. 	If both 'nni' and 'rpeaks' are provided, 'rpeaks' will be chosen over the 'nni' and the 'nni' data will be computed
		from the 'rpeaks'.

	"""
    # Check input
    nn = tools.check_input(nni, rpeaks)

    # Get Histogram data (with or without histogram plot figure)
    if plot:
        fig, ax, D, bins = _get_histogram(nn,
                                          figsize=figsize,
                                          binsize=binsize,
                                          legend=legend,
                                          plot=plot)
    else:
        D, bins = _get_histogram(nn,
                                 figsize=figsize,
                                 binsize=binsize,
                                 legend=legend,
                                 plot=plot)

    # Use only all bins except the last one to avoid indexing error with 'D' (otherwise bins.size = D.size + 1)
    # bins = np.asarray(bins[:-1])

    # Get bins of the triangular's N side (left side of the bin with the highest value of the distribution)
    n_bins = [bin for bin in bins if bin < bins[np.argmax(D)]]

    # Get bins of the triangular's M side (right side of the bin with the highest value of the distribution)
    m_bins = [bin for bin in bins if bin > bins[np.argmax(D)]]

    # Set a maximum error
    min_error = 2**14
    N = 0
    M = 0

    # Compute triangle and error for each possible N value within the bins
    for n in n_bins:
        # Compute triangle and error for each possible M value within the bins
        for m in m_bins:
            # Get bin indices and time array that are valid for q(t) (i.e. q(t)!=0 for N < t < M)
            qi = np.zeros(bins.size)
            for i, bin in enumerate(bins):
                qi[i] = (True if n <= bin <= m else False)
            t = bins[[i for i, q in enumerate(qi) if q]]

            # Compute linear function that describes the N side of the triangle (q(N) = 0 to q(X) = max(D(X)))
            qn = interp1d([t[0], bins[np.argmax(D)]], [0, np.max(D)],
                          'linear',
                          bounds_error=False)
            qn = qn(bins)

            # Compute linear function that describes the M side of the triangle (q(X) = max(D(X)) to q(M) = 0)
            qm = interp1d([bins[np.argmax(D)], t[-1]], [np.max(D), 0],
                          'linear',
                          bounds_error=False)
            qm = qm(bins)

            # Join the linear functions of both sides to single array
            q = np.zeros(len(bins))
            for i, val in enumerate(bins):
                if str(qn[i]) != 'nan':
                    q[i] = qn[i]
                elif str(qm[i]) != 'nan':
                    q[i] = qm[i]
                else:
                    q[i] = 0

            # Compute squared error
            error = np.sum([(D[i] - q[i])**2 for i, _ in enumerate(bins)])

            # Save N and M value if error is < smaller than before
            if error < min_error:
                N, M, min_error = n, m, error
                qf = q

    # Compute TINN
    tinn = M - N

    # If plot figure required, add interpolated triangle and other specified plot characteristics
    if plot:
        # Add triangle to the plot
        ax.plot([N, bins[np.argmax(D)]], [0, D.max()], 'r--', linewidth=0.8)
        ax.plot([bins[np.argmax(D)], M], [D.max(), 0], 'r--', linewidth=0.8)

        # Add legend
        if legend:
            h = mpl.patches.Patch(facecolor='skyblue')
            tri = mpl.lines.Line2D([0, 0], [0, 0],
                                   linestyle='--',
                                   linewidth=0.8,
                                   color='r')
            x = mpl.patches.Patch(facecolor='g', alpha=0.0)
            dx = mpl.patches.Patch(facecolor='g', alpha=0.0)
            n = mpl.patches.Patch(facecolor='white', alpha=0.0)
            m = mpl.patches.Patch(facecolor='white', alpha=0.0)
            tinn_ = mpl.patches.Patch(facecolor='white', alpha=0.0)
            ax.legend([h, tri, x, dx, n, m, tinn_], [
                'Histogram D(NNI)', 'Triangular Interpol.',
                'D(X): %i' % D.max(),
                'X: %.3f$ms$' % bins[np.argmax(D)],
                'N: %.3f$ms$' % N,
                'M: %.3fms' % M,
                'TINN: %.3fms' % tinn
            ],
                      loc=0)

        # Show plot
        if show:
            plt.show()

        # Output
        args = (
            fig,
            N,
            M,
            tinn,
        )
        names = (
            'tinn_histogram',
            'tinn_n',
            'tinn_m',
            'tinn',
        )
    else:
        # Output
        args = (
            N,
            M,
            tinn,
        )
        names = (
            'tinn_n',
            'tinn_m',
            'tinn',
        )

    return utils.ReturnTuple(args, names)
    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)
Example #21
0
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
Example #22
0
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)
Example #23
0
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)
Example #24
0
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
Example #25
0
def poincare(nni=None, rpeaks=None, show=True, plot=True, figsize=None, ellipse=True, vectors=True, legend=True, \
                            marker='o'):
    """Creates Poincaré plot from a series of NN intervals or R-peak locations and derives the Poincaré related
	parameters SD1, SD2, SD1/SD2 ratio, and area of the Poincaré ellipse.

	References: [Tayel2015][Brennan2001]

	Parameters
	----------
	nni : array
		NN intervals in [ms] or [s].
	rpeaks : array
		R-peak times in [ms] or [s].
	show : bool, optional
		If true, shows Poincaré plot (default: True).
	figsize : array, optional
		Matplotlib figure size (width, height) (default: (6, 6)).
	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').

	Returns (biosppy.utils.ReturnTuple Object)
	------------------------------------------
	[key : format]
		Description.
	poincare_plot : matplotlib figure object
		Poincaré plot figure
	sd1 : float
		Standard deviation (SD1) of the major axis
	sd2 : float, key: 'sd2'
		Standard deviation (SD2) of the minor axis
	sd_ratio: float
		Ratio between SD2 and SD1 (SD2/SD1)
	ellipse_area : float
		Area of the fitted ellipse

	"""
    # Check input values
    nn = tools.check_input(nni, rpeaks)

    # Prepare Poincaré data
    x1 = np.asarray(nn[:-1])
    x2 = np.asarray(nn[1:])

    # SD1 & SD2 Computation
    sd1 = np.std(np.subtract(x1, x2) / np.sqrt(2))
    sd2 = np.std(np.add(x1, x2) / np.sqrt(2))

    # Area of ellipse
    area = np.pi * sd1 * sd2

    # if plot is wanted
    if plot:
        # Prepare figure
        if figsize is None:
            figsize = (6, 6)
        fig = plt.figure(figsize=figsize)
        fig.tight_layout()
        ax = fig.add_subplot(111)

        ax.set_title(r'$Poincar\acute{e}$')
        ax.set_ylabel('$NNI_{i+1}$ [ms]')
        ax.set_xlabel('$NNI_i$ [ms]')
        ax.set_xlim([np.min(nn) - 50, np.max(nn) + 50])
        ax.set_ylim([np.min(nn) - 50, np.max(nn) + 50])
        ax.grid()
        ax.plot(x1, x2, 'r%s' % marker, markersize=2, alpha=0.5, zorder=3)

        # Compute mean NNI (center of the Poincaré plot)
        nn_mean = np.mean(nn)

        # Draw poincaré ellipse
        if ellipse:
            ellipse_ = mpl.patches.Ellipse((nn_mean, nn_mean),
                                           sd1 * 2,
                                           sd2 * 2,
                                           angle=-45,
                                           fc='k',
                                           zorder=1)
            ax.add_artist(ellipse_)
            ellipse_ = mpl.patches.Ellipse((nn_mean, nn_mean),
                                           sd1 * 2 - 1,
                                           sd2 * 2 - 1,
                                           angle=-45,
                                           fc='lightyellow',
                                           zorder=1)
            ax.add_artist(ellipse_)

        # Add poincaré vectors (SD1 & SD2)
        if vectors:
            arrow_head_size = 3
            na = 4
            a1 = ax.arrow(nn_mean,
                          nn_mean, (-sd1 + na) * np.cos(np.deg2rad(45)),
                          (sd1 - na) * np.sin(np.deg2rad(45)),
                          head_width=arrow_head_size,
                          head_length=arrow_head_size,
                          fc='g',
                          ec='g',
                          zorder=4,
                          linewidth=1.5)
            a2 = ax.arrow(nn_mean,
                          nn_mean, (sd2 - na) * np.cos(np.deg2rad(45)),
                          (sd2 - na) * np.sin(np.deg2rad(45)),
                          head_width=arrow_head_size,
                          head_length=arrow_head_size,
                          fc='b',
                          ec='b',
                          zorder=4,
                          linewidth=1.5)
            a3 = mpl.patches.Patch(facecolor='white', alpha=0.0)
            a4 = mpl.patches.Patch(facecolor='white', alpha=0.0)
            ax.add_line(
                mpl.lines.Line2D((min(nn), max(nn)), (min(nn), max(nn)),
                                 c='b',
                                 ls=':',
                                 alpha=0.6))
            ax.add_line(
                mpl.lines.Line2D((nn_mean - sd1 * np.cos(np.deg2rad(45)) * na,
                                  nn_mean + sd1 * np.cos(np.deg2rad(45)) * na),
                                 (nn_mean + sd1 * np.sin(np.deg2rad(45)) * na,
                                  nn_mean - sd1 * np.sin(np.deg2rad(45)) * na),
                                 c='g',
                                 ls=':',
                                 alpha=0.6))

            # Add legend
            if legend:
                ax.legend([a1, a2, a3, a4], [
                    'SD1: %.3f$ms$' % sd1,
                    'SD2: %.3f$ms$' % sd2,
                    'S: %.3f$ms^2$' % area,
                    'SD1/SD2: %.3f' % (sd1 / sd2)
                ],
                          framealpha=1)

        # Show plot
        if show:
            plt.show()
    else:
        fig = None

    # Output
    args = (fig, sd1, sd2, sd2 / sd1, area)
    names = ('poincare_plot', 'sd1', 'sd2', 'sd_ratio', 'ellipse_area')
    return utils.ReturnTuple(args, names)
    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