# -*- coding: utf-8 -*-
"""
Created on Sat Mar 18 19:19:25 2017

@author: Sarah Pickus

Poincare plots are an important visualization technique for quantifying the non-linear
characteristics of the RR interval time series. They are generated via plotting 
each RR interval (RR[n]) against the subsequent RR interval (RR[n+1]). There are 
a number of geometrical descriptors that can be extracted from the poincare
plot that have been shown to provide insights into short and long term HRV
trends. This package allows the user to calculate a number of these poincare
features, which are detailed below:

    elipseFittingMethod   : The ellipse fitting technique is one of the most popular
                            methods for characterizing the geometry of the poincare 
                            plot and providing insights into the level of short- and 
                            long-term variability. The short-term variability is
                            represented by SD1, which is the standard deviation of points 
                            perpendicular to the axis line-of-identy, while the 
                            long-term variability is represented by SD2, which is the 
                            tandard deviation of points along the line-of-identy.                           
                            This method is described in detail in:
                                
                            Brennan, Michael, Marimuthu Palaniswami, and Peter Kamen. 
                            "Do existing measures of Poincare plot geometry reflect 
                            nonlinear features of heart rate variability?." IEEE 
                            transactions on biomedical engineering 48.11 (2001): 
                            1342-1347.
        
    hraMethod             : C_UP and C_DOWN are derived from the standard descriptor
                            SD1^2, the variance of which corresponds to short-term HRV.
                            These more refined descriptors quantify the Poincare plot 
                            assymetry about the line-of-identity and in doing so 
                            characterize accelerations (C_DOWN) and decelerations (C_UP) 
                            of heart rate. More details on this method can be found here:
                            
                            Piskorski, J., and P. Guzik. "Geometry of the Poincaré plot 
                            of RR intervals and its asymmetry in healthy adults." 
                            Physiological measurement 28.3 (2007): 287.
        
    correlationCoefficient: The interbeat autocorrelation coefficient of the RR
                            interval time series (Pearson's correlation coefficient) 
                            has been shown to characterize poincare plot geometry,
                            as shown in:
                                
                            Otzenberger, Hélène, et al. "Dynamic heart rate variability: 
                            a tool for exploring sympathovagal balance continuously 
                            during sleep in men." American Journal of Physiology 
                            275 (1998): H946-H950.

Additionally, this package allows you to generate a poincare plot and generate histogram
distributions from mulitple projections. Statistical properties of these projections
can then be calculated as additional descriptors. 

"""

import numpy as np
from matplotlib import style
import matplotlib.pyplot as plt
style.use('ggplot')

def plotPoincare(RRints):
    """
    Input    :
    
     - RRints: [list] of RR intervals
        
    Output   :

     - Poincare plot     
    """
    ax1 = RRints[:-1]
    ax2 = RRints[1:]   
    plt.scatter(ax1, ax2, c = 'r', s = 12)
    plt.xlabel('RR_n (s)')
    plt.ylabel('RR_n+1 (s)')
    plt.show()

def eclipseFittingMethod(RRints):
    """
    Input        :
    
     - RRints    : [list] of RR intervals
        
    Output       : 
             
      - SD1, SD2 : {dict} with keys 'SD1' (numpy.float64), representing short-term 
                   variation, and 'SD2' (numpy.float64), representing long-term
                   variation.   
    """
    SDSD =  np.std(np.diff(RRints))
    SDRR = np.std(RRints)
    SD1 = (1 / np.sqrt(2)) * SDSD #measures the width of poincare cloud
    SD2 = np.sqrt((2 * SDRR ** 2) - (0.5 * SDSD ** 2)) #measures the length of the poincare cloud
 
    return {'SD1': SD1, 'SD2': SD2}
    
def hraMethod(RRints):
    """
    Perform analysis to quantify heart rate assymmetry (HRA).
    
    Input           :
    
     - RRints       : [list] of RR intervals
        
    Output          :

     - C_DOWN, C_UP : {dict} with keys 'C_DOWN' (numpy.float64) and 
                      'C_UP' (numpy.float64)    
     """    
    ax1 = RRints[:-1]
    ax2 = RRints[1:]
    SD1I = np.sqrt((1 / len(ax1)) * (np.sum((ax1 - ax2) ** 2) / 2))
    ax1ax2 = (ax1 - ax2) / np.sqrt(2)
    indices_up = np.where(ax1ax2 > 0)
    indices_down = np.where(ax1ax2 < 0)
    SD1_UP = np.sqrt(np.sum(ax1ax2[indices_up] ** 2) / len(ax1))
    SD1_DOWN = np.sqrt(np.sum(ax1ax2[indices_down] ** 2) / len(ax1))    
    C_UP = SD1_UP ** 2 / SD1I ** 2 #for decelerations
    C_DOWN = SD1_DOWN ** 2 / SD1I ** 2 #for accelerations
    
    return {'C_UP': C_UP, 'C_DOWN': C_DOWN}

def correlationCoef(RRints):
    """
    Computes interbeat autocorrelation coefficient
    
    Input    :
    
     - RRints: [list] of RR intervals
        
    Output   : 
        
     - r_rr  : [numpy.float64] interbeat autocorrelation coefficient
 
     """    
    ax1 = RRints[:-1]
    ax2 = RRints[1:]
    mu_rr = np.mean(RRints)
    r_rr = np.mean((ax1 - mu_rr) * (ax2 - mu_rr)) / (np.sqrt(np.mean((ax1 - mu_rr) ** 2) * np.mean((ax2 - mu_rr) ** 2)))

    return r_rr
    
def plotRRintHist(RRints):
    """ 
    Histogram distribution of poincare points projected onto the x-axis
    
    Input    :
    
     - RRints: [list] of RR intervals
        
    Output   :
        
     - RR interval histogram plot    
    """    
    plt.hist(RRints, bins = 'auto')
    plt.xlabel('RR Interval')
    plt.ylabel('Number of RR Intervals')
    plt.title('RR Interval Histogram')
    plt.show()

def plotWidthHist(RRints):    
    """  
    Histogram distribution of poincare points projected along the direction of 
    line-of-identity, or along the line perpendicular to the line-of-identity.
    
    Input    :
    
     - RRints: [list] of RR intervals
        
    Output   :
        
     - 'Width', or delta-RR interval, histogram plot      
     """   
    ax1 = RRints[:-1]
    ax2 = RRints[1:]
    x1 = (np.cos(np.pi / 4) * ax1) - (np.sin(np.pi / 4) * ax2)
    plt.hist(x1, bins = 'auto')
    plt.title('Width (Delta-RR Interval) Histogram')
    plt.show()
    
def plotLengthHist(RRints):    
    """
    Histogram distribution of poincare points projected along the line-of-identty.
    
    Input    :
    
     - RRints: [list] of RR intervals
        
    Output   :
        
     - 'Length' histogram plot
     """
     
    ax1 = RRints[:-1]
    ax2 = RRints[1:]
    x2 = (np.sin(np.pi / 4) * ax1) + (np.cos(np.pi / 4) * ax2)
    plt.hist(x2, bins = 'auto')
    plt.title('Length Histogram')
    plt.show()