Skip to content

Commit b6c7c36

Browse files
committed
OWBoxPlot: Fix crash when continuous attributes appear only in metas
1 parent 709dfdf commit b6c7c36

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

Orange/data/domain.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,25 +283,35 @@ def index(self, var):
283283
except KeyError:
284284
raise ValueError("'%s' is not in domain" % var)
285285

286-
def has_discrete_attributes(self, include_class=False):
286+
def has_discrete_attributes(self, include_class=False, include_metas=False):
287287
"""
288-
Return `True` if domain has any discrete attributes. If `include_class`
289-
is set, the check includes the class attribute(s).
288+
Return `True` if domain has any discrete attributes. If
289+
`include_class` is set, the check includes the class attribute(s). If
290+
`include_metas` is set, the check includes the meta attributes.
290291
"""
291292
if not include_class:
292-
return any(var.is_discrete for var in self.attributes)
293+
attributes = list(self.attributes) if not include_metas \
294+
else list(self.attributes) + list(self.metas)
295+
return any(var.is_discrete for var in attributes)
293296
else:
294-
return any(var.is_discrete for var in self.variables)
297+
variables = list(self.variables) if not include_metas \
298+
else list(self.variables) + list(self.metas)
299+
return any(var.is_discrete for var in variables)
295300

296-
def has_continuous_attributes(self, include_class=False):
301+
def has_continuous_attributes(self, include_class=False, include_metas=False):
297302
"""
298303
Return `True` if domain has any continuous attributes. If
299-
`include_class` is set, the check includes the class attribute(s).
304+
`include_class` is set, the check includes the class attribute(s). If
305+
`include_metas` is set, the check includes the meta attributes.
300306
"""
301307
if not include_class:
302-
return any(var.is_continuous for var in self.attributes)
308+
attributes = list(self.attributes) if not include_metas \
309+
else list(self.attributes) + list(self.metas)
310+
return any(var.is_continuous for var in attributes)
303311
else:
304-
return any(var.is_continuous for var in self.variables)
312+
variables = list(self.variables) if not include_metas \
313+
else list(self.variables) + list(self.metas)
314+
return any(var.is_continuous for var in variables)
305315

306316
@property
307317
def has_continuous_class(self):

Orange/tests/test_domain.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ def test_has_discrete(self):
310310
self.assertTrue(Domain([race], age).has_discrete_attributes(True))
311311
self.assertTrue(Domain([], [race, age]).has_discrete_attributes(True))
312312

313+
d = Domain([], None, [gender])
314+
self.assertTrue(d.has_discrete_attributes(False, True))
315+
d = Domain([], None, [age])
316+
self.assertFalse(d.has_discrete_attributes(False, True))
317+
d = Domain([], [age], [gender])
318+
self.assertTrue(d.has_discrete_attributes(True, True))
319+
d = Domain([], [incomeA], [age])
320+
self.assertFalse(d.has_discrete_attributes(True, True))
321+
313322
def test_has_continuous(self):
314323
self.assertFalse(Domain([]).has_continuous_attributes())
315324
self.assertFalse(Domain([], [age]).has_continuous_attributes())
@@ -328,6 +337,15 @@ def test_has_continuous(self):
328337
self.assertTrue(Domain([race], age).has_continuous_attributes(True))
329338
self.assertTrue(Domain([], [race, age]).has_continuous_attributes(True))
330339

340+
d = Domain([], None, [age])
341+
self.assertTrue(d.has_continuous_attributes(False, True))
342+
d = Domain([], None, [gender])
343+
self.assertFalse(d.has_continuous_attributes(False, True))
344+
d = Domain([], [gender], [age])
345+
self.assertTrue(d.has_continuous_attributes(True, True))
346+
d = Domain([], [race], [gender])
347+
self.assertFalse(d.has_continuous_attributes(True, True))
348+
331349
def test_get_conversion(self):
332350
compute_value = lambda: 42
333351
new_income = income.copy(compute_value=compute_value)

Orange/widgets/visualize/owboxplot.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def compute_score(attr):
289289
return 3
290290
if attr.is_continuous:
291291
# One-way ANOVA
292-
col = data.get_column_view(attr)[0]
292+
col = data.get_column_view(attr)[0].astype(float)
293293
groups = (col[group_col == i] for i in range(n_groups))
294294
groups = (col[~np.isnan(col)] for col in groups)
295295
groups = [group for group in groups if len(group)]
@@ -320,9 +320,9 @@ def compute_score(attr):
320320
group_var = self.group_var
321321
if self.order_by_importance and group_var is not None:
322322
n_groups = len(group_var.values)
323-
group_col = data.get_column_view(group_var)[0] \
324-
if domain.has_continuous_attributes(include_class=True) \
325-
else None
323+
group_col = data.get_column_view(group_var)[0] if \
324+
domain.has_continuous_attributes(
325+
include_class=True, include_metas=True) else None
326326
self.attrs.sort(key=compute_score)
327327
else:
328328
self.attrs[:] = chain(

Orange/widgets/visualize/tests/test_owboxplot.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import numpy as np
55

6-
from Orange.data import Table, ContinuousVariable
6+
from Orange.data import Table, ContinuousVariable, StringVariable, Domain
77
from Orange.widgets.visualize.owboxplot import OWBoxPlot, FilterGraphicsRectItem
88
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin
99

@@ -113,3 +113,11 @@ def _select_data(self):
113113
return [100, 103, 104, 108, 110, 111, 112, 115, 116,
114114
120, 123, 124, 126, 128, 132, 133, 136, 137,
115115
139, 140, 141, 143, 144, 145, 146, 147, 148]
116+
117+
def test_continuous_metas(self):
118+
domain = self.iris.domain
119+
metas = domain.attributes[:-1] + (StringVariable("str"),)
120+
domain = Domain([], domain.class_var, metas)
121+
data = Table.from_table(domain, self.iris)
122+
self.send_signal("Data", data)
123+
self.widget.controls.order_by_importance.setChecked(True)

0 commit comments

Comments
 (0)