def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pulse_vals = [] for i in range(0, len(minlist) - 1): if not beats_128[i]: continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure pulse_vals.append({'dt': minlist[i] / srate, 'val': pp}) # estimates the upper env(n) and lower env(n) envelopes xa = np.array([data[idx] for idx in minlist]) lower_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: lower_env[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) upper_env = np.array([0.0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: upper_env[i] = np.dot(xb, be) / s pulse_env = upper_env - lower_env pulse_env[pulse_env < 0.0] = 0.0 # estimates resp rate rr = arr.estimate_resp_rate(pulse_env, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] pps = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] for ppe in pulse_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) if len(pps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) ppv = 2 * (pp_max - pp_min) / (pp_max + pp_min) * 100 # estimate if not 0 < ppv < 50: continue # raw_pps.append({'dt': (ibreath * nsamp_in_breath) / srate, 'val': pp}) # # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue # no update pps.append({ 'dt': ((ibreath + 1) * nsamp_in_breath) / srate, 'val': int(ppv) }) return [pps, pulse_vals, [{'dt': cfg['interval'], 'val': rr}]]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ global last_ppv, last_spv data = arr.interp_undefined(inp['ART']['vals']) srate = inp['ART']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: print('hr < 30') return # beat detection minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # beat lengths beatlens = [] beats_128 = [] beats_128_valid = [] for i in range(0, len(minlist) - 1): beatlen = minlist[i + 1] - minlist[i] # in samps if not 30 < beatlen < 300: beats_128.append(None) continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure if not 20 < pp < 100: beats_128.append(None) continue beatlens.append(beatlen) beat = data[minlist[i]:minlist[i + 1]] resampled = arr.resample(beat, 128) beats_128.append(resampled) beats_128_valid.append(resampled) if not beats_128_valid: return avgbeat = np.array(beats_128_valid).mean(axis=0) meanlen = np.mean(beatlens) stdlen = np.std(beatlens) if stdlen > meanlen * 0.2: # irregular rhythm return # remove beats with correlation < 0.9 pp_vals = [] sp_vals = [] for i in range(0, len(minlist) - 1): if beats_128[i] is None or not len(beats_128[i]): continue if np.corrcoef(avgbeat, beats_128[i])[0, 1] < 0.9: continue pp = data[maxlist[i]] - data[minlist[i]] # pulse pressure sp = data[maxlist[i]] pp_vals.append({'dt': minlist[i] / srate, 'val': pp}) sp_vals.append({'dt': minlist[i] / srate, 'val': sp}) dtstart = time.time() # estimates resp rate # upper env idx_start = max(min(minlist), min(maxlist)) idx_end = min(max(minlist), max(maxlist)) xa = scipy.interpolate.CubicSpline( maxlist, [data[idx] for idx in maxlist])(np.arange(idx_start, idx_end)) # lower env xb = scipy.interpolate.CubicSpline( minlist, [data[idx] for idx in minlist])(np.arange(idx_start, idx_end)) rr = arr.estimate_resp_rate(xa - xb, srate) dtend = time.time() #print('rr {}'.format(rr)) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist raw_pps = [] raw_sps = [] ppvs = [] spvs = [] for ibreath in np.arange(0, m - 1, 0.5): pps_breath = [] sps_breath = [] for ppe in pp_vals: if ibreath * nsamp_in_breath < ppe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: pps_breath.append(ppe['val']) for spe in sp_vals: if ibreath * nsamp_in_breath < spe['dt'] * srate < ( ibreath + 1) * nsamp_in_breath: sps_breath.append(spe['val']) if len(pps_breath) < 4: continue if len(sps_breath) < 4: continue pp_min = min(pps_breath) pp_max = max(pps_breath) sp_min = min(sps_breath) sp_max = max(sps_breath) ppv = (pp_max - pp_min) / (pp_max + pp_min) * 200 if not 0 < ppv < 50: continue spv = (sp_max - sp_min) / (sp_max + sp_min) * 200 if not 0 < spv < 50: continue # kalman filter if last_ppv == 0: # first time last_ppv = ppv elif abs(last_ppv - ppv) <= 1.0: ppv = last_ppv elif abs(last_ppv - ppv) <= 25.0: # ppv cannot be changed abruptly ppv = (ppv + last_ppv) * 0.5 last_ppv = ppv else: continue if last_spv == 0: # first time last_spv = spv elif abs(last_spv - spv) <= 1.0: spv = last_spv elif abs(last_spv - spv) <= 25.0: # ppv cannot be changed abruptly spv = (spv + last_spv) * 0.5 last_spv = spv else: continue ppvs.append(ppv) spvs.append(spv) median_ppv = np.median(ppvs) median_spv = np.median(spvs) return [[{ 'dt': cfg['interval'], 'val': median_ppv }], [{ 'dt': cfg['interval'], 'val': median_spv }], [{ 'dt': cfg['interval'], 'val': rr }]]
def run(inp, opt, cfg): """ calculate ppv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ data = arr.interp_undefined(inp['pleth']['vals']) srate = inp['pleth']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: return [{}, {}, {}, {}, {}, [], []] minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # estimates the upper ue(n) and lower le(n) envelopes xa = np.array([data[idx] for idx in minlist]) le = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in minlist]) s = sum(be) if s != 0: le[i] = np.dot(xa, be) / s xb = np.array([data[idx] for idx in maxlist]) ue = np.array([0] * len(data)) for i in range(len(data)): be = np.array([b((i - idx) / (0.2 * srate)) for idx in maxlist]) s = sum(be) if s != 0: ue[i] = np.dot(xb, be) / s re = ue - le re[re < 0] = 0 # estimates resp rate rr = arr.estimate_resp_rate(re, srate) # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist pps = [] for i in range(m - 1): imax = arr.max_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) # 50% overlapping imin = arr.min_idx(re, i * nsamp_in_breath, (i+2) * nsamp_in_breath) ppmax = re[imax] ppmin = re[imin] ppe = 2 * (ppmax - ppmin) / (ppmax + ppmin) * 100 # estimate if ppe > 50 or ppe < 0: continue pp = cfg['pp'] if pp == 0: pp = ppe err = abs(ppe - pp) if err < 1: pp = ppe elif err < 25: pp = (pp + ppe) / 2 else: pass # dont update cfg['pp'] = pp pps.append({'dt': (i * nsamp_in_breath) / srate, 'val': pp}) return [ [{'dt': cfg['interval'], 'val': rr}], pps ]
def run(inp, opt, cfg): """ calculate svv from arterial waveform :param art: arterial waveform :return: max, min, upper envelope, lower envelope, respiratory rate, ppv """ data = arr.interp_undefined(inp['art1']['vals']) srate = inp['art1']['srate'] data = arr.resample_hz(data, srate, 100) srate = 100 if len(data) < 30 * srate: return [[], [], [], []] minlist, maxlist = arr.detect_peaks(data, srate) maxlist = maxlist[1:] # make the same length # calculate each beat's std and put it at the peak time stds = [] lzs = [] for i in range(len(minlist) - 1): maxidx = maxlist[i] beat = data[minlist[i]:minlist[i + 1]] if max(beat) - min(beat) < 20: continue s = np.std(beat) stds.append({'dt': maxidx / srate, 'val': s}) sbp = np.max(beat) dbp = beat[0] lz = (sbp - dbp) / (sbp + dbp) # 0.1~0.3 lzs.append({'dt': maxidx / srate, 'val': lz}) # estimates resp rate rr = np.median([o['val'] for o in inp['vent_rr']]) if not rr > 1: return [[], [], [], []] # split by respiration nsamp_in_breath = int(srate * 60 / rr) m = int(len(data) / nsamp_in_breath) # m segments exist # std svv_stds = [] for i in range(m - 1): # 50% overlapping this_breath_stds = [] for j in range(len(stds)): if i * nsamp_in_breath <= stds[j]['dt'] * srate < ( i + 2) * nsamp_in_breath: this_breath_stds.append(stds[j]['val']) svmax = np.max(this_breath_stds) svmin = np.min(this_breath_stds) svv_stde = 2 * (svmax - svmin) * 100 / (svmax + svmin) # estimate if svv_stde > 40 or svv_stde < 0: continue svv_stds.append(svv_stde) svv_stde = np.median(svv_stds) if svv_stde < 0: svv_stde = 0 svv_std = cfg['svv_std'] if svv_std == 0 or svv_std is None: svv_std = svv_stde err = abs(svv_stde - svv_std) if err < 5: svv_std = svv_stde elif err < 25: svv_std = (svv_std + svv_stde) / 2 else: pass # dont update cfg['svv_std'] = svv_std # lz svv_lzs = [] for i in range(m - 1): # 50% overlapping this_breath_lzs = [] for j in range(len(lzs)): if i * nsamp_in_breath <= lzs[j]['dt'] * srate < ( i + 2) * nsamp_in_breath: this_breath_lzs.append(lzs[j]['val']) svmax = np.max(this_breath_lzs) svmin = np.min(this_breath_lzs) svv_lze = 2 * (svmax - svmin) * 100 / (svmax + svmin) # estimate if svv_lze > 40 or svv_lze < 0: continue svv_lzs.append(svv_lze) svv_lze = np.median(svv_lzs) if svv_lze < 0: svv_lze = 0 svv_lz = cfg['svv_lz'] if svv_lz == 0 or svv_lz is None: svv_lz = svv_lze err = abs(svv_lze - svv_lz) if err < 5: svv_lz = svv_lze elif err < 25: svv_lz = (svv_lz + svv_lze) / 2 else: pass # dont update cfg['svv_lz'] = svv_lz return [ stds, [{ 'dt': cfg['interval'], 'val': svv_std }], lzs, [{ 'dt': cfg['interval'], 'val': svv_lz }] ]