Skip to content

Commit

Permalink
Merge pull request #1677 from VesnaT/annotated_data
Browse files Browse the repository at this point in the history
[ENH] Visualize widgets: Output Annotated data and Fixups
  • Loading branch information
janezd authored Oct 21, 2016
2 parents 7dca993 + 635ba39 commit c90e602
Show file tree
Hide file tree
Showing 24 changed files with 304 additions and 81 deletions.
18 changes: 8 additions & 10 deletions Orange/widgets/data/owtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
from Orange.widgets.settings import (Setting, ContextSetting,
DomainContextHandler)
from Orange.widgets.utils import datacaching
from Orange.widgets.utils.annotated_data import (create_annotated_table,
ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.utils.itemmodels import TableModel


Expand Down Expand Up @@ -367,7 +369,7 @@ class OWDataTable(widget.OWWidget):

inputs = [("Data", Table, "set_dataset", widget.Multiple)]
outputs = [("Selected Data", Table, widget.Default),
("Other Data", Table)]
(ANNOTATED_DATA_SIGNAL_NAME, Table)]

show_distributions = Setting(False)
dist_color_RGB = Setting((220, 220, 220, 255))
Expand Down Expand Up @@ -506,6 +508,7 @@ def update(f):
self.selected_cols = []
self.openContext(data)
self.set_selection()
self.commit()

def _setup_table_view(self, view, data):
"""Setup the `view` (QTableView) with `data` (Orange.data.Table)
Expand Down Expand Up @@ -785,7 +788,7 @@ def commit(self):
"""
Commit/send the current selected row/column selection.
"""
selected_data = other_data = None
selected_data = table = rowsel = None
view = self.tabs.currentWidget()
if view and view.model() is not None:
model = self._get_model(view)
Expand All @@ -795,7 +798,7 @@ def commit(self):
# for SqlTables
if isinstance(table, SqlTable):
self.send("Selected Data", selected_data)
self.send("Other Data", other_data)
self.send(ANNOTATED_DATA_SIGNAL_NAME, None)
return

rowsel, colsel = self.get_selection(view)
Expand Down Expand Up @@ -841,19 +844,14 @@ def select_vars(role):
# Avoid a copy if all/none rows are selected.
if not rowsel:
selected_data = None
other_data = select(table, None, domain)
elif len(rowsel) == len(table):
selected_data = select(table, None, domain)
other_data = None
else:
selected_data = select(table, rowsel, domain)
selmask = numpy.ones((len(table),), dtype=bool)
selmask[rowsel] = False

other_data = select(table, numpy.flatnonzero(selmask), domain)

self.send("Selected Data", selected_data)
self.send("Other Data", other_data)
self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(table, rowsel))

def copy(self):
"""
Expand Down
31 changes: 23 additions & 8 deletions Orange/widgets/data/tests/test_owtable.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
from Orange.data import Table
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
from Orange.widgets.data.owtable import OWDataTable
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin


class TestOWDataTable(WidgetTest):
class TestOWDataTable(WidgetTest, WidgetOutputsTestMixin):
@classmethod
def setUpClass(cls):
super().setUpClass()
WidgetOutputsTestMixin.init(cls)

cls.signal_name = "Data"
cls.signal_data = cls.data

def setUp(self):
self.widget = self.create_widget(OWDataTable)
self.iris = Table("iris")

def test_input_data(self):
"""Check number of tabs with data on the input"""
self.send_signal("Data", self.iris, 1)
self.send_signal("Data", self.data, 1)
self.assertEqual(self.widget.tabs.count(), 1)
self.send_signal("Data", self.iris, 2)
self.send_signal("Data", self.data, 2)
self.assertEqual(self.widget.tabs.count(), 2)
self.send_signal("Data", None, 1)
self.assertEqual(self.widget.tabs.count(), 1)

def test_data_model(self):
self.send_signal("Data", self.iris, 1)
self.assertEqual(self.widget.tabs.widget(0).model().rowCount(), len(self.iris))
self.send_signal("Data", self.data, 1)
self.assertEqual(self.widget.tabs.widget(0).model().rowCount(),
len(self.data))

def _select_data(self):
self.widget.selected_cols = list(range(len(self.data.domain)))
self.widget.selected_rows = list(range(0, len(self.data.domain), 10))
self.widget.set_selection()
return self.widget.selected_rows
2 changes: 1 addition & 1 deletion Orange/widgets/evaluate/tests/test_owconfusionmatrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ def _select_data(self):
selected = [i for i, t in enumerate(zip(
self.widget.results.actual, self.widget.results.predicted[0]))
if t in indices]
self.selected_indices = self.widget.results.row_indices[selected]
return self.widget.results.row_indices[selected]
7 changes: 3 additions & 4 deletions Orange/widgets/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,6 @@ class WidgetOutputsTestMixin:
def init(self):
self.data = Table("iris")
self.same_input_output_domain = True
self.selected_indices = []

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

# select data instances
self._select_data()
selected_indices = self._select_data()

# check selected data output
selected = self.get_output("Selected Data")
Expand All @@ -532,7 +531,7 @@ def test_outputs(self):
self.assertEqual(selected.domain == self.data.domain,
self.same_input_output_domain)
np.testing.assert_array_equal(selected.X[:, :n_attr],
self.data.X[self.selected_indices])
self.data.X[selected_indices])

# check annotated data output
annotated = self.get_output(ANNOTATED_DATA_SIGNAL_NAME)
Expand All @@ -552,4 +551,4 @@ def _select_data(self):
def _compare_selected_annotated_domains(self, selected, annotated):
selected_vars = selected.domain.variables + selected.domain.metas
annotated_vars = annotated.domain.variables + annotated.domain.metas
self.assertTrue(all((var in annotated_vars for var in selected_vars)))
self.assertLess(set(selected_vars), set(annotated_vars))
4 changes: 2 additions & 2 deletions Orange/widgets/unsupervised/owhierarchicalclustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ def commit(self):

if not selected_indices:
self.send("Selected Data", None)
annotated_data = create_annotated_table(items, selected_indices) \
annotated_data = create_annotated_table(items, []) \
if self.selection_method == 0 and self.matrix.axis else None
self.send(ANNOTATED_DATA_SIGNAL_NAME, annotated_data)
return
Expand Down Expand Up @@ -1148,7 +1148,7 @@ def commit(self):
selected_data = data[mask]
if self.append_clusters:
def remove_other_value(vars_):
vars_ = [var for var in vars_]
vars_ = list(vars_)
clust_var = vars_[-1].copy()
clust_var.values.pop()
vars_[-1] = clust_var
Expand Down
5 changes: 3 additions & 2 deletions Orange/widgets/unsupervised/tests/test_owdistancemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def setUp(self):

def _select_data(self):
random.seed(42)
self.selected_indices = random.sample(range(0, len(self.data)), 20)
self.widget._selection = self.selected_indices
selected_indices = random.sample(range(0, len(self.data)), 20)
self.widget._selection = selected_indices
self.widget.commit()
return selected_indices
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,15 @@ def _select_data(self):
items = self.widget.dendrogram._items
cluster = items[sorted(list(items.keys()))[4]]
self.widget.dendrogram.set_selected_items([cluster])
self.selected_indices = [14, 15, 32, 33]
return [14, 15, 32, 33]

def _compare_selected_annotated_domains(self, selected, annotated):
self.assertTrue(all((var in annotated.domain.variables
for var in selected.domain.variables)))
self.assertEqual(annotated.domain.variables,
selected.domain.variables)
self.assertNotIn("Other", selected.domain.metas[0].values)
self.assertIn("Other", annotated.domain.metas[0].values)
self.assertTrue(
all((var in [var.name for var in annotated.domain.metas]
for var in [var.name for var in selected.domain.metas])))
self.assertLess(set(var.name for var in selected.domain.metas),
set(var.name for var in annotated.domain.metas))

def test_selection_box_output(self):
"""Check output if Selection method changes"""
Expand Down
2 changes: 1 addition & 1 deletion Orange/widgets/unsupervised/tests/test_owmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ def _select_data(self):
points = random.sample(range(0, len(self.data)), 20)
self.widget.select_indices(points)
self.widget.commit()
self.selected_indices = sorted(points)
return sorted(points)
9 changes: 8 additions & 1 deletion Orange/widgets/visualize/owlinearprojection.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
from Orange.data.sql.table import SqlTable
from Orange.widgets import widget, gui, settings
from Orange.widgets.utils import itemmodels, colorpalette
from Orange.widgets.utils.annotated_data import (
create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME
)
from .owscatterplotgraph import LegendItem, legend_anchor_pos
from Orange.widgets.utils import classdensity
from Orange.canvas import report
Expand Down Expand Up @@ -238,7 +241,8 @@ class OWLinearProjection(widget.OWWidget):
("Data Subset", Table, "set_subset_data")]
# #TODO: Allow for axes to be supplied from an external source.
# ("Projection", numpy.ndarray, "set_axes"),]
outputs = [("Selected Data", Table)]
outputs = [("Selected Data", Table, widget.Default),
(ANNOTATED_DATA_SIGNAL_NAME, Table)]

settingsHandler = settings.DomainContextHandler()

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

def commit(self):
subset = None
indices = None
if self.data is not None and self._selection_mask is not None:
indices = numpy.flatnonzero(self._selection_mask)
if len(indices) > 0:
subset = self.data[indices]

self.send("Selected Data", subset)
self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(self.data, indices))

def send_report(self):
self.report_plot(name="", plot=self.viewbox.getViewBox())
Expand Down
16 changes: 11 additions & 5 deletions Orange/widgets/visualize/owmosaic.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
from Orange.preprocess import Discretize
from Orange.preprocess.discretize import EqualFreq
from Orange.statistics.distribution import get_distribution
from Orange.widgets import gui
from Orange.widgets import gui, widget
from Orange.widgets.settings import (
Setting, DomainContextHandler, ContextSetting)
from Orange.widgets.utils import to_html, get_variable_values_sorted
from Orange.widgets.utils.annotated_data import (create_annotated_table,
ANNOTATED_DATA_SIGNAL_NAME)
from Orange.widgets.visualize.utils import (
CanvasText, CanvasRectangle, ViewWithPress)
from Orange.widgets.widget import OWWidget, Default, Msg
Expand All @@ -30,7 +32,8 @@ class OWMosaicDisplay(OWWidget):

inputs = [("Data", Table, "set_data", Default),
("Data Subset", Table, "set_subset_data")]
outputs = [("Selected Data", Table)]
outputs = [("Selected Data", Table, widget.Default),
(ANNOTATED_DATA_SIGNAL_NAME, Table)]

settingsHandler = DomainContextHandler()
use_boxes = Setting(True)
Expand Down Expand Up @@ -218,6 +221,8 @@ def select_area(self, index, ev):
def send_selection(self):
if not self.selection or self.data is None:
self.send("Selected Data", None)
self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(self.data, []))
return
filters = []
self.Warning.no_cont_selection_sql.clear()
Expand All @@ -235,12 +240,13 @@ def send_selection(self):
else:
filters = filters[0]
selection = filters(self.discrete_data)
idset = set(selection.ids)
sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
if self.discrete_data is not self.data:
idset = set(selection.ids)
sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
selection = self.data[sel_idx]
self.send("Selected Data", selection)

self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(self.data, sel_idx))

def send_report(self):
self.report_plot(self.canvas)
Expand Down
23 changes: 16 additions & 7 deletions Orange/widgets/visualize/owpythagorastree.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@
)

from Orange.data.table import Table
from Orange.widgets import gui, settings
from Orange.widgets import gui, settings, widget
from Orange.widgets.utils import to_html
from Orange.widgets.utils.annotated_data import (
create_annotated_table,
ANNOTATED_DATA_SIGNAL_NAME
)
from Orange.widgets.utils.colorpalette import ContinuousPaletteGenerator
from Orange.widgets.visualize.pythagorastreeviewer import (
PythagorasTreeViewer,
Expand All @@ -60,7 +64,8 @@ class OWPythagorasTree(OWWidget):
priority = 1000

inputs = [('Tree', TreeModel, 'set_tree')]
outputs = [('Selected Data', Table)]
outputs = [('Selected Data', Table, widget.Default),
(ANNOTATED_DATA_SIGNAL_NAME, Table)]

# Enable the save as feature
graph_name = 'scene'
Expand Down Expand Up @@ -228,6 +233,8 @@ def set_tree(self, model=None):
# if hasattr(model, 'meta_depth_limit'):
# self.depth_limit = model.meta_depth_limit
# self.update_depth()
self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(self.instances, None))

def clear(self):
"""Clear all relevant data from the widget."""
Expand Down Expand Up @@ -343,14 +350,16 @@ def commit(self):
"""Commit the selected data to output."""
if self.instances is None:
self.send('Selected Data', None)
self.send(ANNOTATED_DATA_SIGNAL_NAME, None)
return
# this is taken almost directly from the owclassificationtreegraph.py
items = filter(lambda x: isinstance(x, SquareGraphicsItem),
self.scene.selectedItems())

nodes = [i.tree_node.label for i in self.scene.selectedItems()
if isinstance(i, SquareGraphicsItem)]
data = self.tree_adapter.get_instances_in_nodes(
self.clf_dataset, [item.tree_node.label for item in items])
self.clf_dataset, nodes)
self.send('Selected Data', data)
selected_indices = self.model.get_indices(nodes)
self.send(ANNOTATED_DATA_SIGNAL_NAME,
create_annotated_table(self.instances, selected_indices))

def send_report(self):
"""Send report."""
Expand Down
Loading

0 comments on commit c90e602

Please sign in to comment.