diff --git a/Orange/widgets/unsupervised/owhierarchicalclustering.py b/Orange/widgets/unsupervised/owhierarchicalclustering.py index a831d6eb411..cbf95a97670 100644 --- a/Orange/widgets/unsupervised/owhierarchicalclustering.py +++ b/Orange/widgets/unsupervised/owhierarchicalclustering.py @@ -5,7 +5,7 @@ from itertools import chain from contextlib import contextmanager -import numpy +import numpy as np from AnyQt.QtWidgets import ( QGraphicsWidget, QGraphicsObject, QGraphicsLinearLayout, QGraphicsPathItem, @@ -34,7 +34,7 @@ from Orange.widgets.utils import colorpalette, itemmodels from Orange.widgets.utils.annotated_data import (create_annotated_table, ANNOTATED_DATA_SIGNAL_NAME) -from Orange.widgets.widget import Input, Output +from Orange.widgets.widget import Input, Output, Msg __all__ = ["OWHierarchicalClustering"] @@ -605,7 +605,7 @@ def _rescale(self): else: drect = QSizeF(leaf_count, self._root.value.height) - eps = numpy.finfo(numpy.float64).eps + eps = np.finfo(np.float64).eps if abs(drect.width()) < eps: sx = 1.0 @@ -763,6 +763,9 @@ class Outputs: cluster_roles = ["Attribute", "Class variable", "Meta variable"] basic_annotations = ["None", "Enumeration"] + class Error(widget.OWWidget.Error): + not_finite_distances = Msg("Some distances are infinite") + def __init__(self): super().__init__() @@ -962,11 +965,16 @@ def axis_view(orientation): @Inputs.distances def set_distances(self, matrix): self.error() + self.Error.clear() if matrix is not None: N, _ = matrix.shape if N < 2: self.error("Empty distance matrix") matrix = None + if matrix is not None: + if not np.all(np.isfinite(matrix)): + self.Error.not_finite_distances() + matrix = None self.matrix = matrix if matrix is not None: @@ -1135,7 +1143,7 @@ def commit(self): if isinstance(items, Orange.data.Table) and self.matrix.axis == 1: # Select rows - c = numpy.zeros(self.matrix.shape[0]) + c = np.zeros(self.matrix.shape[0]) for i, indices in enumerate(maps): c[indices] = i diff --git a/Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py b/Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py index 588ac41734a..99699fa2f55 100644 --- a/Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py +++ b/Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py @@ -1,7 +1,8 @@ # Test methods with long descriptive names can omit docstrings # pylint: disable=missing-docstring -import numpy +import numpy as np import Orange.misc +from Orange.data import Table, Domain, ContinuousVariable, DiscreteVariable from Orange.distance import Euclidean from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin from Orange.widgets.unsupervised.owhierarchicalclustering import \ @@ -53,7 +54,7 @@ def test_selection_box_output(self): self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data)) def test_all_zero_inputs(self): - d = Orange.misc.DistMatrix(numpy.zeros((10, 10))) + d = Orange.misc.DistMatrix(np.zeros((10, 10))) self.widget.set_distances(d) def test_annotation_settings_retrieval(self): @@ -61,8 +62,8 @@ def test_annotation_settings_retrieval(self): widget = self.widget dist_names = Orange.misc.DistMatrix( - numpy.zeros((4, 4)), self.data, axis=0) - dist_no_names = Orange.misc.DistMatrix(numpy.zeros((10, 10)), axis=1) + np.zeros((4, 4)), self.data, axis=0) + dist_no_names = Orange.misc.DistMatrix(np.zeros((10, 10)), axis=1) self.send_signal(self.widget.Inputs.distances, self.distances) # Check that default is set (class variable) @@ -102,3 +103,23 @@ def test_domain_loses_class(self): data = self.data[:, :4] distances = Euclidean(data) self.send_signal(self.widget.Inputs.distances, distances) + + def test_infinite_distances(self): + """ + Scipy does not accept infinite distances and neither does this widget. + Error is shown. + GH-2380 + """ + table = Table( + Domain( + [ContinuousVariable("a")], + [DiscreteVariable("b", values=["y"])]), + list(zip([1.79e308, -1e120], + "yy")) + ) + distances = Euclidean(table) + self.assertFalse(self.widget.Error.not_finite_distances.is_shown()) + self.send_signal(self.widget.Inputs.distances, distances) + self.assertTrue(self.widget.Error.not_finite_distances.is_shown()) + self.send_signal(self.widget.Inputs.distances, self.distances) + self.assertFalse(self.widget.Error.not_finite_distances.is_shown())