Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] Distances: prevent inf numbers #2380

Merged
merged 2 commits into from
Jun 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Orange/widgets/unsupervised/owhierarchicalclustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"]

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__()

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
29 changes: 25 additions & 4 deletions Orange/widgets/unsupervised/tests/test_owhierarchicalclustering.py
Original file line number Diff line number Diff line change
@@ -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 \
Expand Down Expand Up @@ -53,16 +54,16 @@ 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):
"""Check whether widget retrieves correct settings for annotation"""
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)
Expand Down Expand Up @@ -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())