Skip to content

Commit 8f9452d

Browse files
authored
Merge pull request #802 from stuart-cls/peakfit-subset
[ENH] OWPeakfit: Show preview fits for provided subset
2 parents 337b058 + a1b07b3 commit 8f9452d

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

doc/widgets/peakfit.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Fit data to a composite peak model.
66
**Inputs**
77

88
- Data: Input data set
9+
- Data Subset: Subset of the data
910

1011
**Outputs**
1112

@@ -23,8 +24,9 @@ resulting total fit.
2324
1. Add a [model](#models-and-parameters) component from the dropdown menu.
2425
2. Input model initial parameters and [constraints](#constraints).
2526
3. Visualize the initial peak and peak color.
26-
4. Select subsample of data for preview fit calculation.
27-
5. Preview plot of fit results for subsample. Center line of selected model is visualized along with
27+
4. Set subsample size of data for preview fit calculation.<br>
28+
If subset input is present, subsample will be selected from the resulting subset of the input data.
29+
5. Preview plot of fit results for subsample.<br> Center line of selected model is visualized along with
2830
the fit results for the selected curve:
2931
- Black dash: selected curve
3032
- Red line: total fit

orangecontrib/spectroscopy/tests/test_owpeakfit.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ def test_bug_iris_crash(self):
134134
self.widget.commit.now()
135135
wait_for_preview(self.widget, 10000)
136136

137+
def test_subset_preview(self):
138+
subset = self.data.from_table_rows(self.data, [2])
139+
self.widget.add_preprocessor(PREPROCESSORS[0])
140+
self.send_signal("Data Subset", subset)
141+
self.send_signal("Data", self.data)
142+
wait_for_preview(self.widget)
143+
assert self.widget.sample_data(self.data).ids == subset.ids
144+
assert self.widget.preview_runner.preview_data.ids == subset.ids
145+
146+
137147
def tearDown(self):
138148
self.widget.onDeleteWidget()
139149
super().tearDown()

orangecontrib/spectroscopy/widgets/owpeakfit.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
from Orange.widgets.data.utils.preprocess import DescriptionRole, ParametersRole
1616
from Orange.widgets.utils.annotated_data import ANNOTATED_DATA_SIGNAL_NAME
1717
from Orange.widgets.utils.concurrent import TaskState
18-
from Orange.widgets.utils.signals import Output
18+
from Orange.widgets.utils.signals import Output, Input
19+
from Orange.widgets.utils.sql import check_sql_input
1920
from Orange.widgets.utils.widgetpreview import WidgetPreview
21+
from orangewidget.widget import Msg
2022

2123
from orangecontrib.spectroscopy.data import getx
2224
from orangecontrib.spectroscopy.preprocess import Cut
@@ -283,17 +285,30 @@ class OWPeakFit(SpectralPreprocess):
283285
PREPROCESSORS = PREPROCESSORS
284286
BUTTON_ADD_LABEL = "Add model..."
285287

288+
class Inputs(SpectralPreprocess.Inputs):
289+
data_subset = Input("Data Subset", Table)
290+
286291
class Outputs:
287292
fit_params = Output("Fit Parameters", Table, default=True)
288293
fits = Output("Fits", Table)
289294
residuals = Output("Residuals", Table)
290295
annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)
291296

297+
class Warning(SpectralPreprocess.Warning):
298+
subset_not_subset = Msg(
299+
"Subset data contains some instances that do not appear in "
300+
"input data")
301+
subset_independent = Msg(
302+
"No subset data instances appear in input data")
303+
292304
preview_on_image = True
293305

294306
def __init__(self):
295307
self.markings_list = []
296308
super().__init__()
309+
self.subset_data = None
310+
self.subset_indices = None
311+
self._invalidated = False
297312
self.preview_runner = PeakPreviewRunner(self)
298313
self.curveplot.selection_type = SELECTONE
299314
self.curveplot.select_at_least_1 = True
@@ -303,6 +318,51 @@ def __init__(self):
303318
# GUI
304319
# box = gui.widgetBox(self.controlArea, "Options")
305320

321+
@Inputs.data
322+
@check_sql_input
323+
def set_data(self, data=None):
324+
self.data = data
325+
self._invalidated = True
326+
327+
@Inputs.data_subset
328+
@check_sql_input
329+
def set_subset_data(self, subset):
330+
self.subset_data = subset
331+
332+
def handleNewSignals(self):
333+
self._handle_subset_data()
334+
if self._invalidated:
335+
self._invalidated = False
336+
super().handleNewSignals()
337+
else:
338+
self.show_preview(show_info_anyway=True)
339+
340+
# OWDataProjectionWidget
341+
def _handle_subset_data(self):
342+
self.Warning.subset_independent.clear()
343+
self.Warning.subset_not_subset.clear()
344+
if self.data is None or self.subset_data is None:
345+
self.subset_indices = set()
346+
else:
347+
self.subset_indices = set(self.subset_data.ids)
348+
ids = set(self.data.ids)
349+
if not self.subset_indices & ids:
350+
self.Warning.subset_independent()
351+
elif self.subset_indices - ids:
352+
self.Warning.subset_not_subset()
353+
354+
def get_subset_mask(self):
355+
if not self.subset_indices:
356+
return None
357+
return np.fromiter((ex.id in self.subset_indices for ex in self.data),
358+
dtype=bool, count=len(self.data))
359+
360+
def sample_data(self, data):
361+
subset = self.get_subset_mask()
362+
if subset is not None:
363+
return data[subset][:self.preview_curves]
364+
return super().sample_data(data)
365+
306366
def redraw_integral(self):
307367
dis = []
308368
if self.curveplot.data:

0 commit comments

Comments
 (0)