U
    g»{jyo  ã                   @   s¨  d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZmZmZmZ ddlmZ ddlmZmZmZmZmZmZmZ dd	lmZmZmZmZmZm Z  dd
l!m"Z"m#Z#m$Z$m%Z%m&Z& ddl'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z- ddl.m/Z/m0Z0m1Z1m2Z2 ddl3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z: ddlm;Z; e; <¡  dddddddddddddddddd d!d"d#d$gZ=d7d.d„Z>d8d3d„Z?d&d&d-d&d'd(d4d'i i f
d5d„Z@d9d6d$„ZAdS ):z
main module for HeartPy.
é    )ÚdatetimeN)ÚUnivariateSpline)ÚbutterÚfiltfiltÚwelchÚperiodogramÚresample_polyÚresampleé   )Ú
exceptions)Úget_dataÚget_samplerate_mstimerÚget_samplerate_datetimeÚrolling_meanÚoutliers_iqr_methodÚoutliers_modified_zÚload_exampledata)Ú
scale_dataÚscale_sectionsÚinterpolate_clippingÚflip_signalÚenhance_peaksÚenhance_ecg_peaks)Úfilter_signalÚhampel_filterÚhampel_correcterÚremove_baseline_wanderÚsmooth_signal)Úmake_windowsÚappend_dictÚ	fit_peaksÚcheck_peaksÚcheck_binary_qualityÚinterpolate_peaks)ÚplotterÚsegment_plotterÚplot_poincareÚplot_breathing)Úcalc_rrÚcalc_rr_segmentÚclean_rr_intervalsÚcalc_ts_measuresÚcalc_fd_measuresÚcalc_breathingÚcalc_poincare)Úconfigr   r   r   r   r   r   r   r   r$   r'   r&   ÚprocessÚ
process_rrÚprocess_segmentwiser   r   r   r   r%   r   r   Ú	run_testsç      è?Fr   éð   éü  é(   é´   ç     @�@úquotient-filterc                 C   s   |dkri }|dkri }t jd dkr>t jd dkr>t ¡ }nt ¡ }t | ¡jdks^tdƒ‚|r||	rnt	| ƒ} t
| ||
d�} |r’t| ƒ} t| |ƒ} t | d¡}|dk r²| t|ƒ } | |d< ||d	< t| ||ƒ}t| |||||d
�}|rüt| |d |||d�}t|d ||d�}t|d |d |d ||d�}|�r>t||d�}t|d |d |d ||d�\}}t|d |d ||d�}zt|d |||d�\}}W n   tj|d< Y nX |�rÈt|d|||d�\}}|�rt jd dk�rt jd dk�rtdt ¡ |  ƒ ntdt ¡ |  ƒ ||fS )a~  processes passed heart rate data.

    Processes the passed heart rate data. Returns measures{} dict containing results.

    Parameters
    ----------
    hrdata : 1d array or list
        array or list containing heart rate data to be analysed

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

    windowsize : int or float
        the window size in seconds to use in the calculation of the moving average.
        Calculated as windowsize * sample_rate
        default : 0.75

    report_time : bool
        whether to report total processing time of algorithm
        default : False

    calc_freq : bool
        whether to compute frequency domain measurements
        default : False

    freq_method : str
        method used to extract the frequency spectrum. Available: 'fft' (Fourier Analysis),
        'periodogram', and 'welch' (Welch's method).
        default : 'welch'

    welch_wsize : float
        Size of window (in sec) when welch method used to compute the spectrogram.
        This choice is based on a trade-off btw temporal res and freq res of the resulting spectrum
        60 sec may seem reasonable, but this would greatly limit frequency resolution!
          1/60 s = 0.017 Hz, so you would only have 2 points in the VLF band
        Therefore, the default is 4 min (9 points in the VLF band)
        default : 240

    freq_square : bool
        whether to square the power spectrum returned when computing frequency measures
        default : False

    interp_clipping : bool
        whether to detect and interpolate clipping segments of the signal
        default : False

    clipping_scale : bool
        whether to scale the data prior to clipping detection. Can correct errors
        if signal amplitude has been affected after digitization (for example through
        filtering). Not recommended by default.
        default : False

    interp_threshold : int or float
        threshold to use to detect clipping segments. Recommended to be a few
        datapoints below the sensor or ADC's maximum value (to account for
        slight data line noise).
        default : 1020, 4 below max of 1024 for 10-bit ADC

    hampel_correct : bool
        whether to reduce noisy segments using large median filter. Disabled by
        default due to computational complexity and (small) distortions induced
        into output measures. Generally it is not necessary.
        default : False

    bpmmin : int or float
        minimum value to see as likely for BPM when fitting peaks
        default : 40

    bpmmax : int or float
        maximum value to see as likely for BPM when fitting peaks
        default : 180

    reject_segmentwise : bool
        whether to reject segments with more than 30% rejected beats.
        By default looks at segments of 10 beats at a time.
        default : False

    high_precision : bool
        whether to estimate peak positions by upsampling signal to sample rate
        as specified in high_precision_fs
        default : False

    high_precision_fs : int or float
        the sample rate to which to upsample for more accurate peak position estimation
        default : 1000 Hz

    breathing_method : str
        method to use for estimating breathing rate, should be 'welch' or 'fft'
        default : welch

    clean_rr : bool
        if true, the RR_list is further cleaned with an outlier rejection pass
        default : False

    clean_rr_method: str
        how to find and reject outliers. Available methods are ' quotient-filter',
        'iqr' (interquartile range), and 'z-score'.
        default : 'quotient-filter'

    measures : dict
        dictionary object used by heartpy to store computed measures. Will be created
        if not passed to function.

    working_data : dict
        dictionary object that contains all heartpy's working data (temp) objects.
        Will be created if not passed to function.

    Returns
    -------
    working_data : dict
        dictionary object used to store temporary values.

    measures : dict
        dictionary object used by heartpy to store computed measures.

    Examples
    --------
    There's example data included in HeartPy to help you get up to speed. Here are
    provided two examples of how to approach heart rate analysis.

    The first example contains noisy sections and comes with a timer column that
    counts miliseconds since start of recording.

    >>> import heartpy as hp
    >>> data, timer = hp.load_exampledata(1)
    >>> sample_rate = hp.get_samplerate_mstimer(timer)
    >>> '%.3f' %sample_rate
    '116.996'

    The sample rate is one of the most important characteristics during the
    heart rate analysis, as all measures are relative to this.

    With all data loaded and the sample rate determined, analysis is now easy:

    >>> wd, m = hp.process(data, sample_rate = sample_rate)

    The measures ('m') dictionary returned contains all determined measures

    >>> '%.3f' %m['bpm']
    '62.376'
    >>> '%.3f' %m['rmssd']
    '57.070'

    Using a slightly longer example:

    >>> data, timer = hp.load_exampledata(2)
    >>> print(timer[0])
    2016-11-24 13:58:58.081000

    As you can see something is going on here: we have a datetime-based timer.
    HeartPy can accomodate this and determine sample rate nontheless:

    >>> sample_rate = hp.get_samplerate_datetime(timer, timeformat = '%Y-%m-%d %H:%M:%S.%f')
    >>> '%.3f' %sample_rate
    '100.420'

    Now analysis can proceed. Let's also compute frequency domain data and interpolate clipping.
    In this segment the clipping is visible around amplitude 980 so let's set that as well:

    >>> data, timer = hp.load_exampledata(1)
    >>> sample_rate = hp.get_samplerate_mstimer(timer)
    >>> wd, m = hp.process(data, sample_rate = sample_rate, calc_freq = True,
    ... interp_clipping = True, interp_threshold = 975)
    >>> '%.3f' %m['bpm']
    '62.376'
    >>> '%.3f' %m['rmssd']
    '57.070'
    >>> '%.3f' %m['lf/hf']
    '1.090'

    High precision mode will upsample 200ms of data surrounding detected peak
    and attempt to estimate the peak's real position with higher accuracy.
    Use high_precision_fs to set the virtual sample rate to which the peak
    will be upsampled (e.g. 1000Hz gives an estimated 1ms accuracy)

    >>> wd, m = hp.process(data, sample_rate = sample_rate, calc_freq = True,
    ... high_precision = True, high_precision_fs = 1000.0)

    Finally setting reject_segmentwise will reject segments with more than 30% rejected beats
    See check_binary_quality in the peakdetection.py module.

    >>> wd, m = hp.process(data, sample_rate = sample_rate, calc_freq = True,
    ... reject_segmentwise = True)

    Final test for code coverage, let's turn all bells and whistles on that haven't been
    tested yet

    >>> wd, m = hp.process(data, sample_rate = 100.0, calc_freq = True,
    ... interp_clipping = True, clipping_scale = True, reject_segmentwise = True, clean_rr = True)
    Nr   é   r
   zªerror: multi-dimensional data passed to process() function. Please supply a 1d array or list containing heart rate signal data. 

Did you perhaps include an index column?)Ú	thresholdgš™™™™™¹?ÚhrÚsample_rate)ÚbpmminÚbpmmaxÚworking_dataÚpeaklist)r>   Zdesired_sample_raterA   )rA   ÚRR_listZybeat)ÚmethodÚRR_list_corZRR_diffZ	RR_sqdiff)ÚmeasuresrA   ÚRR_masklist©rD   rF   rA   Úbreathingrater5   ©rD   Úwelch_wsizeÚsquare_spectrumrF   rA   z
Finished in %.8s sec)ÚsysÚversion_infoÚtimeÚperf_counterÚclockÚnpÚasarrayÚndimÚAssertionErrorr   r   r   r   Z
percentileÚabsr   r    r#   r(   r!   r*   r+   r.   r-   Únanr,   Úprint)Úhrdatar>   Z
windowsizeZreport_timeÚ	calc_freqÚfreq_methodrK   Zfreq_squareZinterp_clippingZclipping_scaleZinterp_thresholdZhampel_correctr?   r@   Zreject_segmentwiseZhigh_precisionZhigh_precision_fsÚbreathing_methodÚclean_rrÚclean_rr_methodrF   rA   Út1Zbl_valZrol_mean© r`   úN/var/www/html/bwave/python/venv/lib/python3.8/site-packages/heartpy/heartpy.pyr0   7   s~     G


 ÿ ÿ ÿ þ
ÿ
 ÿ ÿ
 éx   é   ÚiqrÚfullc              	   K   sÀ  d|  krdk sn t dƒ‚|dks.t dƒ‚i }	i }
t| ||||ƒ}|dkrò|D ]š\}}zzt| ||… |f|Ž\}}| ¡ D ]}t|	||| ƒ}	q~| ¡ D ]}t|
||| ƒ}
qœt|	d||fƒ}	t|
d||fƒ}
W qR tjk
rê   Y qRX qR�nF|dk�r,t| |f|Ž\}}t |d	 ¡}|D �]\}}|t 	||k||k @ ¡ }|d
 t
t 	||d k¡d ƒt
t 	||d k¡d ƒd … }t |¡| d }t||ƒ\}}}t|||ƒ\}}| ¡ D ]}t|	||| ƒ}	�q¾t|	d||fƒ}	t|
d||fƒ}
t|
d|ƒ}
t|
d|ƒ}
t|
d|ƒ}
t|
d	|ƒ}
�q ntd| ƒ‚|�r¸| ¡ dk�r||	 ¡ D ]$}|dk�rTt|	| ƒ\|	|< }�qTn<| ¡ dk�r¸|	 ¡ D ]$}|dk�r’t|	| ƒ\|	|< }�q’|
|	fS )aâ  processes passed heart rate data with a windowed function

    Analyses a long heart rate data array by running a moving window
    over the data, computing measures in each iteration. Both the window width
    and the overlap with the previous window location are settable.

    Parameters
    ----------
    hrdata : 1d array or list
        array or list containing heart rate data to be analysed

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

    segment_width : int or float
        width of segments in seconds
        default : 120

    segment_overlap: float
        overlap fraction of adjacent segments.
        Needs to be 0 <= segment_overlap < 1.
        default : 0 (no overlap)

    segment_min_size : int
        often a tail end of the data remains after segmenting into segments.
        segment_min_size indicates the minimum length (in seconds) the tail
        end needs  to be in order to be included in analysis. It is discarded
        if it's shorter.
        default : 20

    replace_outliers : bool
        whether to detct and replace outliers in the segments. Will iterate over
        all computed measures and evaluate each.

    outlier_method : str
        what method to use to detect outlers. Available are 'iqr', which uses the
        inter-quartile range, and 'z-score', which uses the modified z-score approach.

    mode : str
        'full' or 'fast'

    Keyword arguments:
    ------------------
    hrdata -- 1-dimensional numpy array or list containing heart rate data
    sample_rate -- the sample rate of the heart rate data
    segment_width -- the width of the segment, in seconds, within which all measures
                     will be computed.
    segment_overlap -- the fraction of overlap of adjacent segments,
                       needs to be 0 <= segment_overlap < 1
    segment_min_size -- After segmenting the data, a tail end will likely remain that is shorter than the specified
                        segment_size. segment_min_size sets the minimum size for the last segment of the
                        generated series of segments to still be included. Default = 20.
    replace_outliers -- bool, whether to replace outliers (likely caused by peak fitting
                        errors on one or more segments) with the median.
    outlier_method -- which  method to use to detect outliers. Available are the
                      'interquartile-range' ('iqr') and the 'modified z-score' ('z-score') methods.

    Returns
    -------
    working_data : dict
        dictionary object used to store temporary values.

    measures : dict
        dictionary object used by heartpy to store computed measures.

    Examples
    --------
    Given one of the included example datasets we can demonstrate this function:

    >>> import heartpy as hp
    >>> data, timer = hp.load_exampledata(2)
    >>> sample_rate = hp.get_samplerate_datetime(timer, timeformat = '%Y-%m-%d %H:%M:%S.%f')
    >>> wd, m = hp.process_segmentwise(data, sample_rate, segment_width=120, segment_overlap=0.5)
    >>> len(m['bpm'])
    11

    The function has split the data into 11 segments and analysed each one. Every key in the
    measures (m) dict now contains a list of that measure for each segment.

    >>> [round(x, 1) for x in m['bpm']]
    [100.0, 96.8, 97.2, 97.9, 97.0, 96.8, 96.8, 95.0, 92.9, 96.7, 99.2]

    Specifying mode = 'fast' will run peak detection once and use detections
    to compute measures over each segment. Useful for speed ups, but typically
    the full mode has better results.

    >>> wd, m = hp.process_segmentwise(data, sample_rate, segment_width=120, segment_overlap=0.5,
    ... mode = 'fast', replace_outliers = True)

    You can specify the outlier detection method ('iqr' - interquartile range, or 'z-score' for
    modified z-score approach).

    >>> wd, m = hp.process_segmentwise(data, sample_rate, segment_width=120, segment_overlap=0.5,
    ... mode = 'fast', replace_outliers = True, outlier_method = 'z-score')

    r   g      ð?zDvalue error: segment_overlap needs to be 0 <= segment_overlap < 1.0!)rd   úz-scorezIUnknown outlier detection method specified, use either 'iqr' or 'z-score're   Úsegment_indicesÚfastrB   Zbinary_peaklistéÿÿÿÿr
   r9   Úrr_listÚrr_diffÚ	rr_sqdiffzDmode not understood! Needs to be either 'fast' or 'full', passed: %srd   )Znn20Znn50Zinterp_rr_functionZinterp_rr_linspacerg   rf   )rU   r   r0   Úkeysr   r   ZBadSignalWarningrR   rS   ÚwhereÚintÚdiffr)   r+   Ú
ValueErrorÚlowerr   r   )rY   r>   Zsegment_widthZsegment_overlapZsegment_min_sizeZreplace_outliersZoutlier_methodÚmodeÚkwargsZ
s_measuresZs_working_dataZslice_indicesÚiÚiirA   rF   ÚkrB   ZpksZpks_brj   rk   rl   Ú_Útmpr`   r`   ra   r2   K  s^    d
ÿ

Tc                    s�  | |
d< |rŽt  | ¡}d| dkr*|d n
|d|  ‰d| dkrJ|d n
|d|  ‰ ‡ ‡fdd„| D ƒ}‡ ‡fdd„| D ƒ}||
d< ||
d< |ržt|
|d	�}
|sæ|sæ| |
d< d
d„ tt| ƒƒD ƒ|
d< t  t  | ¡¡}t  |d¡}n t  t  |
d ¡¡}t  |d¡}t|
d |||	|
d�\}
}	t	|
d |
d |	|
d�}	|�rTt
|d||	|
d�\}
}	zt|
d ||	|
d�\}	}
W n   t j|	d< Y nX |
|	fS )aM  process rr-list

    Function that takes and processes a list of peak-peak intervals (tachogram).
    Computes all measures as computed by the regular process() function, and
    sets up all dicts required for plotting poincare plots.

    Note: This method assumes ms-based tachogram

    Several filtering methods are available as well.

    Parameters
    ----------
    rr_list : 1d array or list
        list or array containing peak-peak intervals (in ms).

    threshold_rr : bool
        if true, the peak-peak intervals are cleaned using a threshold filter, which
        rejects all intervals that differ 30% from the mean peak-peak interval, with
        a minimum of 300ms.
        default : false

    clean_rr : bool
        if true, the RR_list is further cleaned with an outlier rejection pass. This pass
        is performed after threshold_rr, if that is specified.
        default : false

    clean_rr_method: str
        how to find and reject outliers. Available methods are 'quotient-filter',
        'iqr' (interquartile range), and 'z-score'.
        default : 'quotient-filter'

    calc_freq : bool
        whether to compute time-series measurements
        default : False

    freq_method : str
        method used to extract the frequency spectrum. Available: 'fft' (Fourier Analysis),
        'periodogram', and 'welch' (Welch's method).
        default : 'welch'

    welch_wsize : float
        Size of window (in sec) when welch method used to compute the spectrogram.
        This choice is based on a trade-off btw temporal res and freq res of the resulting spectrum
        60 sec may seem reasonable, but this would greatly limit frequency resolution!
          1/60 s = 0.017 Hz, so you would only have 2 points in the VLF band
        Therefore, the default is 4 min (9 points in the VLF band)
        default : 240

    square_spectrum : bool
        whether to square the power spectrum returned.
        default : true

    measures : dict
        dictionary object used by heartpy to store computed measures. Will be created
        if not passed to function.

    working_data : dict
        dictionary object that contains all heartpy's working data (temp) objects.
        will be created if not passed to function

    Returns
    -------
    working_data : dict
        dictionary object used to store temporary values.

    measures : dict
        dictionary object used by heartpy to store computed measures.

    Examples
    --------
    Let's generate an RR-list first.

    >>> import heartpy as hp
    >>> data, timer = hp.load_exampledata(2)
    >>> sample_rate = hp.get_samplerate_datetime(timer, timeformat = '%Y-%m-%d %H:%M:%S.%f')
    >>> wd, m = hp.process(data, sample_rate)
    >>> rr_list = wd['RR_list']

    Using only the RR-list (in ms!) we can now call this function, and let's put the results
    into a differently named container so we're sure all measures are unique:
    >>> wd2, m2 = process_rr(rr_list, threshold_rr = True, clean_rr = True, calc_freq = True)
    >>> '%.3f' %m2['rmssd']
    '45.641'

    If you want to, you can turn off all filters and rejection features:
    >>> wd2, m2 = process_rr(rr_list, threshold_rr = False, clean_rr = False)
    >>> '%.3f' %m2['rmssd']
    '162.645'

    In this case it seems the filtering was necessary: without the RMSSD lies outside the
    range expected in healthy humans.
    rC   g333333Ó?i,  c                    s    g | ]}|ˆ kr|ˆk r|‘qS r`   r`   ©Ú.0Úx©Zlower_thresholdZupper_thresholdr`   ra   Ú
<listcomp>U  s       zprocess_rr.<locals>.<listcomp>c                    s$   g | ]}|ˆ ks|ˆkrd nd‘qS )r
   r   r`   rz   r}   r`   ra   r~   V  s     rE   rG   )rA   rD   c                 S   s   g | ]}d ‘qS )r   r`   )r{   ru   r`   r`   ra   r~   `  s     é   )rj   rk   rl   rF   rA   )rj   Úrr_maskrF   rA   r5   rJ   rH   rI   )rR   Zmeanr*   ÚrangeÚlenrV   rp   Úpowerr+   r.   r,   r-   rW   )rj   Zthreshold_rrr]   r^   rZ   r[   rK   rL   r\   rF   rA   Zmean_rrZrr_list_corr€   rk   rl   r`   r}   ra   r1   í  sP    a
  
 þ
 ÿ ÿ

 ÿc                 C   sä  ddl m}m}m}m}m}m}m} ddl}d}	t	dƒ |j
|| d�}
|
jdkr`t	dƒ |	d7 }	t	dƒ |j
|| d�}
|
jdkr�t	dƒ |	d7 }	t	d	ƒ |j
|| d�}
|
jdkrÀt	dƒ |	d7 }	t	d
ƒ |j
|| d�}
|
jdkrðt	dƒ |	d7 }	t	dƒ |j
|| d�}
|
jdk�r"t	dƒ |	d7 }	t	dƒ |j
|| d�}
|
jdk�rTt	dƒ |	d7 }	t	dƒ |j
|| d�}
|
jdk�r†t	dƒ |	d7 }	t	dƒ ddl m} |j
|| d�}
|
jdk�rÄt	dƒ |	d7 }	|	dk�rØt	dƒ nt	dƒ dS )z3
    function to run doctest on all of HeartPy
    r
   )ÚanalysisÚ	datautilsÚ	filteringÚpeakdetectionÚpreprocessingÚvisualizeutilsr/   r   Nztesting config)Úverbosezsuccess!ztesting analysisztesting datautilsztesting filteringztesting peakdetectionztesting preprocessingztesting visualization utilsz testing main processing pipeline)Úheartpyé   zall tests passed, ready to go!zsome tests failed...)Ú r„   r…   r†   r‡   rˆ   r‰   r/   ÚdoctestrX   ÚtestmodZfailedr‹   )rŠ   r„   r…   r†   r‡   rˆ   r‰   r/   rŽ   Z	succeededÚresultsZhptesterr`   r`   ra   r3   }  s^    $





)r4   FFr   r5   FFFr6   Fr7   r8   FFr9   r   Fr:   NN)rb   r   rc   Frd   re   )r   )BÚ__doc__r   rO   ÚosrM   ÚnumpyrR   Zscipy.interpolater   Zscipy.signalr   r   r   r   r   r	   r�   r   r…   r   r   r   r   r   r   r   rˆ   r   r   r   r   r   r   r†   r   r   r   r   r   r‡   r   r   r    r!   r"   r#   r‰   r$   r%   r&   r'   r„   r(   r)   r*   r+   r,   r-   r.   r/   ÚinitÚ__all__r0   r2   r1   r3   r`   r`   r`   ra   Ú<module>   s     $  $ë                                û
         þ
 #     ý
 