Repository: biolab/orange3

Issue:
ValueError:  minlength must be positive
https://sentry.io/biolab/orange3/issues/206599981/

```
ValueError:  minlength must be positive
(5 additional frame(s) were not displayed)
...
  Module "Orange.statistics.distribution", line 295, in get_distribution

    return Discrete(dat, variable, unknowns)
  Module "Orange.statistics.distribution", line 39, in __new__

    return cls.from_data(dat, variable)
  Module "Orange.statistics.distribution", line 63, in from_data

    dist, unknowns = data._compute_distributions([variable])[0]
  Module "Orange.data.table", line 1314, in _compute_distributions

    dist, unknowns = bincount(m, len(var.values) - 1, W)
  Module "Orange.statistics.util", line 52, in bincount

    minlength=minlength).astype(float),
```


Discussion:

Resolving patch:
diff --git a/Orange/statistics/util.py b/Orange/statistics/util.py
--- a/Orange/statistics/util.py
+++ b/Orange/statistics/util.py
@@ -47,10 +47,10 @@ def bincount(X, max_val=None, weights=None, minlength=None):
         nans = 0. if X.ndim == 1 else np.zeros(X.shape[1], dtype=float)
     if minlength is None and max_val is not None:
         minlength = max_val + 1
-    return (np.bincount(X.astype(np.int32, copy=False),
-                        weights=weights,
-                        minlength=minlength).astype(float),
-            nans)
+    bc = np.array([]) if minlength is not None and minlength <= 0 else \
+        np.bincount(X.astype(np.int32, copy=False),
+                    weights=weights, minlength=minlength).astype(float)
+    return bc, nans
 
 
 def countnans(X, weights=None, axis=None, dtype=None, keepdims=False):
diff --git a/Orange/widgets/visualize/owmosaic.py b/Orange/widgets/visualize/owmosaic.py
--- a/Orange/widgets/visualize/owmosaic.py
+++ b/Orange/widgets/visualize/owmosaic.py
@@ -898,6 +898,13 @@ def draw_legend(x0_x1, y0_y1):
         else:
             self.Warning.no_valid_data.clear()
 
+        attrs = [attr for attr in attr_list if not data.domain[attr].values]
+        if attrs:
+            CanvasText(self.canvas,
+                       "Feature {} has no values".format(attrs[0]),
+                       (self.canvas_view.width() - 120) / 2,
+                       self.canvas_view.height() / 2)
+            return
         if self.interior_coloring == self.PEARSON:
             apriori_dists = [get_distribution(data, attr) for attr in attr_list]
         else:


Test patch:
diff --git a/Orange/widgets/visualize/tests/test_owmosaic.py b/Orange/widgets/visualize/tests/test_owmosaic.py
--- a/Orange/widgets/visualize/tests/test_owmosaic.py
+++ b/Orange/widgets/visualize/tests/test_owmosaic.py
@@ -40,6 +40,19 @@ def test_continuous_metas(self):
                      metas=np.arange(6).reshape(6, 1))
         self.send_signal("Data", data)
 
+    def test_missing_values(self):
+        """Check widget for dataset with missing values"""
+        data = Table(Domain([DiscreteVariable("c1", [])]),
+                     np.array([np.nan] * 6)[:, None])
+        self.send_signal("Data", data)
+
+        # missings in class variable
+        attrs = [DiscreteVariable("c1", ["a", "b", "c"])]
+        class_var = DiscreteVariable("cls", [])
+        X = np.array([1, 2, 0, 1, 0, 2])[:, None]
+        data = Table(Domain(attrs, class_var), X, np.array([np.nan] * 6))
+        self.send_signal("Data", data)
+
 
 # Derive from WidgetTest to simplify creation of the Mosaic widget, although
 # we are actually testing the MosaicVizRank dialog and not the widget