From b83787845d08dda881531760a7653ba3c92b2eca Mon Sep 17 00:00:00 2001 From: astaric Date: Fri, 7 Oct 2016 16:45:38 +0200 Subject: [PATCH 1/2] BoxPlot: Do not crash on empty variables --- Orange/widgets/visualize/owboxplot.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Orange/widgets/visualize/owboxplot.py b/Orange/widgets/visualize/owboxplot.py index 1d7021cab00..1aa4e4e8ba7 100644 --- a/Orange/widgets/visualize/owboxplot.py +++ b/Orange/widgets/visualize/owboxplot.py @@ -497,8 +497,11 @@ def mean_label(self, stat, attr, val_name): def draw_axis(self): """Draw the horizontal axis and sets self.scale_x""" - bottom = min(stat.a_min for stat in self.stats) - top = max(stat.a_max for stat in self.stats) + misssing_stats = not self.stats + stats = self.stats or [BoxData(np.array([[0.], [1.]]))] + mean_labels = self.mean_labels or [self.mean_label(stats[0], self.attribute, "")] + bottom = min(stat.a_min for stat in stats) + top = max(stat.a_max for stat in stats) first_val, step = compute_scale(bottom, top) while bottom <= first_val: @@ -507,8 +510,8 @@ def draw_axis(self): no_ticks = math.ceil((top - first_val) / step) + 1 top = max(top, first_val + no_ticks * step) - gbottom = min(bottom, min(stat.mean - stat.dev for stat in self.stats)) - gtop = max(top, max(stat.mean + stat.dev for stat in self.stats)) + gbottom = min(bottom, min(stat.mean - stat.dev for stat in stats)) + gtop = max(top, max(stat.mean + stat.dev for stat in stats)) bv = self.box_view viewrect = bv.viewport().rect().adjusted(15, 15, -15, -30) @@ -517,7 +520,7 @@ def draw_axis(self): # In principle we should repeat this until convergence since the new # scaling is too conservative. (No chance am I doing this.) mlb = min(stat.mean + mean_lab.min_x / scale_x - for stat, mean_lab in zip(self.stats, self.mean_labels)) + for stat, mean_lab in zip(stats, mean_labels)) if mlb < gbottom: gbottom = mlb self.scale_x = scale_x = viewrect.width() / (gtop - gbottom) @@ -530,8 +533,10 @@ def draw_axis(self): l = self.box_scene.addLine(val * scale_x, -1, val * scale_x, 1, self._pen_axis_tick) l.setZValue(100) + t = self.box_scene.addSimpleText( - self.attribute.repr_val(val), self._axis_font) + self.attribute.repr_val(val) if not misssing_stats else "?", + self._axis_font) t.setFlags( t.flags() | QtGui.QGraphicsItem.ItemIgnoresTransformations) r = t.boundingRect() From 953278ec93184bad7586b69aa55ef1f087419edd Mon Sep 17 00:00:00 2001 From: Vesna Tanko Date: Fri, 7 Oct 2016 12:28:04 +0200 Subject: [PATCH 2/2] OWBoxPlot: Fix crash with missing values --- .../widgets/visualize/tests/test_owboxplot.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 Orange/widgets/visualize/tests/test_owboxplot.py diff --git a/Orange/widgets/visualize/tests/test_owboxplot.py b/Orange/widgets/visualize/tests/test_owboxplot.py new file mode 100644 index 00000000000..ac45cd51ea8 --- /dev/null +++ b/Orange/widgets/visualize/tests/test_owboxplot.py @@ -0,0 +1,55 @@ +# Test methods with long descriptive names can omit docstrings +# pylint: disable=missing-docstring +from unittest import skip + +import numpy as np +from Orange.data import Table, ContinuousVariable +from Orange.widgets.visualize.owboxplot import OWBoxPlot +from Orange.widgets.tests.base import WidgetTest + + +class TestOWBoxPlot(WidgetTest): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = Table("iris") + cls.zoo = Table("zoo") + cls.housing = Table("housing") + + def setUp(self): + self.widget = self.create_widget(OWBoxPlot) + + @skip("Known bug, FIXME!") + def test_input_data(self): + """Check widget's data""" + self.send_signal("Data", self.iris) + self.assertGreater(len(self.widget.attrs), 0) + self.send_signal("Data", None) + self.assertEqual(len(self.widget.attrs), 0) + + def test_input_data_missings_cont_group_var(self): + """Check widget with continuous data with missing values and group variable""" + data = self.iris + data.X[:, 0] = np.nan + self.send_signal("Data", data) + # used to crash, see #1568 + + def test_input_data_missings_cont_no_group_var(self): + """Check widget with continuous data with missing values and no group variable""" + data = self.housing + data.X[:, 0] = np.nan + self.send_signal("Data", data) + # used to crash, see #1568 + + def test_input_data_missings_disc_group_var(self): + """Check widget with discrete data with missing values and group variable""" + data = self.zoo + data.X[:, 0] = np.nan + self.send_signal("Data", data) + + def test_input_data_missings_disc_no_group_var(self): + """Check widget discrete data with missing values and no group variable""" + data = self.zoo + data.domain.class_var = ContinuousVariable("cls") + data.X[:, 0] = np.nan + self.send_signal("Data", data)