def _get_qrs_power_signal(self): if self.apply_filter: filtered_ecg = normalize( bandpass(self.ecg_signal, self.bandpass_min, self.bandpass_max, self.ecg_ts.sampling_rate)) else: filtered_ecg = self.ecg_signal # Differentiate and square the signal? if self.apply_diff_sq: # Differentiate the signal and square it diff_sq = np.ediff1d(filtered_ecg, to_begin=0)**2 else: diff_sq = filtered_ecg # If we're to apply a Moving Average smoothing if self.apply_smooth_ma: # MA smoothing smooth_ma = smooth(diff_sq, window_len=self.smoothing_window_len, window=self.smoothing_window) else: smooth_ma = diff_sq if not self.use_ECG2: # for visualization purposes return normalize(smooth_ma) logger.info("Using 2nd ECG signal") # Use secondary ECG signal and combine QRS power if self.apply_filter: filtered_ecg2 = normalize( bandpass(self.ecg2_signal, self.bandpass_min, self.bandpass_max, self.ecg_ts.sampling_rate)) else: filtered_ecg2 = self.ecg2_signal if self.apply_diff_sq: diff_sq2 = np.ediff1d(filtered_ecg2, to_begin=0)**2 else: diff_sq2 = filtered_ecg2 if self.apply_smooth_ma: smooth_ma2 = smooth(diff_sq2, window_len=self.smoothing_window_len, window=self.smoothing_window) else: smooth_ma2 = diff_sq2 return normalize(((1 - self.ecg2_weight) * smooth_ma + self.ecg2_weight * smooth_ma2)**2)
def beat_to_time_feature_matrix(self, beatnum): """ Turns a heartbeat into a matrix. There is one row for each millisecond between the r point and the c point. Each row represents the time corresponding to its center. That timepoint is padded by self.pre_point_msec and self.post_point_msec. If self.include_derivative, the derivative is appended to the end of each row """ include_derivative = self.include_derivative pre_msec = self.pre_point_msec post_msec = self.post_point_msec r_ind = self.physiodata.r_indices[beatnum] c_ind = self.physiodata.c_indices[beatnum] if r_ind == c_ind: raise ValueError("Invalid r and c point for beat %d" % beatnum) _sig = self.physiodata.mea_dzdt_matrix[beatnum][:] # The targets are msec from the b-point if include_derivative: signal = [np.concatenate([_sig[(ind-pre_msec):(ind+post_msec+1)], 10*np.diff( smooth(_sig[(ind-pre_msec):(ind+post_msec+1)], 7))]) \ for ind in xrange(r_ind,c_ind)] else: signal = [_sig[(ind-pre_msec):(ind+post_msec+1)] for ind \ in xrange(r_ind,c_ind)] return np.row_stack(signal)
def _get_aux_signal(self): if not self.use_secondary_heartbeat: return np.array([]) sig = getattr(self.physiodata, self.secondary_heartbeat + "_data") if self.secondary_heartbeat_abs: sig = np.abs(sig) if self.secondary_heartbeat_window_len > 0: sig = smooth(sig, window_len=self.secondary_heartbeat_window_len, window=self.secondary_heartbeat_window) return normalize(sig)
def mark_similar(self, unmarked_beat, smoothing_window_len=21, search_window=SEARCH_WINDOW): """ Extracts a time point specific to ``unmarked_beat`` that has similar signal property (min/max/inflection) within a specific time window """ # Get the necessary info from the unmarked beat unmarked_point = getattr(unmarked_beat, self.name) ts = getattr(unmarked_beat, self.applies_to + "_signal") # Define the search area window_min_idx = self.index - search_window window_max_idx = self.index + search_window + 1 if self.index < search_window and self.point_type != "average": logger.warn( "timepoint too close to 0 for a symmetric search window") return if self.name == "q": search_window = 9 smoothing_window_len = 0 search_region = ts[(self.index - search_window):(self.index + search_window + 1)] # Smooth the timeseries if requested if smoothing_window_len > 0: search_region = smooth(search_region, window_len=smoothing_window_len) # find the actual time of the point if self.point_type == "max": t_ind = window_min_idx + search_region.argmax() elif self.point_type == "min": t_ind = window_min_idx + search_region.argmin() elif self.point_type == "inflection": diff = np.ediff1d(search_region, to_begin=0) diff2 = np.ediff1d(diff, to_begin=0) t_ind = window_min_idx + np.abs(diff2).argmax() elif self.point_type == "geom_trick": r_idx = unmarked_beat.r.index c_idx = unmarked_beat.c.index if not r_idx < c_idx: unmarked_beat.b.needs_attention = True return self roi = ts[r_idx:c_idx] line = np.interp(np.arange(len(roi)), [0, len(roi)], [roi[0], roi[-1]]) t_ind = r_idx + np.argmax(line - roi) # Average is a special case. elif self.point_type == "average": unmarked_point.value = ts.mean() unmarked_point.set_index(0) return # Check that we aren't hitting an edge of the search reg if t_ind in (window_min_idx, window_max_idx): bnum = unmarked_beat.id if unmarked_beat.id is not None else -1 logger.warn("[%d] %s point detected at edge of search region", bnum, self.name) unmarked_point.needs_attention = True unmarked_point.set_index(t_ind) # If this timepoint is tracked by beat id, if unmarked_beat.id is not None: index_array = getattr(self.physiodata, self.name + "_indices", None) assert index_array[unmarked_beat.id] == t_ind return True