Repository: biolab/orange3

Issue:
Silhouette Plot could output silhouette score
**Is your feature request related to a problem? Please describe.**
Silhouette Plot uses categorized data to compute cluster centrality of data instances. It visualizes this score as a silhouette plot, where data instances can be selected manually. It outputs the selection, or the whole data table with a selection indicator. Strangely, the output lacks the new meta column that would report the silhouette score.

**Describe the solution you'd like**
Both outputs of this widget should include a new meta attribute called "Silhouette" with the silhouette value of data instances.


Discussion:
There's a checkbox "Add silhouette scores" just above the send button. It doesn't work for you?

Resolving patch:
diff --git a/Orange/widgets/visualize/owsilhouetteplot.py b/Orange/widgets/visualize/owsilhouetteplot.py
--- a/Orange/widgets/visualize/owsilhouetteplot.py
+++ b/Orange/widgets/visualize/owsilhouetteplot.py
@@ -89,7 +89,6 @@ class Outputs:
     #: A fixed size for an instance bar
     bar_size = settings.Setting(3)
     #: Add silhouette scores to output data
-    add_scores = settings.Setting(False)
     auto_commit = settings.Setting(True)
 
     pending_selection = settings.Setting(None, schema_only=True)
@@ -176,11 +175,7 @@ def __init__(self):
 
         gui.rubber(self.controlArea)
 
-        gui.separator(self.buttonsArea)
-        box = gui.vBox(self.buttonsArea, "Output")
-        # Thunk the call to commit to call conditional commit
-        gui.checkBox(box, self, "add_scores", "Add silhouette scores",
-                     callback=lambda: self.commit())
+        box = gui.vBox(self.buttonsArea, box=True)
         gui.auto_send(box, self, "auto_commit", box=False)
         # Ensure that the controlArea is not narrower than buttonsArea
         self.controlArea.layout().addWidget(self.buttonsArea)
@@ -499,28 +494,22 @@ def commit(self):
             else:
                 scores = self._silhouette
 
-            silhouette_var = None
-            if self.add_scores:
-                var = self.cluster_var_model[self.cluster_var_idx]
-                silhouette_var = Orange.data.ContinuousVariable(
-                    "Silhouette ({})".format(escape(var.name)))
-                domain = Orange.data.Domain(
-                    self.data.domain.attributes,
-                    self.data.domain.class_vars,
-                    self.data.domain.metas + (silhouette_var, ))
-                data = self.data.transform(domain)
-            else:
-                domain = self.data.domain
-                data = self.data
+            var = self.cluster_var_model[self.cluster_var_idx]
+            silhouette_var = Orange.data.ContinuousVariable(
+                "Silhouette ({})".format(escape(var.name)))
+            domain = Orange.data.Domain(
+                self.data.domain.attributes,
+                self.data.domain.class_vars,
+                self.data.domain.metas + (silhouette_var, ))
+            data = self.data.transform(domain)
 
             if np.count_nonzero(selectedmask):
                 selected = self.data.from_table(
                     domain, self.data, np.flatnonzero(selectedmask))
 
-            if self.add_scores:
-                if selected is not None:
-                    selected[:, silhouette_var] = np.c_[scores[selectedmask]]
-                data[:, silhouette_var] = np.c_[scores]
+            if selected is not None:
+                selected[:, silhouette_var] = np.c_[scores[selectedmask]]
+            data[:, silhouette_var] = np.c_[scores]
 
         self.Outputs.selected_data.send(selected)
         self.Outputs.annotated_data.send(create_annotated_table(data, indices))


Test patch:
diff --git a/Orange/widgets/visualize/tests/test_owsilhouetteplot.py b/Orange/widgets/visualize/tests/test_owsilhouetteplot.py
--- a/Orange/widgets/visualize/tests/test_owsilhouetteplot.py
+++ b/Orange/widgets/visualize/tests/test_owsilhouetteplot.py
@@ -20,6 +20,7 @@ class TestOWSilhouettePlot(WidgetTest, WidgetOutputsTestMixin):
     def setUpClass(cls):
         super().setUpClass()
         WidgetOutputsTestMixin.init(cls)
+        cls.same_input_output_domain = False
 
         cls.signal_name = "Data"
         cls.signal_data = cls.data
@@ -37,7 +38,6 @@ def test_no_data(self):
     def test_outputs_add_scores(self):
         # check output when appending scores
         self.send_signal(self.widget.Inputs.data, self.data)
-        self.widget.controls.add_scores.setChecked(1)
         selected_indices = self._select_data()
         selected = self.get_output(self.widget.Outputs.selected_data)
         annotated = self.get_output(self.widget.Outputs.annotated_data)
@@ -63,7 +63,6 @@ def test_insufficient_clusters(self):
         self.assertTrue(self.widget.Error.singleton_clusters_all.is_shown())
 
     def test_unknowns_in_labels(self):
-        self.widget.controls.add_scores.setChecked(1)
         data = self.data[[0, 1, 2, 50, 51, 52, 100, 101, 102]]
         data.Y[::3] = np.nan
         valid = ~np.isnan(data.Y.flatten())
@@ -83,7 +82,6 @@ def test_unknowns_in_labels(self):
         np.testing.assert_almost_equal(scores_1, scores[valid], decimal=12)
 
     def test_nan_distances(self):
-        self.widget.controls.add_scores.setChecked(1)
         self.widget.distance_idx = 2
         self.assertEqual(self.widget.Distances[self.widget.distance_idx][0],
                          'Cosine')
@@ -157,7 +155,6 @@ def test_bad_data_range(self):
                      [16, nan, nan],
                      "nyy"))
         )
-        self.widget.controls.add_scores.setChecked(1)
         self.send_signal(self.widget.Inputs.data, table)
 
     def test_saved_selection(self):