Esempio n. 1
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)))
Esempio n. 2
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)))
def eval_vflut(anns, _):
    """Evaluates the ventricular flutter presence"""
    lth, uth, dth = ms2sp(
        (4 * 60 + 45) * 1000), ms2sp(5 * 60 * 1000), ms2sp(3500)
    #We remove separations between consecutive flutter fragments
    i = 0
    while i < len(anns):
        if anns[i].code is ECGCodes.VFOFF:
            onset = next((j for j in range(i, len(anns))
                          if anns[j].code is ECGCodes.VFON), None)
            if onset is not None and anns[i].time == anns[onset].time:
                anns.pop(onset)
                anns.pop(i)
                i -= 1
        i += 1
    vflim = (a for a in anns if a.code in (ECGCodes.VFON, ECGCodes.VFOFF))
    vfluts = []
    while True:
        try:
            beg = next(vflim)
            end = next(vflim)
            vfluts.append(Iv(beg.time, end.time))
        except StopIteration:
            break
    #If the record shows many flutter fragments, we simply check some flutter
    #waves in the last 15 seconds.
    if sum(fl.length for fl in vfluts) > ms2sp(20000):
        vfw = [
            a.time for a in anns
            if a.code is ECGCodes.FLWAV and lth <= a.time <= uth
        ]
        return len(vfw) > 5
    interv = Iv(lth, uth)
    return any([interv.intersection(vflut).length > dth for vflut in vfluts])
Esempio n. 4
0
def _reg_ae_tconst(pattern, qrs):
    """
    Temporal constraints for regular beats coming after ectopic beats.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    assert _is_ectopic(idx - 1)
    tnet = pattern.tnet
    hyp = pattern.hypothesis
    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)
    #The first regular beat takes the reference RR from the previous rhythm
    #and the subsequent take the reference from the proper trigeminy.
    if idx == 2:
        refrr = pattern.evidence[o.Cardiac_Rhythm][0].meas[0][0]
    else:
        refrr = beats[idx - 2].time.end - beats[idx - 3].time.start
    const = Iv(min(2 * refrr - C.RR_MAX_DIFF, refrr * C.COMPAUSE_MIN_F),
               max(2 * refrr + C.RR_MAX_DIFF, refrr * C.COMPAUSE_MAX_F))
    tnet.add_constraint(beats[idx - 2].time, qrs.time, const)
    tnet.add_constraint(beats[idx - 2].start, qrs.start, const)
    tnet.add_constraint(beats[idx - 2].end, qrs.end, const)
    #Compensatory pause RR constraints
    minrr = beats[idx - 1].time.start - beats[idx - 2].time.start
    maxrr = beats[idx - 1].time.end - beats[idx - 2].time.start
    mincompause = max(
        C.COMPAUSE_MIN_DUR,
        min(minrr * C.COMPAUSE_RREXT_MIN_F, minrr + C.COMPAUSE_RREXT_MIN))
    tnet.add_constraint(beats[idx - 1].time, qrs.time,
                        Iv(mincompause, maxrr * C.COMPAUSE_RREXT_MAX_F))
    #The morphology should be similar to the previous non-ectopic QRS
    qrs.shape = beats[idx - 2].shape
    qrs.paced = beats[idx - 2].paced
Esempio n. 5
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
Esempio n. 6
0
 def _t_tconst(pattern, twave):
     """
     Temporal constraints of the T wave.
     """
     beats = pattern.evidence[o.QRS]
     tnet = pattern.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)
Esempio n. 7
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 xrange(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
Esempio n. 8
0
 def _qrs_tconst(pattern, qrs):
     """
     Temporal constraints to observe a new QRS complex.
     """
     beats = pattern.evidence[o.QRS]
     idx = beats.index(qrs)
     hyp = pattern.hypothesis
     tnet = pattern.tnet
     obseq = pattern.obs_seq
     oidx = pattern.get_step(qrs)
     #The environment complex sets the start of the rhythm observation.
     if pattern.get_evidence_type(qrs)[1] is ENVIRONMENT:
         tnet.set_equal(hyp.start, qrs.time)
     else:
         if idx > 0:
             prev = beats[idx - 1]
             tnet.add_constraint(prev.time, qrs.time, rr_bounds)
             if rr_bounds is not C.TACHY_RR:
                 #Also bounding on begin and end, but with relaxed variation
                 #margin.
                 rlx_rrb = Iv(rr_bounds.start - C.TMARGIN,
                              rr_bounds.end + C.TMARGIN)
                 tnet.add_constraint(prev.start, qrs.start, rlx_rrb)
                 tnet.add_constraint(prev.end, qrs.end, rlx_rrb)
             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)
             ##RR evolution constraint. We combine the statistical limits
             #with a dynamic evolution.
             if idx > 1:
                 prev2 = beats[idx - 2]
                 rrev = prev.time.start - prev2.time.start
                 if hyp.meas.rr[0] > 0:
                     meanrr, stdrr = hyp.meas.rr
                     const = Iv(
                         min(0.8 * rrev, rrev - C.RR_MAX_DIFF,
                             meanrr - 2 * stdrr),
                         max(1.2 * rrev, rrev + C.RR_MAX_DIFF,
                             meanrr + 2 * stdrr))
                 else:
                     const = Iv(min(0.8 * rrev, rrev - C.RR_MAX_DIFF),
                                max(1.2 * rrev, rrev + C.RR_MAX_DIFF))
                 tnet.add_constraint(prev.time, qrs.time, const)
             #TODO improve
             if not qrs.frozen and hyp.morph:
                 nullsh = o.QRSShape()
                 refbeat = next((b for b in reversed(beats[:idx])
                                 if not b.clustered and all(
                                     b.shape.get(lead, nullsh).tag ==
                                     hyp.morph[lead].tag
                                     for lead in hyp.morph)), None)
                 if refbeat is not None:
                     qrs.shape = refbeat.shape
                     qrs.paced = refbeat.paced
     tnet.add_constraint(qrs.start, qrs.end, C.QRS_DUR)
     tnet.set_before(qrs.time, hyp.end)
Esempio n. 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)
Esempio n. 10
0
 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)
Esempio n. 11
0
 def _def_gconst(pattern, _):
     """General constraints for the energy interval abstraction pattern"""
     verify(pattern.hypothesis.lateend < np.inf)
     #The margin to group consecutive fragments is 1 mm
     #Limits for the detection.
     beg = int(pattern.hypothesis.earlystart)
     end = int(pattern.hypothesis.lateend)
     #Now we get the energy accumulated in all leads.
     energy = None
     for lead in sig_buf.get_available_leads():
         lenerg, fbeg, fend = sig_buf.get_energy_fragment(
             beg, end, TWINDOW, lead)
         energy = lenerg if energy is None else energy + lenerg
     if energy is None:
         return 0.0
     #We get the already published fragments affecting our temporal support.
     conflictive = []
     published = SortedList(obs_buf.get_observations(o.Deflection))
     idx = published.bisect_left(pattern.hypothesis)
     if idx > 0 and published[idx - 1].lateend > beg:
         idx -= 1
     while (idx < len(published) and Iv(beg, end).overlap(
             Iv(published[idx].earlystart, published[idx].lateend))):
         conflictive.append(
             Iv(published[idx].earlystart - beg + fbeg,
                published[idx].lateend - beg + fbeg))
         idx += 1
     #We obtain the relative limits of the energy interval wrt the fragment
     iv_start = Iv(fbeg, fbeg + int(pattern.hypothesis.latestart - beg))
     iv_end = Iv(fend - int(end - pattern.hypothesis.earlyend), fend)
     #We look for the highest-level interval satisfying the limits.
     interval = None
     lev = 0
     while interval is None and lev <= 20:
         areas = [
             iv for iv in get_energy_intervals(energy, lev, group=TMARGIN)
             if iv.start in iv_start and iv.end in iv_end and all(
                 not iv.overlapm(ein) for ein in conflictive)
         ]
         #We sort the areas by energy, with the highest energy first.
         areas.sort(
             key=lambda interv: np.sum(energy[interv.start:interv.end + 1]),
             reverse=True)
         #Now we take the element indicated by the index.
         if len(areas) > int_idx:
             interval = areas[int_idx]
         else:
             lev += 1
     verify(interval is not None)
     pattern.hypothesis.start.set(interval.start + beg - fbeg,
                                  interval.start + beg - fbeg)
     pattern.hypothesis.end.set(interval.end + beg - fbeg,
                                interval.end + beg - fbeg)
     for lead in sig_buf.get_available_leads():
         pattern.hypothesis.level[lead] = lev
Esempio n. 12
0
def _reg_qrs_tconst(pattern, qrs):
    """
    Temporal constraints for regular beats, which appear after every ectopic
    beat.
    """
    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.NQRS_DUR)
    tnet.set_before(qrs.time, hyp.end)
    #Constraints with the precedent T Wave
    _qrs_after_twave(pattern, qrs)
    #The environment QRS complex determines the beginning of the bigeminy.
    if pattern.get_evidence_type(qrs)[1] is ENV:
        tnet.set_equal(hyp.start, qrs.time)
    else:
        #The first regular beat takes the reference RR from the previous rhythm
        #and the subsequent take the reference from the proper bigeminy.
        if idx == 2:
            refrr, stdrr = pattern.evidence[o.Cardiac_Rhythm][0].meas[0]
            max_var = max(2 * C.RR_MAX_DIFF, 4 * stdrr)
            tnet.add_constraint(
                beats[0].time, qrs.time,
                Iv(min(2 * refrr - max_var, refrr * C.COMPAUSE_MIN_F),
                   max(2 * refrr + max_var, refrr * C.COMPAUSE_MAX_F)))
        else:
            ref2rr = beats[idx - 2].time.end - beats[idx - 4].time.start
            mrr, srr = hyp.meas.rr
            const = Iv(min(ref2rr - 2 * C.RR_MAX_DIFF, 2 * mrr - 4 * srr),
                       max(ref2rr + 2 * C.RR_MAX_DIFF, 2 * mrr + 4 * srr))
            tnet.add_constraint(beats[idx - 2].time, qrs.time, const)
            tnet.add_constraint(beats[idx - 2].start, qrs.start, const)
            tnet.add_constraint(beats[idx - 2].end, qrs.end, const)
        #We guide the morphology search to be similar to the previous regular
        #QRS complex.
        qrs.shape = beats[idx - 2].shape
        qrs.paced = beats[idx - 2].paced
        #Compensatory pause RR
        minrr = beats[idx - 1].time.start - beats[idx - 2].time.end
        maxrr = beats[idx - 1].time.end - beats[idx - 2].time.start
        refcompause = (beats[idx - 2].time.start -
                       beats[idx - 3].time.start if idx > 2 else maxrr *
                       C.COMPAUSE_RREXT_MAX_F)
        mincompause = max(
            C.COMPAUSE_MIN_DUR, maxrr,
            min(minrr * C.COMPAUSE_RREXT_MIN_F, refcompause - C.TMARGIN,
                minrr + C.COMPAUSE_RREXT_MIN))
        tnet.add_constraint(beats[idx - 1].time, qrs.time,
                            Iv(mincompause, maxrr * C.COMPAUSE_RREXT_MAX_F))
        #Beats cannot overlap
        tnet.add_constraint(beats[idx - 1].end, qrs.start,
                            Iv(C.TQ_INTERVAL_MIN, np.Inf))
Esempio n. 13
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.values()):
        for i in range(1, len(dicts)):
            conflictive = []
            for lst in dicts[i].values():
                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.keys())
                                      for w in conflictive)) - set(
                                          wint.level.keys())
                for lead in alleads:
                    wint.level[lead] = min(
                        w.level.get(lead, np.Inf) for w in conflictive)
                for wconf in conflictive:
                    dicts[i][next(wconf.level.values())].remove(wconf)
Esempio n. 14
0
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)
Esempio n. 15
0
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))
Esempio n. 16
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))
Esempio n. 17
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
Esempio n. 18
0
def _delimit_p(signal, lead, es_lim, ls_lim, ee_lim):
    """
    Performs the delimitation of a P wave in a signal fragment. If a waveform
    compatible with a P wave cannot be found, returns None, else return an
    Interval within signal length.
    """
    #shape simplification (ignoring the environment signal)
    delta = ph2dg(0.02)
    points = DP.arrayRDP(signal[int(es_lim):], delta, 6) + int(es_lim)
    #If no relevant disturbances are detected, there is no a P wave
    if len(points) == 2:
        return None
    #Now we look for the shorter limits that satisfy the P-Wave classifier.
    cand = None
    i = next(k for k in range(len(points)-1, -1, -1) if points[k] <= ls_lim)
    while i >= 0:
        j = next(k for k in range(i+1, len(points)) if points[k] >= ee_lim)
        while j < len(points):
            sigfr = signal[points[i]:points[j]+1]
            #We consider a good P wave environment if the signal has no
            #amplitude variations
            beg = int(max(0, points[i]-C.PWAVE_ENV))
            plainenv = not np.any(signal[beg:points[i]+1]-signal[beg])
            #The minimum threshold varies with the environment quality
            ampthres = C.PWAVE_MIN_AMP if not plainenv else delta
            if (seems_pwave(sigfr, lead) and np.ptp(sigfr) >= ampthres):
                cand = (points[i], points[j])
                break
            j += 1
        if cand is not None:
            break
        i -= 1
    return None if cand is None else Iv(int(cand[0]-es_lim),
                                        int(cand[1]-es_lim))
Esempio n. 19
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)
Esempio n. 20
0
def _qrs_tconst(pattern, qrs):
    """
    Temporal constraints for the QRS complexes.
    """
    beats = pattern.evidence[o.QRS]
    idx = beats.index(qrs)
    hyp = pattern.hypothesis
    tnet = pattern.tnet
    obseq = pattern.obs_seq
    oidx = pattern.get_step(qrs)
    if idx > 0:
        prev = beats[idx-1]
        rr_bounds = Iv(C.TACHY_RR.start, C.BRADY_RR.end)
        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)
    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
Esempio n. 21
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
Esempio n. 22
0
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)
Esempio n. 23
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
    """
    pattern.tnet.add_constraint(afib.end, pattern.hypothesis.start,
                                                       Iv(0, C.AFIB_MAX_DELAY))
Esempio n. 24
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 qidx % 2 == 0:
             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)
Esempio n. 25
0
 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))
Esempio n. 26
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
Esempio n. 27
0
def _qrs_env_tconst(pattern, qrs):
    """Temporal constraints of the second environment QRS complex"""
    pattern.tnet.set_equal(pattern.hypothesis.start, qrs.time)
    if pattern.evidence[o.QRS].index(qrs) == 1:
        prev = pattern.evidence[o.QRS][0]
        pattern.tnet.add_constraint(prev.time, qrs.time,
                                    Iv(C.TACHY_RR.start, C.BRADY_RR.end))
Esempio n. 28
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)
Esempio n. 29
0
def _qrs0_tconst(pattern, qrs):
    """
    Temporal constraints of the QRS complex that must be at the beginning of
    the flutter.
    """
    pattern.tnet.set_equal(pattern.hypothesis.start, qrs.time)
    pattern.tnet.add_constraint(pattern.hypothesis.start, pattern.hypothesis.end,
                                                   Iv(C.VFLUT_MIN_DUR, np.inf))
Esempio n. 30
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)