Source code for crikit.ui.widget_ALS

"""
Widget for PlotEffect that adjusts the parameters appropriate for
asymmetric least squares (ALS)

Created on Thu Dec 22 01:16:01 2016

@author: chc
"""
import numpy as _np

from PyQt5.QtWidgets import QTableWidgetItem as _QTableWidgetItem

from crikit.ui.dialog_AbstractPlotEffect import (AbstractPlotEffectPlugin
                                                 as _AbstractPlotEffectPlugin)

from crikit.ui.qt_PlotEffect_ALS3 import Ui_Form as _Ui_Form

from crikit.ui.widget_scientificspin import (ScientificDoubleSpinBox as
                                             _SciSpin)

from crikit.preprocess.algorithms.als import AlsCvxopt as _Als
from crikit.utils.general import find_nearest as _find_nearest

[docs]class widgetALS(_AbstractPlotEffectPlugin): """ Widget for PlotEffect that adjusts the parameters appropriate for asymmetric least squares (ALS) Parameters ---------- wavenumber_increasing : bool, optional (default, True) If wavenumber is increasing left-to-right. Opposite value as the KK conjugate value smoothness_param : float, optional (default, 1e3) Smoothness parameter asym_param : float, optional (default, 1e-4) Assymetry parameter redux : int, optional (default, 1) Reduction parameter to sub-sample input signal order : int, optional (default, 2) Derivative regularization term. Order=2 for Whittaker-smoother fix_end_points : bool, optional (default, False) Weight the baseline endpoints to approach equally the end-points of the data. max_iter : int, optional (default, 100) Maximum number of least-squares iterations to perform min_diff : float, optional (default, 1e-5) Break iterative calculations if difference is less than min_diff parent: QObject Parent Methods ------- fcn : Perform ALS detrending Signals: changed : a value in the UI has changed """ # Parameter dict that will be returned from PlotEffect # Will be updated later in program to contain all parameters # to pass to underlying algorithm parameters = {'name' : 'ALS', 'long_name' : 'Asymmetric least squares'} # Labeling options for original data plot labels_orig = { 'x_label' : 'Wavenumber (cm$^{-1}$)', 'y_label' : 'Input Int (au)', 'title' : 'Original' } # Labeling options for affected data plot labels_affected = { 'x_label' : labels_orig['x_label'], 'y_label' : 'Output Int (au)', 'title' : 'Detrended' } def __init__(self, x=None, wavenumber_increasing=True, rng=None, smoothness_param=1, asym_param=1e-3, redux=10, order=2, fix_end_points=True, fix_const=1, fix_rng=None, max_iter=100, min_diff=1e-6, verbose=False, sub_asym_list=None, sub_w_list=None, parent = None): super(widgetALS, self).__init__(parent) ### EDIT ### self._x = x self.rng = rng self.ui = _Ui_Form() self.ui.setupUi(self) self.sub_asym_list = sub_asym_list self.sub_w_list = sub_w_list # ! Change this into its own function after # ! the asym table widget is all setup if not self.sub_asym_list: self.parameters['asym_param'] = asym_param else: self.parameters['asym_param'] = 0*self.x + asym_param # Update parameter dict self.parameters['smoothness_param'] = smoothness_param self.parameters['rng'] = rng self.parameters['redux'] = redux self.parameters['order'] = order self.parameters['fix_end_points'] = fix_end_points self.parameters['fix_rng'] = fix_rng self.parameters['fix_const'] = fix_const self.parameters['max_iter'] = max_iter self.parameters['min_diff'] = min_diff self.parameters['verbose'] = verbose self.parameters['wavenumber_increasing'] = wavenumber_increasing self.setup_asym() # Setup controls for asymmetry parameter self.setup_smoothness() # Setup controls for smoothness parameter # Redux factor self.ui.spinBoxRedux.setValue(self.parameters['redux']) # Fixed ends self.ui.checkBox.setChecked(self.parameters['fix_end_points']) # Max iterations self.ui.spinBoxMaxIter.setValue(self.parameters['max_iter']) # Min Difference self.ui.spinBoxMinDiff = _SciSpin() self.ui.verticalLayoutMiNMaxIterDiff.insertWidget(3, self.ui.spinBoxMinDiff) self.ui.spinBoxMinDiff.setValue(self.parameters['min_diff']) self.ui.checkBoxWNIncreasing.setChecked(self.parameters['wavenumber_increasing']) # SIGNALS & SLOTS self.ui.spinBoxP.editingFinished.connect(self.spinBoxChanged) self.ui.spinBoxLambda.editingFinished.connect(self.spinBoxChanged) self.ui.spinBoxRedux.editingFinished.connect(self.spinBoxChanged) self.ui.spinBoxMaxIter.editingFinished.connect(self.spinBoxChanged) self.ui.spinBoxMinDiff.editingFinished.connect(self.spinBoxChanged) self.ui.checkBox.clicked.connect(self.selectFixedEnds) self.ui.spinBoxAsymSubSections.valueChanged.connect(self.asym_sub_val_change) self.ui.spinBoxWSubSections.valueChanged.connect(self.weight_sub_val_change) self.ui.checkBoxWNIncreasing.clicked.connect(self.selectWNIncrease) self.ui.spinBoxWeight = _SciSpin() self.ui.spinBoxWeight.setMinimum(0) self.ui.spinBoxWeight.setMaximum(1e10) self.ui.spinBoxWeight.setValue(1) self.ui.spinBoxWeight.editingFinished.connect(self.weightspinboxchanged) self.ui.horizontalLayout_4.insertWidget(1, self.ui.spinBoxWeight) @property def x(self): if self.rng is None: return self._x else: return self._x[self.rng]
[docs] def weight_sub_val_change(self): """ Weights Subsections spinbox has changed value """ sbval = self.ui.spinBoxWSubSections.value() n_rows = self.ui.tableWidgetWeights.rowCount() n_cols = self.ui.tableWidgetWeights.columnCount() max_x = _np.max(self.x) min_x = _np.min(self.x) if sbval > n_rows: for nr in _np.arange(n_rows, sbval): self.ui.tableWidgetWeights.setRowCount(sbval) n_rows = self.ui.tableWidgetWeights.rowCount() # Start X scispin = _SciSpin() scispin.setMinimum(self._x.min()) scispin.setMaximum(self._x.max()) scispin.setValue(min_x) scispin.editingFinished.connect(self.weightspinboxchanged) self.ui.tableWidgetWeights.setCellWidget(nr, 0, scispin) # Stop X scispin = _SciSpin() scispin.setMinimum(self._x.min()) scispin.setMaximum(self._x.max()) scispin.setValue(max_x) scispin.editingFinished.connect(self.weightspinboxchanged) self.ui.tableWidgetWeights.setCellWidget(nr, 1, scispin) # Weight Parameter # scispin = _SciSpin() # scispin.setMinimum(0) # scispin.setMaximum(1e4) # scispin.setValue(1) # scispin.editingFinished.connect(self.weightspinboxchanged) # self.ui.tableWidgetWeights.setCellWidget(nr, 2, scispin) elif sbval < n_rows: for nr in _np.arange(sbval, n_rows): for nc in range(n_cols): sw = self.ui.tableWidgetWeights.cellWidget(nr, nc) sw.editingFinished.disconnect() self.ui.tableWidgetWeights.setRowCount(sbval) n_rows = self.ui.tableWidgetWeights.rowCount() self.weightspinboxchanged()
[docs] def asym_sub_val_change(self): """ P Subsections spinbox has changed value """ sbval = self.ui.spinBoxAsymSubSections.value() n_rows = self.ui.tableWidgetAsym.rowCount() n_cols = self.ui.tableWidgetAsym.columnCount() max_x = _np.max(self.x) min_x = _np.min(self.x) if sbval > n_rows: for nr in _np.arange(n_rows, sbval): self.ui.tableWidgetAsym.setRowCount(sbval) n_rows = self.ui.tableWidgetAsym.rowCount() # Start X scispin = _SciSpin() scispin.setMinimum(self._x.min()) scispin.setMaximum(self._x.max()) scispin.setValue(min_x) scispin.editingFinished.connect(self.asymspinboxchanged) self.ui.tableWidgetAsym.setCellWidget(nr, 0, scispin) # Stop X scispin = _SciSpin() scispin.setMinimum(self._x.min()) scispin.setMaximum(self._x.max()) scispin.setValue(max_x) scispin.editingFinished.connect(self.asymspinboxchanged) self.ui.tableWidgetAsym.setCellWidget(nr, 1, scispin) # Asym Parameter scispin = _SciSpin() scispin.setMinimum(0) scispin.setMaximum(1e20) scispin.setValue(self.ui.spinBoxP.value()) scispin.editingFinished.connect(self.asymspinboxchanged) self.ui.tableWidgetAsym.setCellWidget(nr, 2, scispin) elif sbval < n_rows: for nr in _np.arange(sbval, n_rows): for nc in range(n_cols): sw = self.ui.tableWidgetAsym.cellWidget(nr, nc) sw.editingFinished.disconnect() self.ui.tableWidgetAsym.setRowCount(sbval) n_rows = self.ui.tableWidgetAsym.rowCount() self.asymspinboxchanged()
[docs] def fcn(self, data_in): """ If return list, [0] goes to original, [1] goes to affected """ data_out = _np.zeros(data_in.shape) baseline = _np.zeros(data_in.shape) # if callable(self.parameters['asym_param']): # self.parameters['asym_param'] = \ # self.parameters['asym_param'](data_in.shape[-1]) # smoothness_param = self.parameters['smoothness_param'] # asym_param = self.parameters['asym_param'] # redux = self.parameters['redux'] # fep = self.parameters['fix_end_points'] # max_iter = self.parameters['max_iter'] # min_diff = self.parameters['min_diff'] # order = self.parameters['order'] # rng = self.parameters['rng'] # fix_rng = self.parameters['fix_rng'] # smoothness_param=1e3, asym_param=1e-4, redux=1, # order=2, rng=None, fix_end_points=False, fix_rng=None, # fix_const=1, max_iter=100, min_diff=1e-5, verbose=False _als = _Als(**self.parameters) if data_in.ndim == 1: baseline = _als.calculate(data_in) data_out = data_in - baseline else: for num, spectrum in enumerate(data_in): baseline[num,:] = _als.calculate(spectrum) data_out[num,:] = spectrum - baseline[num,:] return [baseline, data_out]
[docs] def setup_smoothness(self): """ Lambda/smoothness parameter rlated """ self.ui.label_2.setText('{} (Smoothness)'.format(u'\u03BB')) self.ui.spinBoxLambda = _SciSpin() self.ui.verticalLayout_2.insertWidget(1, self.ui.spinBoxLambda) self.ui.spinBoxLambda.setValue(self.parameters['smoothness_param'])
[docs] def setup_asym(self): """ P/asymmetry parameter related """ self.ui.spinBoxP = _SciSpin() self.ui.verticalLayout.insertWidget(1, self.ui.spinBoxP) self.ui.spinBoxP.setValue(self.parameters['asym_param'])
[docs] def asymspinboxchanged(self): """ Asymetry parameter-related values have changed """ n_rows = self.ui.tableWidgetAsym.rowCount() # * Currently, asym_param in ALS is in the full vector space x = self._x self.sub_asym_list = [] if n_rows == 0: self.parameters['asym_param'] = self.ui.spinBoxP.value() else: self.parameters['asym_param'] = self.ui.spinBoxP.value() + 0*x for rc in range(n_rows): xstart_pix = _find_nearest(x, self.ui.tableWidgetAsym.cellWidget(rc, 0).value())[1] xstop_pix = _find_nearest(x, self.ui.tableWidgetAsym.cellWidget(rc, 1).value())[1] asym = self.ui.tableWidgetAsym.cellWidget(rc, 2).value() self.parameters['asym_param'][_np.minimum(xstart_pix, xstop_pix):_np.maximum(xstart_pix, xstop_pix)+1] = 1*asym print('XStart: {}, XStop: {}, ASym: {}'.format(xstart_pix, xstop_pix, asym)) self.sub_asym_list.append([xstart_pix, xstop_pix, asym]) print('---------') self.changed.emit()
[docs] def weightspinboxchanged(self): """ Weight parameter-related values have changed """ n_rows = self.ui.tableWidgetWeights.rowCount() # * Currently, fix_rng (weight parameters) in ALS is in the rng vector space x = self.x self.sub_w_list = [] if n_rows == 0: self.parameters['fix_rng'] = None else: self.parameters['fix_rng'] = [] for rc in range(n_rows): xstart_pix = _find_nearest(x, self.ui.tableWidgetWeights.cellWidget(rc, 0).value())[1] xstop_pix = _find_nearest(x, self.ui.tableWidgetWeights.cellWidget(rc, 1).value())[1] weight = self.ui.spinBoxWeight.value() # self.parameters['fix_rng'].extend(_np.arange(xstart_pix, xstop_pix+1).tolist()) self.parameters['fix_rng'].extend(_np.arange(_np.minimum(xstart_pix, xstop_pix),_np.maximum(xstart_pix, xstop_pix)).tolist()) # No +1 # self.parameters['asym_param'][xstart_pix:xstop_pix+1] = 1*asym # print('XStart: {}, XStop: {}, ASym: {}'.format(xstart_pix, xstop_pix, asym)) self.sub_w_list.append([xstart_pix, xstop_pix, weight]) self.parameters['fix_rng'] = _np.array(self.parameters['fix_rng']) self.parameters['fix_const'] = self.ui.spinBoxWeight.value() print('---------') self.changed.emit()
[docs] def spinBoxChanged(self): """ Controller for all spinBoxes """ sdr = self.sender() if sdr == self.ui.spinBoxLambda: self.parameters['smoothness_param'] = self.ui.spinBoxLambda.value() elif sdr == self.ui.spinBoxP: self.asymspinboxchanged() elif sdr == self.ui.spinBoxRedux: self.parameters['redux'] = self.ui.spinBoxRedux.value() elif sdr == self.ui.spinBoxMaxIter: self.parameters['max_iter'] = self.ui.spinBoxMaxIter.value() elif sdr == self.ui.spinBoxMinDiff: self.parameters['min_diff'] = self.ui.spinBoxMinDiff.value() self.changed.emit()
[docs] def selectFixedEnds(self): """ Check selection of fixed end-points """ self.parameters['fix_end_points'] =self.ui.checkBox.isChecked() self.changed.emit()
[docs] def selectWNIncrease(self): """ Check selection of conjugate """ self.parameters['wavenumber_increasing'] = self.ui.checkBoxWNIncreasing.isChecked() self.changed.emit()
if __name__ == '__main__': import sys as _sys from PyQt5.QtWidgets import (QApplication as _QApplication) app = _QApplication(_sys.argv) app.setStyle('Cleanlooks') x = _np.linspace(500, 4000, 1001) winALS = widgetALS(x=x) winALS.show() app.exec_() print(winALS.parameters) _sys.exit()