Skip to content

Commit

Permalink
Merge pull request #3506 from janezd/label-subset
Browse files Browse the repository at this point in the history
[ENH] Label subset
  • Loading branch information
markotoplak authored Jan 4, 2019
2 parents b9597a8 + 8bc0b3d commit 25f224f
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Orange/widgets/utils/plot/owplotgui.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ def regression_line_check_box(self, widget):

def label_only_selected_check_box(self, widget):
self._check_box(widget=widget, value="label_only_selected",
label="Label only selected points",
label="Label only selection and subset",
cb_name=self._plot.update_labels)

def filled_symbols_check_box(self, widget):
Expand Down
53 changes: 34 additions & 19 deletions Orange/widgets/visualize/owscatterplotgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -1024,25 +1024,22 @@ def update_labels(self):
for label in self.labels:
self.plot_widget.removeItem(label)
self.labels = []
if self.scatterplot_item is None \
or self.label_only_selected and self.selection is None:
self._signal_too_many_labels(False)
return
labels = self.get_labels()
if labels is None:
self._signal_too_many_labels(False)
return
(x0, x1), (y0, y1) = self.view_box.viewRange()
x, y = self.scatterplot_item.getData()
mask = np.logical_and(
np.logical_and(x >= x0, x <= x1),
np.logical_and(y >= y0, y <= y1))
if self.label_only_selected:
mask = np.logical_and(
mask, self._filter_visible(self.selection) != 0)
if mask.sum() > self.MAX_VISIBLE_LABELS:
self._signal_too_many_labels(True)

mask = None
if self.scatterplot_item is not None:
x, y = self.scatterplot_item.getData()
mask = self._label_mask(x, y)

if mask is not None:
labels = self.get_labels()
if labels is None:
mask = None

self._signal_too_many_labels(
mask is not None and mask.sum() > self.MAX_VISIBLE_LABELS)
if self._too_many_labels or mask is None or not np.any(mask):
return

black = pg.mkColor(0, 0, 0)
labels = labels[mask]
x = x[mask]
Expand All @@ -1052,13 +1049,31 @@ def update_labels(self):
ti.setPos(xp, yp)
self.plot_widget.addItem(ti)
self.labels.append(ti)
self._signal_too_many_labels(False)

def _signal_too_many_labels(self, too_many):
if self._too_many_labels != too_many:
self._too_many_labels = too_many
self.too_many_labels.emit(too_many)

def _label_mask(self, x, y):
(x0, x1), (y0, y1) = self.view_box.viewRange()
mask = np.logical_and(
np.logical_and(x >= x0, x <= x1),
np.logical_and(y >= y0, y <= y1))
if self.label_only_selected:
sub_mask = self._filter_visible(self.master.get_subset_mask())
if self.selection is None:
if sub_mask is None:
return None
else:
sel_mask = sub_mask
else:
sel_mask = self._filter_visible(self.selection) != 0
if sub_mask is not None:
sel_mask = np.logical_or(sel_mask, sub_mask)
mask = np.logical_and(mask, sel_mask)
return mask

# Shapes
def get_shapes(self):
"""
Expand Down
195 changes: 195 additions & 0 deletions Orange/widgets/visualize/tests/test_owscatterplotbase.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# pylint: disable=missing-docstring,too-many-lines,too-many-public-methods
# pylint: disable=protected-access
from unittest.mock import patch, Mock
import numpy as np

Expand Down Expand Up @@ -729,6 +730,200 @@ def test_labels(self):
self.assertEqual(label.x(), x[ind])
self.assertEqual(label.y(), y[ind])

def test_label_mask_all_visible(self):
graph = self.graph

x, y = np.arange(10) / 10, np.arange(10) / 10
sel = np.array(
[True, True, False, False, False, True, True, True, False, False])
subset = np.array(
[True, False, True, True, False, True, True, False, False, False])
trues = np.ones(10, dtype=bool)

np.testing.assert_equal(graph._label_mask(x, y), trues)

# Selection present, subset is None
graph.selection = sel
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(graph._label_mask(x, y), sel)

# Selection and subset present
graph.selection = sel
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(graph._label_mask(x, y), np.array(
[True, True, True, True, False, True, True, True, False, False]
))

# No selection, subset present
graph.selection = None
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(graph._label_mask(x, y), subset)

# No selection, no subset
graph.selection = None
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
self.assertIsNone(graph._label_mask(x, y))

def test_label_mask_with_invisible(self):
graph = self.graph

x, y = np.arange(5, 10) / 10, np.arange(5, 10) / 10
sel = np.array(
[True, True, False, False, False, # these 5 are not in the sample
True, True, True, False, False])
subset = np.array(
[True, False, True, True, False, # these 5 are not in the sample
True, True, False, False, True])
graph.sample_indices = np.arange(5, 10, dtype=int)
trues = np.ones(5, dtype=bool)

np.testing.assert_equal(graph._label_mask(x, y), trues)

# Selection present, subset is None
graph.selection = sel
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(graph._label_mask(x, y), sel[5:])

# Selection and subset present
graph.selection = sel
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(
graph._label_mask(x, y),
np.array([True, True, True, False, True]))

# No selection, subset present
graph.selection = None
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
np.testing.assert_equal(graph._label_mask(x, y), subset[5:])

# No selection, no subset
graph.selection = None
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), trues)

graph.label_only_selected = True
self.assertIsNone(graph._label_mask(x, y))

def test_label_mask_with_invisible_and_view(self):
graph = self.graph

x, y = np.arange(5, 10) / 10, np.arange(5) / 10
sel = np.array(
[True, True, False, False, False, # these 5 are not in the sample
True, True, True, False, False]) # first and last out of the view
subset = np.array(
[True, False, True, True, False, # these 5 are not in the sample
True, True, False, True, True]) # first and last out of the view
graph.sample_indices = np.arange(5, 10, dtype=int)
graph.view_box.viewRange = lambda: ((0.6, 1), (0, 0.3))
viewed = np.array([False, True, True, True, False])

np.testing.assert_equal(graph._label_mask(x, y), viewed)

# Selection present, subset is None
graph.selection = sel
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), viewed)

graph.label_only_selected = True
np.testing.assert_equal(
graph._label_mask(x, y),
np.array([False, True, True, False, False]))

# Selection and subset present
graph.selection = sel
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), viewed)

graph.label_only_selected = True
np.testing.assert_equal(
graph._label_mask(x, y),
np.array([False, True, True, True, False]))

# No selection, subset present
graph.selection = None
graph.master.get_subset_mask = lambda: subset

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), viewed)

graph.label_only_selected = True
np.testing.assert_equal(
graph._label_mask(x, y),
np.array([False, True, False, True, False]))

# No selection, no subset
graph.selection = None
graph.master.get_subset_mask = lambda: None

graph.label_only_selected = False
np.testing.assert_equal(graph._label_mask(x, y), viewed)

graph.label_only_selected = True
self.assertIsNone(graph._label_mask(x, y))

def test_labels_observes_mask(self):
graph = self.graph
get_label_data = graph.master.get_label_data
graph.reset_graph()

self.assertEqual(graph.labels, [])

get_label_data.reset_mock()
graph._label_mask = lambda *_: None
graph.update_labels()
get_label_data.assert_not_called()

self.master.get_label_data = lambda: \
np.array([str(x) for x in range(10)], dtype=object)
graph._label_mask = \
lambda *_: np.array([False, True, True] + [False] * 7)
graph.update_labels()
self.assertEqual(
[label.textItem.toPlainText() for label in graph.labels],
["1", "2"])

def test_labels_update_coordinates(self):
graph = self.graph
self.master.get_label_data = lambda: \
Expand Down

0 comments on commit 25f224f

Please sign in to comment.