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

[FIX] Radviz: Enable projection for less than two selected variables #3444

Merged
merged 3 commits into from
Dec 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 11 additions & 31 deletions Orange/widgets/visualize/owradviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,16 +275,10 @@ class OWRadviz(OWAnchorProjectionWidget):
graph = SettingProvider(OWRadvizGraph)

class Warning(OWAnchorProjectionWidget.Warning):
no_features = widget.Msg("Radviz requires at least two features.")
invalid_embedding = widget.Msg("No projection for selected features")
removed_vars = widget.Msg("Categorical variables with more than"
" two values are not shown.")

class Error(OWAnchorProjectionWidget.Error):
no_features = widget.Msg(
"At least three numeric or categorical variables are required"
)

def __init__(self):
self.model_selected = VariableListModel(enable_dnd=True)
self.model_selected.removed.connect(self.__model_selected_changed)
Expand Down Expand Up @@ -331,12 +325,6 @@ def __vizrank_set_attrs(self, attrs):
def __model_selected_changed(self):
self.selected_vars = [(var.name, vartype(var)) for var
in self.model_selected]

self.Warning.no_features.clear()
if len(self.model_selected) < 2:
self.Warning.no_features()
return

self.init_projection()
self.setup_plot()
self.commit()
Expand All @@ -347,6 +335,12 @@ def colors_changed(self):

def set_data(self, data):
super().set_data(data)
self._init_vizrank()
self.init_projection()

def use_context(self):
janezd marked this conversation as resolved.
Show resolved Hide resolved
self.model_selected.clear()
self.model_other.clear()
if self.data is not None and len(self.selected_vars):
d, selected = self.data.domain, [v[0] for v in self.selected_vars]
self.model_selected[:] = [d[name] for name in selected]
Expand All @@ -360,9 +354,6 @@ def set_data(self, data):
self.model_selected[:] = variables[:5]
self.model_other[:] = variables[5:] + class_var

self._init_vizrank()
self.init_projection()

def _init_vizrank(self):
is_enabled = self.data is not None and \
len(self.primitive_variables) > 3 and \
Expand All @@ -376,20 +367,13 @@ def _init_vizrank(self):
self.vizrank.initialize()

def check_data(self):
def error(err):
err()
self.data = None

super().check_data()
if self.data is not None:
if len(self.primitive_variables) < 3:
error(self.Error.no_features)
else:
domain = self.data.domain
vars_ = chain(domain.variables, domain.metas)
n_vars = sum(v.is_primitive() for v in vars_)
if len(self.primitive_variables) < n_vars:
self.Warning.removed_vars()
domain = self.data.domain
vars_ = chain(domain.variables, domain.metas)
n_vars = sum(v.is_primitive() for v in vars_)
if len(self.primitive_variables) < n_vars:
self.Warning.removed_vars()

def init_attr_values(self):
super().init_attr_values()
Expand All @@ -408,10 +392,6 @@ def _send_components_metas(self):
return np.vstack((super()._send_components_metas(), ["angle"]))

def clear(self):
if self.model_selected:
self.model_selected.clear()
if self.model_other:
self.model_other.clear()
super().clear()
self.projector = RadViz()

Expand Down
47 changes: 38 additions & 9 deletions Orange/widgets/visualize/tests/test_owradviz.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Test methods with long descriptive names can omit docstrings
# pylint: disable=missing-docstring
from unittest.mock import Mock
import numpy as np

from Orange.data import Table, Domain
from Orange.data import Table
from Orange.widgets.tests.base import (
WidgetTest, WidgetOutputsTestMixin,
AnchorProjectionWidgetTestMixin, datasets
Expand Down Expand Up @@ -43,14 +44,7 @@ def check_vizrank(data):
check_vizrank(ds)

def test_no_features(self):
w = self.widget
data2 = self.data.transform(Domain(self.data.domain.attributes[:1],
self.data.domain.class_vars))
self.assertFalse(w.Error.no_features.is_shown())
self.send_signal(w.Inputs.data, data2)
self.assertTrue(w.Error.no_features.is_shown())
self.send_signal(w.Inputs.data, None)
self.assertFalse(w.Error.no_features.is_shown())
self.send_signal(self.widget.Inputs.data, self.data[:, :0])

def test_not_enough_instances(self):
w = self.widget
Expand Down Expand Up @@ -101,3 +95,38 @@ def test_discrete_attributes(self):
self.assertTrue(self.widget.Warning.removed_vars.is_shown())
self.send_signal(self.widget.Inputs.data, None)
self.assertFalse(self.widget.Warning.removed_vars.is_shown())

def test_saved_selected_vars(self):
self.send_signal(self.widget.Inputs.data, self.data)

self.widget.model_selected[:] = self.data.domain[:1]
self.widget.variables_selection.removed.emit()
self.send_signal(self.widget.Inputs.data, self.data)
self.assertEqual(len(self.widget.model_selected[:]), 1)

self.widget.model_selected[:] = self.data.domain[:0]
self.widget.variables_selection.removed.emit()
self.send_signal(self.widget.Inputs.data, self.data)
self.assertEqual(len(self.widget.model_selected[:]), 4)

def test_invalidated_model_selected(self):
self.widget.setup_plot = Mock()
self.send_signal(self.widget.Inputs.data, self.data)
self.widget.setup_plot.assert_called_once()

self.widget.setup_plot.reset_mock()
self.widget.model_selected[:] = self.data.domain[2:]
self.widget.variables_selection.removed.emit()
self.widget.setup_plot.assert_called_once()

self.widget.setup_plot.reset_mock()
self.send_signal(self.widget.Inputs.data, self.data[:, 2:])
self.widget.setup_plot.assert_not_called()

self.widget.model_selected[:] = self.data.domain[3:]
self.widget.variables_selection.removed.emit()
self.widget.setup_plot.assert_called_once()

self.widget.setup_plot.reset_mock()
self.send_signal(self.widget.Inputs.data, self.data)
self.widget.setup_plot.assert_called_once()
4 changes: 4 additions & 0 deletions Orange/widgets/visualize/utils/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ def set_data(self, data):
if not same_domain:
self.init_attr_values()
self.openContext(self.data)
self.use_context()
self.__invalidated = not (
data_existed and self.data is not None and
effective_data.X.shape == self.effective_data.X.shape and
Expand All @@ -436,6 +437,9 @@ def check_data(self):
self.valid_data = None
self.clear_messages()

def use_context(self):
pass

@Inputs.data_subset
@check_sql_input
def set_subset_data(self, subset):
Expand Down