Example #1
0
def _tag_qrs(waves):
    """
    Creates a new string tag for a QRS complex from a sequence of waves. This
    tag matches the name given by cardiologists to the different QRS waveforms.
    """
    #This method consists in a concatenation of heuristic rules described with
    #more or less precision in "European Heart Journal: Recommendations for
    #measurement standards in quantitative electrocardiography. (1985)".
    result = ''
    waves = list(waves)
    while waves:
        wav = waves.pop(0)
        #If the first wave is negative...
        if not result and wav.sign == -1:
            if not waves:
                result = 'QS' if abs(wav.amp) > ph2dg(0.5) else 'Q'
            else:
                result = 'Q' if abs(wav.amp) > ph2dg(0.2) else 'q'
        else:
            newt = 'r' if wav.sign == 1 else 's'
            if abs(wav.amp) > ph2dg(0.5):
                newt = newt.upper()
            result += newt
    return result
Example #2
0
def _paced_qrs_delineation(signal, points, peak, baseline):
    """
    Checks if a sequence of waves is a paced heartbeat. The main criteria is
    the presence of a spike at the beginning of the beat, followed by at least
    one significant wave.
    """
    try:
        #Gets the slope between two points.
        slope = lambda a, b : abs(dg2mm((signal[b]-signal[a])/sp2mm(b-a)))
        #First we search for the spike.
        spike = _find_spike(signal, points)
        verify(spike)
        if not spike[-1] in points:
            points = np.insert(points, bisect.bisect(points, spike[-1]),
                                                                     spike[-1])
        #Now we get relevant points, checking some related constraints.
        bpts = points[points <= spike[0]]
        apts = points[points >= spike[-1]]
        verify(len(apts) >= 2)
        #Before and after the spike there must be a significant slope change.
        verify(slope(spike[0], spike[1]) > 2.0 * slope(bpts[-2], bpts[-1]))
        verify(slope(spike[1], spike[-1]) > 2.0 * slope(apts[0], apts[1]))
        #Now we look for the end of the QRS complex, by applying the same
        #clustering strategy than regular QRS, but only for the end.
        slopes = (signal[apts][1:]-signal[apts][:-1])/(apts[1:]-apts[:-1])
        features = []
        for i in xrange(len(slopes)):
            #The features are the slope in logarithmic scale and the distance to
            #the peak.
            features.append([math.log(abs(slopes[i])+1.0),
                                                        abs(apts[i+1] - peak)])
        features = whiten(features)
        #We initialize the centroids in the extremes (considering what is
        #interesting of each feature for us)
        fmin = np.min(features, 0)
        fmax = np.max(features, 0)
        valid = np.where(kmeans2(features, np.array([[fmin[0], fmax[1]],
                                 [fmax[0], fmin[1]]]), minit = 'matrix')[1])[0]
        verify(np.any(valid))
        end = apts[valid[-1]+1]
        #The duration of the QRS complex after the spike must be more than 2
        #times the duration of the spike.
        verify((end-apts[0]) > 2.0 * (spike[-1]-spike[0]))
        #The amplitude of the qrs complex must higher than 0.5 the amplitude
        #of the spike.
        sgspike = signal[spike[0]:spike[-1]+1]
        sgqrs = signal[apts[0]:end+1]
        verify(np.ptp(sgqrs) > ph2dg(0.5))
        verify(np.ptp(sgqrs) > 0.5 * np.ptp(sgspike))
        #There must be at least one peak in the QRS fragment.
        qrspt = signal[apts[apts <= end]]
        verify(len(qrspt) >= 3)
        verify(abs(signal[end] - signal[spike[0]]) <= ph2dg(0.3)
                                                  or len(get_peaks(qrspt)) > 0)
        #The area of the rest of the QRS complex must be higher than the spike.
        verify(np.sum(np.abs(sgspike-sgspike[0])) <
                                              np.sum(np.abs(sgqrs-sgspike[0])))
        #The distance between the beginning of the spike and the baseline
        #cannot be more than the 30% of the amplitude of the complex.
        verify(abs(signal[spike[0]]-baseline) <
                                          0.3 * np.ptp(signal[spike[0]:end+1]))
        #At last, we have found the paced QRS limits.
        return Iv(spike[0], end)
    except InconsistencyError:
        return None