Skip to content

Commit bf51f51

Browse files
authored
Merge pull request #1647 from astaric/fix-boxplot
[FIX] BoxPlot crashes on variables with no known values (Fixes #1568)
2 parents 3f82460 + 953278e commit bf51f51

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

Orange/widgets/visualize/owboxplot.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,11 @@ def mean_label(self, stat, attr, val_name):
497497

498498
def draw_axis(self):
499499
"""Draw the horizontal axis and sets self.scale_x"""
500-
bottom = min(stat.a_min for stat in self.stats)
501-
top = max(stat.a_max for stat in self.stats)
500+
misssing_stats = not self.stats
501+
stats = self.stats or [BoxData(np.array([[0.], [1.]]))]
502+
mean_labels = self.mean_labels or [self.mean_label(stats[0], self.attribute, "")]
503+
bottom = min(stat.a_min for stat in stats)
504+
top = max(stat.a_max for stat in stats)
502505

503506
first_val, step = compute_scale(bottom, top)
504507
while bottom <= first_val:
@@ -507,8 +510,8 @@ def draw_axis(self):
507510
no_ticks = math.ceil((top - first_val) / step) + 1
508511
top = max(top, first_val + no_ticks * step)
509512

510-
gbottom = min(bottom, min(stat.mean - stat.dev for stat in self.stats))
511-
gtop = max(top, max(stat.mean + stat.dev for stat in self.stats))
513+
gbottom = min(bottom, min(stat.mean - stat.dev for stat in stats))
514+
gtop = max(top, max(stat.mean + stat.dev for stat in stats))
512515

513516
bv = self.box_view
514517
viewrect = bv.viewport().rect().adjusted(15, 15, -15, -30)
@@ -517,7 +520,7 @@ def draw_axis(self):
517520
# In principle we should repeat this until convergence since the new
518521
# scaling is too conservative. (No chance am I doing this.)
519522
mlb = min(stat.mean + mean_lab.min_x / scale_x
520-
for stat, mean_lab in zip(self.stats, self.mean_labels))
523+
for stat, mean_lab in zip(stats, mean_labels))
521524
if mlb < gbottom:
522525
gbottom = mlb
523526
self.scale_x = scale_x = viewrect.width() / (gtop - gbottom)
@@ -530,8 +533,10 @@ def draw_axis(self):
530533
l = self.box_scene.addLine(val * scale_x, -1, val * scale_x, 1,
531534
self._pen_axis_tick)
532535
l.setZValue(100)
536+
533537
t = self.box_scene.addSimpleText(
534-
self.attribute.repr_val(val), self._axis_font)
538+
self.attribute.repr_val(val) if not misssing_stats else "?",
539+
self._axis_font)
535540
t.setFlags(
536541
t.flags() | QtGui.QGraphicsItem.ItemIgnoresTransformations)
537542
r = t.boundingRect()
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Test methods with long descriptive names can omit docstrings
2+
# pylint: disable=missing-docstring
3+
from unittest import skip
4+
5+
import numpy as np
6+
from Orange.data import Table, ContinuousVariable
7+
from Orange.widgets.visualize.owboxplot import OWBoxPlot
8+
from Orange.widgets.tests.base import WidgetTest
9+
10+
11+
class TestOWBoxPlot(WidgetTest):
12+
@classmethod
13+
def setUpClass(cls):
14+
super().setUpClass()
15+
cls.iris = Table("iris")
16+
cls.zoo = Table("zoo")
17+
cls.housing = Table("housing")
18+
19+
def setUp(self):
20+
self.widget = self.create_widget(OWBoxPlot)
21+
22+
@skip("Known bug, FIXME!")
23+
def test_input_data(self):
24+
"""Check widget's data"""
25+
self.send_signal("Data", self.iris)
26+
self.assertGreater(len(self.widget.attrs), 0)
27+
self.send_signal("Data", None)
28+
self.assertEqual(len(self.widget.attrs), 0)
29+
30+
def test_input_data_missings_cont_group_var(self):
31+
"""Check widget with continuous data with missing values and group variable"""
32+
data = self.iris
33+
data.X[:, 0] = np.nan
34+
self.send_signal("Data", data)
35+
# used to crash, see #1568
36+
37+
def test_input_data_missings_cont_no_group_var(self):
38+
"""Check widget with continuous data with missing values and no group variable"""
39+
data = self.housing
40+
data.X[:, 0] = np.nan
41+
self.send_signal("Data", data)
42+
# used to crash, see #1568
43+
44+
def test_input_data_missings_disc_group_var(self):
45+
"""Check widget with discrete data with missing values and group variable"""
46+
data = self.zoo
47+
data.X[:, 0] = np.nan
48+
self.send_signal("Data", data)
49+
50+
def test_input_data_missings_disc_no_group_var(self):
51+
"""Check widget discrete data with missing values and no group variable"""
52+
data = self.zoo
53+
data.domain.class_var = ContinuousVariable("cls")
54+
data.X[:, 0] = np.nan
55+
self.send_signal("Data", data)

0 commit comments

Comments
 (0)