Repository: biolab/orange3

Issue:
Heat Map doesn't output a specific selection
<!--
This is an issue template. Please fill in the relevant details in the
sections below.

Wrap code and verbatim terminal window output into triple backticks, see:
https://help.github.com/articles/basic-writing-and-formatting-syntax/#quoting-code

If you're raising an issue about an add-on (e.g. installed via
Options > Add-ons), raise an issue on the relevant add-on's issue
tracker instead. See: https://github.com/biolab?q=orange3
-->

##### Orange version
<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->
3.10.dev

##### Expected behavior
Heat Map outputs data subset when selected in the plot.


##### Actual behavior
Heat Map fails to output only some instances.


##### Steps to reproduce the behavior
File - Heat Map - Data Table
Cluster by k-means. Select bottom part of yellow instances.

##### Additional info (worksheets, data, screenshots, ...)
Data set available for testing. Ask in comment.

<img width="1252" alt="screen shot 2018-02-14 at 11 54 20" src="https://user-images.githubusercontent.com/12524972/36201036-ed70c148-117e-11e8-88bb-57ddabc5dd68.png">



Discussion:

Resolving patch:
diff --git a/Orange/widgets/visualize/owheatmap.py b/Orange/widgets/visualize/owheatmap.py
--- a/Orange/widgets/visualize/owheatmap.py
+++ b/Orange/widgets/visualize/owheatmap.py
@@ -444,6 +444,9 @@ class Error(widget.OWWidget.Error):
             "Not enough instances for k-means merging")
         not_enough_memory = Msg("Not enough memory to show this data")
 
+    class Warning(widget.OWWidget.Warning):
+        empty_clusters = Msg("Empty clusters were removed")
+
     def __init__(self):
         super().__init__()
 
@@ -863,11 +866,17 @@ def construct_heatmaps(self, data, split_label=None):
                 nclust = min(self.merge_kmeans_k, len(effective_data) - 1)
                 self.kmeans_model = kmeans_compress(effective_data, k=nclust)
                 effective_data.domain = self.kmeans_model.pre_domain
-                self.merge_indices = [np.flatnonzero(self.kmeans_model.labels_ == ind)
-                                      for ind in range(nclust)]
+                merge_indices = [np.flatnonzero(self.kmeans_model.labels_ == ind)
+                                 for ind in range(nclust)]
+                not_empty_indices = [i for i, x in enumerate(merge_indices)
+                                     if len(x) > 0]
+                self.merge_indices = \
+                    [merge_indices[i] for i in not_empty_indices]
+                if len(merge_indices) != len(self.merge_indices):
+                    self.Warning.empty_clusters()
                 effective_data = Orange.data.Table(
                     Orange.data.Domain(effective_data.domain.attributes),
-                    self.kmeans_model.centroids
+                    self.kmeans_model.centroids[not_empty_indices]
                 )
             else:
                 effective_data = self.effective_data


Test patch:
diff --git a/Orange/widgets/visualize/tests/test_owheatmap.py b/Orange/widgets/visualize/tests/test_owheatmap.py
--- a/Orange/widgets/visualize/tests/test_owheatmap.py
+++ b/Orange/widgets/visualize/tests/test_owheatmap.py
@@ -1,5 +1,7 @@
 # Test methods with long descriptive names can omit docstrings
 # pylint: disable=missing-docstring
+import numpy as np
+
 from Orange.data import Table, Domain, DiscreteVariable, ContinuousVariable
 from Orange.preprocess import Continuize
 from Orange.widgets.visualize.owheatmap import OWHeatMap
@@ -126,3 +128,15 @@ def test_cluster_column_on_all_zero_column(self):
 
         self.widget.col_clustering = True
         self.widget.set_dataset(iris)
+
+    def test_empty_clusters(self):
+        """Test if empty clusters are not displayed and warning is shown"""
+        data = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
+
+        table = Table.from_numpy(Domain([ContinuousVariable()]),
+                                 data.reshape((9, 1)))
+        self.widget.controls.merge_kmeans.setChecked(True)
+        self.send_signal(self.widget.Inputs.data, table)
+
+        self.assertTrue(self.widget.Warning.empty_clusters.is_shown())
+        self.assertEqual(len(self.widget.merge_indices), 3)