Skip to content

Commit c90e602

Browse files
authored
Merge pull request #1677 from VesnaT/annotated_data
[ENH] Visualize widgets: Output Annotated data and Fixups
2 parents 7dca993 + 635ba39 commit c90e602

24 files changed

+304
-81
lines changed

Orange/widgets/data/owtable.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
from Orange.widgets.settings import (Setting, ContextSetting,
3535
DomainContextHandler)
3636
from Orange.widgets.utils import datacaching
37+
from Orange.widgets.utils.annotated_data import (create_annotated_table,
38+
ANNOTATED_DATA_SIGNAL_NAME)
3739
from Orange.widgets.utils.itemmodels import TableModel
3840

3941

@@ -367,7 +369,7 @@ class OWDataTable(widget.OWWidget):
367369

368370
inputs = [("Data", Table, "set_dataset", widget.Multiple)]
369371
outputs = [("Selected Data", Table, widget.Default),
370-
("Other Data", Table)]
372+
(ANNOTATED_DATA_SIGNAL_NAME, Table)]
371373

372374
show_distributions = Setting(False)
373375
dist_color_RGB = Setting((220, 220, 220, 255))
@@ -506,6 +508,7 @@ def update(f):
506508
self.selected_cols = []
507509
self.openContext(data)
508510
self.set_selection()
511+
self.commit()
509512

510513
def _setup_table_view(self, view, data):
511514
"""Setup the `view` (QTableView) with `data` (Orange.data.Table)
@@ -785,7 +788,7 @@ def commit(self):
785788
"""
786789
Commit/send the current selected row/column selection.
787790
"""
788-
selected_data = other_data = None
791+
selected_data = table = rowsel = None
789792
view = self.tabs.currentWidget()
790793
if view and view.model() is not None:
791794
model = self._get_model(view)
@@ -795,7 +798,7 @@ def commit(self):
795798
# for SqlTables
796799
if isinstance(table, SqlTable):
797800
self.send("Selected Data", selected_data)
798-
self.send("Other Data", other_data)
801+
self.send(ANNOTATED_DATA_SIGNAL_NAME, None)
799802
return
800803

801804
rowsel, colsel = self.get_selection(view)
@@ -841,19 +844,14 @@ def select_vars(role):
841844
# Avoid a copy if all/none rows are selected.
842845
if not rowsel:
843846
selected_data = None
844-
other_data = select(table, None, domain)
845847
elif len(rowsel) == len(table):
846848
selected_data = select(table, None, domain)
847-
other_data = None
848849
else:
849850
selected_data = select(table, rowsel, domain)
850-
selmask = numpy.ones((len(table),), dtype=bool)
851-
selmask[rowsel] = False
852-
853-
other_data = select(table, numpy.flatnonzero(selmask), domain)
854851

855852
self.send("Selected Data", selected_data)
856-
self.send("Other Data", other_data)
853+
self.send(ANNOTATED_DATA_SIGNAL_NAME,
854+
create_annotated_table(table, rowsel))
857855

858856
def copy(self):
859857
"""
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
1-
from Orange.data import Table
1+
# Test methods with long descriptive names can omit docstrings
2+
# pylint: disable=missing-docstring
23
from Orange.widgets.data.owtable import OWDataTable
3-
from Orange.widgets.tests.base import WidgetTest
4+
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin
45

56

6-
class TestOWDataTable(WidgetTest):
7+
class TestOWDataTable(WidgetTest, WidgetOutputsTestMixin):
8+
@classmethod
9+
def setUpClass(cls):
10+
super().setUpClass()
11+
WidgetOutputsTestMixin.init(cls)
12+
13+
cls.signal_name = "Data"
14+
cls.signal_data = cls.data
15+
716
def setUp(self):
817
self.widget = self.create_widget(OWDataTable)
9-
self.iris = Table("iris")
1018

1119
def test_input_data(self):
1220
"""Check number of tabs with data on the input"""
13-
self.send_signal("Data", self.iris, 1)
21+
self.send_signal("Data", self.data, 1)
1422
self.assertEqual(self.widget.tabs.count(), 1)
15-
self.send_signal("Data", self.iris, 2)
23+
self.send_signal("Data", self.data, 2)
1624
self.assertEqual(self.widget.tabs.count(), 2)
1725
self.send_signal("Data", None, 1)
1826
self.assertEqual(self.widget.tabs.count(), 1)
1927

2028
def test_data_model(self):
21-
self.send_signal("Data", self.iris, 1)
22-
self.assertEqual(self.widget.tabs.widget(0).model().rowCount(), len(self.iris))
29+
self.send_signal("Data", self.data, 1)
30+
self.assertEqual(self.widget.tabs.widget(0).model().rowCount(),
31+
len(self.data))
32+
33+
def _select_data(self):
34+
self.widget.selected_cols = list(range(len(self.data.domain)))
35+
self.widget.selected_rows = list(range(0, len(self.data.domain), 10))
36+
self.widget.set_selection()
37+
return self.widget.selected_rows

Orange/widgets/evaluate/tests/test_owconfusionmatrix.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@ def _select_data(self):
5353
selected = [i for i, t in enumerate(zip(
5454
self.widget.results.actual, self.widget.results.predicted[0]))
5555
if t in indices]
56-
self.selected_indices = self.widget.results.row_indices[selected]
56+
return self.widget.results.row_indices[selected]

Orange/widgets/tests/base.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,6 @@ class WidgetOutputsTestMixin:
503503
def init(self):
504504
self.data = Table("iris")
505505
self.same_input_output_domain = True
506-
self.selected_indices = []
507506

508507
def test_outputs(self):
509508
self.send_signal(self.signal_name, self.signal_data)
@@ -523,7 +522,7 @@ def test_outputs(self):
523522
self.assertEqual(0, np.sum([i[feature_name] for i in annotated]))
524523

525524
# select data instances
526-
self._select_data()
525+
selected_indices = self._select_data()
527526

528527
# check selected data output
529528
selected = self.get_output("Selected Data")
@@ -532,7 +531,7 @@ def test_outputs(self):
532531
self.assertEqual(selected.domain == self.data.domain,
533532
self.same_input_output_domain)
534533
np.testing.assert_array_equal(selected.X[:, :n_attr],
535-
self.data.X[self.selected_indices])
534+
self.data.X[selected_indices])
536535

537536
# check annotated data output
538537
annotated = self.get_output(ANNOTATED_DATA_SIGNAL_NAME)
@@ -552,4 +551,4 @@ def _select_data(self):
552551
def _compare_selected_annotated_domains(self, selected, annotated):
553552
selected_vars = selected.domain.variables + selected.domain.metas
554553
annotated_vars = annotated.domain.variables + annotated.domain.metas
555-
self.assertTrue(all((var in annotated_vars for var in selected_vars)))
554+
self.assertLess(set(selected_vars), set(annotated_vars))

Orange/widgets/unsupervised/owhierarchicalclustering.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,7 @@ def commit(self):
11011101

11021102
if not selected_indices:
11031103
self.send("Selected Data", None)
1104-
annotated_data = create_annotated_table(items, selected_indices) \
1104+
annotated_data = create_annotated_table(items, []) \
11051105
if self.selection_method == 0 and self.matrix.axis else None
11061106
self.send(ANNOTATED_DATA_SIGNAL_NAME, annotated_data)
11071107
return
@@ -1148,7 +1148,7 @@ def commit(self):
11481148
selected_data = data[mask]
11491149
if self.append_clusters:
11501150
def remove_other_value(vars_):
1151-
vars_ = [var for var in vars_]
1151+
vars_ = list(vars_)
11521152
clust_var = vars_[-1].copy()
11531153
clust_var.values.pop()
11541154
vars_[-1] = clust_var

Orange/widgets/unsupervised/tests/test_owdistancemap.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def setUp(self):
2020

2121
def _select_data(self):
2222
random.seed(42)
23-
self.selected_indices = random.sample(range(0, len(self.data)), 20)
24-
self.widget._selection = self.selected_indices
23+
selected_indices = random.sample(range(0, len(self.data)), 20)
24+
self.widget._selection = selected_indices
2525
self.widget.commit()
26+
return selected_indices

Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,15 @@ def _select_data(self):
2525
items = self.widget.dendrogram._items
2626
cluster = items[sorted(list(items.keys()))[4]]
2727
self.widget.dendrogram.set_selected_items([cluster])
28-
self.selected_indices = [14, 15, 32, 33]
28+
return [14, 15, 32, 33]
2929

3030
def _compare_selected_annotated_domains(self, selected, annotated):
31-
self.assertTrue(all((var in annotated.domain.variables
32-
for var in selected.domain.variables)))
31+
self.assertEqual(annotated.domain.variables,
32+
selected.domain.variables)
3333
self.assertNotIn("Other", selected.domain.metas[0].values)
3434
self.assertIn("Other", annotated.domain.metas[0].values)
35-
self.assertTrue(
36-
all((var in [var.name for var in annotated.domain.metas]
37-
for var in [var.name for var in selected.domain.metas])))
35+
self.assertLess(set(var.name for var in selected.domain.metas),
36+
set(var.name for var in annotated.domain.metas))
3837

3938
def test_selection_box_output(self):
4039
"""Check output if Selection method changes"""

Orange/widgets/unsupervised/tests/test_owmds.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ def _select_data(self):
2525
points = random.sample(range(0, len(self.data)), 20)
2626
self.widget.select_indices(points)
2727
self.widget.commit()
28-
self.selected_indices = sorted(points)
28+
return sorted(points)

Orange/widgets/visualize/owlinearprojection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
from Orange.data.sql.table import SqlTable
3232
from Orange.widgets import widget, gui, settings
3333
from Orange.widgets.utils import itemmodels, colorpalette
34+
from Orange.widgets.utils.annotated_data import (
35+
create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME
36+
)
3437
from .owscatterplotgraph import LegendItem, legend_anchor_pos
3538
from Orange.widgets.utils import classdensity
3639
from Orange.canvas import report
@@ -238,7 +241,8 @@ class OWLinearProjection(widget.OWWidget):
238241
("Data Subset", Table, "set_subset_data")]
239242
# #TODO: Allow for axes to be supplied from an external source.
240243
# ("Projection", numpy.ndarray, "set_axes"),]
241-
outputs = [("Selected Data", Table)]
244+
outputs = [("Selected Data", Table, widget.Default),
245+
(ANNOTATED_DATA_SIGNAL_NAME, Table)]
242246

243247
settingsHandler = settings.DomainContextHandler()
244248

@@ -1064,12 +1068,15 @@ def select_indices(self, indices, modifiers=Qt.NoModifier):
10641068

10651069
def commit(self):
10661070
subset = None
1071+
indices = None
10671072
if self.data is not None and self._selection_mask is not None:
10681073
indices = numpy.flatnonzero(self._selection_mask)
10691074
if len(indices) > 0:
10701075
subset = self.data[indices]
10711076

10721077
self.send("Selected Data", subset)
1078+
self.send(ANNOTATED_DATA_SIGNAL_NAME,
1079+
create_annotated_table(self.data, indices))
10731080

10741081
def send_report(self):
10751082
self.report_plot(name="", plot=self.viewbox.getViewBox())

Orange/widgets/visualize/owmosaic.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
from Orange.preprocess import Discretize
1414
from Orange.preprocess.discretize import EqualFreq
1515
from Orange.statistics.distribution import get_distribution
16-
from Orange.widgets import gui
16+
from Orange.widgets import gui, widget
1717
from Orange.widgets.settings import (
1818
Setting, DomainContextHandler, ContextSetting)
1919
from Orange.widgets.utils import to_html, get_variable_values_sorted
20+
from Orange.widgets.utils.annotated_data import (create_annotated_table,
21+
ANNOTATED_DATA_SIGNAL_NAME)
2022
from Orange.widgets.visualize.utils import (
2123
CanvasText, CanvasRectangle, ViewWithPress)
2224
from Orange.widgets.widget import OWWidget, Default, Msg
@@ -30,7 +32,8 @@ class OWMosaicDisplay(OWWidget):
3032

3133
inputs = [("Data", Table, "set_data", Default),
3234
("Data Subset", Table, "set_subset_data")]
33-
outputs = [("Selected Data", Table)]
35+
outputs = [("Selected Data", Table, widget.Default),
36+
(ANNOTATED_DATA_SIGNAL_NAME, Table)]
3437

3538
settingsHandler = DomainContextHandler()
3639
use_boxes = Setting(True)
@@ -218,6 +221,8 @@ def select_area(self, index, ev):
218221
def send_selection(self):
219222
if not self.selection or self.data is None:
220223
self.send("Selected Data", None)
224+
self.send(ANNOTATED_DATA_SIGNAL_NAME,
225+
create_annotated_table(self.data, []))
221226
return
222227
filters = []
223228
self.Warning.no_cont_selection_sql.clear()
@@ -235,12 +240,13 @@ def send_selection(self):
235240
else:
236241
filters = filters[0]
237242
selection = filters(self.discrete_data)
243+
idset = set(selection.ids)
244+
sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
238245
if self.discrete_data is not self.data:
239-
idset = set(selection.ids)
240-
sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
241246
selection = self.data[sel_idx]
242247
self.send("Selected Data", selection)
243-
248+
self.send(ANNOTATED_DATA_SIGNAL_NAME,
249+
create_annotated_table(self.data, sel_idx))
244250

245251
def send_report(self):
246252
self.report_plot(self.canvas)

0 commit comments

Comments
 (0)