def set_wlet_pars(self): # period validator vali = self.periodV # read all the LineEdits: text = self.T_min.text() T_min = text.replace(",", ".") check, _, _ = vali.validate(T_min, 0) if self.debug: print("Min periodValidator output:", check, "value:", T_min) if check == 0: self.OutOfBounds = MessageWindow("Wavelet periods out of bounds!", "Value Error") return False self.T_min_value = float(T_min) step_num = self.step_num.text() check, _, _ = posintV.validate(step_num, 0) if self.debug: print("# Periods posintValidator:", check, "value:", step_num) if check == 0: self.OutOfBounds = MessageWindow( "The Number of periods must be a positive integer!", "Value Error") return False self.step_num_value = int(step_num) text = self.T_max.text() T_max = text.replace(",", ".") check, _, _ = vali.validate(T_max, 0) if self.debug: print("Max periodValidator output:", check) print(f"Max period value: {self.T_max.text()}") if check == 0 or check == 1: self.OutOfBounds = MessageWindow( "Wavelet highest period out of bounds!", "Value Error") return False self.T_max_value = float(T_max) text = self.p_max.text() p_max = text.replace(",", ".") check, _, _ = posfloatV.validate(p_max, 0) # checks for positive float if check == 0: self.OutOfBounds = MessageWindow("Powers are positive!", "Value Error") return False # check for empty string: if p_max: self.p_max_value = float(p_max) else: self.p_max_value = None # success! return True
def calc_envelope(self): L = self.get_L(self.L_edit) if not L: return if self.debug: print("Calculating envelope with L = ", L) if L/self.dt < 3: self.OutOfBounds = MessageWindow( f"Minimum sliding\nwindow size is {3*self.dt}{self.time_unit} !", "Value Error", ) L = None return if L / self.dt > self.df.shape[0]: maxL = self.df.shape[0] * self.dt self.OutOfBounds = MessageWindow( f"Maximum sliding window\nsize is {maxL:.2f} {self.time_unit}!", "Value Error", ) L = None return # cut of frequency set?! if self.cb_trend.isChecked(): trend = self.calc_trend() if trend is None: return signal = self.raw_signal - trend envelope = pyboat.sliding_window_amplitude(signal, window_size = L, dt = self.dt) if self.cb_detrend.isChecked(): return envelope # fits on the original signal! else: return envelope + trend # otherwise add the mean else: if self.debug: print("calculating envelope for raw signal", L) mean = self.raw_signal.mean() envelope = pyboat.sliding_window_amplitude( self.raw_signal, window_size = L, dt = self.dt ) return envelope + mean
def run_fourier_ana(self): if not np.any(self.raw_signal): self.NoSignalSelected = MessageWindow( "Please select a signal first!", "No Signal") return False # shift new analyser windows self.w_position += 20 if self.cb_use_detrended2.isChecked(): trend = self.calc_trend() signal = self.raw_signal - trend else: signal = self.raw_signal if self.cb_use_envelope2.isChecked(): L = self.get_L(self.L_edit) signal = pyboat.normalize_with_envelope(signal, L, self.dt) # periods or frequencies? if self.cb_FourierT.isChecked(): show_T = False else: show_T = True self.anaWindows[self.w_position] = FourierAnalyzer( signal=signal, dt=self.dt, signal_id=self.signal_id, position=self.w_position, time_unit=self.time_unit, show_T=show_T, )
def get_T_c(self, T_c_edit): ''' Uses self.T_c_edit, argument just for clarity. Checks for empty input, this function only gets called when a detrending operation is requested. Hence, an empty QLineEdit will display a user warning and return nothing.. ''' # value checking done by validator, accepts also comma '1,1' ! tc = T_c_edit.text().replace(",", ".") try: T_c = float(tc) if self.debug: print("T_c set to:", T_c) return T_c # empty line edit except ValueError: self.NoTrend = MessageWindow( "Detrending parameter not set,\n" + "specify a cut-off period!", "No Trend", ) if self.debug: print("T_c ValueError", tc) return None
def do_annealRidge_detection(self, anneal_pars): """ Gets called from the AnnealConfigWindow deactivated for the public version..! """ if anneal_pars is None: self.noValues = MessageWindow( "No parameters set for\nsimulated annealing!", "No Parameters") return # todo add out-of-bounds parameter check in config window ini_per = anneal_pars["ini_per"] ini_T = anneal_pars["ini_T"] Nsteps = int(anneal_pars["Nsteps"]) max_jump = int(anneal_pars["max_jump"]) curve_pen = anneal_pars["curve_pen"] # get modulus index of initial straight line ridge y0 = np.where(self.periods < ini_per)[0][-1] ridge_y, cost = core.find_ridge_anneal(self.modulus, y0, ini_T, Nsteps, mx_jump=max_jump, curve_pen=curve_pen) self.ridge = ridge_y # draw the ridge and make ridge_data self._has_ridge = True self.draw_ridge()
def draw_ridge(self): """ makes also the ridge_data !! """ if not self._has_ridge: self.e = MessageWindow("Run a ridge detection first!", "No Ridge") return ridge_data = core.eval_ridge( self.ridge, self.wlet, self.signal, self.periods, self.tvec, power_thresh=self.power_thresh, smoothing_wsize=self.rsmoothing, ) # plot the ridge ax_spec = self.wCanvas.fig.axes[1] # the spectrum # already has a plotted ridge if ax_spec.lines: ax_spec.lines = [] # remove old ridge line self.cb_coi.setCheckState(0) # remove COI pl.draw_Wavelet_ridge(ax_spec, ridge_data, marker_size=1.5) # refresh the canvas self.wCanvas.draw() self.ridge_data = ridge_data
def calc_envelope(self): if self.L < 3: self.OutOfBounds = MessageWindow( f"Minimum sliding\nwindow size is {3*self.dt}{self.time_unit} !", "Value Error", ) self.L = None return if self.L > self.Nt: maxL = self.Nt * self.dt self.OutOfBounds = MessageWindow( f"Maximum sliding window\nsize is {maxL:.2f} {self.time_unit}!", "Value Error", ) self.L = None return # cut of frequency set?! if self.T_c: if self.debug: print("calculating envelope for detrended signal", self.L, self.T_c) trend = self.calc_trend() signal = self.raw_signal - trend envelope = pyboat.sliding_window_amplitude(signal, window_size=self.L) if self.cb_detrend.isChecked(): return envelope # fits on the original signal! else: return envelope + trend # otherwise add the mean else: if self.debug: print("calculating envelope for raw signal", self.L) mean = self.raw_signal.mean() envelope = pyboat.sliding_window_amplitude(self.raw_signal, window_size=self.L) return envelope + mean
def run_fourier_ana(self): if not np.any(self.raw_signal): self.NoSignalSelected = MessageWindow( "Please select a signal first!", "No Signal") return False # shift new analyser windows self.w_position += 20 if self.cb_use_detrended2.isChecked() and not self.T_c: self.NoTrend = MessageWindow( "Detrending not set, can not use detrended signal!", "No Trend") return elif self.cb_use_detrended2.isChecked(): trend = self.calc_trend() signal = self.raw_signal - trend else: signal = self.raw_signal if self.cb_use_envelope2.isChecked() and not self.L: self.NoTrend = MessageWindow( "Envelope parameter not set,\n" + "specify a sliding window size!", "No Envelope", ) return elif self.cb_use_envelope2.isChecked(): signal = pyboat.normalize_with_envelope(signal, self.L) # periods or frequencies? if self.cb_FourierT.isChecked(): show_T = False else: show_T = True self.anaWindows[self.w_position] = FourierAnalyzer( signal=signal, dt=self.dt, signal_id="Synthetic Signal", position=self.w_position, time_unit=self.time_unit, show_T=show_T, )
def toggle_envelope(self, state): if state == Qt.Checked: # user warning - no effect without L set if not self.L: self.NoEnvelope = MessageWindow( "Specify a sliding window size!", "Missing value") # signal selected? if self.raw_signal is not None: self.doPlot()
def do_maxRidge_detection(self): ridge_y = core.get_maxRidge_ys(self.modulus) self.ridge = ridge_y if not np.any(ridge_y): self.e = MessageWindow("No ridge found..check spectrum!", "Ridge detection error") return self._has_ridge = True self.draw_ridge() # ridge_data made here
def vector_prep(self, signal_id): """ prepares raw signal vector (NaN removal) and corresponding time vector """ if self.debug: print("preparing", signal_id) # checks for empty signal_id string if signal_id: raw_signal = self.df[signal_id] NaNswitches = np.sum(np.diff(np.isnan(raw_signal))) if NaNswitches > 1: print( f'Warning, non-contiguous NaN region found in {signal_id}!' ) self.NonContiguous = MessageWindow( ''' Non contiguous regions of missing values encountered, using linear interpolation. Try 'Import..' from the main menu to interpolate missing values in all signals! ''', "Missing Values") self.raw_signal = pyboat.core.interpolate_NaNs(raw_signal) else: # remove contiguous (like trailing) NaN region self.raw_signal = raw_signal[~np.isnan(raw_signal)] self.tvec = np.arange(0, len(self.raw_signal), step=1) * self.dt return True # success else: self.NoSignalSelected = MessageWindow("Please select a signal!", "No Signal") return False
def get_OutPath(self): ''' Reads the self.OutPath_edit There is no validator but an os.path check is done! ''' path = self.OutPath_edit.text() if not os.path.isdir(path): self.e = MessageWindow("Specified path is not a valid directory..", "Invalid export path") return None return path
def toggle_trend(self, state): if self.debug: print("old state:", self.cb_trend.isChecked()) if state == Qt.Checked: # user warning - no effect without T_c set if not self.T_c: self.NoTrend = MessageWindow("Specify a cut-off period!", "Missing value") # signal selected? if self.raw_signal is not None: self.doPlot()
def Load_and_init_Viewer(self): if self.debug: print("function Viewer_Ini called") # load a table directly df, err_msg = load_data(self.debug) if err_msg: self.error = MessageWindow(err_msg, "Loading error") return self.nViewers += 1 # initialize new DataViewer with the loaded data self.DataViewers[self.nViewers] = DataViewer(data=df, debug=self.debug)
def ini_plot_readout(self): if not self._has_ridge: self.e = MessageWindow('Do a ridge detection first!', 'No Ridge') return # to keep the line shorter.. wo = self.w_offset self.ResultWindows[wo] = WaveletReadoutWindow( self.signal_id, self.ridge_data, time_unit=self.time_unit, draw_coi=self.cb_coi.isChecked(), pos_offset=self.w_offset, DEBUG=self.DEBUG) self.w_offset += 20
def run_wavelet_ana(self): """ run the Wavelet Analysis """ if not np.any(self.raw_signal): self.NoSignalSelected = MessageWindow( "Please select a signal first!", "No Signal" ) return False wlet_pars = self.set_wlet_pars() # Error handling done there if not wlet_pars: if self.debug: print("Wavelet parameters could not be set!") return False if self.cb_use_detrended.isChecked(): trend = self.calc_trend() signal = self.raw_signal - trend else: signal = self.raw_signal if self.cb_use_envelope.isChecked(): L = self.get_L(self.L_edit) signal = pyboat.normalize_with_envelope(signal, L, dt = self.dt) self.w_position += 20 self.anaWindows[self.w_position] = WaveletAnalyzer( signal=signal, dt=self.dt, T_min = wlet_pars['T_min'], T_max = wlet_pars['T_max'], p_max = wlet_pars['p_max'], step_num=wlet_pars['step_num'], position=self.w_position, signal_id=self.signal_id, time_unit=self.time_unit, DEBUG=self.debug, )
def get_L(self, L_edit): # value checking done by validator, accepts also comma '1,1' ! L = L_edit.text().replace(",", ".") try: L = int(L) # empty line edit except ValueError: self.NoTrend = MessageWindow( "Envelope parameter not set,\n" + "specify a sliding window size!", "No Envelope", ) if self.debug: print("L ValueError", L) return None if self.debug: print("L set to:", L) return L
def vector_prep(self, signal_id): """ prepares raw signal vector (NaN removal) and corresponding time vector """ if self.debug: print("preparing", signal_id) # checks for empty signal_id string if signal_id: self.raw_signal = self.df[signal_id] # remove NaNs self.raw_signal = self.raw_signal[~np.isnan(self.raw_signal)] self.tvec = np.arange(0, len(self.raw_signal), step=1) * self.dt return True # success else: self.NoSignalSelected = MessageWindow( "Please select a signal!", "No Signal" ) return False
def run_batch(self): """ Takes the ui wavelet settings and spwans the batch processing Widget """ # reads the wavelet analysis settings from the ui input wlet_pars = self.set_wlet_pars() # Error handling done there if not wlet_pars: return if self.debug: print(f'Started batch processing with {wlet_pars}') # Spawning the batch processing config widget # is bound to parent Wavelet Window self.bc = BatchProcessWindow(self, self.debug) self.bc.initUI(wlet_pars) return print("batch processing done!") msg = f"Processed {Nproc} signals!\n ..saved results to {dir_name}" self.msg = MessageWindow(msg, "Finished")
def save_out_trend(self): if not np.any(self.raw_signal): self.NoSignalSelected = MessageWindow( "Please select a signal first!", "No Signal" ) return if self.debug: print("saving trend out") # -------calculate trend and detrended signal------------ trend = self.calc_trend() dsignal = self.raw_signal - trend # add everything to a pandas data frame data = np.array([self.raw_signal, trend, dsignal]).T # stupid pandas.. columns = ["raw", "trend", "detrended"] df_out = pd.DataFrame(data=data, columns=columns) # ------------------------------------------------------ if self.debug: print("df_out", df_out[:10]) print("trend", trend[:10]) dialog = QFileDialog() options = QFileDialog.Options() # ---------------------------------------------------------- default_name = "trend_" + str(self.signal_id) format_filter = "Text File (*.txt);; CSV ( *.csv);; Excel (*.xlsx)" # ----------------------------------------------------------- file_name, sel_filter = dialog.getSaveFileName( self, "Save as", default_name, format_filter, None, options=options ) # dialog cancelled if not file_name: return file_ext = file_name.split(".")[-1] if self.debug: print("selected filter:", sel_filter) print("out-path:", file_name) print("extracted extension:", file_ext) if file_ext not in ["txt", "csv", "xlsx"]: self.e = MessageWindow( "Ouput format not supported..\n" + "Please append .txt, .csv or .xlsx\n" + "to the file name!", "Unknown format", ) return # ------the write out calls to pandas---------------- float_format = "%.2f" # still old style :/ if file_ext == "txt": df_out.to_csv(file_name, index=False, sep="\t", float_format=float_format) elif file_ext == "csv": df_out.to_csv(file_name, index=False, sep=",", float_format=float_format) elif file_ext == "xlsx": df_out.to_excel(file_name, index=False, float_format=float_format) else: if self.debug: print("Something went wrong during save out..") return if self.debug: print("Saved!")
def save_out(self): dialog = QFileDialog() options = QFileDialog.Options() # ---------------------------------------------------------- base_name = str(self.signal_id).replace(' ', '-') default_name = os.path.join(os.path.expanduser('~'), base_name + '_ridgeRO') format_filter = "Text File (*.txt);; csv ( *.csv);; MS Excel (*.xlsx)" # ----------------------------------------------------------- file_name, sel_filter = dialog.getSaveFileName(self, "Save ridge readout as", default_name, format_filter, "(*.txt)", options=options) # dialog cancelled if not file_name: return file_ext = file_name.split(".")[-1] if self.DEBUG: print("selected filter:", sel_filter) print("out-path:", file_name) print("extracted extension:", file_ext) print("ridge data keys:", self.ridge_data.keys()) if file_ext not in ["txt", "csv", "xlsx"]: self.e = MessageWindow( "Ouput format not supported..\n" + "Please append .txt, .csv or .xlsx\n" + "to the file name!", "Unknown format", ) return # the write out calls float_format = "%.2f" # still old style :/ if file_ext == "txt": self.ridge_data.to_csv(file_name, index=False, sep="\t", float_format=float_format) elif file_ext == "csv": self.ridge_data.to_csv(file_name, index=False, sep=",", float_format=float_format) elif file_ext == "xlsx": self.ridge_data.to_excel(file_name, index=False, float_format=float_format) else: if self.DEBUG: print("Something went wrong during save out..") return if self.DEBUG: print("Saved!")
def set_wlet_pars(self): ''' Retrieves and checks the set wavelet parameters of the 'Analysis' input box reading the following QLineEdits: self.Tmin self.Tmax self.step_num self.pmax Further the checkboxes regarding detrending and amplitude normalization are evaluated. And self.get_L() self.get_T_c() are called if needed. These respective parameters are set to False if the opereation in question is not requested. Returns ------- wlet_pars : dictionary holding the retrieved parameters, L and T_c are set to None if no amplitude normalization or detrending operation should be done ''' wlet_pars = {} # period validator vali = self.periodV # -- read all the QLineEdits -- text = self.T_min.text() T_min = text.replace(",", ".") check, _, _ = vali.validate(T_min, 0) if self.debug: print("Min periodValidator output:", check, "value:", T_min) if check == 0: self.OutOfBounds = MessageWindow( "Wavelet periods out of bounds!", "Value Error" ) return False wlet_pars['T_min'] = float(T_min) step_num = self.step_num.text() check, _, _ = posintV.validate(step_num, 0) if self.debug: print("# Periods posintValidator:", check, "value:", step_num) if check == 0: self.OutOfBounds = MessageWindow( "The Number of periods must be a positive integer!", "Value Error" ) return False wlet_pars['step_num'] = int(step_num) if int(step_num) > 1000: choice = QMessageBox.question( self, "Too much periods?: ", "High number of periods: Do you want to continue?", QMessageBox.Yes | QMessageBox.No, ) if choice == QMessageBox.Yes: pass else: return False text = self.T_max.text() T_max = text.replace(",", ".") check, _, _ = vali.validate(T_max, 0) if self.debug: print("Max periodValidator output:", check) print(f"Max period value: {self.T_max.text()}") if check == 0 or check == 1: self.OutOfBounds = MessageWindow( "Wavelet highest period out of bounds!", "Value Error" ) return False wlet_pars['T_max'] = float(T_max) text = self.p_max.text() p_max = text.replace(",", ".") check, _, _ = posfloatV.validate(p_max, 0) # checks for positive float if check == 0: self.OutOfBounds = MessageWindow("Powers are positive!", "Value Error") return False # check for empty string: if p_max: wlet_pars['p_max'] = float(p_max) else: wlet_pars['p_max'] = None # -- the checkboxes -- # detrend for the analysis? if self.cb_use_detrended.isChecked(): T_c = self.get_T_c(self.T_c_edit) if T_c is None: return False # abort settings wlet_pars['T_c'] = T_c else: # indicates no detrending requested wlet_pars['T_c'] = False # amplitude normalization is downstram of detrending! if self.cb_use_envelope.isChecked(): L = self.get_L(self.L_edit) if L is None: return False # abort settings wlet_pars['L'] = L else: # indicates no ampl. normalization wlet_pars['L'] = False # success! return wlet_pars
def run_batch(self): ''' Retrieve all batch settings and loop over the signals present in the parentDV ''' dataset_name = self.parentDV.df.name if self.export_options.isChecked(): OutPath = self.get_OutPath() if OutPath is None: return # TODO: parallelize ridge_results, df_fouriers = self.do_the_loop() # check for empty ridge_results if not ridge_results: self.NoResults = MessageWindow( 'All ridges below threshold.. no results!', 'No results') return # --- compute the time-averaged powers --- if self.cb_power_dis.isChecked() or self.cb_sorted_powers.isChecked(): powers_series = em.average_power_distribution( ridge_results.values(), ridge_results.keys(), exclude_coi=True) if self.cb_power_dis.isChecked(): # plot the distribution self.pdw = PowerDistributionWindow(powers_series, dataset_name=dataset_name) # save out the sorted average powers if self.export_options.isChecked() and self.cb_sorted_powers.isChecked( ): fname = os.path.join(OutPath, f'{dataset_name}_ridge-powers.csv') powers_series.to_csv(fname, sep=',', index=True, header=False) # --- compute summary statistics over time --- if self.cb_plot_ens_dynamics.isChecked( ) or self.cb_save_ensemble_dynamics.isChecked(): # res is a tuple of DataFrames, one each for # periods, amplitude, power and phase res = em.get_ensemble_dynamics(ridge_results.values()) if self.cb_plot_ens_dynamics.isChecked(): self.edw = EnsembleDynamicsWindow( res, dt=self.parentDV.dt, time_unit=self.parentDV.time_unit, dataset_name=dataset_name) if self.export_options.isChecked( ) and self.cb_save_ensemble_dynamics.isChecked(): # create time axis, all DataFrames have same number of rows tvec = np.arange(res[0].shape[0]) * self.parentDV.dt for obs, df in zip(['periods', 'amplitudes', 'powers', 'phasesR'], res): fname = os.path.join(OutPath, f'{dataset_name}_{obs}.csv') df.index = tvec df.index.name = 'time' df.to_csv(fname, sep=',', float_format='%.3f') # --- Fourier Distribution Outputs --- if self.cb_plot_Fourier_dis.isChecked(): self.fdw = FourierDistributionWindow(df_fouriers, self.parentDV.time_unit, dataset_name) if self.export_options.isChecked( ) and self.cb_save_Fourier_dis.isChecked(): fname = os.path.join(OutPath, f'{dataset_name}_fourier-distribution.csv') # save out median and quartiles of Fourier powers df_fdis = pd.DataFrame(index=df_fouriers.index) df_fdis['Median'] = df_fouriers.median(axis=1) df_fdis['Q1'] = df_fouriers.quantile(q=0.25, axis=1) df_fdis['Q3'] = df_fouriers.quantile(q=0.75, axis=1) df_fdis.to_csv(fname, sep=',', float_format='%.3f') if self.debug: print(list(ridge_results.items())[:2])
def run_wavelet_ana(self): """ run the Wavelet Analysis """ if not np.any(self.raw_signal): self.NoSignalSelected = MessageWindow( "Please select a signal first!", "No Signal") return False succ = self.set_wlet_pars() # Error handling done there if not succ: if self.debug: print("Wavelet parameters could not be set!") return False # move to set_wlet_pars?! if self.step_num_value > 1000: choice = QMessageBox.question( self, "Too much periods?: ", "High number of periods: Do you want to continue?", QMessageBox.Yes | QMessageBox.No, ) if choice == QMessageBox.Yes: pass else: return # detrend for the analysis? if self.cb_use_detrended.isChecked() and not self.T_c: self.NoTrend = MessageWindow( "Detrending parameter not set,\n" + "specify a cut-off period!", "No Trend", ) return elif self.cb_use_detrended.isChecked(): trend = self.calc_trend() signal = self.raw_signal - trend else: signal = self.raw_signal # amplitude normalization is downstram of detrending! if self.cb_use_envelope.isChecked() and not self.L: self.NoTrend = MessageWindow( "Envelope parameter not set,\n" + "specify a sliding window size!", "No Envelope", ) return elif self.cb_use_envelope.isChecked(): signal = pyboat.normalize_with_envelope(signal, self.L) self.w_position += 20 self.anaWindows[self.w_position] = WaveletAnalyzer( signal=signal, dt=self.dt, T_min=self.T_min_value, T_max=self.T_max_value, position=self.w_position, signal_id="Synthetic Signal", step_num=self.step_num_value, p_max=self.p_max_value, time_unit=self.time_unit, DEBUG=self.debug, )
def doPlot(self): if self.raw_signal is None: self.NoSignal = MessageWindow("Please create a signal first!", "No Signal") if self.debug: print( "called Plotting [raw] [trend] [detrended] [envelope]", self.cb_raw.isChecked(), self.cb_trend.isChecked(), self.cb_detrend.isChecked(), self.cb_envelope.isChecked(), ) # check if trend is needed if self.T_c and (self.cb_trend.isChecked() or self.cb_detrend.isChecked()): if self.debug: print("Calculating trend with T_c = ", self.T_c) trend = self.calc_trend() else: trend = None # envelope calculation if self.L and self.cb_envelope.isChecked(): if self.debug: print("Calculating envelope with L = ", self.L) envelope = self.calc_envelope() else: envelope = None self.tsCanvas.fig1.clf() ax1 = pl.mk_signal_ax(self.time_unit, fig=self.tsCanvas.fig1) self.tsCanvas.fig1.add_axes(ax1) if self.debug: print( f"plotting signal and trend with {self.tvec[:10]}, {self.raw_signal[:10]}" ) if self.cb_raw.isChecked(): pl.draw_signal(ax1, time_vector=self.tvec, signal=self.raw_signal) if trend is not None and self.cb_trend.isChecked(): pl.draw_trend(ax1, time_vector=self.tvec, trend=trend) if trend is not None and self.cb_detrend.isChecked(): ax2 = pl.draw_detrended(ax1, time_vector=self.tvec, detrended=self.raw_signal - trend) ax2.legend(fontsize=pl.tick_label_size) if envelope is not None and not self.cb_detrend.isChecked(): pl.draw_envelope(ax1, time_vector=self.tvec, envelope=envelope) # plot on detrended axis if envelope is not None and self.cb_detrend.isChecked(): pl.draw_envelope(ax2, time_vector=self.tvec, envelope=envelope) ax2.legend(fontsize=pl.tick_label_size) self.tsCanvas.fig1.subplots_adjust(bottom=0.15, left=0.15, right=0.85) # add a simple legend ax1.legend(fontsize=pl.tick_label_size) self.tsCanvas.draw() self.tsCanvas.show()
def create_signal(self): """ Retrieves all paramters from the synthesizer controls and calls the ssg. All line edits have validators set, however we have to check for intermediate empty inputs.. """ # the signal components components = [] weights = [] # number of sample points if not self.Nt_edit.hasAcceptableInput(): self.OutOfBounds = MessageWindow( "Minimum number of sample points is 10!", "Value Error") return self.Nt = int(self.Nt_edit.text()) if self.debug: print("Nt changed to:", self.Nt) self.set_initial_periods(force=True) self.set_initial_T_c(force=True) T_edits = [self.T11_edit, self.T12_edit, self.T21_edit, self.T22_edit] # check for valid inputs for T_e in T_edits: if self.debug: print(f"Is enabled: {T_e.isEnabled()}") print(f"Checking T_edits: {T_e.text()}") print(f"Validator output: {T_e.hasAcceptableInput()}") if not T_e.isEnabled(): continue if not T_e.hasAcceptableInput(): self.OutOfBounds = MessageWindow( "All periods must be greater than 0!", "Value Error") return # envelope before chirp creation if self.env_box.isChecked(): if not self.tau_edit.hasAcceptableInput(): self.OutOfBounds = MessageWindow("Missing envelope parameter!", "Value Error") return tau = float(self.tau_edit.text()) / self.dt env = ssg.create_exp_envelope(tau, self.Nt) if self.debug: print(f"Creating the envelope with tau = {tau}") else: env = 1 # no envelope if self.chirp1_box.isChecked(): if not self.A1_edit.hasAcceptableInput(): self.OutOfBounds = MessageWindow( "Set an amplitude for oscillator1!", "Value Error") return # the periods T11 = float(self.T11_edit.text()) / self.dt T12 = float(self.T12_edit.text()) / self.dt A1 = float(self.A1_edit.text()) chirp1 = ssg.create_chirp(T11, T12, self.Nt) components.append(env * chirp1) weights.append(A1) if self.chirp2_box.isChecked(): if not self.A2_edit.hasAcceptableInput(): self.OutOfBounds = MessageWindow( "Set an amplitude for oscillator2!", "Value Error") return T21 = float(self.T21_edit.text()) / self.dt T22 = float(self.T22_edit.text()) / self.dt A2 = float(self.A2_edit.text()) chirp2 = ssg.create_chirp(T21, T22, self.Nt) components.append(env * chirp2) weights.append(A2) # noise if self.noise_box.isChecked(): # QDoubleValidator is a screw up.. alpha = float(self.alpha_edit.text()) if not 0 < alpha < 1: self.OutOfBounds = MessageWindow( "AR1 parameter must be smaller than 1!", "Value Error") return if not self.d_edit.hasAcceptableInput(): self.OutOfBounds = MessageWindow("Missing noise parameters!", "Value Error") return d = float(self.d_edit.text()) noise = ssg.ar1_sim(alpha, self.Nt) components.append(noise) weights.append(d) if len(components) == 0: self.OutOfBounds = MessageWindow( "Activate at least one signal component!", "No Signal") return signal = ssg.assemble_signal(components, weights) # ---------------------------------------- self.raw_signal = signal self.tvec = self.dt * np.arange(self.Nt) # --------------------------------------- if self.debug: print("created synth. signal:", self.raw_signal[:10]) # plot right away self.set_initial_periods() self.set_initial_T_c() self.doPlot()
def do_the_loop(self): ''' Uses the explicitly parsed self.wlet_pars to control signal analysis settings. Takes general analysis Parameters self.parentDV.dt self.parentDV.time_unit and the DataFrame self.parentDV.df from the parent DataViewer. Reads additional settings from this Batch Process Window. ''' EmptyRidge = 0 if self.export_options.isChecked(): OutPath = self.get_OutPath() if OutPath is None: return periods = np.linspace(self.wlet_pars['T_min'], self.wlet_pars['T_max'], self.wlet_pars['step_num']) # retrieve batch settings power_thresh = self.get_thresh() rsmooth = self.get_ridge_smooth() # results get stored here ridge_results = {} df_fouriers = pd.DataFrame(index=periods) df_fouriers.index.name = 'period' for i, signal_id in enumerate(self.parentDV.df): # log to terminal print(f"processing {signal_id}..") # sets parentDV.raw_signal and parentDV.tvec succ = self.parentDV.vector_prep(signal_id) # ui silently passes over.. if not succ: print(f"Can't process signal {signal_id}..") continue # detrend?! if self.parentDV.cb_use_detrended.isChecked(): trend = self.parentDV.calc_trend() signal = self.parentDV.raw_signal - trend else: signal = self.parentDV.raw_signal # amplitude normalization? if self.parentDV.cb_use_envelope.isChecked(): if self.debug: print('Calculating envelope with L=', self.wlet_pars['L']) signal = pyboat.normalize_with_envelope( signal, self.wlet_pars['L'], self.parentDV.dt) # compute the spectrum modulus, wlet = pyboat.compute_spectrum(signal, self.parentDV.dt, periods) # get maximum ridge ridge = pyboat.get_maxRidge_ys(modulus) # generate time vector tvec = np.arange(len(signal)) * self.parentDV.dt # evaluate along the ridge ridge_data = pyboat.eval_ridge(ridge, wlet, signal, periods, tvec, power_thresh, smoothing_wsize=rsmooth) # from ridge thresholding.. if ridge_data.empty: EmptyRidge += 1 else: ridge_results[signal_id] = ridge_data # time average the spectrum, all have shape len(periods)! averaged_Wspec = np.mean(modulus, axis=1) df_fouriers[signal_id] = averaged_Wspec # -- Save out individual results -- if self.cb_specs.isChecked(): # plot spectrum and ridge ax_sig, ax_spec = pl.mk_signal_modulus_ax( self.parentDV.time_unit) pl.plot_signal_modulus((ax_sig, ax_spec), tvec, signal, modulus, periods, p_max=self.wlet_pars['p_max']) pl.draw_Wavelet_ridge(ax_spec, ridge_data) plt.tight_layout() fname = os.path.join(OutPath, f'{signal_id}_wspec.png') if self.debug: print( f'Plotting and saving spectrum {signal_id} to {fname}') plt.savefig(fname, dpi=DPI) plt.close() if self.cb_specs_noridge.isChecked(): # plot spectrum without ridge ax_sig, ax_spec = pl.mk_signal_modulus_ax( self.parentDV.time_unit) pl.plot_signal_modulus((ax_sig, ax_spec), tvec, signal, modulus, periods, p_max=self.wlet_pars['p_max']) plt.tight_layout() fname = os.path.join(OutPath, f'{signal_id}_wspecNR.png') if self.debug: print( f'Plotting and saving spectrum {signal_id} to {fname}') plt.savefig(fname, dpi=DPI) plt.close() if self.cb_readout_plots.isChecked() and not ridge_data.empty: pl.plot_readout(ridge_data) fname = os.path.join(OutPath, f'{signal_id}_readout.png') if self.debug: print(f'Plotting and saving {signal_id} to {fname}') plt.savefig(fname, dpi=DPI) plt.close() if self.cb_readout.isChecked() and not ridge_data.empty: fname = os.path.join(OutPath, f'{signal_id}_readout.csv') if self.debug: print(f'Saving ridge reatout to {fname}') ridge_data.to_csv(fname, sep=',', float_format='%.3f', index=False) self.progress.setValue(i) if EmptyRidge > 0: self.NoRidges = MessageWindow( f'{EmptyRidge} ridge readouts entirely below threshold..', 'Discarded ridges') return ridge_results, df_fouriers
def import_file(self): """ Reads the values from the config grid and prepares kwargs for the pandas read_... functions NaN interpolation is done here if requested """ kwargs = {} if self.cb_header.isChecked(): header = None # pandas will assign numeric column names else: header = "infer" if not self.cb_use_ext.isChecked(): sep = self.sep_edit.text() # empty field if sep == "": sep = None kwargs["sep"] = sep if self.debug: print(f"Separator is {sep}, with type {type(sep)}") NaN_value = self.NaN_edit.text() # empty field if NaN_value == "": NaN_value = None if self.debug: print(f"NaN value is {NaN_value}, with type {type(NaN_value)}") # assemble key-words for pandas read_... functions kwargs["header"] = header kwargs["na_values"] = NaN_value if self.debug: print(f"kwargs for load_data: {kwargs}") # ----------------------------------------------------- df, err_msg = load_data(debug=self.debug, **kwargs) if err_msg: self.error = MessageWindow(err_msg, "Loading error") return # ----------------------------------------------------- if self.cb_NaN.isChecked(): N_NaNs = df.isna().to_numpy().sum() if N_NaNs > 0: msg = f"Found {N_NaNs} missing values in total\nlinearly interpolating through.." self.Interpolation = MessageWindow(msg, "NaN Interpolation") else: self.Interpolation = MessageWindow("No missing values found!", "NaN Interpolation") name = df.name df = interpol_NaNs(df) df.name = name # restore name # initialize new DataViewer with the loaded data # doesn't help as ini window gets destroyed.. self.parent.DataViewers[self.parent.nViewers] = DataViewer( data=df, debug=self.debug) self.parent.nViewers += 1 self.close()
def save_out(self): dialog = QFileDialog() options = QFileDialog.Options() #---------------------------------------------------------- default_name = os.getenv('HOME') + '/TFAres_' + str(self.signal_id) format_filter = "Text File (*.txt);; CSV ( *.csv);; Excel (*.xlsx)" #----------------------------------------------------------- file_name, sel_filter = dialog.getSaveFileName(self, "Save as", default_name, format_filter, '(*.txt)', options=options) # dialog cancelled if not file_name: return file_ext = file_name.split('.')[-1] if self.DEBUG: print('selected filter:', sel_filter) print('out-path:', file_name) print('extracted extension:', file_ext) print('ridge data keys:', self.ridge_data.keys()) if file_ext not in ['txt', 'csv', 'xlsx']: self.e = MessageWindow( "Ouput format not supported..\n" + "Please append .txt, .csv or .xlsx\n" + "to the file name!", "Unknown format") return # the write out calls float_format = '%.2f' # still old style :/ if file_ext == 'txt': self.ridge_data.to_csv(file_name, index=False, sep='\t', float_format=float_format) elif file_ext == 'csv': self.ridge_data.to_csv(file_name, index=False, sep=',', float_format=float_format) elif file_ext == 'xlsx': self.ridge_data.to_excel(file_name, index=False, float_format=float_format) else: if self.DEBUG: print("Something went wrong during save out..") return if self.DEBUG: print('Saved!')