From 0fe6f7153d73864f4d646f88650dff7189542b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 3 May 2024 08:59:05 -0300 Subject: [PATCH 1/7] extend CurveShift preprocessor to accept a scale factor and rename it into LinearTransform --- .../spectroscopy/preprocess/__init__.py | 28 +++++++++++++------ .../widgets/preprocessors/misc.py | 20 ++++++++----- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/orangecontrib/spectroscopy/preprocess/__init__.py b/orangecontrib/spectroscopy/preprocess/__init__.py index cf97ef11e..47346d3a8 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 LinearTransformFeature(SelectColumn): InheritEq = True -class _CurveShiftCommon(CommonDomain): +class _LinearTransformCommon(CommonDomain): - def __init__(self, amount, domain): + def __init__(self, amount, scale, domain): super().__init__(domain) self.amount = amount + self.scale = scale + def transformed(self, data): - return data.X + self.amount + return self.scale * data.X + self.amount + +class LinearTransform(Preprocess): + """ + Linear transformation of the data into the form: + y = scale * x + amount -class CurveShift(Preprocess): + Parameters + ---------- + amount : float + scale : float + """ - def __init__(self, amount=0.): + def __init__(self, amount=0., scale=1.): self.amount = amount + self.scale = scale def __call__(self, data): - common = _CurveShiftCommon(self.amount, data.domain) - atts = [a.copy(compute_value=CurveShiftFeature(i, common)) + common = _LinearTransformCommon(self.amount, self.scale, data.domain) + atts = [a.copy(compute_value=LinearTransformFeature(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/widgets/preprocessors/misc.py b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py index 96b1e80ef..1284e1446 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 + LinearTransform, 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 LinearTransformEditor(BaseEditorOrange): """ - Editor for CurveShift + Editor for LinearTransform """ # 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 = "Linear Transformation" + qualname = "orangecontrib.infrared.lineartransform" + replaces = ["orangecontrib.infrared.curveshift"] def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) self.amount = 0. + self.scale = 1. form = QFormLayout() amounte = lineEditFloatRange(self, self, "amount", callback=self.edited.emit) form.addRow("Shift Amount", amounte) + scale_input = lineEditFloatRange(self, self, "scale", callback=self.edited.emit) + form.addRow("Scale Factor", scale_input) self.controlArea.setLayout(form) def setParameters(self, params): self.amount = params.get("amount", 0.) + self.scale = params.get("scale", 1.) @staticmethod def createinstance(params): params = dict(params) amount = float(params.get("amount", 0.)) - return CurveShift(amount=amount) + scale = float(params.get("scale", 1.)) + return LinearTransform(amount=amount, 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(LinearTransformEditor, 250) preprocess_editors.register(SpSubtractEditor, 275) From e5ba0a938361691acac9249ae76290d96edb32b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 3 May 2024 09:00:13 -0300 Subject: [PATCH 2/7] refactor CurveShift/LinearTransform preprocessor tests to include the scale factor --- orangecontrib/spectroscopy/tests/test_preprocess.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/orangecontrib/spectroscopy/tests/test_preprocess.py b/orangecontrib/spectroscopy/tests/test_preprocess.py index 07c00ac2e..c16295f55 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, LinearTransform, 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), + LinearTransform(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 TestLinearTransform(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 = LinearTransform(amount=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): From fa6544de0eea7ffa451283ec386dda6a8202095b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 3 May 2024 12:56:06 -0300 Subject: [PATCH 3/7] dropping legacy infrared namespace for LinearTransform preprocessor --- orangecontrib/spectroscopy/widgets/preprocessors/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py index 1284e1446..b0c442cd2 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py @@ -303,7 +303,7 @@ class LinearTransformEditor(BaseEditorOrange): # EFFECT: the sidebar snaps in name = "Linear Transformation" - qualname = "orangecontrib.infrared.lineartransform" + qualname = "orangecontrib.spectroscopy.lineartransform" replaces = ["orangecontrib.infrared.curveshift"] def __init__(self, parent=None, **kwargs): From 09218885927cc4abfa66fa832c97b994764df965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 3 May 2024 13:05:13 -0300 Subject: [PATCH 4/7] migrate CurveShift preprocessor into LInearTransform --- orangecontrib/spectroscopy/widgets/owpreprocess.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/orangecontrib/spectroscopy/widgets/owpreprocess.py b/orangecontrib/spectroscopy/widgets/owpreprocess.py index ec5a72deb..58a8569f9 100644 --- a/orangecontrib/spectroscopy/widgets/owpreprocess.py +++ b/orangecontrib/spectroscopy/widgets/owpreprocess.py @@ -961,6 +961,9 @@ 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.lineartransform" + settings["scale"] = 1. return [((name, settings), version)] @classmethod From a870dbb79cb77d882aae775d4b7eb7a7170340a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 12 Jul 2024 14:25:20 -0300 Subject: [PATCH 5/7] refactor LinearTransformation preprocessor into Shift and scale --- .../spectroscopy/preprocess/__init__.py | 26 +++++++++---------- .../spectroscopy/widgets/owpreprocess.py | 5 ++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/orangecontrib/spectroscopy/preprocess/__init__.py b/orangecontrib/spectroscopy/preprocess/__init__.py index 47346d3a8..fd462d10c 100644 --- a/orangecontrib/spectroscopy/preprocess/__init__.py +++ b/orangecontrib/spectroscopy/preprocess/__init__.py @@ -730,40 +730,40 @@ def __call__(self, data): return data.transform(domain) -class LinearTransformFeature(SelectColumn): +class ShiftAndScaleFeature(SelectColumn): InheritEq = True -class _LinearTransformCommon(CommonDomain): +class _ShiftAndScaleCommon(CommonDomain): - def __init__(self, amount, scale, domain): + def __init__(self, offset, scale, domain): super().__init__(domain) - self.amount = amount + self.offset = offset self.scale = scale def transformed(self, data): - return self.scale * data.X + self.amount + return self.scale * data.X + self.offset -class LinearTransform(Preprocess): +class ShiftAndScale(Preprocess): """ - Linear transformation of the data into the form: - y = scale * x + amount + Shift and scale of the data into the form: + y = scale * x + offset Parameters ---------- - amount : float + offset : float scale : float """ - def __init__(self, amount=0., scale=1.): - self.amount = amount + def __init__(self, offset=0., scale=1.): + self.offset = offset self.scale = scale def __call__(self, data): - common = _LinearTransformCommon(self.amount, self.scale, data.domain) - atts = [a.copy(compute_value=LinearTransformFeature(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/widgets/owpreprocess.py b/orangecontrib/spectroscopy/widgets/owpreprocess.py index 58a8569f9..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 @@ -962,7 +962,8 @@ def migrate_preprocessor(cls, preprocessor, version): name = "orangecontrib.spectroscopy.cut" settings["inverse"] = True if name == "orangecontrib.infrared.curveshift": - name = "orangecontrib.spectroscopy.lineartransform" + name = "orangecontrib.spectroscopy.shiftandscale" + settings["offset"] = settings.get("amount", 0.) settings["scale"] = 1. return [((name, settings), version)] From 5a3f3d636aab32b7033863fd606f2868bf994ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 12 Jul 2024 14:25:53 -0300 Subject: [PATCH 6/7] refactor LinearTransformation editor into Shift and scale --- .../widgets/preprocessors/misc.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py index b0c442cd2..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, - LinearTransform, SpSubtract + ShiftAndScale, SpSubtract ) from orangecontrib.spectroscopy.preprocess.transform import SpecTypes from orangecontrib.spectroscopy.widgets.gui import lineEditFloatRange, MovableVline, \ @@ -295,40 +295,40 @@ def createinstance(cls, params): return SavitzkyGolayFiltering(window=window, polyorder=polyorder, deriv=deriv) -class LinearTransformEditor(BaseEditorOrange): +class ShiftAndScaleEditor(BaseEditorOrange): """ - Editor for LinearTransform + Editor for ShiftAndScale """ # TODO: the layout changes when I click the area of the preprocessor # EFFECT: the sidebar snaps in - name = "Linear Transformation" - qualname = "orangecontrib.spectroscopy.lineartransform" + 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("Scale Factor", scale_input) + 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.)) + offset = float(params.get("offset", 0.)) scale = float(params.get("scale", 1.)) - return LinearTransform(amount=amount, scale=scale) + return ShiftAndScale(offset=offset, scale=scale) class PCADenoisingEditor(BaseEditor): @@ -468,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(LinearTransformEditor, 250) +preprocess_editors.register(ShiftAndScaleEditor, 250) preprocess_editors.register(SpSubtractEditor, 275) From b1929715a6e2edd3205bc6b701e55f361a7ac75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Lu=C3=ADs?= Date: Fri, 12 Jul 2024 14:26:36 -0300 Subject: [PATCH 7/7] refactor LinearTransformation test into Shift and scale --- orangecontrib/spectroscopy/tests/test_preprocess.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/orangecontrib/spectroscopy/tests/test_preprocess.py b/orangecontrib/spectroscopy/tests/test_preprocess.py index c16295f55..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, LinearTransform, 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), - LinearTransform(1, 2), + 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,11 +588,11 @@ def test_iris(self): [4.75015528, 3.15366444, 1.46254138, 0.23693223]]) -class TestLinearTransform(unittest.TestCase): +class TestShiftAndScale(unittest.TestCase): def test_simple(self): data = Table.from_numpy(None, [[1.0, 2.0, 3.0, 4.0]]) - f = LinearTransform(amount=1.1, scale=2.) + f = ShiftAndScale(offset=1.1, scale=2.) fdata = f(data) np.testing.assert_almost_equal(fdata.X, [[3.1, 5.1, 7.1, 9.1]])