Esempio n. 1
0
def correct_bxc_bxd():
    UNITS.set_sampling_freq(360.0)
    ANNOTS_DIR = ('/home/remoto/tomas.teijeiro/Escritorio/anots_dani/')
    RECORDS = [
        100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114,
        115, 116, 117, 118, 119, 121, 122, 123, 124, 200, 201, 202, 203, 205,
        207, 208, 209, 210, 212, 213, 214, 215, 217, 219, 220, 221, 222, 223,
        228, 230, 231, 232, 233, 234
    ]
    for rec in RECORDS:
        REF = ANNOTS_DIR + str(rec) + '.atr'
        TEST = ANNOTS_DIR + str(rec) + '.bxd'
        OUT = ANNOTS_DIR + str(rec) + '.bxD'
        ref = SortedList(MIT.read_annotations(REF))
        test = MIT.read_annotations(TEST)
        for tann in test:
            dummy = MIT.MITAnnotation()
            dummy.time = int(tann.time - UNITS.msec2samples(150))
            idx = ref.bisect_left(dummy)
            try:
                rann = next(
                    a for a in ref[idx:] if MIT.is_qrs_annotation(a)
                    and abs(a.time - tann.time) <= UNITS.msec2samples(150))
                tann.code = rann.code
            except StopIteration:
                pass
        MIT.save_annotations(test, OUT)
        print('Record {0} processed'.format(rec))
    print('The full database was successfully processed')
Esempio n. 2
0
def correct_bxb():
    ANNOTS_DIR = ('/home/remoto/tomas.teijeiro/Escritorio/anots_dani/')
    RECORDS = [
        100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114,
        115, 116, 117, 118, 119, 121, 122, 123, 124, 200, 201, 202, 203, 205,
        207, 208, 209, 210, 212, 213, 214, 215, 217, 219, 220, 221, 222, 223,
        228, 230, 231, 232, 233, 234
    ]
    for rec in RECORDS:
        IN_FILE = ANNOTS_DIR + str(rec) + '.bxb'
        OUT_FILE = ANNOTS_DIR + str(rec) + '.bxd'
        anots = MIT.read_annotations(IN_FILE)
        out = []
        #Corrections according to the -o flag of the bxb utility.
        for ann in anots:
            if MIT.is_qrs_annotation(ann):
                out.append(ann)
            #Missed beats
            elif ann.code == CODES.NOTE and ann.aux[0] not in ('O', 'X'):
                new = MIT.MITAnnotation()
                new.code = CODES.CHARMAP[ann.aux[0]]
                new.time = ann.time
                out.append(new)
        MIT.save_annotations(out, OUT_FILE)
        print('Record {0} processed'.format(rec))
    print('The full database was successfully processed')
Esempio n. 3
0
def _merge_annots(annlst, interp, reftime):
    """
    Merges an annotations list and an interpretation by selecting on the
    overlap interval the sequence with highest coverage.
    """
    beg = next((ob.earlystart + reftime
                for ob in interp.get_observations(o.Cardiac_Rhythm)),
               np.inf) - ms2sp(150)
    #Ventricular flutter episodes change the reference point.
    vflut = next((a for a in reversed(annlst)
                  if a.code is ECGCodes.VFOFF and a.time >= beg), None)
    if vflut is not None:
        beg = vflut.time + 1
    bidx = next((i for i in xrange(len(annlst)) if annlst[i].time >= beg),
                len(annlst))
    end = next((a.time for a in reversed(annlst)
                if a.code is ECGCodes.RHYTHM and a.aux == ')'),
               annlst[-1].time)
    #First we calculate the possible 'join points' of the two sequences.
    jpts = (
        set(a.time for a in annlst[bidx:]
            if a.time <= end and a.code is ECGCodes.RHYTHM)
        & set(reftime + r.earlystart for r in interp.get_observations(
            o.Cardiac_Rhythm,
            filt=lambda rh: beg - reftime <= rh.earlystart <= end - reftime)))
    #If there are no join points, we give priority to the interpretation.
    if not jpts:
        jpt = beg
    else:
        #We select the join point with highest coverage.
        score = {}
        for jpt in jpts:
            score[jpt] = (len([
                a for a in annlst[bidx:] if a.time <= jpt and
                (a.code in (ECGCodes.TWAVE, ECGCodes.PWAVE)
                 or MITAnnotation.is_qrs_annotation(a))
            ]) + len(
                list(
                    interp.get_observations((o.QRS, o.PWave, o.TWave),
                                            jpt - reftime, end - reftime))))
        jpt = max(jpts, key=lambda pt: score[pt])
    #We remove the discarded annotations (those after the selected join point),
    #ensuring the WFON/WFOFF pairs are consistent.
    offsets = 0
    while annlst and annlst[-1].time >= jpt:
        if annlst[-1].code is ECGCodes.WFOFF:
            offsets += 1
        elif annlst[-1].code is ECGCodes.WFON:
            offsets -= 1
        annlst.pop()
    while offsets > 0:
        ann = annlst.pop()
        if ann.code is ECGCodes.WFON:
            offsets -= 1
    return jpt - reftime
Esempio n. 4
0
def _clean_artifacts_redundancy(annots):
    """
    Removes those artifact annotations that are close to a QRS annotation,  as
    well as redundant rhythm annotations.
    """
    DISTANCE = ms2sp(150)
    banns = [
        a for a in annots
        if MITAnnotation.is_qrs_annotation(a) or a.code == ECGCodes.ARFCT
    ]
    i = 0
    while i < len(banns):
        if (banns[i].code == ECGCodes.ARFCT and
            ((i > 0 and banns[i].time - banns[i - 1].time < DISTANCE) or
             (i < len(banns) - 1
              and banns[i + 1].time - banns[i].time < DISTANCE))):
            #We cannot use 'remove' due to a bug in SortedList.
            j = annots.bisect_left(banns[i])
            while annots[j] is not banns[i]:
                j += 1
            annots.pop(j)
            banns.pop(i)
        else:
            i += 1
    #Redundant rhythms
    i = 1
    while i < len(annots):
        if annots[i].code is ECGCodes.RHYTHM:
            prev = next(
                (a for a in reversed(annots[:i]) if a.code is ECGCodes.RHYTHM),
                None)
            if prev is not None and prev.aux == annots[i].aux:
                annots.pop(i)
            else:
                i += 1
        else:
            i += 1
    return annots
Esempio n. 5
0
def _standardize_rhythm_annots(annots):
    """
    Standardizes a set of annotations obtained from the interpretation
    procedure to make them compatible with the criteria applied in the
    MIT-BIH Arrhythmia database in the labeling of rhythms.
    """
    dest = sortedcontainers.SortedList()
    for ann in annots:
        code = ann.code
        if code in (ECGCodes.RHYTHM, ECGCodes.VFON):
            #TODO remove this if not necessary
            if code is ECGCodes.VFON:
                newann = MITAnnotation.MITAnnotation()
                newann.code = ECGCodes.RHYTHM
                newann.aux = b'(VFL'
                newann.time = ann.time
                dest.add(newann)
            ############################################################
            #For convention with original annotations, we only admit   #
            #bigeminies with more than two pairs, and trigeminies with #
            #more than two triplets,                                   #
            ############################################################
            if ann.aux == b'(B':
                end = next((a for a in annots if a.time > ann.time
                           and a.code in (ECGCodes.RHYTHM, ECGCodes.VFON)),
                                                                annots[-1])
                nbeats = searching.ilen(a for a in annots if a.time >= ann.time
                                        and a.time <= end.time and
                                        MITAnnotation.is_qrs_annotation(a))
                if nbeats < 7:
                    continue
            if ann.aux == '(T':
                end = next((a for a in annots if a.time > ann.time
                           and a.code in (ECGCodes.RHYTHM, ECGCodes.VFON)),
                                                                annots[-1])
                nbeats = searching.ilen(a for a in annots if a.time >= ann.time
                                        and a.time <= end.time and
                                        MITAnnotation.is_qrs_annotation(a))
                if nbeats < 7:
                    continue
            #############################################################
            # Pauses and missed beats are replaced by bradycardias (for #
            # consistency with the reference annotations).              #
            #############################################################
            if ann.aux in (b'(BK', b'P'):
                ann.aux = b'(SBR'
            if ann.aux not in (b'(EXT', b'(CPT'):
                prev = next((a for a in reversed(dest)
                                       if a.code is ECGCodes.RHYTHM), None)
                if prev is None or prev.aux != ann.aux:
                    dest.add(ann)
        else:
            dest.add(ann)
    #################################
    #Atrial fibrillation correction #
    #################################
    iterator = iter(dest)
    afibtime = 0
    while True:
        try:
            start = next(a.time for a in iterator
                         if a.code == ECGCodes.RHYTHM and a.aux == b'(AFIB')
            end = next((a.time for a in iterator
                              if a.code == ECGCodes.RHYTHM), dest[-1].time)
            afibtime += end-start
        except StopIteration:
            break
    #If more than 1/20 of the time of atrial fibrillation...
    if annots and afibtime > (annots[-1].time-annots[0].time)/20.0:
        iterator = iter(dest)
        rhythms = ('(N', '(SVTA')
        start = next((a for a in iterator if a.code == ECGCodes.RHYTHM
                                               and a.aux in rhythms), None)
        while start is not None:
            end = next((a for a in iterator if a.code == ECGCodes.RHYTHM),
                                                                  dest[-1])
            #All normal rhythms that satisfy the Lian method to identify
            #afib by rhythm are now considered afib. We also check the
            #method considering alternate RRs to avoid false positives with
            #bigeminies.
            fragment = dest[dest.bisect_left(start):dest.bisect_right(end)]
            rrs = np.diff([a.time for a in fragment
                                        if MITAnnotation.is_qrs_annotation(a)])
            if (is_afib_rhythm_lian(rrs) and
                            is_afib_rhythm_lian(rrs[0::2]) and
                                           is_afib_rhythm_lian(rrs[1::2])):
                start.aux = b'(AFIB'
            #Next rhythm
            start = (end if end.aux in rhythms else
                     next((a for a in iterator
                                        if a.code == ECGCodes.RHYTHM
                                              and a.aux in rhythms), None))
    ##############################
    #Paced rhythm identification #
    ##############################
    #To consider the presence of paced rhythms in a record, we require at
    #least a mean of one paced beat each 10 seconds.
    pacedrec = sum(1 for a in dest if a.code == ECGCodes.PACE) > 180
    if pacedrec:
        iterator = iter(dest)
        rhythms = (b'(AFIB', b'(N', b'(SBR', b'(SVTA')
        start = next((a for a in iterator if a.code == ECGCodes.RHYTHM
                                               and a.aux in rhythms), None)
        while start is not None:
            end = next((a for a in iterator if a.code == ECGCodes.RHYTHM),
                                                                  dest[-1])
            #If there are paced beats in a rhythm fragment, the full
            #rhythm is identified as paced.
            if any([start.time < a.time < end.time
                    and a.code == ECGCodes.PACE
                        for a in dest[dest.index(start):dest.index(end)]]):
                start.aux = b'(P'
            #Next rhythm
            start = (end if end.aux in rhythms else
                     next((a for a in iterator
                                        if a.code == ECGCodes.RHYTHM
                                              and a.aux in rhythms), None))
    #########################################
    # Redundant rhythm description removing #
    #########################################
    i = 1
    while i < len(dest):
        if dest[i].code is ECGCodes.RHYTHM:
            prev = next((a for a in reversed(dest[:i])
                                       if a.code is ECGCodes.RHYTHM), None)
            if prev is not None and prev.aux == dest[i].aux:
                dest.pop(i)
            else:
                i += 1
        else:
            i += 1
    return dest
Esempio n. 6
0
     100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114,
     115, 116, 117, 118, 119, 121, 122, 123, 124, 200, 201, 202, 203, 205,
     207, 208, 209, 210, 212, 213, 214, 215, 217, 219, 220, 221, 222, 223,
     228, 230, 231, 232, 233, 234
 ]
 set_sampling_freq(360.0)
 #Dictionary to save the discrepancy at record-level
 dist = {}
 miss = 0
 for rec in RECORDS:
     dist[rec] = []
     REF_FILE = ANNOTS_DIR + str(rec) + '.atr'
     TEST_FILE = ANNOTS_DIR + str(rec) + '.wbr'
     reference = np.array([
         anot.time for anot in MIT.read_annotations(REF_FILE)
         if MIT.is_qrs_annotation(anot)
     ])
     test = np.array([
         anot.time for anot in MIT.read_annotations(TEST_FILE)
         if MIT.is_qrs_annotation(anot)
     ])
     #Missing beat search
     for b in reference:
         err = np.Inf
         for t in test:
             bdist = t - b
             if abs(bdist) > abs(err):
                 break
             else:
                 err = bdist
         if abs(err) > ms2sp(150.0):