Skip to content

Commit

Permalink
Merge pull request #6530 from PrimozGodec/scatterplot-fix-hidden-attrs
Browse files Browse the repository at this point in the history
[FIX] Scatter Plot - Fix Vizrank for hidden attributes
  • Loading branch information
janezd committed Aug 16, 2023
2 parents 9eb6490 + 6a657e5 commit 0d13870
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 32 deletions.
9 changes: 5 additions & 4 deletions Orange/widgets/visualize/owscatterplot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from itertools import chain
from xml.sax.saxutils import escape

import numpy as np
Expand Down Expand Up @@ -87,9 +86,11 @@ def bar_length(self, score):

def score_heuristic(self):
assert self.attr_color is not None
master_domain = self.master.data.domain
vars = [v for v in chain(master_domain.variables, master_domain.metas)
if v is not self.attr_color and v.is_primitive()]
vars = [
v
for v in self.master.xy_model # same attributes that are in xy combos
if v is not self.attr_color and v.is_primitive()
]
domain = Domain(attributes=vars, class_vars=self.attr_color)
data = self.master.data.transform(domain)
relief = ReliefF if isinstance(domain.class_var, DiscreteVariable) \
Expand Down
70 changes: 42 additions & 28 deletions Orange/widgets/visualize/tests/test_owscatterplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from AnyQt.QtCore import QRectF, Qt
from AnyQt.QtWidgets import QToolTip
from AnyQt.QtGui import QColor, QFont
from orangewidget.tests.base import DEFAULT_TIMEOUT

from Orange.data import (
Table, Domain, ContinuousVariable, DiscreteVariable, TimeVariable
Expand Down Expand Up @@ -139,17 +140,17 @@ def test_data_column_infs(self):
attr_x = self.widget.controls.attr_x
simulate.combobox_activate_item(attr_x, "b")

def test_regression_line(self):
def test_regression_line_pair(self):
"""It is possible to draw the line only for pair of continuous attrs"""
self.send_signal(self.widget.Inputs.data, self.data)
self.assertTrue(self.widget.cb_reg_line.isEnabled())
self.assertIsNone(self.widget.graph.reg_line_item)
self.assertListEqual([], self.widget.graph.reg_line_items)
self.widget.cb_reg_line.setChecked(True)
self.assertIsNotNone(self.widget.graph.reg_line_item)
self.assertEqual(4, len(self.widget.graph.reg_line_items))
self.widget.cb_attr_y.activated.emit(4)
self.widget.cb_attr_y.setCurrentIndex(4)
self.assertFalse(self.widget.cb_reg_line.isEnabled())
self.assertIsNone(self.widget.graph.reg_line_item)
self.assertListEqual([], self.widget.graph.reg_line_items)

def test_points_combo_boxes(self):
"""Check Point box combo models and values"""
Expand Down Expand Up @@ -277,7 +278,7 @@ def test_points_selection(self):
self.assertIsNone(selected_data)

def test_migrate_selection(self):
settings = dict(selection=list(range(2)))
settings = {"selection": list(range(2))}
OWScatterPlot.migrate_settings(settings, 0)
self.assertEqual(settings["selection_group"], [(0, 1), (1, 1)])

Expand Down Expand Up @@ -371,7 +372,7 @@ def test_vizrank(self):
vizrank = ScatterPlotVizRank(self.widget)
n_states = len(data.domain.attributes)
n_states = n_states * (n_states - 1) / 2
states = [state for state in vizrank.iterate_states(None)]
states = list(vizrank.iterate_states(None))
self.assertEqual(len(states), n_states)
self.assertEqual(len(set(states)), n_states)
self.assertIsNotNone(vizrank.compute_score(states[0]))
Expand All @@ -381,8 +382,7 @@ def test_vizrank(self):
data = Table("housing")[::10]
self.send_signal(self.widget.Inputs.data, data)
vizrank = ScatterPlotVizRank(self.widget)
states = [state for state in vizrank.iterate_states(None)]
self.assertIsNotNone(vizrank.compute_score(states[0]))
self.assertIsNotNone(vizrank.compute_score(next(vizrank.iterate_states(None))))

def test_vizrank_class_nan(self):
"""
Expand Down Expand Up @@ -472,6 +472,20 @@ def test_vizrank_enabled_color_var_nans(self):
self.assertEqual(self.widget.vizrank_button.toolTip(),
"Color variable has no values")

def test_vizrank_hidden_attributes(self):
"""
Test hidden attributes not considered in Find Informative Projections
"""
new_domain = self.data.domain.copy()
new_domain.attributes[0].attributes["hidden"] = True
data = self.data.transform(new_domain)
self.send_signal(self.widget.Inputs.data, data)
vizrank = ScatterPlotVizRank(self.widget)
self.assertListEqual(
["petal width", "petal length", "sepal width"],
[x.name for x in vizrank.score_heuristic()],
)

def test_auto_send_selection(self):
"""
Scatter Plot automatically sends selection only when the checkbox Send automatically
Expand All @@ -491,7 +505,7 @@ def test_auto_send_selection(self):

def test_color_is_optional(self):
zoo = Table("zoo")
backbone, breathes, airborne, type = \
backbone, breathes, airborne, type_ = \
[zoo.domain[x] for x in ["backbone", "breathes", "airborne", "type"]]
default_x, default_y, default_color = \
zoo.domain[0], zoo.domain[1], zoo.domain.class_var
Expand All @@ -510,23 +524,23 @@ def test_color_is_optional(self):
simulate.combobox_activate_item(attr_color, airborne.name)

# Send compatible dataset, values should not change
zoo2 = zoo[:, (backbone, breathes, airborne, type)]
zoo2 = zoo[:, (backbone, breathes, airborne, type_)]
self.send_signal(self.widget.Inputs.data, zoo2)
self.assertEqual(attr_x.currentText(), backbone.name)
self.assertEqual(attr_y.currentText(), breathes.name)
self.assertEqual(attr_color.currentText(), airborne.name)

# Send dataset without color variable
# x and y should remain, color reset to default
zoo3 = zoo[:, (backbone, breathes, type)]
zoo3 = zoo[:, (backbone, breathes, type_)]
self.send_signal(self.widget.Inputs.data, zoo3)
self.assertEqual(attr_x.currentText(), backbone.name)
self.assertEqual(attr_y.currentText(), breathes.name)
self.assertEqual(attr_color.currentText(), default_color.name)

# Send dataset without x
# y and color should be the same as with zoo
zoo4 = zoo[:, (default_x, default_y, breathes, airborne, type)]
zoo4 = zoo[:, (default_x, default_y, breathes, airborne, type_)]
self.send_signal(self.widget.Inputs.data, zoo4)
self.assertEqual(attr_x.currentText(), default_x.name)
self.assertEqual(attr_y.currentText(), default_y.name)
Expand All @@ -535,11 +549,11 @@ def test_color_is_optional(self):
# Send dataset compatible with zoo2 and zoo3
# Color should reset to one in zoo3, as it was used more
# recently
zoo5 = zoo[:, (default_x, backbone, breathes, airborne, type)]
zoo5 = zoo[:, (default_x, backbone, breathes, airborne, type_)]
self.send_signal(self.widget.Inputs.data, zoo5)
self.assertEqual(attr_x.currentText(), backbone.name)
self.assertEqual(attr_y.currentText(), breathes.name)
self.assertEqual(attr_color.currentText(), type.name)
self.assertEqual(attr_color.currentText(), type_.name)

def test_handle_metas(self):
"""
Expand Down Expand Up @@ -632,29 +646,29 @@ def test_tooltip(self):
widget.tooltip_shows_all = False
self.assertTrue(graph.help_event(event))
(_, text), _ = show_text.call_args
self.assertIn("age = {}".format(data[42, "age"]), text)
self.assertIn("gender = {}".format(data[42, "gender"]), text)
self.assertNotIn("max HR = {}".format(data[42, "max HR"]), text)
self.assertIn(f"age = {data[42, 'age']}", text)
self.assertIn(f"gender = {data[42, 'gender']}", text)
self.assertNotIn(f"max HR = {data[42, 'max HR']}", text)
self.assertNotIn("others", text)

# Show all attributes
widget.tooltip_shows_all = True
self.assertTrue(graph.help_event(event))
(_, text), _ = show_text.call_args
self.assertIn("age = {}".format(data[42, "age"]), text)
self.assertIn("gender = {}".format(data[42, "gender"]), text)
self.assertIn("max HR = {}".format(data[42, "max HR"]), text)
self.assertIn(f"age = {data[42, 'age']}", text)
self.assertIn(f"gender = {data[42, 'gender']}", text)
self.assertIn(f"max HR = {data[42, 'max HR']}", text)
self.assertIn("... and 4 others", text)

# Two points hovered
with patch.object(scatterplot_item, "pointsAt",
return_value=[all_points[42], all_points[100]]):
self.assertTrue(graph.help_event(event))
(_, text), _ = show_text.call_args
self.assertIn("age = {}".format(data[42, "age"]), text)
self.assertIn("gender = {}".format(data[42, "gender"]), text)
self.assertIn("age = {}".format(data[100, "age"]), text)
self.assertIn("gender = {}".format(data[100, "gender"]), text)
self.assertIn(f"age = {data[42, 'age']}", text)
self.assertIn(f"gender = {data[42, 'gender']}", text)
self.assertIn(f"age = {data[100, 'age']}", text)
self.assertIn(f"gender = {data[100, 'gender']}", text)

# No points hovered
with patch.object(scatterplot_item, "pointsAt",
Expand All @@ -680,10 +694,10 @@ def prepare_data():
data.Y = np.array(values * 10, dtype=float)
return data

def assert_equal(data, max):
def assert_equal(data, max_):
self.send_signal(self.widget.Inputs.data, data)
pen_data, brush_data = self.widget.graph.get_colors()
self.assertEqual(max, len(np.unique([id(p) for p in pen_data])), )
pen_data, _ = self.widget.graph.get_colors()
self.assertEqual(max_, len(np.unique([id(p) for p in pen_data])), )

assert_equal(prepare_data(), MAX_COLORS)
# data with nan value
Expand Down Expand Up @@ -1142,7 +1156,7 @@ def test_clear_plot(self):
with excepthook_catch():
self.send_signal(self.widget.Inputs.data, self.data)

def test_visual_settings(self):
def test_visual_settings(self, timeout=DEFAULT_TIMEOUT):
super().test_visual_settings()

graph = self.widget.graph
Expand Down

0 comments on commit 0d13870

Please sign in to comment.