diff --git a/orangecontrib/spectroscopy/preprocess/__init__.py b/orangecontrib/spectroscopy/preprocess/__init__.py index cf97ef11e..fd462d10c 100644 --- a/orangecontrib/spectroscopy/preprocess/__init__.py +++ b/orangecontrib/spectroscopy/preprocess/__init__.py @@ -730,28 +730,40 @@ def __call__(self, data): return data.transform(domain) -class CurveShiftFeature(SelectColumn): +class ShiftAndScaleFeature(SelectColumn): InheritEq = True -class _CurveShiftCommon(CommonDomain): +class _ShiftAndScaleCommon(CommonDomain): - def __init__(self, amount, domain): + def __init__(self, offset, scale, domain): super().__init__(domain) - self.amount = amount + self.offset = offset + self.scale = scale + def transformed(self, data): - return data.X + self.amount + return self.scale * data.X + self.offset -class CurveShift(Preprocess): +class ShiftAndScale(Preprocess): + """ + Shift and scale of the data into the form: + y = scale * x + offset - def __init__(self, amount=0.): - self.amount = amount + Parameters + ---------- + offset : float + scale : float + """ + + def __init__(self, offset=0., scale=1.): + self.offset = offset + self.scale = scale def __call__(self, data): - common = _CurveShiftCommon(self.amount, data.domain) - atts = [a.copy(compute_value=CurveShiftFeature(i, common)) + common = _ShiftAndScaleCommon(self.offset, self.scale, data.domain) + atts = [a.copy(compute_value=ShiftAndScaleFeature(i, common)) for i, a in enumerate(data.domain.attributes)] domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) diff --git a/orangecontrib/spectroscopy/tests/test_preprocess.py b/orangecontrib/spectroscopy/tests/test_preprocess.py index 07c00ac2e..98e13466e 100644 --- a/orangecontrib/spectroscopy/tests/test_preprocess.py +++ b/orangecontrib/spectroscopy/tests/test_preprocess.py @@ -11,7 +11,7 @@ from orangecontrib.spectroscopy.preprocess import Absorbance, Transmittance, \ Integrate, Interpolate, Cut, SavitzkyGolayFiltering, \ GaussianSmoothing, PCADenoising, RubberbandBaseline, \ - Normalize, LinearBaseline, CurveShift, EMSC, MissingReferenceException, \ + Normalize, LinearBaseline, ShiftAndScale, EMSC, MissingReferenceException, \ WrongReferenceException, NormalizeReference, XASnormalization, ExtractEXAFS, \ PreprocessException, NormalizePhaseReference, Despike, SpSubtract from orangecontrib.spectroscopy.preprocess.als import ALSP, ARPLS, AIRPLS @@ -58,7 +58,7 @@ def preprocessor_data(preproc): Normalize(method=Normalize.Vector), Normalize(method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=10000), Normalize(method=Normalize.MinMax), - CurveShift(1), + ShiftAndScale(1, 2), Despike(threshold=5, cutoff=60, dis=5), ALSP(lam=100E+6, itermax=5, p=0.5), ARPLS(lam=100E+5, itermax=5, ratio=0.5), @@ -588,14 +588,14 @@ def test_iris(self): [4.75015528, 3.15366444, 1.46254138, 0.23693223]]) -class TestCurveShift(unittest.TestCase): +class TestShiftAndScale(unittest.TestCase): def test_simple(self): data = Table.from_numpy(None, [[1.0, 2.0, 3.0, 4.0]]) - f = CurveShift(amount=1.1) + f = ShiftAndScale(offset=1.1, scale=2.) fdata = f(data) np.testing.assert_almost_equal(fdata.X, - [[2.1, 3.1, 4.1, 5.1]]) + [[3.1, 5.1, 7.1, 9.1]]) class TestUtils(unittest.TestCase): diff --git a/orangecontrib/spectroscopy/widgets/owpreprocess.py b/orangecontrib/spectroscopy/widgets/owpreprocess.py index ec5a72deb..bd198b7b3 100644 --- a/orangecontrib/spectroscopy/widgets/owpreprocess.py +++ b/orangecontrib/spectroscopy/widgets/owpreprocess.py @@ -14,7 +14,7 @@ from Orange.widgets.data.utils.preprocess import SequenceFlow, Controller, \ StandardItemModel from Orange.widgets.data.owpreprocess import ( - PreprocessAction, Description, icon_path, DescriptionRole, ParametersRole, blocked + PreprocessAction, Description, icon_path, DescriptionRole, ParametersRole ) from Orange.widgets.utils.sql import check_sql_input from Orange.widgets.utils.overlay import OverlayWidget @@ -961,6 +961,10 @@ def migrate_preprocessor(cls, preprocessor, version): if name == "orangecontrib.infrared.cutinverse": name = "orangecontrib.spectroscopy.cut" settings["inverse"] = True + if name == "orangecontrib.infrared.curveshift": + name = "orangecontrib.spectroscopy.shiftandscale" + settings["offset"] = settings.get("amount", 0.) + settings["scale"] = 1. return [((name, settings), version)] @classmethod diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py index 96b1e80ef..f19e8fdd8 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py @@ -16,7 +16,7 @@ from orangecontrib.spectroscopy.preprocess import ( PCADenoising, GaussianSmoothing, Cut, SavitzkyGolayFiltering, Absorbance, Transmittance, - CurveShift, SpSubtract + ShiftAndScale, SpSubtract ) from orangecontrib.spectroscopy.preprocess.transform import SpecTypes from orangecontrib.spectroscopy.widgets.gui import lineEditFloatRange, MovableVline, \ @@ -295,34 +295,40 @@ def createinstance(cls, params): return SavitzkyGolayFiltering(window=window, polyorder=polyorder, deriv=deriv) -class CurveShiftEditor(BaseEditorOrange): +class ShiftAndScaleEditor(BaseEditorOrange): """ - Editor for CurveShift + Editor for ShiftAndScale """ # TODO: the layout changes when I click the area of the preprocessor # EFFECT: the sidebar snaps in - name = "Shift Spectra" - qualname = "orangecontrib.infrared.curveshift" + name = "Shift and scale" + qualname = "orangecontrib.spectroscopy.shiftandscale" + replaces = ["orangecontrib.infrared.curveshift"] def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) - self.amount = 0. + self.offset = 0. + self.scale = 1. form = QFormLayout() - amounte = lineEditFloatRange(self, self, "amount", callback=self.edited.emit) - form.addRow("Shift Amount", amounte) + offset_input = lineEditFloatRange(self, self, "offset", callback=self.edited.emit) + form.addRow("Vertical offset", offset_input) + scale_input = lineEditFloatRange(self, self, "scale", callback=self.edited.emit) + form.addRow("Vertical scaling", scale_input) self.controlArea.setLayout(form) def setParameters(self, params): - self.amount = params.get("amount", 0.) + self.amount = params.get("offset", 0.) + self.scale = params.get("scale", 1.) @staticmethod def createinstance(params): params = dict(params) - amount = float(params.get("amount", 0.)) - return CurveShift(amount=amount) + offset = float(params.get("offset", 0.)) + scale = float(params.get("scale", 1.)) + return ShiftAndScale(offset=offset, scale=scale) class PCADenoisingEditor(BaseEditor): @@ -462,5 +468,5 @@ def update_reference_info(self): preprocess_editors.register(SavitzkyGolayFilteringEditor, 100) preprocess_editors.register(PCADenoisingEditor, 200) preprocess_editors.register(SpectralTransformEditor, 225) -preprocess_editors.register(CurveShiftEditor, 250) +preprocess_editors.register(ShiftAndScaleEditor, 250) preprocess_editors.register(SpSubtractEditor, 275)