示例#1
0
def _t_tconst(pattern, twave):
    """
    Temporal constraints of the T Waves wrt the corresponding QRS complex.
    """
    BASIC_TCONST(pattern, twave)
    obseq = pattern.obs_seq
    idx = pattern.get_step(twave)
    try:
        tnet = pattern.last_tnet
        # We find the qrs observation precedent to this T wave.
        qrs = next(obseq[i] for i in range(idx - 1, -1, -1)
                   if isinstance(obseq[i], o.QRS))
        # If we have more than one QRS, it is possible to constrain even more
        # the location of the T-Wave, based on rhythm information.
        qidx = pattern.evidence[o.QRS].index(qrs)
        if qidx > 1:
            refrr = (qrs.time.end -
                     pattern.evidence[o.QRS][qidx - 2].time.start) / 2.0
            tnet.add_constraint(qrs.time, twave.end,
                                Iv(0, refrr - C.TQ_INTERVAL_MIN))
        if idx > 0 and isinstance(obseq[idx - 1], o.PWave):
            pwave = obseq[idx - 1]
            tnet.add_constraint(
                pwave.end, twave.start,
                Iv(C.ST_INTERVAL.start, C.PQ_INTERVAL.end + C.QRS_DUR.end))
        # ST interval
        tnet.add_constraint(qrs.end, twave.start, C.ST_INTERVAL)
        # QT duration
        tnet.add_constraint(qrs.start, twave.end, C.N_QT_INTERVAL)
    except StopIteration:
        pass
示例#2
0
def _qrs3_tconst(pattern, qrs):
    """Temporal constraints of the third QRS complex"""
    BASIC_TCONST(pattern, qrs)
    tnet = pattern.last_tnet
    tnet.set_before(qrs.time, pattern.hypothesis.end)
    tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
    beats = pattern.evidence[o.QRS]
    # If there is a previous QRS
    if beats.index(qrs) == 1:
        tnet.add_constraint(
            beats[0].time, qrs.time,
            Iv(C.TACHY_RR.start + C.RR_MAX_DIFF, C.BRADY_RR.end))
    # If we have reached an initial state.
    if pattern.istate == 0:
        idx = beats.index(qrs)
        meanrr, stdrr = pattern.hypothesis.meas.rr
        minrr = beats[1].time.start - beats[
            0].time.end if idx == 2 else meanrr - stdrr
        tnet.add_constraint(
            beats[idx - 1].time,
            qrs.time,
            Iv(min(C.ASYSTOLE_RR.start, minrr + C.RR_MAX_DIFF),
               C.ASYSTOLE_RR.start),
        )
        # The block time has to be higher than the mean RR plus the standard
        # deviation.
        if meanrr > 0:
            tnet.add_constraint(
                beats[idx - 1].time, qrs.time,
                Iv(meanrr + stdrr, max(meanrr + stdrr, C.ASYSTOLE_RR.start)))
示例#3
0
def _rdef_gconst(pattern, _):
    """
    General constraints of the R-Deflection pattern, that simply looks in
    the global list for an appropriate annotation.
    """
    if ANNOTS is None:
        _load_annots()
    leads = IN.SIG.get_available_leads()
    # We find all the annotations in the given interval.
    rdef = pattern.hypothesis
    beg = min(int(rdef.earlystart), IN.get_acquisition_point()) + IN._OFFSET
    end = min(int(rdef.lateend), IN.get_acquisition_point()) + IN._OFFSET
    dummy = MITAnnotation()
    dummy.time = beg
    bidx = ANNOTS.bisect_left(dummy)
    dummy.time = end
    eidx = ANNOTS.bisect_right(dummy)
    verify(eidx > bidx)
    selected = max(ANNOTS[bidx:eidx], key=operator.attrgetter('num'))
    time = selected.time - IN._OFFSET
    rdef.time.value = Iv(time, time)
    rdef.start.value = Iv(time, time)
    rdef.end.value = Iv(time, time)
    rdef.level = {lead: 127 for lead in leads}
    rdef.level[leads[selected.chan]] = 127 - selected.num
示例#4
0
 def tconst(pattern, qrs):
     """
     Defines the temporal constraints function for the ectopic beat in an
     extrasystole, depending on its ventricular nature or not.
     """
     BASIC_TCONST(pattern, qrs)
     tnet = pattern.last_tnet
     tnet.set_before(qrs.end, pattern.hypothesis.end)
     if ventricular:
         tnet.add_constraint(qrs.start, qrs.end, C.VQRS_DUR)
     # It must be the third beat.
     beats = pattern.evidence[o.QRS]
     idx = beats.index(qrs)
     # If there is a previous beat
     if idx > 0:
         tnet.add_constraint(beats[idx - 1].time, qrs.time,
                             Iv(C.TACHY_RR.start, 0.9 * C.BRADY_RR.end))
     # If all the previous evidence has been observed
     if pattern.istate == 0:
         # Anticipation of at least the 10% of the reference RR, or 1mm.
         if idx == 2:
             refrr = beats[1].time.end - beats[0].time.start
         elif pattern.evidence[o.Cardiac_Rhythm][0] is not pattern.finding:
             refrr = pattern.hypothesis.meas.rr[0]
         else:
             refrr = None
         if refrr is not None:
             short = min(0.1 * refrr, C.TMARGIN)
             tnet.add_constraint(
                 beats[idx - 1].time, qrs.time,
                 Iv(C.TACHY_RR.start, max(C.TACHY_RR.start, refrr - short)))
示例#5
0
 def _t_tconst(pattern, twave):
     """
     Temporal constraints of the T wave.
     """
     BASIC_TCONST(pattern, twave)
     beats = pattern.evidence[o.QRS]
     tnet = pattern.last_tnet
     qidx = qrsidx + len(beats) if qrsidx < 0 else qrsidx
     qrs = beats[qidx]
     if qidx < len(beats) - 1:
         tnet.set_before(twave.end, beats[qidx + 1].start)
     if qidx > 0:
         refrr = qrs.time.end - pattern.evidence[o.QRS][qidx - 1].time.start
         refrr = max(min(refrr, C.QTC_RR_LIMITS.end), C.QTC_RR_LIMITS.start)
         rtc, rtstd = pattern.hypothesis.meas.rt
         if rtc > 0:
             # Expected QT value from the QT corrected value
             rtmean = ms2sp(1000.0 * sp2sc(rtc) * np.cbrt(sp2sc(refrr)))
             tnet.add_constraint(
                 qrs.time, twave.end,
                 Iv(rtmean - 2.5 * rtstd, rtmean + 2.5 * rtstd))
         try:
             tnet.add_constraint(qrs.time, twave.end,
                                 Iv(0, refrr - C.TQ_INTERVAL_MIN))
         except ValueError:
             pass
     tnet.add_constraint(qrs.start, twave.end, C.N_QT_INTERVAL)
     # ST interval
     tnet.add_constraint(qrs.end, twave.start, C.ST_INTERVAL)
 def _t_tconst(pattern, twave):
     """
     Temporal constraints of the T Waves wrt the corresponding QRS complex.
     """
     BASIC_TCONST(pattern, twave)
     tnet = pattern.last_tnet
     obseq = pattern.obs_seq
     idx = pattern.get_step(twave)
     beats = pattern.evidence[o.QRS]
     qidx = qrsidx + len(beats) if qrsidx < 0 else qrsidx
     qrs = beats[qidx]
     if qidx > 1:
         refsq = beats[qidx - 1].earlystart - beats[qidx - 2].lateend
         tnet.add_constraint(qrs.time, twave.end,
                             Iv(0, max(0, refsq - C.TQ_INTERVAL_MIN)))
     if idx > 0 and isinstance(obseq[idx - 1], o.PWave):
         pwave = obseq[idx - 1]
         tnet.add_constraint(
             pwave.end, twave.start,
             Iv(C.ST_INTERVAL.start, C.PQ_INTERVAL.end + C.QRS_DUR.end))
     if qidx < len(beats) - 1:
         tnet.set_before(twave.end, beats[qidx + 1].start)
     # ST interval
     tnet.add_constraint(qrs.end, twave.start, C.ST_INTERVAL)
     # QT duration
     tnet.add_constraint(qrs.start, twave.end, C.N_QT_INTERVAL)
     # RT variation
     if qidx % 2 == 0:
         rtmean, rtstd = pattern.hypothesis.meas.rt
         # We also define a constraint on T wave end based on the last
         # distance between normal and ectopic QRS.
         if qidx > 0:
             tnet.add_constraint(
                 qrs.end, twave.end,
                 Iv(0,
                    beats[qidx - 1].earlystart - beats[qidx - 2].lateend))
     else:
         rts = _get_measures(pattern, 1)[2]
         rtmean, rtstd = np.mean(rts), np.std(rts)
     if rtmean > 0:
         # The mean and standard deviation of the PQ measurements will
         # influence the following observations.
         maxdiff = C.QT_ERR_STD if len(
             pattern.evidence[o.TWave]) < 10 else rtstd
         maxdiff = max(maxdiff, C.MIN_QT_STD)
         interv = Iv(int(rtmean - 2.5 * maxdiff),
                     int(rtmean + 2.5 * maxdiff))
         # We avoid possible inconsistencies with constraint introduced by
         # the rhythm information.
         try:
             existing = tnet.get_constraint(qrs.time, twave.end).constraint
         except KeyError:
             existing = Iv(-np.inf, np.inf)
         if interv.overlap(existing):
             tnet.add_constraint(qrs.time, twave.end, interv)
示例#7
0
def _qrs_fin_pause_tconst(pattern, qrs):
    """
    Temporal constraints for the compensatory pause of the last QRS of the
    extrasystole.
    """
    BASIC_TCONST(pattern, qrs)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.end, qrs.time)
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    # We need all the previous evidence.
    if pattern.istate == 0:
        step = pattern.get_step(qrs)
        if isinstance(pattern.trseq[step - 1][1], o.TWave):
            twave = pattern.trseq[step - 1][1]
            tnet.set_before(twave.end, qrs.start)
        # Reference RR
        minrr = beats[1].time.start - beats[0].time.end if len(
            beats) == 4 else pattern.hypothesis.meas.rr[0]
        maxrr = beats[1].time.end - beats[0].time.start if len(
            beats) == 4 else pattern.hypothesis.meas.rr[0]
        # Compensatory pause
        tnet.add_constraint(
            beats[-3].time,
            qrs.time,
            Iv(min(minrr + C.COMPAUSE_MIN_DUR, minrr * C.COMPAUSE_MIN_F),
               maxrr * C.COMPAUSE_MAX_F),
        )
        # Advanced beat RR
        minrr = beats[-2].time.start - beats[-3].time.end
        maxrr = beats[-2].time.end - beats[-3].time.start
        tnet.add_constraint(
            beats[-2].time,
            qrs.time,
            Iv(
                min(minrr * C.COMPAUSE_RREXT_MIN_F,
                    minrr + C.COMPAUSE_RREXT_MIN),
                maxrr * C.COMPAUSE_RREXT_MAX_F,
            ),
        )
        # The last QRS should have the same morphology than the one before the
        # extrasystole.
        if not qrs.frozen:
            qrs.shape = beats[-3].shape
            qrs.paced = beats[-3].paced
    elif idx > 0:
        # We constraint the previous beat location.
        tnet.add_constraint(
            beats[idx - 1].time,
            qrs.time,
            Iv(C.TACHY_RR.start * C.COMPAUSE_MIN_F,
               C.BRADY_RR.start * C.COMPAUSE_RREXT_MAX_F),
        )
示例#8
0
def _qrs_tconst(pattern, rdef):
    """
    Adds the temporal constraints of the QRS abstraction pattern automata.
    """
    tnet = pattern.last_tnet
    qrs = pattern.hypothesis
    # QRS complex duration constraint
    tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
    # Constraints related to the peak of the complex.
    tnet.add_constraint(qrs.start, qrs.time, Iv(C.QRS_START_PK, C.QRS_RDEF_DMAX))
    tnet.add_constraint(qrs.time, qrs.end, Iv(C.QRS_PK_END, np.inf))
    # Constraints between QRS and R-Deflection
    tnet.add_constraint(rdef.time, qrs.start, Iv(-C.QRS_RDEF_DMAX, C.QRS_RDEF_DMAX))
    tnet.add_constraint(rdef.time, qrs.end, C.QRS_DUR)
示例#9
0
def _t_gconst(pattern, defl):
    """
    T Wave abstraction pattern general constraints, checked when all the
    evidence has been observed.
    """
    twave = pattern.hypothesis
    if defl.earlystart != defl.latestart or not pattern.evidence[o.QRS]:
        return
    qrs = pattern.evidence[o.QRS][0]
    # Wave limits
    beg = int(twave.earlystart)
    end = int(twave.lateend)
    ls_lim = int(twave.latestart - beg)
    ee_lim = int(twave.earlyend - beg)
    # Start and end estimation.
    endpoints = {}
    for lead in sorted(qrs.shape, key=lambda l: qrs.shape[l].amplitude, reverse=True):
        baseline, _ = characterize_baseline(lead, beg, end)
        sig = sig_buf.get_signal_fragment(beg, end, lead=lead)[0]
        verify(len(sig) == end - beg + 1)
        ep = _delimit_t(sig, baseline, ls_lim, ee_lim, qrs.shape[lead])
        if ep is not None:
            endpoints[lead] = ep
    verify(endpoints)
    limits = max(endpoints.iteritems(), key=lambda ep: ep[1][1])[1][0]
    # We verify that in all leads the maximum slope of the T wave fragment does
    # not exceed the threshold.
    for lead in endpoints:
        sig = sig_buf.get_signal_fragment(beg + limits.start, beg + limits.end, lead=lead)[0]
        verify(np.max(np.abs(np.diff(sig))) <= qrs.shape[lead].maxslope * C.TQRS_MAX_DIFFR)
        # Amplitude measure
        if lead in endpoints:
            mx, mn = np.amax(sig), np.amin(sig)
            pol = 1.0 if max(mx - sig[0], mx - sig[-1]) >= -min(mn - sig[0], mn - sig[1]) else -1.0
            twave.amplitude[lead] = pol * np.ptp(sig)
    twave.start.value = Iv(beg + limits.start, beg + limits.start)
    twave.end.value = Iv(beg + limits.end, beg + limits.end)
    # The duration of the T Wave must be greater than the QRS
    # (with a security margin)
    verify(twave.earlyend - twave.latestart > qrs.earlyend - qrs.latestart - C.TMARGIN)
    # The overlapping between the energy interval and the T Wave must be at
    # least the half of the duration of the energy interval.
    verify(
        Iv(twave.earlystart, twave.lateend).intersection(Iv(defl.earlystart, defl.lateend)).length
        >= (defl.lateend - defl.earlystart) / 2.0
    )
    # If the Deflection is a R-Deflection, we require a margin before
    # the end of the twave.
    if isinstance(defl, o.RDeflection):
        verify(twave.lateend - defl.time.end > C.TW_RDEF_MIN_DIST)
示例#10
0
def combine_energy_intervals(dicts, margin=ms2sp(20)):
    """
    Combines the overlapping observations in several dicts in the result format
    of the get_deflection_observations() function.

    Parameters
    ----------
    dicts:
        List of dictionaries. The combination is always performed to the
        first dictionary.
    score:
        Dictionary that stores the score for each observation. For overlapping
        observations, the result score is the sum of the overlapped
        observations.
    margin:
        Group margin. Intervals separated by less than this margin are removed.
    """
    chain = it.chain.from_iterable
    dict1 = dicts[0]
    for wint in chain(dict1.itervalues()):
        for i in range(1, len(dicts)):
            conflictive = []
            for lst in dicts[i].itervalues():
                if not lst:
                    continue
                idx = bisect.bisect_left(lst, wint)
                # We go to the first real index
                while idx > 0 and lst[
                        idx - 1].lateend + margin >= wint.earlystart - margin:
                    idx -= 1
                # Now we search for overlapping intervals
                while idx < len(lst) and lst[
                        idx].earlystart - margin <= wint.lateend + margin:
                    w = lst[idx]
                    if Iv(w.earlystart - margin, w.lateend + margin).overlap(
                            Iv(wint.earlystart - margin,
                               wint.lateend + margin)):
                        conflictive.append(w)
                    idx += 1
            if conflictive:
                alleads = set.union(*(set(w.level.iterkeys())
                                      for w in conflictive)) - set(
                                          wint.level.iterkeys())
                for lead in alleads:
                    wint.level[lead] = min(
                        w.level.get(lead, np.Inf) for w in conflictive)
                for wconf in conflictive:
                    dicts[i][wconf.level.values()[0]].remove(wconf)
def _ect_qrs_tconst(pattern, qrs):
    """
    Temporal constraints for ectopic beats, which appear after every regular
    beat.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    tnet = pattern.last_tnet
    hyp = pattern.hypothesis
    if idx > 0:
        prev = beats[idx - 1]
        # After the second couplet, every ectopic beat introduces a new temporal
        # network in the pattern to make it easier the minimization.
        if idx > 3:
            tnet.remove_constraint(hyp.end, prev.time)
            # We create a new temporal network for the cyclic observations
            tnet = ConstraintNetwork()
            pattern.temporal_constraints.append(tnet)
            # The duration of each couplet should not have high instantaneous
            # variations.
            refrr = beats[idx - 2].time.end - beats[idx - 3].time.start
            tnet.add_constraint(
                prev.time, qrs.time,
                Iv(refrr - C.RR_MAX_DIFF, refrr + C.RR_MAX_DIFF))
            # We guide the morphology search to be similar to the previous
            # ectopic QRS complex.
            qrs.shape = beats[idx - 2].shape
        # The reference RR varies from an upper limit to the last measurement,
        # through the contextual previous rhythm.
        refrr = C.BRADY_RR.end
        stdrr = 0.1 * refrr
        if pattern.evidence[o.Cardiac_Rhythm] and idx == 1:
            mrr, srr = pattern.evidence[o.Cardiac_Rhythm][0].meas.rr
            if mrr > 0:
                refrr, stdrr = mrr, srr
        elif idx > 1:
            refrr, stdrr = hyp.meas.rr
        # Ectopic beats must be advanced wrt the reference RR
        tnet.add_constraint(
            prev.time, qrs.time,
            Iv(C.TACHY_RR.start, max(C.TACHY_RR.start, refrr - stdrr)))
        # Beats cannot overlap
        tnet.add_constraint(prev.end, qrs.start, Iv(C.TQ_INTERVAL_MIN, np.Inf))
    BASIC_TCONST(pattern, qrs)
    tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
    tnet.set_before(qrs.time, hyp.end)
    # Constraints with the precedent T Wave
    _qrs_after_twave(pattern, qrs)
def _prev_rhythm_tconst(pattern, rhythm):
    """Temporal constraints of the flutter with the precedent rhythm"""
    BASIC_TCONST(pattern, rhythm)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.start, rhythm.end)
    tnet.add_constraint(pattern.hypothesis.start, pattern.hypothesis.end,
                        Iv(C.VFLUT_MIN_DUR, np.inf))
示例#13
0
 def _p_tconst(pattern, pwave):
     """P waves temporal constraints"""
     BASIC_TCONST(pattern, pwave)
     tnet = pattern.last_tnet
     tnet.add_constraint(pwave.start, pwave.end, C.PW_DURATION)
     # We find the associated QRS.
     beats = pattern.evidence[o.QRS]
     qidx = qrsidx + len(beats) if qrsidx < 0 else qrsidx
     qrs = beats[qidx]
     if qidx > 0:
         tnet.set_before(beats[qidx - 1].end, pwave.start)
     tnet.add_constraint(pwave.start, qrs.start, C.N_PR_INTERVAL)
     tnet.set_before(pwave.end, qrs.start)
     if len(pattern.evidence[o.PWave]) > 10:
         # The mean and standard deviation of the PQ measurements will
         # influence the following observations.
         if not _is_ectopic(qidx):
             pqmean, pqstd = pattern.hypothesis.meas.pq
         else:
             pqs = _get_measures(pattern, True)[2]
             pqmean, pqstd = np.mean(pqs), np.std(pqs)
         if not np.isnan(pqmean) and not np.isnan(pqstd):
             interv = Iv(int(pqmean - 2 * pqstd), int(pqmean + 2 * pqstd))
             if interv.overlap(C.N_PR_INTERVAL):
                 tnet.add_constraint(pwave.start, qrs.start, interv)
def QT_FROM_RR(rr):
    """
    Returns the interval of acceptable QT durations with the given RR
    intervals. It applies a linear regression model with the coefficients
    obtained from the referenced study.
    """
    return Iv(m2s(220) + 0.1 * rr.start, m2s(240) + 0.25 * rr.end)
 def _pair_gconst(pattern, _):
     """
     General constraints to be satisfied when a regular rhythm consists
     of only two beats.
     """
     if pattern.evidence[o.Cardiac_Rhythm]:
         _check_missed_beats(pattern)
         prhythm = pattern.evidence[o.Cardiac_Rhythm][0]
         rhythm = pattern.hypothesis
         # Previous rhythm cannot be a regular rhythm.
         verify(not isinstance(prhythm, o.RegularCardiacRhythm))
         mrr, stdrr = prhythm.meas.rr
         beats = pattern.evidence[o.QRS]
         rr = beats[-1].time.start - beats[0].time.start
         verify(rr in rr_bounds)
         # Avoid duplicate hypotheses with overlapping rhythms.
         if pattern.automata is SINUS_PATTERN:
             verify(C.TACHY_RR.end < rr < C.BRADY_RR.start)
         maxvar = max(C.TMARGIN, min(C.RR_MAX_DIFF, 2.5 * stdrr))
         verify(rr in Iv(mrr - maxvar, mrr + maxvar))
         # Besides being in rhythm, the two beats must share the morphology.
         verify(signal_match(beats[0].shape, beats[1].shape))
         # The amplitude difference is also constrained
         for lead in beats[0].shape:
             if lead in beats[1].shape:
                 samp, qamp = (beats[0].shape[lead].amplitude,
                               beats[1].shape[lead].amplitude)
                 verify(
                     min(samp, qamp) /
                     max(samp, qamp) >= C.MISSED_QRS_MAX_DIFF)
         rhythm.meas = o.CycleMeasurements(
             (rr, stdrr), (prhythm.meas.rt[0], C.QT_ERR_STD),
             (prhythm.meas.pq[0], C.QT_ERR_STD))
示例#16
0
def _qrsn_tconst(pattern, qrs):
    """
    Temporal constraints for the QRS complexes.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    hyp = pattern.hypothesis
    tnet = pattern.last_tnet
    obseq = pattern.obs_seq
    oidx = pattern.get_step(qrs)
    prev = beats[idx - 1]
    # In cyclic observations, we have to introduce more networks to simplify
    # the minimization operation.
    tnet.remove_constraint(hyp.end, prev.time)
    tnet = ConstraintNetwork()
    pattern.temporal_constraints.append(tnet)
    meanrr, stdrr = pattern.hypothesis.meas.rr
    rr_bounds = Iv(min(C.ASYSTOLE_RR.start, meanrr - stdrr + C.RR_MAX_DIFF),
                   C.ASYSTOLE_RR.start)
    tnet.add_constraint(prev.time, qrs.time, rr_bounds)
    tnet.add_constraint(prev.start, qrs.start, rr_bounds)
    tnet.add_constraint(prev.end, qrs.end, rr_bounds)
    tnet.set_before(prev.end, qrs.start)
    # If there is a prior T Wave, it must finish before the start
    # of the QRS complex.
    if isinstance(obseq[oidx - 1], o.TWave):
        prevt = obseq[oidx - 1]
        tnet.set_before(prevt.end, qrs.start)
    BASIC_TCONST(pattern, qrs)
    tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
    tnet.set_before(qrs.time, hyp.end)
    # We can introduce constraints on the morphology of the new QRS complex.
    if hyp.morph and not qrs.frozen:
        qrs.shape = hyp.morph
示例#17
0
def _prev_rhythm_tconst(pattern, rhythm):
    """Temporal constraints of a cardiac rhythm with the precedent one."""
    BASIC_TCONST(pattern, rhythm)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.start, rhythm.end)
    tnet.add_constraint(pattern.hypothesis.start, pattern.hypothesis.end,
                        Iv(2 * C.TACHY_RR.start, 3 * C.BRADY_RR.end))
示例#18
0
def _n1_tconst(pattern, qrs):
    """Temporal constraints of the normal beat determining the couplet end"""
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    tnet = pattern.last_tnet
    hyp = pattern.hypothesis
    _common_qrs_constraints(pattern, qrs)
    _qrs_after_twave(pattern, qrs)
    # Compensatory pause RR
    minrr = min(
        beats[idx - 1].time.start - beats[idx - 2].time.start,
        beats[idx - 2].time.start - beats[idx - 3].time.start,
    )
    maxrr = max(
        beats[idx - 1].time.end - beats[idx - 2].time.start,
        beats[idx - 2].time.end - beats[idx - 3].time.start,
    )
    mincompause = max(
        C.COMPAUSE_MIN_DUR,
        min(minrr * C.ICOUPLET_MIN_RREXT_F, minrr + C.ICOUPLET_MIN_RREXT))
    tnet.add_constraint(beats[idx - 1].time, qrs.time,
                        Iv(mincompause, maxrr * C.COMPAUSE_RREXT_MAX_F))
    tnet.set_equal(qrs.time, hyp.end)
    # The morphology of the first and last QRS complexes should be similar
    qrs.shape = beats[idx - 3].shape
    qrs.paced = beats[idx - 3].paced
示例#19
0
def _qrs_fin_npause_tconst(pattern, qrs):
    """
    Temporal constraints of the fourth beat in an extrasystole without
    compensatory pause.
    """
    BASIC_TCONST(pattern, qrs)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.end, qrs.time)
    beats = pattern.evidence[o.QRS]
    # We need all previous evidence
    if pattern.istate == 0:
        step = pattern.get_step(qrs)
        twave = pattern.trseq[step - 1][1]
        if isinstance(twave, o.TWave):
            tnet.set_before(twave.end, qrs.start)
        # Reference RR
        minrr = beats[1].time.start - beats[0].time.end if len(
            beats) == 4 else pattern.hypothesis.meas.rr[0]
        maxrr = beats[1].time.end - beats[0].time.start if len(
            beats) == 4 else pattern.hypothesis.meas.rr[0]
        tnet.add_constraint(beats[-3].time, qrs.time,
                            Iv(minrr - C.RR_MAX_DIFF, maxrr + C.RR_MAX_DIFF))
        # The last QRS should have the same morphology than the one before the
        # extrasystole.
        qrs.shape = beats[-3].shape
示例#20
0
def _reg_nae_tconst(pattern, qrs):
    """
    Temporal constraints for regular beats not coming after ectopic beats.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    assert not _is_ectopic(idx)
    hyp = pattern.hypothesis
    tnet = pattern.last_tnet
    prev = beats[idx - 1]
    if idx > 3:
        # We create a new temporal network for the new trigeminy cycle.
        tnet.remove_constraint(hyp.end, prev.time)
        tnet = ConstraintNetwork()
        pattern.temporal_constraints.append(tnet)
        rrev = beats[idx - 3].time.start - beats[idx - 4].time.start
    ##RR evolution constraint.
    else:
        rrev = pattern.evidence[o.Cardiac_Rhythm][0].meas.rr[0]
    tnet.add_constraint(prev.time, qrs.time,
                        Iv(rrev - C.RR_MAX_DIFF, rrev + C.RR_MAX_DIFF))
    BASIC_TCONST(pattern, qrs)
    tnet.add_constraint(qrs.start, qrs.end, C.NQRS_DUR)
    tnet.set_before(qrs.time, hyp.end)
    # Constraints with the precedent T Wave
    _qrs_after_twave(pattern, qrs)
    # Morphology should be similar to the previous QRS, since both are normal
    qrs.shape = prev.shape
    qrs.paced = prev.paced
示例#21
0
def _ect_qrs_tconst(pattern, qrs):
    """
    Temporal constraints for ectopic beats, which appear after every pair of
    regular beats.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    tnet = pattern.last_tnet
    hyp = pattern.hypothesis
    BASIC_TCONST(pattern, qrs)
    tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
    tnet.set_before(qrs.time, hyp.end)
    # Constraints with the precedent T Wave
    _qrs_after_twave(pattern, qrs)
    # This check is needed because there is an abduction point invoking this
    # function.
    if idx > 0:
        assert _is_ectopic(idx)
        prev = beats[idx - 1]
        # The interval between ectopic beats should also be stable.
        if idx > 6:
            refrr = beats[idx - 3].time.end - beats[idx - 4].time.start
            tnet.add_constraint(
                prev.time, qrs.time,
                Iv(refrr - C.RR_MAX_DIFF, refrr + C.RR_MAX_DIFF))
        # The reference RR varies from an upper limit to the last measurement,
        # through the contextual previous rhythm.
        refrr = C.BRADY_RR.end
        stdrr = 0.1 * refrr
        if pattern.evidence[o.Cardiac_Rhythm] and idx == 1:
            mrr, srr = pattern.evidence[o.Cardiac_Rhythm][0].meas.rr
            if mrr > 0:
                refrr, stdrr = mrr, srr
        elif idx > 1:
            refrr, stdrr = hyp.meas.rr
            # There must be an instantaneous shortening of the RR.
            prevrr = prev.time.end - beats[idx - 2].time.start
            tnet.add_constraint(
                prev.time, qrs.time,
                Iv(C.TACHY_RR.start, max(C.TACHY_RR.start,
                                         prevrr - C.TMARGIN)))
        # Ectopic beats must be advanced wrt the reference RR.
        tnet.add_constraint(
            prev.time, qrs.time,
            Iv(C.TACHY_RR.start, max(C.TACHY_RR.start, refrr - stdrr)))
        tnet.set_before(prev.end, qrs.start)
示例#22
0
def _prev_rhythm_tconst(pattern, rhythm):
    """Temporal constraints of the fibrillation with the precedent rhythm"""
    BASIC_TCONST(pattern, rhythm)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.start, rhythm.end)
    # An atrial fibrillation needs at least 7 QRS complexes.
    tnet.add_constraint(pattern.hypothesis.start, pattern.hypothesis.end,
                        Iv(7 * C.TACHY_RR.start, np.inf))
示例#23
0
def _v1_tconst(pattern, qrs):
    """Temporal constraints of the second extrasystole in the couplet"""
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    tnet = pattern.last_tnet
    prev = beats[idx - 1]
    # The second extrasystole must be shorter or approximately equal to the
    # first one, so we give the standard margin for the RR increasing.
    refrr = prev.time.end - beats[idx - 2].time.start
    const = Iv(C.TACHY_RR.start, refrr + C.ICOUPLET_MAX_DIFF)
    tnet.add_constraint(prev.time, qrs.time, const)
    tnet.add_constraint(prev.end, qrs.end, const)
    tnet.add_constraint(prev.end, qrs.start, Iv(C.TQ_INTERVAL_MIN, np.Inf))
    # The second extrasystole should include also the same RR shortening
    # constraints of the first one.
    _v0_tconst(pattern, qrs)
    _qrs_after_twave(pattern, qrs)
示例#24
0
def _prev_afib_tconst(pattern, afib):
    """
    Temporal constraints of the fibrillation wrt a previous atrial
    fibrillation that will helps us to reduce the necessary evidence
    """
    BASIC_TCONST(pattern, afib)
    pattern.last_tnet.add_constraint(afib.end, pattern.hypothesis.start,
                                     Iv(0, C.AFIB_MAX_DELAY))
示例#25
0
def _t_qrs_tconst(pattern, twave):
    """
    Temporal constraints of the T waves with the corresponding QRS complex
    """
    BASIC_TCONST(pattern, twave)
    obseq = pattern.obs_seq
    idx = pattern.get_step(twave)
    tnet = pattern.last_tnet
    # Beat end
    tnet.set_equal(twave.end, pattern.hypothesis.end)
    # We find the qrs observation precedent to this T wave.
    try:
        qrs = next(obseq[i] for i in range(idx - 1, -1, -1)
                   if isinstance(obseq[i], o.QRS))
        # If there is no P Wave, the beat start is the QRS start.
        if pattern.trseq[idx][0].istate in (1, 3):
            tnet.set_equal(qrs.start, pattern.hypothesis.start)
        if idx > 0 and isinstance(obseq[idx - 1], o.PWave):
            pwave = obseq[idx - 1]
            tnet.add_constraint(
                pwave.end, twave.start,
                Iv(C.ST_INTERVAL.start, C.PQ_INTERVAL.end + C.QRS_DUR.end))
        # ST interval
        tnet.add_constraint(qrs.end, twave.start, C.ST_INTERVAL)
        # QT duration
        tnet.add_constraint(qrs.start, twave.end, C.N_QT_INTERVAL)
        # If we observed a previous QRS complex, we set a limit for the QT
        # interval according to the RR and to the previous measures.
        if isinstance(obseq[1], o.QRS):
            rr = qrs.time.start - obseq[1].time.start
            rr = max(min(rr, RR_LIMITS.end), RR_LIMITS.start)
            rtc, rtstd = obseq[0].meas.rt
            if rtc > 0:
                # Expected QT value from the QT corrected value
                rtmean = msec2samples(1000.0 * sp2sg(rtc) * np.cbrt(sp2sg(rr)))
                tnet.add_constraint(
                    qrs.time, twave.end,
                    Iv(rtmean - 2.5 * rtstd, rtmean + 2.5 * rtstd))
            # The PR interval is also included to limit the T wave duration
            pr = obseq[0].meas.pq[0]
            try:
                tnet.add_constraint(qrs.time, twave.end, Iv(0, rr - pr))
            except ValueError:
                pass
    except StopIteration:
        pass
示例#26
0
def nobs_before(time):
    """
    Obtains the number of observations in the observation buffer before a
    given time.
    """
    dummy = EventObservable()
    dummy.time.value = Iv(time, time)
    return _OBS.bisect_right(dummy)
示例#27
0
def _delimit_t(signal, baseline, ls_lim, ee_lim, qrs_shape):
    """
    This function performs the delineation of a possible T Wave present
    in the fragment. To obtain the endpoint of the T Wave, it uses a method
    based on the work by Zhang: 'An algorithm for robust and efficient location
    of T-wave ends in electrocardiograms'. To get the beginning, it uses a
    probabilistic approach with some basic morphology constraints. All the
    processing is made to a simplification of the signal fragment with at most
    7 points.
    """
    try:
        # We exclude the areas in which the slope of the signal exceeds limit.
        maxtslope = qrs_shape.maxslope * C.TQRS_MAX_DIFFR
        lidx, uidx = 0, len(signal)
        if ls_lim > 0:
            idx = np.where(np.max(np.abs(np.diff(signal[: ls_lim + 1]))) > maxtslope)[0] + 1
            lidx = max(idx) if len(idx) > 0 else 0
        if ee_lim < len(signal) - 1:
            idx = np.where(np.max(np.abs(np.diff(signal[ee_lim:]))) > maxtslope)[0] + ee_lim
            uidx = min(idx) if len(idx) > 0 else len(signal) - 1
            if uidx > 1 and abs(signal[uidx] - baseline) > C.TWEND_BASELINE_MAX_DIFF:
                dfsign = np.sign(np.diff(signal[: uidx + 1]))
                signchange = ((np.roll(dfsign, 1) - dfsign) != 0).astype(int)
                if np.any(signchange):
                    uidx = np.where(signchange)[0][-1]
        verify(uidx >= lidx)
        signal = signal[lidx : uidx + 1]
        ls_lim -= lidx
        ee_lim -= lidx
        # Any T waveform should be representable with at most 7 points.
        points = DP.arrayRDP(signal, max(ph2dg(0.02), qrs_shape.amplitude / 20.0), 7)
        n = len(points)
        verify(n >= 3)
        # 1. Endpoint estimation
        epts = points[points >= ee_lim]
        verify(len(epts) > 0)
        Tend, dum = _zhang_tendpoint(signal, epts)
        # 2. Onset point estimation.
        bpts = points[np.logical_and(points < Tend, points <= ls_lim)]
        score = {}
        # Range to normalize differences in the signal values
        rang = max(baseline, signal.max()) - min(signal.min(), baseline)
        # There must be between one and 3 peaks in the T Wave.
        for i in range(len(bpts)):
            sigpt = signal[points[i : np.where(points == Tend)[0][0] + 1]]
            npks = len(get_peaks(sigpt)) if len(sigpt) >= 3 else 0
            if npks < 1 or npks > 2 or np.ptp(sigpt) <= ph2dg(0.05):
                continue
            bl_dist = 1.0 - np.abs(signal[bpts[i]] - baseline) / rang
            tdur = sp2ms(Tend - bpts[i])
            score[bpts[i]] = bl_dist * _check_histogram(_TDUR_HIST, tdur)
        verify(score)
        Tbeg = max(score, key=score.get)
        verify(score[Tbeg] > 0)
        verify(np.max(np.abs(np.diff(signal[Tbeg : Tend + 1]))) <= maxtslope)
        return (Iv(Tbeg + lidx, Tend + lidx), dum)
    except InconsistencyError:
        return None
示例#28
0
def _qrs_env_tconst(pattern, qrs):
    """Temporal constraints of the second environment QRS complex"""
    BASIC_TCONST(pattern, qrs)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.start, qrs.time)
    if pattern.evidence[o.QRS].index(qrs) == 1:
        prev = pattern.evidence[o.QRS][0]
        tnet.add_constraint(prev.time, qrs.time,
                            Iv(C.TACHY_RR.start, C.BRADY_RR.end))
示例#29
0
def _qrs0_tconst(pattern, qrs):
    """
    Temporal constraints of the QRS complex that must be at the beginning of
    the flutter.
    """
    BASIC_TCONST(pattern, qrs)
    tnet = pattern.last_tnet
    tnet.set_equal(pattern.hypothesis.start, qrs.time)
    tnet.add_constraint(pattern.hypothesis.start, pattern.hypothesis.end,
                        Iv(5 * C.TACHY_RR.start, np.inf))
示例#30
0
def _t_defl_tconst(pattern, defl):
    """
    Temporal constraints wrt the abstracted energy interval.
    """
    BASIC_TCONST(pattern, defl)
    qrs = pattern.evidence[o.QRS][0] if pattern.evidence[o.QRS] else None
    twave = pattern.hypothesis
    tc = pattern.last_tnet
    tc.add_constraint(defl.start, defl.end, Iv(0, C.TW_DURATION.end))
    tc.add_constraint(twave.start, defl.start, Iv(-C.TW_DEF_OVER_MAX, C.TW_DEF_OVER_MIN))
    tc.add_constraint(twave.end, defl.end, Iv(-C.TW_DEF_OVER_MIN, C.TW_DEF_ENDIFF))
    tc.set_before(defl.start, twave.end)
    tc.set_before(twave.start, defl.end)
    if qrs is not None:
        qrsdur = qrs.earlyend - qrs.latestart
        if qrsdur - C.TMARGIN <= C.TW_DURATION.end:
            tc.add_constraint(twave.start, twave.end, Iv(qrsdur - C.TMARGIN, np.inf))
        tc.add_constraint(qrs.start, defl.end, Iv(0, C.QT_INTERVAL.end))
        tc.set_before(qrs.end, defl.start)