Repository: bids-standard/pybids

Issue:
adjusted test_transformations.py::test_filter fails randomly
Merging https://github.com/bids-standard/pybids/pull/413 did bring back that failure I saw but which went away after rebasing on master, but not for long - apparently there is some randomless somewhere, and specific to python 3.5, see e.g. https://travis-ci.org/bids-standard/pybids/builds/509649783  build

```
collection = <bids.variables.kollekshuns.BIDSRunVariableCollection object at 0x7f66900471d0>
    def test_filter(collection):
        orig = collection['parametric gain'].clone()
        q = "parametric gain > 0"
        transform.Filter(collection, 'parametric gain', query=q)
        assert len(orig.values) == 2 * len(collection['parametric gain'].values)
        assert np.all(collection['parametric gain'].values > 0)
    
        orig = collection['RT'].clone()
        q = 'parametric gain > 0.1'
        transform.Filter(collection, 'RT', query=q, by='parametric gain')
        assert len(orig.values) != len(collection['RT'].values)
        # There is some bizarro thing going on where, on travis, the result is
        # randomly either 1536 (when all subjects are used) or 3909 when running
        # on Python 3 (on linux or mac).
        # Never happens locally, and I've had no luck tracking down the problem.
        # Best guess is it reflects either some non-deterministic ordering of
        # variables somewhere, or some weird precision issues when resampling to
        # dense. Needs to be tracked down and fixed.
>       assert len(collection['RT'].values) in [96 * len(SUBJECTS), 3909]
E       assert 499 in [192, 3909]
E        +  where 499 = len(1      1.793\n2      1.637\n3      1.316\n4      1.670\n5      1.232\n6      1.502\n7      1.925\n8      1.061\n9      1.079\n1...289\n505    2.976\n506    1.257\n507    1.199\n508    1.432\n509    1.440\n510    2.071\nName: RT, Length: 499, dtype: float64)
E        +    where 1      1.793\n2      1.637\n3      1.316\n4      1.670\n5      1.232\n6      1.502\n7      1.925\n8      1.061\n9      1.079\n1...289\n505    2.976\n506    1.257\n507    1.199\n508    1.432\n509    1.440\n510    2.071\nName: RT, Length: 499, dtype: float64 = <bids.variables.variables.SparseRunVariable object at 0x7f66903d1a20>.values
bids/analysis/tests/test_transformations.py:382: AssertionError
```
I have reproduced it locally in venv with 3.5,  fails some times... 50/50. looking into it to get more details


Discussion:
ha ha -- it is that leftover 3909, which I guess should have been 2*len(SUBJECTS) to retain the full mystery here... looking more ;-)
here is the header of the diff between 96* and 2* version (so what is 48 of we have here?)
```shell
$> diff -Naur c-192.json c-499.json | head -n 20
--- c-192.json	2019-03-21 22:48:55.549472698 -0400
+++ c-499.json	2019-03-21 22:48:49.749567598 -0400
@@ -1,192 +1,499 @@
-{"3":1.316
+{"1":1.793
+"2":1.637
+"3":1.316
+"4":1.67
+"5":1.232
 "6":1.502
+"7":1.925
+"8":1.061
 "9":1.079
+"10":1.477
 "11":1.599
+"12":1.822
+"13":1.405
+"14":1.206
+"15":1.272
 "16":0.9
```
Got it, PR is coming. an unordered dict to blame (ordered iirc since 3.7?)

Resolving patch:
diff --git a/bids/analysis/transformations/munge.py b/bids/analysis/transformations/munge.py
--- a/bids/analysis/transformations/munge.py
+++ b/bids/analysis/transformations/munge.py
@@ -5,12 +5,17 @@
 
 import numpy as np
 import pandas as pd
+# Might not be needed since python 3.7
+# See https://github.com/bids-standard/pybids/issues/423 for more info
+from collections import OrderedDict as odict
+
 from bids.utils import listify
 from .base import Transformation
 from patsy import dmatrix
 import re
 from bids.variables import DenseRunVariable, SimpleVariable
 
+
 class Assign(Transformation):
     ''' Assign one variable's amplitude, duration, or onset attribute to
     another. '''
@@ -160,6 +165,7 @@ def _transform(self, var, constraint='none', ref_level=None, sep='.'):
 
 
 class Filter(Transformation):
+    '''Filter (remove) the values given a query to satisfy'''
 
     _groupable = False
     _input_type = 'variable'
@@ -174,13 +180,15 @@ def _transform(self, var, query, by=None):
 
         names = [var.name] + listify(by)
 
+        # assure ordered dict so we have consistent (if not correct) operation,
+        # because later we ask for name_map.values
         # pandas .query can't handle non-identifiers in variable names, so we
         # need to replace them in both the variable names and the query string.
-        name_map = {n: re.sub('[^a-zA-Z0-9_]+', '_', n) for n in names}
+        name_map = odict((n, re.sub('[^a-zA-Z0-9_]+', '_', n)) for n in names)
         for k, v in name_map.items():
             query = query.replace(k, v)
 
-        data = pd.concat([self.collection[c].values for c in names],
+        data = pd.concat([self.collection[n].values for n in names],
                          axis=1, sort=True)
         # Make sure we can use integer index
         data = data.reset_index(drop=True)


Test patch:
diff --git a/bids/analysis/tests/test_transformations.py b/bids/analysis/tests/test_transformations.py
--- a/bids/analysis/tests/test_transformations.py
+++ b/bids/analysis/tests/test_transformations.py
@@ -372,14 +372,7 @@ def test_filter(collection):
     q = 'parametric gain > 0.1'
     transform.Filter(collection, 'RT', query=q, by='parametric gain')
     assert len(orig.values) != len(collection['RT'].values)
-    # There is some bizarro thing going on where, on travis, the result is
-    # randomly either 1536 (when all subjects are used) or 3909 when running
-    # on Python 3 (on linux or mac).
-    # Never happens locally, and I've had no luck tracking down the problem.
-    # Best guess is it reflects either some non-deterministic ordering of
-    # variables somewhere, or some weird precision issues when resampling to
-    # dense. Needs to be tracked down and fixed.
-    assert len(collection['RT'].values) in [96 * len(SUBJECTS), 3909]
+    assert len(collection['RT'].values) == 96 * len(SUBJECTS)
 
 
 def test_replace(collection):