Repository: biolab/orange3

Issue:
Feature Statistics: order of variables
<!-- 
Thanks for taking the time to report a bug!
If you're raising an issue about an add-on (i.e., installed via Options > Add-ons), raise an issue in the relevant add-on's issue tracker instead. See: https://github.com/biolab?q=orange3
To fix the bug, we need to be able to reproduce it. Please answer the following questions to the best of your ability.
-->


- [ ] **What's wrong?**
<!-- Be specific, clear, and concise. Include screenshots if relevant. -->
<!-- If you're getting an error message, copy it, and enclose it with three backticks (```). -->
Not sure if this is wrong or just a heuristic I do not yet understand, but Feature Statistics has a strange order of variables. Does the order mean anything? Because it is not the same as in domain.




- [ ] **How can we reproduce the problem?** 
<!-- Upload a zip with the .ows file and data. -->
<!-- Describe the steps (open this widget, click there, then add this...) -->
Look at any file in Feature Statistics. The weirdest one is heart-disease.




- [ ] **What's your environment?**
<!-- To find your Orange version, see "Help → About → Version" or `Orange.version.full_version` in code -->
- Operating system: OSX
- Orange version: 3.28.dev
- How you installed Orange: git



Discussion:
They are ordered by type -- first numeric, then categorical. Within a type, they are in the same order as in the domain, just backwards. Any child can see this.

You're arrogant enough to believe that you have a better idea?

Seriously, the widget has no button for "original order" (nor would I add it), so it has to be sorted by something. Type is convenient because variables of different types show different statistics. Yet within each type, I suppose alphabetical order would be better.

Note that sorting is stable. If you sort by names and then re-sort by type, they renamed sorted alphabetically within a type. I suppose that we should set the initial order to be like that.

Resolving patch:
diff --git a/Orange/widgets/data/owfeaturestatistics.py b/Orange/widgets/data/owfeaturestatistics.py
--- a/Orange/widgets/data/owfeaturestatistics.py
+++ b/Orange/widgets/data/owfeaturestatistics.py
@@ -375,14 +375,17 @@ def sortColumnData(self, column):
         to group those variable types together.
         """
         # Prepare indices for variable types so we can group them together
-        order = [DiscreteVariable, ContinuousVariable, TimeVariable, StringVariable]
+        order = [ContinuousVariable, TimeVariable,
+                 DiscreteVariable, StringVariable]
         mapping = {var: idx for idx, var in enumerate(order)}
         vmapping = np.vectorize(mapping.__getitem__)
         var_types_indices = vmapping(self._variable_types)
 
         # Store the variable name sorted indices so we can pass a default
         # order when sorting by multiple keys
-        var_name_indices = np.argsort(self._variable_names)
+        # Double argsort is "inverse" argsort:
+        # data will be *sorted* by these indices
+        var_name_indices = np.argsort(np.argsort(self._variable_names))
 
         # Prepare vartype indices so ready when needed
         disc_idx, _, time_idx, str_idx = self._attr_indices(self.variables)
@@ -443,12 +446,21 @@ def _sortColumnData(self, column):
 
     def _argsortData(self, data, order):
         if data.ndim == 1:
-            indices = np.argsort(data, kind='mergesort')
-            if order == Qt.DescendingOrder:
-                indices = indices[::-1]
-            # Always sort NaNs last
             if np.issubdtype(data.dtype, np.number):
-                indices = np.roll(indices, -np.isnan(data).sum())
+                if order == Qt.DescendingOrder:
+                    data = -data
+                indices = np.argsort(data, kind='stable')
+                # Always sort NaNs last
+                if np.issubdtype(data.dtype, np.number):
+                    indices = np.roll(indices, -np.isnan(data).sum())
+            else:
+                # When not sorting by numbers, we can't do data = -data, but
+                # use indices = indices[::-1] instead. This is not stable, but
+                # doesn't matter because we use this only for variable names
+                # which are guaranteed to be unique
+                indices = np.argsort(data)
+                if order == Qt.DescendingOrder:
+                    indices = indices[::-1]
         else:
             assert np.issubdtype(data.dtype, np.number), \
                 'We do not deal with non numeric values in sorting by ' \
@@ -719,7 +731,7 @@ class Outputs:
     color_var = ContextSetting(None)  # type: Optional[Variable]
     # filter_string = ContextSetting('')
 
-    sorting = Setting((0, Qt.DescendingOrder))
+    sorting = Setting((0, Qt.AscendingOrder))
     selected_vars = ContextSetting([], schema_only=True)
 
     def __init__(self):


Test patch: