Repository: biolab/orange3

Issue:
k-Means: Silhouette scoring fails on missing values
**Describe the bug**
Asking k-Means to compute silhouette score on data with missing values results in an error.

**To Reproduce**
Steps to reproduce the behavior:
Datasets (Online retails) - k-Means (use From 2 to 8 clusters).

**Orange version:**
3.23.dev

**Expected behavior**
k-Means either imputes missing values or handles them in some other nice way.

**Additional context**
```
---------------------------- ValueError Exception -----------------------------
Traceback (most recent call last):
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Orange/widgets/unsupervised/owkmeans.py", line 322, in __commit_finished
    self.update_results()
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Orange/widgets/unsupervised/owkmeans.py", line 438, in update_results
    self.clusterings[k] for k in range(self.k_from, self.k_to + 1))]
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Orange/widgets/unsupervised/owkmeans.py", line 437, in <listcomp>
    self.data.X, mk.labels) for mk in (
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/metrics/cluster/unsupervised.py", line 117, in silhouette_score
    return np.mean(silhouette_samples(X, labels, metric=metric, **kwds))
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/metrics/cluster/unsupervised.py", line 212, in silhouette_samples
    X, labels = check_X_y(X, labels, accept_sparse=['csc', 'csr'])
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/utils/validation.py", line 719, in check_X_y
    estimator=estimator)
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/utils/validation.py", line 542, in check_array
    allow_nan=force_all_finite == 'allow-nan')
  File "/Applications/Orange3.app/Contents/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sklearn/utils/validation.py", line 56, in _assert_all_finite
    raise ValueError(msg_err.format(type_err, X.dtype))
ValueError: Input contains NaN, infinity or a value too large for dtype('float64').
-------------------------------------------------------------------------------
```



Discussion:

Resolving patch:
diff --git a/Orange/widgets/unsupervised/owkmeans.py b/Orange/widgets/unsupervised/owkmeans.py
--- a/Orange/widgets/unsupervised/owkmeans.py
+++ b/Orange/widgets/unsupervised/owkmeans.py
@@ -436,7 +436,7 @@ def invalidate(self, unconditional=False):
 
     def update_results(self):
         scores = [mk if isinstance(mk, str) else silhouette_score(
-            self.data.X, mk.labels) for mk in (
+            self.preproces(self.data).X, mk.labels) for mk in (
                 self.clusterings[k] for k in range(self.k_from, self.k_to + 1))]
         best_row = max(
             range(len(scores)), default=0,
@@ -569,4 +569,4 @@ def onDeleteWidget(self):
 
 
 if __name__ == "__main__":  # pragma: no cover
-    WidgetPreview(OWKMeans).run(Table("iris.tab"))
+    WidgetPreview(OWKMeans).run(Table("heart_disease"))


Test patch:
diff --git a/Orange/widgets/unsupervised/tests/test_owkmeans.py b/Orange/widgets/unsupervised/tests/test_owkmeans.py
--- a/Orange/widgets/unsupervised/tests/test_owkmeans.py
+++ b/Orange/widgets/unsupervised/tests/test_owkmeans.py
@@ -45,6 +45,7 @@ def setUp(self):
             OWKMeans, stored_settings={"auto_commit": False, "version": 2}
         )  # type: OWKMeans
         self.iris = Table("iris")
+        self.iris.X[0, 0] = np.nan
 
     def tearDown(self):
         self.widget.onDeleteWidget()
@@ -263,10 +264,11 @@ def test_optimization_fails(self):
                 model, "set_scores", wraps=model.set_scores) as set_scores:
             self.send_signal(self.widget.Inputs.data, self.iris, wait=5000)
             scores, start_k = set_scores.call_args[0]
+            X = self.widget.preproces(self.iris).X
             self.assertEqual(
                 scores,
                 [km if isinstance(km, str) else silhouette_score(
-                    self.iris.X, km(self.iris))
+                    X, km(self.iris))
                  for km in (widget.clusterings[k] for k in range(3, 9))]
             )
             self.assertEqual(start_k, 3)