Skip to content

Commit 47647c2

Browse files
authored
Merge pull request #2380 from jerneju/inf-distances
[FIX] Distances: prevent inf numbers
2 parents 5add0a5 + 510901e commit 47647c2

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

Orange/widgets/unsupervised/owhierarchicalclustering.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from itertools import chain
66
from contextlib import contextmanager
77

8-
import numpy
8+
import numpy as np
99

1010
from AnyQt.QtWidgets import (
1111
QGraphicsWidget, QGraphicsObject, QGraphicsLinearLayout, QGraphicsPathItem,
@@ -34,7 +34,7 @@
3434
from Orange.widgets.utils import colorpalette, itemmodels
3535
from Orange.widgets.utils.annotated_data import (create_annotated_table,
3636
ANNOTATED_DATA_SIGNAL_NAME)
37-
from Orange.widgets.widget import Input, Output
37+
from Orange.widgets.widget import Input, Output, Msg
3838

3939
__all__ = ["OWHierarchicalClustering"]
4040

@@ -605,7 +605,7 @@ def _rescale(self):
605605
else:
606606
drect = QSizeF(leaf_count, self._root.value.height)
607607

608-
eps = numpy.finfo(numpy.float64).eps
608+
eps = np.finfo(np.float64).eps
609609

610610
if abs(drect.width()) < eps:
611611
sx = 1.0
@@ -763,6 +763,9 @@ class Outputs:
763763
cluster_roles = ["Attribute", "Class variable", "Meta variable"]
764764
basic_annotations = ["None", "Enumeration"]
765765

766+
class Error(widget.OWWidget.Error):
767+
not_finite_distances = Msg("Some distances are infinite")
768+
766769
def __init__(self):
767770
super().__init__()
768771

@@ -962,11 +965,16 @@ def axis_view(orientation):
962965
@Inputs.distances
963966
def set_distances(self, matrix):
964967
self.error()
968+
self.Error.clear()
965969
if matrix is not None:
966970
N, _ = matrix.shape
967971
if N < 2:
968972
self.error("Empty distance matrix")
969973
matrix = None
974+
if matrix is not None:
975+
if not np.all(np.isfinite(matrix)):
976+
self.Error.not_finite_distances()
977+
matrix = None
970978

971979
self.matrix = matrix
972980
if matrix is not None:
@@ -1135,7 +1143,7 @@ def commit(self):
11351143

11361144
if isinstance(items, Orange.data.Table) and self.matrix.axis == 1:
11371145
# Select rows
1138-
c = numpy.zeros(self.matrix.shape[0])
1146+
c = np.zeros(self.matrix.shape[0])
11391147

11401148
for i, indices in enumerate(maps):
11411149
c[indices] = i

Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Test methods with long descriptive names can omit docstrings
22
# pylint: disable=missing-docstring
3-
import numpy
3+
import numpy as np
44
import Orange.misc
5+
from Orange.data import Table, Domain, ContinuousVariable, DiscreteVariable
56
from Orange.distance import Euclidean
67
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin
78
from Orange.widgets.unsupervised.owhierarchicalclustering import \
@@ -53,16 +54,16 @@ def test_selection_box_output(self):
5354
self.assertIsNone(self.get_output(self.widget.Outputs.annotated_data))
5455

5556
def test_all_zero_inputs(self):
56-
d = Orange.misc.DistMatrix(numpy.zeros((10, 10)))
57+
d = Orange.misc.DistMatrix(np.zeros((10, 10)))
5758
self.widget.set_distances(d)
5859

5960
def test_annotation_settings_retrieval(self):
6061
"""Check whether widget retrieves correct settings for annotation"""
6162
widget = self.widget
6263

6364
dist_names = Orange.misc.DistMatrix(
64-
numpy.zeros((4, 4)), self.data, axis=0)
65-
dist_no_names = Orange.misc.DistMatrix(numpy.zeros((10, 10)), axis=1)
65+
np.zeros((4, 4)), self.data, axis=0)
66+
dist_no_names = Orange.misc.DistMatrix(np.zeros((10, 10)), axis=1)
6667

6768
self.send_signal(self.widget.Inputs.distances, self.distances)
6869
# Check that default is set (class variable)
@@ -102,3 +103,23 @@ def test_domain_loses_class(self):
102103
data = self.data[:, :4]
103104
distances = Euclidean(data)
104105
self.send_signal(self.widget.Inputs.distances, distances)
106+
107+
def test_infinite_distances(self):
108+
"""
109+
Scipy does not accept infinite distances and neither does this widget.
110+
Error is shown.
111+
GH-2380
112+
"""
113+
table = Table(
114+
Domain(
115+
[ContinuousVariable("a")],
116+
[DiscreteVariable("b", values=["y"])]),
117+
list(zip([1.79e308, -1e120],
118+
"yy"))
119+
)
120+
distances = Euclidean(table)
121+
self.assertFalse(self.widget.Error.not_finite_distances.is_shown())
122+
self.send_signal(self.widget.Inputs.distances, distances)
123+
self.assertTrue(self.widget.Error.not_finite_distances.is_shown())
124+
self.send_signal(self.widget.Inputs.distances, self.distances)
125+
self.assertFalse(self.widget.Error.not_finite_distances.is_shown())

0 commit comments

Comments
 (0)