def get_clipped_heartbeat_signals(self, i, start, interval, composite_start, composite_end): # Center signals in current interval at the R peak heartbeat_time = self.peaks.time[interval] - self.peaks.time[ self.peaks.R.data[start + 1 + i]] heartbeat_signal = self.peaks.signal[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]] heartbeat_seis = self.peaks.seis[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]] heartbeat_phono = self.peaks.phono[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]] # Clip Front remove_from_start = int( np.where(heartbeat_time == 0)[0] - composite_start) if remove_from_start != 0: heartbeat_time = np.delete(heartbeat_time, range(remove_from_start)) heartbeat_signal = np.delete(heartbeat_signal, range(remove_from_start)) heartbeat_seis = np.delete(heartbeat_seis, range(remove_from_start)) heartbeat_phono = np.delete(heartbeat_phono, range(remove_from_start)) # Clip Back remove_from_end = int((len(heartbeat_time) - np.where(heartbeat_time == 0)[0]) - composite_end) if remove_from_end != 0: heartbeat_time = np.delete( heartbeat_time, range( len(heartbeat_time) - remove_from_end, len(heartbeat_time))) heartbeat_signal = np.delete( heartbeat_signal, range( len(heartbeat_time) - remove_from_end, len(heartbeat_time))) heartbeat_seis = np.delete( heartbeat_seis, range( len(heartbeat_time) - remove_from_end, len(heartbeat_time))) heartbeat_phono = np.delete( heartbeat_phono, range( len(heartbeat_time) - remove_from_end, len(heartbeat_time))) # Normalize heartbeat_signal = hb.normalize(heartbeat_signal) heartbeat_seis = hb.normalize(heartbeat_seis) heartbeat_phono = hb.normalize(heartbeat_phono) return heartbeat_time, heartbeat_signal, heartbeat_seis, heartbeat_phono
def update_plot(self): # Update index self.i_text.set_text("Heartbeat: " + str(self.index + 1) + "/" + str(len(self.peaks.R.data) - 1)) self.signal = hb.normalize(self.peaks.signal[range(self.peaks.P.data[self.index], self.peaks.R.data[self.index + 1])]) # Pass through a Low pass self.smoothed_signal = hb.lowpass_filter(signal = self.signal, cutoff_freq = self.cutoff_freq) # Calculate first derivative self.first, _ = hb.get_derivatives(self.smoothed_signal) # Update cross hairs self.switch_signal(None) # Plot ECG, Phono and Seismo self.signal_line.set_data(range(len(self.signal)), self.signal_amp_slider.val * self.signal) self.smooth_signal_line.set_data(range(len(self.signal)), self.signal_amp_slider.val * self.smoothed_signal) self.first_line.set_data(range(len(self.signal)), (self.first_amp_slider.val * 5*self.first) + self.first_height_slider.val + 1) self.ax.set_xlim(0, len(self.signal)) # T Peak self.T_point.set_offsets((self.peaks.T.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.T.data[self.index] - self.peaks.P.data[self.index]])) self.T_text.set_position((self.peaks.T.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.T.data[self.index] - self.peaks.P.data[self.index]] + 0.2)) # ST Start Peak self.ST_start_point.set_offsets((self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index]])) self.ST_start_text.set_position((self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index]] + 0.2)) # T''max Peak self.dT_point.set_offsets((self.peaks.dT.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.dT.data[self.index] - self.peaks.P.data[self.index]])) self.dT_text.set_position((self.peaks.dT.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.dT.data[self.index] - self.peaks.P.data[self.index]] + 0.2)) # # T''max Peak # self.ddT_point.set_offsets((self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index]])) # self.ddT_text.set_position((self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index], self.signal_amp_slider.val * self.smoothed_signal[self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index]] + 0.2)) self.fig.canvas.draw()
def get_N_composite_signal_dataset(self, N, slide_step_size, display=False, dosage=None): # Get all heartbeats in composite composite_endpoints = self.get_N_composite_endpoints( N, slide_step_size) composites = [] count = 0 for start, end in composite_endpoints: count += 1 # Find bounds for composite endpoints composite_start, composite_end = self.get_composite_bounds( start, end) # Clip all heartbeats to same length for i in range(N): # Define current interval interval = range(self.peaks.T.data[start + i], self.peaks.P.data[start + i + 2]) # Clip signals and center them based off R peak heartbeat_time, heartbeat_signal, heartbeat_seis, heartbeat_phono = self.get_clipped_heartbeat_signals( i, start, interval, composite_start, composite_end) # Cumulative sum composite_time = heartbeat_time if i == 0 else composite_time + heartbeat_time composite_signal = heartbeat_signal if i == 0 else composite_signal + heartbeat_signal composite_seis = heartbeat_seis if i == 0 else composite_seis + heartbeat_seis composite_phono = heartbeat_phono if i == 0 else composite_phono + heartbeat_phono # Divide by sample size composite_time /= N composite_signal /= N composite_seis /= N composite_phono /= N composites.append([ composite_time, composite_signal, composite_seis, composite_phono ]) if display: # Display fig, axes2d = plt.subplots(nrows=N, ncols=3) signal_lay_over_cell = fig.add_subplot(3, 3, 2) seis_lay_over_cell = fig.add_subplot(3, 3, 5) phono_lay_over_cell = fig.add_subplot(3, 3, 8) composite_cell = fig.add_subplot(1, 3, 3) if dosage is None: plt.suptitle( str(count) + " of " + str(len(composite_endpoints)) + " INO Composites given " + str(N) + " Heartbeats w/ Step Size of " + str(slide_step_size), fontsize=20) else: plt.suptitle( str(count) + " of " + str(len(composite_endpoints)) + " INO Composites for Dosage " + str(dosage) + " given " + str(N) + " Heartbeats w/ Step Size of " + str(slide_step_size), fontsize=20) for i, row in enumerate(axes2d): for j, cell in enumerate(row): interval = range(self.peaks.T.data[start + i], self.peaks.P.data[start + i + 2]) cell.set_xticks([]) cell.set_yticks([]) if j == 0: if i == 0: cell.set_title("Individual Signals") cell.plot( self.peaks.time[interval], hb.normalize(self.peaks.signal[interval])) cell.plot(self.peaks.time[interval], hb.normalize(self.peaks.seis[interval])) cell.plot(self.peaks.time[interval], hb.normalize(self.peaks.phono[interval])) cell.set_ylabel(start + i) elif j == 1: signal_lay_over_cell.plot( self.peaks.time[interval] - self.peaks.time[self.peaks.R.data[start + 1 + i]], hb.normalize(self.peaks.signal[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]]), "--", linewidth=0.5) seis_lay_over_cell.plot( self.peaks.time[interval] - self.peaks.time[self.peaks.R.data[start + 1 + i]], hb.normalize(self.peaks.seis[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]]), "--", linewidth=0.5) phono_lay_over_cell.plot( self.peaks.time[interval] - self.peaks.time[self.peaks.R.data[start + 1 + i]], hb.normalize(self.peaks.phono[interval] - self.peaks.signal[ self.peaks.R.data[start + 1 + i]]), "--", linewidth=0.5) signal_lay_over_cell.set_xticks([]) signal_lay_over_cell.set_yticks([]) seis_lay_over_cell.set_xticks([]) seis_lay_over_cell.set_yticks([]) phono_lay_over_cell.set_xticks([]) phono_lay_over_cell.set_yticks([]) else: if i == 0: signal_lay_over_cell.plot( composite_time, hb.normalize(composite_signal), 'r', linewidth=2, label="Composite Signal") signal_lay_over_cell.legend(loc='lower left') seis_lay_over_cell.plot( composite_time, hb.normalize(composite_seis), 'r', linewidth=2, label="Composite Seis") seis_lay_over_cell.legend(loc='lower left') phono_lay_over_cell.plot( composite_time, hb.normalize(composite_phono), 'r', linewidth=2, label="Composite Phono") phono_lay_over_cell.legend(loc='lower left') composite_cell.plot(composite_signal, 'r', label="EKG") composite_cell.plot(composite_seis, 'b', label="Seis", linewidth=0.5) composite_cell.plot(composite_phono, 'g', label="Phono", linewidth=0.5) composite_cell.legend(loc='lower left') composite_cell.set_xticks([]) composite_cell.set_yticks([]) composite_cell.set_title("Composite") signal_lay_over_cell.set_title("Superimposed") # Maximize Frame mng = plt.get_current_fig_manager() mng.full_screen_toggle() plt.show() self.composites = composites return composites
def plot_signals(self): # Create figure self.fig, self.ax = plt.subplots() self.signal = hb.normalize(self.peaks.signal[range(self.peaks.P.data[self.index], self.peaks.T.data[self.index + 1])]) # Determine what cutoff freq to use self.cutoff_freq = 15 if np.mean(np.diff(self.peaks.R.data)) > 2500 else 10 # Pass through a Low pass self.smoothed_signal = hb.lowpass_filter(signal = self.signal, cutoff_freq = self.cutoff_freq) # Calculate first derivative self.first, _ = hb.get_derivatives(self.smoothed_signal) # Plot ECG, Phono and Seismo self.signal_line, = self.ax.plot(range(len(self.signal)), self.signal, linewidth = 0.75, c = "r", label = "ECG") self.smooth_signal_line, = self.ax.plot(range(len(self.signal)), self.smoothed_signal, linewidth = 1, c = "b", label = "Low Pass ECG") self.first_line, = self.ax.plot(range(len(self.signal)), 1 + 5*self.first, '--', linewidth = 0.5, c = 'k', label = "ECG 1st Derv.") self.ax.set_xlim(0, len(self.signal)) sig_min = min(self.signal) sig_max = max(self.signal) self.ax.set_ylim(sig_min - 0.2*(sig_max - sig_min), sig_max + 0.2*(sig_max - sig_min)) plt.legend(loc='upper right') # T Peak self.T_point = self.ax.scatter(self.peaks.T.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.T.data[self.index] - self.peaks.P.data[self.index]], c = '#9467bd') self.T_text = self.ax.text(self.peaks.T.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.T.data[self.index] - self.peaks.P.data[self.index]] + 0.2, "T", fontsize=9, horizontalalignment = 'center') # ST Start Peak self.ST_start_point = self.ax.scatter(self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index]], c = 'y') self.ST_start_text = self.ax.text(self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.ST_start.data[self.index] - self.peaks.P.data[self.index]] + 0.2, "ST Start", fontsize=9, horizontalalignment = 'center') # T'max Peak self.dT_point = self.ax.scatter(self.peaks.dT.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.dT.data[self.index] - self.peaks.P.data[self.index]], c = '#2ca02c') self.dT_text = self.ax.text(self.peaks.dT.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.dT.data[self.index] - self.peaks.P.data[self.index]] + 0.2, "T'max", fontsize=9, horizontalalignment = 'center') # # T''max Peak # self.ddT_point = self.ax.scatter(self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index]], c = '#2ca02c') # self.ddT_text = self.ax.text(self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index], self.smoothed_signal[self.peaks.ddT.data[self.index] - self.peaks.P.data[self.index]] + 0.2, "T''max", fontsize=9, horizontalalignment = 'center') # Initalize axes and data points self.x = range(len(self.signal)) self.y = self.smoothed_signal # Cross hairs self.lx = self.ax.axhline(color='k', linewidth=0.2) # the horiz line self.ly = self.ax.axvline(color='k', linewidth=0.2) # the vert line # Add data left_shift = 0.45 start = 0.96 space = 0.04 self.ax.text(0.01, start, transform = self.ax.transAxes, s = "Folder: " + self.folder_name, fontsize=12, horizontalalignment = 'left') self.ax.text(0.01, start - space, transform = self.ax.transAxes, s = "Dosage: " + str(self.dosage), fontsize=12, horizontalalignment = 'left') self.i_text = self.ax.text(0.60 - left_shift, 1.1 - space, transform = self.ax.transAxes, s = "Heartbeat: " + str(self.index + 1) + "/" + str(len(self.peaks.R.data) - 1), fontsize=12, horizontalalignment = 'left') # Add index buttons ax_prev = plt.axes([0.575 - left_shift, 0.9, 0.1, 0.075]) self.bprev = Button(ax_prev, 'Previous') self.bprev.on_clicked(self.prev) ax_next = plt.axes([0.8 - left_shift, 0.9, 0.1, 0.075]) self.b_next = Button(ax_next, 'Next') self.b_next.on_clicked(self.next) self.fig.canvas.mpl_connect('motion_notify_event', self.mouse_move) self.fig.canvas.mpl_connect('button_press_event', self.on_click) self.fig.canvas.mpl_connect('button_release_event', self.off_click) # Add Sliders start = 0.91 slider_width = 0.0075 slider_height = 0.47 self.cutoff_amp_slider = Slider(plt.axes([0.05, 0.15, 2*slider_width, slider_height]), label = "Cutoff (Hz)", valmin = 1, valmax = 50, valinit = self.cutoff_freq, orientation = 'vertical', valfmt='%0.0f') self.cutoff_amp_slider.label.set_size(8) self.cutoff_amp_slider.on_changed(self.switch_signal) self.signal_amp_slider = Slider(plt.axes([start, 0.15, slider_width, slider_height]), label = "ECG\n\nA", valmin = 0.01, valmax = 10, valinit = 1, orientation = 'vertical') self.signal_amp_slider.label.set_size(8) self.signal_amp_slider.on_changed(self.switch_signal) self.signal_amp_slider.valtext.set_visible(False) self.first_height_slider = Slider(plt.axes([start + 2*slider_width, 0.15, slider_width, slider_height]), label = " 1st\n Derv.\nH", valmin = 1.5 * min(self.signal), valmax = 1.5 * max(self.signal), valinit = 0, orientation = 'vertical') self.first_height_slider.label.set_size(8) self.first_height_slider.on_changed(self.switch_signal) self.first_height_slider.valtext.set_visible(False) self.first_amp_slider = Slider(plt.axes([start + 3*slider_width, 0.15, slider_width, slider_height]), label = "\nA", valmin = 0.01, valmax = 10, valinit = 1, orientation = 'vertical') self.first_amp_slider.label.set_size(8) self.first_amp_slider.on_changed(self.switch_signal) self.first_amp_slider.valtext.set_visible(False) # Maximize frame mng = plt.get_current_fig_manager() mng.full_screen_toggle() plt.show()