diff --git a/Orange/widgets/unsupervised/owkmeans.py b/Orange/widgets/unsupervised/owkmeans.py index ce0c759fbb1..673e6357b25 100644 --- a/Orange/widgets/unsupervised/owkmeans.py +++ b/Orange/widgets/unsupervised/owkmeans.py @@ -469,8 +469,13 @@ def send_data(self): @Inputs.data @check_sql_input def set_data(self, data): - self.data = data - self.invalidate() + self.data, old_data = data, self.data + + # Do not needlessly recluster the data if X hasn't changed + if old_data and self.data and np.array_equal(self.data.X, old_data.X): + self.send_data() + else: + self.invalidate() def send_report(self): # False positives (Setting is not recognized as int) diff --git a/Orange/widgets/unsupervised/tests/test_owkmeans.py b/Orange/widgets/unsupervised/tests/test_owkmeans.py index 0f876c114db..45d5e4a9507 100644 --- a/Orange/widgets/unsupervised/tests/test_owkmeans.py +++ b/Orange/widgets/unsupervised/tests/test_owkmeans.py @@ -8,7 +8,7 @@ from AnyQt.QtWidgets import QRadioButton import Orange.clustering -from Orange.data import Table +from Orange.data import Table, Domain from Orange.widgets import gui from Orange.widgets.tests.base import WidgetTest from Orange.widgets.unsupervised.owkmeans import OWKMeans, ClusterTableModel @@ -377,6 +377,43 @@ def test_invalidate_clusterings_cancels_jobs(self): self.assertEqual(widget.clusterings, {}) + def test_do_not_recluster_on_same_data(self): + """Do not recluster data points when targets or metas change.""" + + # Prepare some dummy data + x = np.eye(5) + y1, y2 = np.ones((5, 1)), np.ones((5, 2)) + meta1, meta2 = np.ones((5, 1)), np.ones((5, 2)) + + table1 = Table.from_numpy( + domain=Domain.from_numpy(X=x, Y=y1, metas=meta1), + X=x, Y=y1, metas=meta1, + ) + # X is same, should not cause update + table2 = Table.from_numpy( + domain=Domain.from_numpy(X=x, Y=y2, metas=meta2), + X=x, Y=y2, metas=meta2, + ) + # X is different, should cause update + table3 = table1.copy() + table3.X[:, 0] = 1 + + with patch.object(self.widget, 'commit') as commit: + self.send_signal(self.widget.Inputs.data, table1) + self.commit_and_wait() + call_count = commit.call_count + print(call_count) + + # Sending data with same X should not recompute the clustering + self.send_signal(self.widget.Inputs.data, table2) + self.commit_and_wait() + self.assertEqual(call_count, commit.call_count) + + # Sending data with different X should recompute the clustering + self.send_signal(self.widget.Inputs.data, table3) + self.commit_and_wait() + self.assertEqual(call_count + 1, commit.call_count) + if __name__ == "__main__": unittest.main()