Skip to content

Commit

Permalink
Merge pull request #6702 from VesnaT/empty_dist_mat
Browse files Browse the repository at this point in the history
Distance Matrix: Crash on empty matrix
  • Loading branch information
janezd committed Jan 15, 2024
2 parents f5a0f58 + 5885793 commit e20133c
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 2 deletions.
8 changes: 8 additions & 0 deletions Orange/widgets/unsupervised/owdistancematrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class Outputs:
distances = Output("Distances", DistMatrix, dynamic=False)
table = Output("Selected Data", Table, replaces=["Table"])

class Error(widget.OWWidget.Error):
empty_matrix = widget.Msg("Distance matrix is empty.")

settingsHandler = DistanceMatrixContextHandler()
settings_version = 2
auto_commit = Setting(True)
Expand Down Expand Up @@ -107,7 +110,12 @@ def sizeHint(self):

@Inputs.distances
def set_distances(self, distances):
self.clear_messages()
self.closeContext()
if distances is not None:
if len(distances) == 0:
distances = None
self.Error.empty_matrix()
self.distances = distances
self.tablemodel.set_data(self.distances)
self.items = None
Expand Down
7 changes: 7 additions & 0 deletions Orange/widgets/unsupervised/owdistances.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Warning(OWWidget.Warning):
unsupported_sparse = Msg("Some metrics don't support sparse data\n"
"and were disabled: {}")
imputing_data = Msg("Missing values were imputed")
no_features = Msg("Data has no features")

def __init__(self):
OWWidget.__init__(self)
Expand Down Expand Up @@ -242,11 +243,17 @@ def _check_tractability():
return False
return True

def _check_no_features():
if len(data.domain.attributes) == 0:
self.Warning.no_features()
return True

metric_def = MetricDefs[self.metric_id]
metric = metric_def.metric
self.clear_messages()
if data is not None:
for check in (_check_sparse, _check_tractability,
_check_no_features,
_fix_discrete, _fix_missing, _fix_nonbinary):
if not check():
data = None
Expand Down
7 changes: 7 additions & 0 deletions Orange/widgets/unsupervised/tests/test_owdistancematrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ def test_square_settings(self):
self.assertEqual(widget._get_selection(), (([0], [0, 2]), False))
self.assertEqual(widget.annotation_idx, 0)

def test_empty_matrix(self):
matrix = DistMatrix(np.empty((0, 0)))
self.send_signal(self.widget.Inputs.distances, matrix)
self.assertTrue(self.widget.Error.empty_matrix.is_shown())
self.send_signal(self.widget.Inputs.distances, None)
self.assertFalse(self.widget.Error.empty_matrix.is_shown())


if __name__ == "__main__":
unittest.main()
21 changes: 21 additions & 0 deletions Orange/widgets/unsupervised/tests/test_owdistances.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,27 @@ def test_non_binary_in_metas(self):
out_domain = out.row_items.domain
self.assertEqual(out_domain.metas, (domain["name"], domain["legs"]))

def test_no_features(self):
zoo = Table("zoo")[:5, 16:]

self.send_signal(self.widget.Inputs.data, zoo)
self.wait_until_finished()
out = self.get_output(self.widget.Outputs.distances)
self.assertEqual(out.shape, (5, 5))
self.assertTrue((out == 0).all())
self.assertTrue(self.widget.Warning.no_features.is_shown())

self.widget.controls.axis.buttons[1].click()
self.wait_until_finished()
out = self.get_output(self.widget.Outputs.distances)
self.assertEqual(out.shape, (0, 0))
self.assertTrue((out == 0).all())
self.assertTrue(self.widget.Warning.no_features.is_shown())

self.send_signal(self.widget.Inputs.data, None)
self.wait_until_finished()
self.assertFalse(self.widget.Warning.no_features.is_shown())


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion Orange/widgets/utils/distmatrixmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def set_data(self, distances):
self.beginResetModel()
self.distances = distances
self.__header_data = dict.fromkeys(self.__header_data, LabelData())
if distances is None:
if distances is None or len(distances) == 0:
self.__span = self.colors = self.brushes = None
return
minc = min(0, np.min(distances))
Expand Down
22 changes: 21 additions & 1 deletion Orange/widgets/utils/tests/test_state_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
import numpy as np

from AnyQt.QtCore import Qt
from AnyQt.QtWidgets import QTableView

from orangecanvas.scheme.signalmanager import LazyValue
from orangewidget.utils.signals import summarize

from Orange.data import Table, Domain, StringVariable, ContinuousVariable, \
DiscreteVariable, TimeVariable
from Orange.misc import DistMatrix
from Orange.widgets.tests.base import WidgetTest
from Orange.widgets.utils.state_summary import format_summary_details, \
format_multiple_summaries
format_multiple_summaries, summarize_matrix

VarDataPair = namedtuple('VarDataPair', ['variable', 'data'])

Expand Down Expand Up @@ -315,5 +317,23 @@ def test_summarize_lazy_table(self, previewer):
previewer.assert_called_with(data)


class TestSummarizeMatrix(WidgetTest):
def test_summarize_matrix(self):
matrix = DistMatrix(np.arange(9).reshape(3, 3))
summary = summarize_matrix(matrix)
self.assertEqual(summary.summary, "3脳3")
self.assertEqual(summary.details, "<nobr>3脳3 distance matrix</nobr>")
view = summary.preview_func()
self.assertIsInstance(view, QTableView)

def test_summarize_matrix_empty(self):
matrix = DistMatrix(np.empty((0, 0)))
summary = summarize_matrix(matrix)
self.assertEqual(summary.summary, "0脳0")
self.assertEqual(summary.details, "<nobr>0脳0 distance matrix</nobr>")
view = summary.preview_func()
self.assertIsInstance(view, QTableView)


if __name__ == "__main__":
unittest.main()

0 comments on commit e20133c

Please sign in to comment.