Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend shift spectra #718

Merged
merged 7 commits into from
Jul 24, 2024
32 changes: 22 additions & 10 deletions orangecontrib/spectroscopy/preprocess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions orangecontrib/spectroscopy/tests/test_preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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):
Expand Down
6 changes: 5 additions & 1 deletion orangecontrib/spectroscopy/widgets/owpreprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
30 changes: 18 additions & 12 deletions orangecontrib/spectroscopy/widgets/preprocessors/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, \
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Loading