def findUniformDecelCandidates(
        sig,
        mask,
        sigSmoothed,
        baseline,
        ts,
        threshBase=-1,
        minChange=5,
        threshDeriv=1.0,  # 0.5
        threshDeriv2=0.125,  # 0.25
):
    deriv = np.diff(sigSmoothed)
    delta = sigSmoothed - baseline

    allMin, allMax = getCrossings(deriv)
    allPoints = [[i, 'max'] for i in allMax if delta[i] > threshBase]
    allPoints += [[i, 'min'] for i in allMin if delta[i] <= threshBase]

    _, allEnd = getCrossings(deriv - threshDeriv)
    allPoints += [[i, 'end'] for i in allEnd if delta[i] >= -minChange]

    _, allStart = getCrossings(deriv + threshDeriv)
    allPoints += [[i, 'start'] for i in allStart if delta[i] >= -minChange]

    if threshDeriv2:
        _, allEnd = getCrossings(deriv - threshDeriv2)
        allPoints += [[i, 'end'] for i in allEnd if delta[i] >= -minChange]

        _, allStart = getCrossings(deriv + threshDeriv2)
        allPoints += [[i, 'start'] for i in allStart if delta[i] >= -minChange]

    allPoints = sorted(allPoints)

    allPoints = pruneMinima(allPoints, sigSmoothed)
    allPoints = pruneEndpoints(allPoints, sigSmoothed)

    allDecels = identifyUniformDecel(allPoints, sigSmoothed, baseline, ts,
                                     mask)

    return allPoints, allDecels
Esempio n. 2
0
def findUC(sigUC, ts, minSpacing=10.0 / 8, verbose=False):
    idxMinima, idxMaxima = getCrossings(np.diff(sigUC))

    peaks = sorted([[sigUC[i], i] for i in idxMaxima], reverse=True)
    peaks = [i for _, i in peaks]

    selected = []
    for i, idxThis in enumerate(peaks):
        adjacent = [
            True for idxOther in peaks[:i]
            if abs(ts[idxThis] - ts[idxOther]) < minSpacing
        ]
        if not adjacent:
            selected.append(idxThis)

    selected = sorted(selected)
    if verbose:
        for i in selected:
            print '{:0.1f}: {:0.1f}'.format(ts[i], sigUC[i])

    return selected, [ts[i] for i in selected]
def findVarDecelCandidates(sig,
                           mask,
                           sigSmoothed,
                           baseline,
                           ts,
                           nadirWidth=5,
                           includeEnd=True):
    """Find potential variable decelerations  """
    delta = sig - baseline
    deltaSmoothed5 = sigSmoothed - baseline + 5
    allRelease, allOnset = getCrossings(deltaSmoothed5)

    allEndpoints = [[i, 'onset'] for i in allOnset] + [[i, 'release']
                                                       for i in allRelease]
    allEndpoints = sorted(allEndpoints)

    if includeEnd and allEndpoints and allEndpoints[-1] != 'release':
        # create release at end of recording
        allEndpoints.append([len(sig) - 1, 'release'])

    results = []
    for i, entry in enumerate(allEndpoints):
        if entry[1] != 'release':
            continue
        if i == 0 or allEndpoints[i - 1][1] != 'onset':
            continue

        idxStart = allEndpoints[i - 1][0]
        idxEnd = entry[0]

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        if duration < 10:
            continue

        # refine onset/release estimates using local slops
        slopeL = (sigSmoothed[idxStart] - sigSmoothed[idxStart + 5]) / 5.0
        slopeR = (sigSmoothed[idxEnd] - sigSmoothed[idxEnd - 5]) / 5.0
        if slopeL > 0:
            idxStart = max(idxStart - int(5.0 / slopeL), 0)
        if slopeR > 0:
            idxEnd = min(idxEnd + int(5.0 / slopeR), len(sig) - 1)

        startingBaseline = baseline[idxStart]

        # estimate magnitude based on triangle
        auc = np.sum(-delta[idxStart:idxEnd])
        triangleRatio = 2 * auc / (idxEnd - idxStart)

        pctValid = np.mean(mask[idxStart:idxEnd])

        idxNadir = np.argmin(sigSmoothed[idxStart:idxEnd]) + idxStart
        # find actual
        idxMin = np.argmin(sig[idxNadir - nadirWidth:idxNadir +
                               nadirWidth]) + idxNadir - nadirWidth
        # idxMin = np.argmin(sig[idxStart:idxEnd]) + idxStart
        relMag = -delta[idxMin]
        mag = sig[idxMin]

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        tOnset = (ts[idxNadir] - ts[idxStart]) * 60
        tRelease = (ts[idxEnd] - ts[idxNadir]) * 60

        decel = {
            'relMag': relMag,
            'mag': mag,
            'pctValid': pctValid,
            'base': startingBaseline,
            'duration': duration,
            'tOnset': tOnset,
            'tRelease': tRelease,
            'idxNadir': idxNadir,
            'idxStart': idxStart,
            'idxEnd': idxEnd,
            'tNadir': float(ts[idxNadir]),
            'tStart': float(ts[idxStart]),
            'tEnd': float(ts[idxEnd]),
            'triangleRatio': triangleRatio
        }

        results.append(decel)

    return results
def findProlongedDecelCandidates(sig,
                                 mask,
                                 sigSmoothed,
                                 baseline,
                                 ts,
                                 var,
                                 minRelMag=15,
                                 minDuration=(2.5 * 60)):
    """Find potential prolonged decelerations"""

    delta = sig - baseline
    deltaSmoothed5 = sigSmoothed - baseline + 5
    allRelease, allOnset = getCrossings(deltaSmoothed5)

    results = []
    lastEnd = -1
    for idxStart in allOnset:
        if idxStart < lastEnd:  # overlap
            continue

        # use starting point as baseline
        i = np.argmax(sigSmoothed[idxStart:] > sigSmoothed[idxStart])
        if i == 0:
            idxEnd = len(sigSmoothed) - 1
        else:
            idxEnd = idxStart + i
        lastEnd = idxEnd

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        if duration < minDuration:
            continue

        pctValid = np.mean(mask[idxStart:idxEnd])

        idxNadir = np.argmin(sigSmoothed[idxStart:idxEnd]) + idxStart
        startingBaseline = baseline[idxStart]

        mag = sigSmoothed[idxNadir]
        relMag = startingBaseline - mag

        if relMag < minRelMag:
            continue

        # Time under 15 bpm
        deltaT = ts[1] - ts[0]
        sigSmoothed[idxStart:idxEnd] < startingBaseline - 15
        time_under_15 = np.sum(
            sigSmoothed[idxStart:idxEnd] < startingBaseline - 15) * deltaT

        # time under 50% of relMag
        sigSmoothed[idxStart:idxEnd] < startingBaseline - 15
        time_under_50pct = np.sum(
            sigSmoothed[idxStart:idxEnd] < startingBaseline -
            0.5 * relMag) * deltaT

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        tOnset = (ts[idxNadir] - ts[idxStart]) * 60
        tRelease = (ts[idxEnd] - ts[idxNadir]) * 60

        segVar = computeSegmentVariability(idxStart, idxEnd, var, mask)

        decel = {
            'relMag': relMag,
            'mag': mag,
            'pctValid': pctValid,
            'base': startingBaseline,
            'duration': duration,
            'tOnset': tOnset,
            'tRelease': tRelease,
            'idxNadir': idxNadir,
            'idxStart': idxStart,
            'idxEnd': idxEnd,
            'tNadir': float(ts[idxNadir]),
            'tStart': float(ts[idxStart]),
            'tEnd': float(ts[idxEnd]),
            'var': segVar,
            'time_under_15': time_under_15,
            'time_under_50pct': time_under_50pct
        }
        results.append(decel)

    return results
def findAccelCandidates(sig,
                        mask,
                        sigSmoothed,
                        baseline,
                        ts,
                        classifiers=[],
                        peakWidth=5,
                        includeEnd=True,
                        verbose=True):
    """Find accelerations"""

    delta = sig - baseline
    deltaSmoothed5 = sigSmoothed - baseline - 5
    allRelease, allOnset = getCrossings(-deltaSmoothed5)

    allEndpoints = [[i, 'onset'] for i in allOnset] + [[i, 'release']
                                                       for i in allRelease]
    allEndpoints = sorted(allEndpoints)

    if includeEnd and allEndpoints and allEndpoints[-1] != 'release':
        # create release at end of recording
        allEndpoints.append([len(sig) - 1, 'release'])

    results = []
    for i, entry in enumerate(allEndpoints):
        if entry[1] != 'release':
            continue
        if i == 0 or allEndpoints[i - 1][1] != 'onset':
            continue

        idxStart = allEndpoints[i - 1][0]
        idxEnd = entry[0]

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        if duration < 10:
            continue

        pctValid = np.mean(mask[idxStart:idxEnd])

        idxPeak = np.argmax(sigSmoothed[idxStart:idxEnd]) + idxStart
        # find actual
        idxPeak = np.argmax(
            sig[idxPeak - peakWidth:idxPeak + peakWidth]) + idxPeak - peakWidth
        # idxMin = np.argmin(sig[idxStart:idxEnd]) + idxStart
        relMag = delta[idxPeak]
        mag = sig[idxPeak]

        duration = (ts[idxEnd] - ts[idxStart]) * 60
        tOnset = (ts[idxPeak] - ts[idxStart]) * 60
        tRelease = (ts[idxEnd] - ts[idxPeak]) * 60

        decel = {
            'relMag': relMag,
            'mag': mag,
            'pctValid': pctValid,
            'duration': duration,
            'tOnset': tOnset,
            'tRelease': tRelease,
            'idxNadir': idxPeak,
            'idxStart': idxStart,
            'idxEnd': idxEnd,
            'tNadir': float(ts[idxPeak]),
            'tStart': float(ts[idxStart]),
            'tEnd': float(ts[idxEnd]),
            'triangleRatio': None
        }

        results.append(decel)
    return results