diff --git a/Orange/widgets/classify/owclassificationtree.py b/Orange/widgets/classify/owclassificationtree.py index f0a1accf7c2..eb95da3306c 100644 --- a/Orange/widgets/classify/owclassificationtree.py +++ b/Orange/widgets/classify/owclassificationtree.py @@ -2,8 +2,10 @@ from Orange.data import Table from Orange.modelling.tree import TreeLearner +from Orange.classification.tree import TreeLearner as ClassificationTreeLearner from Orange.widgets.model.owtree import OWTreeLearner from Orange.widgets.utils.owlearnerwidget import OWBaseLearner +from Orange.widgets.widget import Msg class OWTreeLearner(OWTreeLearner): @@ -21,6 +23,29 @@ class OWTreeLearner(OWTreeLearner): "limit_majority", "sufficient_majority", 51, 100),) + \ OWTreeLearner.spin_boxes[-1:] + class Error(OWTreeLearner.Error): + cannot_binarize = Msg("Binarization cannot handle '{}'\n" + "because it has {} values. " + "Binarization can handle up to {}.\n" + "Disable 'Induce binary tree' to proceed.") + + def check_data(self): + self.Error.cannot_binarize.clear() + if not super().check_data(): + return False + if not self.binary_trees: + return True + max_values, max_attr = max( + ((len(attr.values), attr) + for attr in self.data.domain.attributes if attr.is_discrete), + default=(0, None)) + MAX_BINARIZATION = ClassificationTreeLearner.MAX_BINARIZATION + if max_values > MAX_BINARIZATION: + self.Error.cannot_binarize( + max_attr.name, max_values, MAX_BINARIZATION) + return False + return True + def learner_kwargs(self): opts = super().learner_kwargs() opts['sufficient_majority'] = \ diff --git a/Orange/widgets/classify/tests/test_owclassificationtree.py b/Orange/widgets/classify/tests/test_owclassificationtree.py index 24e2af13e18..0445023d7d4 100644 --- a/Orange/widgets/classify/tests/test_owclassificationtree.py +++ b/Orange/widgets/classify/tests/test_owclassificationtree.py @@ -1,5 +1,7 @@ # Test methods with long descriptive names can omit docstrings # pylint: disable=missing-docstring +from Orange.data import Table, Domain, DiscreteVariable +from Orange.classification.tree import TreeLearner as ClassificationTreeLearner from Orange.base import Model from Orange.widgets.classify.owclassificationtree import OWTreeLearner from Orange.widgets.tests.base import (WidgetTest, DefaultParameterMapping, @@ -7,6 +9,11 @@ class TestOWClassificationTree(WidgetTest, WidgetLearnerTestMixin): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.iris = Table("iris") + def setUp(self): self.widget = self.create_widget( OWTreeLearner, stored_settings={"auto_apply": False}) @@ -34,3 +41,50 @@ def test_parameters_unchecked(self): for par, val in zip(self.parameters, (None, 2, 1))] self.test_parameters() + def test_cannot_binarize(self): + widget = self.widget + error_shown = widget.Error.cannot_binarize.is_shown + self.assertFalse(error_shown()) + self.send_signal("Data", self.iris) + + # The widget outputs ClassificationTreeLearner. + # If not, below tests may not make sense + learner = self.get_output("Learner") + dlearner = learner.get_learner(learner.CLASSIFICATION) + self.assertTrue(dlearner, ClassificationTreeLearner) + + # No error on Iris + max_binarization = dlearner.MAX_BINARIZATION + self.assertFalse(error_shown()) + + # Error when too many values + domain = Domain([ + DiscreteVariable( + values=[str(x) for x in range(max_binarization + 1)])], + DiscreteVariable(values="01")) + self.send_signal("Data", Table(domain, [[0, 0], [1, 1]])) + self.assertTrue(error_shown()) + # No more error on Iris + self.send_signal("Data", self.iris) + self.assertFalse(error_shown()) + + # Checking and unchecking binarization works + widget.controls.binary_trees.click() + self.assertFalse(widget.binary_trees) + widget.unconditional_apply() + self.send_signal("Data", Table(domain, [[0, 0], [1, 1]])) + self.assertFalse(error_shown()) + widget.controls.binary_trees.click() + widget.unconditional_apply() + self.assertTrue(error_shown()) + widget.controls.binary_trees.click() + widget.unconditional_apply() + self.assertFalse(error_shown()) + + # If something is wrong with the data, no error appears + domain = Domain([ + DiscreteVariable( + values=[str(x) for x in range(max_binarization + 1)])], + DiscreteVariable(values="01")) + self.send_signal("Data", Table(domain)) + self.assertFalse(error_shown()) diff --git a/Orange/widgets/utils/messages.py b/Orange/widgets/utils/messages.py index a8ef0b20e79..d7109f59c9e 100644 --- a/Orange/widgets/utils/messages.py +++ b/Orange/widgets/utils/messages.py @@ -335,7 +335,7 @@ def update_message_state(self): elif self.message_bar is not None: font_size = self.message_bar.fontInfo().pixelSize() group = messages[0].group - text = str(messages[0]) if len(messages) == 1 \ + text = str(messages[0]).split("\n")[0] if len(messages) == 1 \ else "{} problems during execution".format(len(messages)) # TODO: fix tooltip background color - it is not white tooltip = ''.join( @@ -346,7 +346,8 @@ def update_message_state(self):    

'''. - format(msg.group.bar_background, font_size, str(msg)) + format(msg.group.bar_background, font_size, + str(msg).replace("\n", "
    ")) for msg in messages) self._set_message_bar(group, text, tooltip)