U
    g»{jò<  ã                	   @   sö   d dl Z d dlZd dlmZmZ ejdd� ejdd� ddlmZ dd	d
dddgZ	d/dd„Z
d0dd	„Zd1dd„Zd2dd„Zd3dd„Zd4dd
„Zd5dd„Zddd d!d"gd ddd#d$d%gd dd&ddd#gd d#d$dd'd(ggd d)dd*d d gfd+d,„Zd-d.„ ZdS )6é    N)ÚUnivariateSplineÚinterp1dÚignore)Údivide)Úinvalidé   )Úfilter_signalÚ
scale_dataÚscale_sectionsÚenhance_peaksÚenhance_ecg_peaksÚinterpolate_clippingÚflip_signalé   c                 C   s:   t  | ¡t  | ¡ }t  | ¡}|| | | |  | } | S )a\  scales passed sequence between thresholds

    Function that scales passed data so that it has specified lower 
    and upper bounds.
    
    Parameters
    ----------
    data : 1-d array or list
        Sequence to be scaled

    lower : int or float
        lower threshold for scaling
        default : 0

    upper : int or float
        upper threshold for scaling
        default : 1024

    Returns
    -------
    out : 1-d array
        contains scaled data

    Examples
    --------
    When passing data without further arguments to the function means it scales 0-1024
    
    >>> x = [2, 3, 4, 5]
    >>> scale_data(x)
    array([   0.        ,  341.33333333,  682.66666667, 1024.        ])

    Or you can specify a range:

    >>> scale_data(x, lower = 50, upper = 124)
    array([ 50.        ,  74.66666667,  99.33333333, 124.        ])
    )ÚnpÚmaxÚmin)ÚdataÚlowerÚupperÚrngÚminimum© r   úT/var/www/html/bwave/python/venv/lib/python3.8/site-packages/heartpy/preprocessing.pyr	      s    &
ç      @c                 C   s¶   t  dt¡ t jdtdd� t  dt¡ t| ƒ| }t|| ƒ}d}|}t t| ƒ¡}	|t| ƒkr¤| ||… }
t |
d¡}
t	|
||ƒ}||	||…< ||7 }||7 }qVt 
|	d|… ¡S )a¹  scales data using sliding window approach

    Function that scales the data within the defined sliding window between 
    the defined lower and upper bounds.

    Parameters
    ----------
    data : 1-d array or list
        Sequence to be scaled

    sample_rate : int or float
        Sample rate of the passed signal

    windowsize : int or float
        size of the window within which signal is scaled, in seconds
        default : 2.5

    lower : int or float
        lower threshold for scaling. Passed to scale_data.
        default : 0

    upper : int or float
        upper threshold for scaling. Passed to scale_data.
        default : 1024

    Returns
    -------
    out : 1-d array
        contains scaled data

    Examples
    --------
    >>> x = [20, 30, 20, 30, 70, 80, 20, 30, 20, 30]
    >>> scale_sections(x, sample_rate=1, windowsize=2, lower=20, upper=30)
    array([20., 30., 20., 30., 20., 30., 20., 30., 20., 30.])
    ÚalwayszMscale_sections function is deprecated and will be removed in a future releaseé   )ÚcategoryÚ
stacklevelÚdefaultr   )ÚwarningsÚsimplefilterÚDeprecationWarningÚwarnÚlenÚintr   ÚemptyÚpowerr	   Úarray)r   Úsample_rateZ
windowsizer   r   Útotal_lengthZwindow_dimensionZ
data_startZdata_endÚoutputZslicedZscaledr   r   r   r
   @   s&    'þ
éü  c                 C   sÔ   t  | |k¡}t  t  |¡dk¡d }g }tdt|ƒƒD ]–}|dkrh| |d d |d |d  f¡ q8|t|ƒd kr | |d || d  |d d f¡ q8| |d ||d  d  |d ||  f¡ q8|S )a  marks clipping sections
    
    Function that marks start and end of clipping part
    it detects the start and end of clipping segments and returns them

    Parameters
    ----------
    data : 1-d numpy array
        Sequence to be scaled

    threshold: int or float
        the threshold for clipping, recommended to
        be a few data points below ADC or sensor max value, 
        to compensate for signal noise 
        default : 1020

    Returns
    -------
    out : list of tuples
        the output is a list of tuples. Each tuple marks the start
        and endpoint of the detected clipping segment

    Examples
    --------
    Import heartpy and load example data

    >>> import heartpy as hp
    >>> data, _ = hp.load_exampledata(example=2)

    Let's slice a part of the data that I know contains clipping

    >>> x = data[2000:3000]
    >>> mark_clipping(x, threshold=970)
    [(369, 375), (426, 437), (486, 493), (544, 552), (604, 610), (663, 665), (721, 722), (776, 781), (831, 836), (883, 891), (995, 999)]
    r   r   éÿÿÿÿ)r   ÚwhereÚdiffÚranger$   Úappend)r   Ú	thresholdZclip_binaryZclipping_edgesÚclipping_segmentsÚir   r   r   Úmark_clipping�   s     &ÿ
ÿÿr5   c              	   C   s:  t | |ƒ}td| ƒ}g }g }d}|D �]}|d |k r:q&| |d | |d … }	| |d |d | … }
t |	|
f¡}t dd„ t|d | |d ƒD ƒdd„ t|d |d | ƒD ƒf¡}t |d | |d | |d |d  d|  ¡}z6t||dd	�}||ƒ}|| |d | |d | …< W q&   Y q&X q&| S )
aE  interpolate peak waveform

    Function that interpolates peaks between the clipping segments using 
    cubic spline interpolation. It takes the clipping start +/- 100ms to 
    calculate the spline.
    
    Parameters
    ----------
    data : 1d list or numpy array
        data section to be evaluated 

    sample_rate : int or float
        sample rate with which the data array is sampled

    threshold : int or float
        the threshold for clipping, recommended to
        be a few data points below ADC or sensor max value, 
        to compensate for signal noise 
        default : 1020

    Returns
    -------
    out : array 
        the output is an array with clipping segments replaced
        by interpolated segments

    Examples
    --------
    First let's load some example data:

    >>> import heartpy as hp
    >>> data, _ = hp.load_exampledata(example=2)
    >>> x = data[2000:3000]
    >>> x[425:445]
    array([948, 977, 977, 977, 977, 978, 978, 977, 978, 977, 977, 977, 977,
           914, 820, 722, 627, 536, 460, 394])

    And interpolate any clipping segments as such:

    >>> intp = interpolate_clipping(x, sample_rate=117, threshold=970)
    >>> intp[425:445]
    array([ 972, 1043, 1098, 1138, 1163, 1174, 1173, 1159, 1134, 1098, 1053,
            998,  934,  848,  747,  646,  552,  470,  402,  348])
    gš™™™™™¹?r   r   c                 S   s   g | ]}|‘qS r   r   ©Ú.0Úxr   r   r   Ú
<listcomp>ú   s     z(interpolate_clipping.<locals>.<listcomp>c                 S   s   g | ]}|‘qS r   r   r6   r   r   r   r9   û   s     r   é   )Úk)r5   r%   r   Zconcatenater0   Úlinspacer   )r   r)   r2   r3   Znum_datapointsZnewxZnewyr4   ÚsegmentZ
antecedentZ
consequentZsegment_dataZinterpdata_xZx_newÚinterp_funcZinterp_datar   r   r   r   »   s8    .

"ÿ
þÿ
ÿFTc                 C   sL   t  | ¡}t  | ¡}t  | ¡}||  | } |r<t| ||d�} |rHt| ƒ} | S )aU  invert signal waveforms.

    Function that flips raw signal with negative mV peaks to normal ECG.
    Required for proper peak finding in case peaks are expressed as
    negative dips.

    Parameters
    ----------
    data : 1d list or numpy array
        data section to be evaluated 
    
    enhance_peaks : bool
        whether to apply peak accentuation 
        default : False

    keep_range : bool
        whether to scale the inverted data so that the original
        range is maintained

    Returns
    -------
    out : 1d array

    Examples
    --------
    Given an array of data

    >>> x = [200, 300, 500, 900, 500, 300, 200]

    We can call the function. If keep_range is False, the signal
    will be inverted relative to its mean.

    >>> flip_signal(x, keep_range=False)
    array([628.57142857, 528.57142857, 328.57142857, -71.42857143,
           328.57142857, 528.57142857, 628.57142857])

    However, by specifying keep_range, the inverted signal will be
    put 'back in place' in its original range.

    >>> flip_signal(x, keep_range=True)
    array([900., 800., 600., 200., 600., 800., 900.])

    It's also possible to use the enhance_peaks function:

    >>> flip_signal(x, enhancepeaks=True)
    array([1024.        ,  621.75746332,  176.85545623,    0.        ,
            176.85545623,  621.75746332, 1024.        ])
    )r   r   )r   Úmeanr   r   r	   r   )r   ZenhancepeaksZ
keep_rangeZ	data_meanZdata_minZdata_maxr   r   r   r     s    1


r   c                 C   s.   t | ƒ t|ƒD ]}t | d¡} t | ƒ} q| S )a  enhances peak amplitude relative to rest of signal
    
    Function thta attempts to enhance the signal-noise ratio by accentuating 
    the highest peaks. Note: denoise first
    
    Parameters
    ----------
    hrdata : 1-d numpy array or list 
        sequence containing heart rate data

    iterations : int
        the number of scaling steps to perform 
        default : 2

    Returns
    -------
    out : 1-d numpy array
        array containing enhanced peaks

    Examples
    --------
    Given an array of data, the peaks can be enhanced using the function

    >>> x = [200, 300, 500, 900, 500, 300, 200]
    >>> enhance_peaks(x)
    array([   0.        ,    4.31776016,   76.16528926, 1024.        ,
             76.16528926,    4.31776016,    0.        ])
    r   )r	   r0   r   r'   )ÚhrdataÚ
iterationsr4   r   r   r   r   M  s
    
é   r?   c           	      C   s�   t  | ¡}t|ƒ}tt|ƒƒD ]>}t|||ƒ}|dkrFt j|dd�}q|dkrt j|dd�}q|t|ƒd t|ƒ … }|rŒt|d|dd�}|S )	ad  enhances ECG peaks

    Function that convolves synthetic QRS templates with the signal, leading
    to a strong increase signal-to-noise ratio. Function ends with an optional
    Notch filterstep (default : true) to reduce noise from the iterating
    convolution steps.

    Parameters
    ----------
    hrdata : 1-d numpy array or list 
        sequence containing heart rate data

    sample_rate : int or float
        sample rate with which the data is sampled 

    iterations : int
        how many convolutional iterations should be run. More will result in
        stronger peak enhancement, but over a certain point (usually between 12-16)
        overtones start appearing in the signal. Only increase this if the peaks
        aren't amplified enough.
        default : 4

    aggregation : str
        how the data from the different convolutions should be aggregated.
        Can be either 'mean' or 'median'.
        default : mean

    notch_filter : bool
        whether to apply a notch filter after the last convolution to get rid of
        remaining low frequency noise.
        default : true

    Returns
    -------
    output : 1d array
        The array containing the filtered data with enhanced peaks

    Examples
    --------
    First let's import the module and load the data

    >>> import heartpy as hp
    >>> data, timer = hp.load_exampledata(1)
    >>> sample_rate = hp.get_samplerate_mstimer(timer)

    After loading the data we call the function like so:

    >>> filtered_data = enhance_ecg_peaks(data, sample_rate, iterations = 3)

    By default the module uses the mean to aggregate convolutional outputs. It
    is also possible to use the median.

    >>> filtered_data = enhance_ecg_peaks(data, sample_rate, iterations = 3,
    ... aggregation = 'median', notch_filter = False)

    In the last example we also disabled the notch filter.
    r?   r   )ZaxisZmedianr   gš™™™™™©?Znotch)Z
filtertype)	r   ÚcopyÚgenerate_ecg_templatesr0   r%   Údenoise_convolutionsZnanmeanZ	nanmedianr   )	r@   r)   rA   ZaggregationZnotch_filterr+   Ú	templatesr4   Ú	convolvedr   r   r   r   q  s    =
é2   é<   éF   éP   éd   r:   g      @é   g      ø?g      @é   gš™™™™™¹¿g      à¿c                    s|   g }|D ]n}|D ]d}|d |  }|t |ƒ ‰ ‡ fdd„|D ƒ}t |d |d |d ¡}	t||dd�}
| |
|	ƒ¡ qq|S )zœhelper function for enhance_ecg_peaks
    
    Helper function that generates synthetic QRS complexes of varying sizes
    to convolve with the signal.
    iè  c                    s   g | ]}t ˆ | ƒ‘qS r   )r%   r6   ©Ústepr   r   r9   Õ  s     z*generate_ecg_templates.<locals>.<listcomp>r   r-   Zlinear)Úkind)r$   r   r<   r   r1   )r)   ÚwidthsZpresetsZ	amplituderF   r4   ÚjÚdurationZnew_tZnew_xr>   r   rO   r   rD   Ã  s    rD   c                 C   s>   g }t t|ƒƒD ]"}tj| || dd�}| |¡ qt |¡S )z–helper function for enhance_ecg_peaks
    
    Helper function that convolves the generated synthetic QRS templates
    with the provided signal.
    Zsame)Úmode)r0   r$   r   Zconvolver1   Zasarray)r   r)   rF   Zconvolutionsr4   rG   r   r   r   rE   Þ  s
    rE   )r   r   )r   r   r   )r,   )r,   )FT)r   )rB   r?   T)r    Únumpyr   Zscipy.interpolater   r   ZseterrZ	filteringr   Ú__all__r	   r
   r5   r   r   r   r   rD   rE   r   r   r   r   Ú<module>   s:   û
,
A
:
R
@
$  ÿ
Rþü
