Repository: bids-standard/pybids

Issue:
_transform breaks if sampling_rate="auto"
`_transform` calls `to_dense` which runs the following code:

```
if isinstance(var, SparseRunVariable):
    sr = self.collection.sampling_rate
    var = var.to_dense(sr)
```

If `self.collection.sampling_rate` returns "auto" then to_dense breaks here:
`duration = int(math.ceil(sampling_rate * self.get_duration()))`
with this error:
`TypeError: can't multiply sequence by non-int of type 'numpy.float64'`


Discussion:
Can you provide the code you tried to run (or model you tried to load), and the full traceback?
Code:
```

# Initialise a BIDSLayout of an example dataset
data_path = os.path.join('/data/KRNS_BIDS/bids')
layout = BIDSLayout(data_path)
layout

model = json.loads('''{
  "Name": "krns96",
  "Description": "",
  "Input": {
    "Run": [
      5,
      4,
      8,
      3,
      7,
      6,
      2,
      1
    ],
    "Session": [
      "96passages01",
      "96passages02",
      "96passages03",
      "96passages04"
    ],
    "Subject": [
      "M02",
      "P01",
      "M08",
      "M04",
      "M01",
      "M13",
      "M09",
      "M14",
      "M15",
      "M07"
    ],
    "Task": "96passages"
  },
  "Steps": [
    {
      "Level": "Run",
      "Transformations": [
        {
          "Name": "Convolve",
          "Input": [
            "passage_num",
            "sentence_num"
          ]
        }
      ],
      "Contrasts": [],
      "AutoContrasts": true,
      "Model": {
        "X": [
          "passage_num",
          "sentence_num"
        ]
      }
    },
    {
      "Level": "Subject",
      "AutoContrasts": true
    },
    {
      "Level": "Dataset",
      "AutoContrasts": true
    }
  ]
}''')

analysis = Analysis(layout, model)
analysis.setup()
```

Traceback:
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-c1ef2a04c3cd> in <module>
----> 1 foo.setup()

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/analysis.py in setup(self, steps, drop_na, **kwargs)
     87                 continue
     88 
---> 89             b.setup(input_nodes, drop_na=drop_na, **selectors)
     90             input_nodes = b.output_nodes
     91 

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/analysis.py in setup(self, input_nodes, drop_na, **kwargs)
    209             coll = merge_collections(colls) if len(colls) > 1 else colls[0]
    210 
--> 211             coll = apply_transformations(coll, self.transformations)
    212             if X:
    213                 transform.Select(coll, X)

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/analysis.py in apply_transformations(collection, transformations, select)
    506                 raise ValueError("No transformation '%s' found!" % func)
    507             func = getattr(transform, func)
--> 508             func(collection, cols, **kwargs)
    509 
    510     if select is not None:

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/transformations/base.py in __new__(cls, collection, variables, *args, **kwargs)
     85         t = super(Transformation, cls).__new__(cls)
     86         t._setup(collection, variables, *args, **kwargs)
---> 87         return t.transform()
     88 
     89     def _setup(self, collection, variables, *args, **kwargs):

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/transformations/base.py in transform(self)
    259                                        **kwargs)
    260                 else:
--> 261                     result = self._transform(data[i], **kwargs)
    262 
    263             if self._return_type in ['none', None]:

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/analysis/transformations/compute.py in _transform(self, var, model, derivative, dispersion, fir_delays)
     36         if isinstance(var, SparseRunVariable):
     37             sr = self.collection.sampling_rate
---> 38             var = var.to_dense(sr)
     39 
     40         df = var.to_df(entities=False)

/data/MLcore/miniconda3/envs/krns/lib/python3.7/site-packages/bids/variables/variables.py in to_dense(self, sampling_rate)
    323 
    324         '''
--> 325         duration = int(math.ceil(sampling_rate * self.get_duration()))
    326         ts = np.zeros(duration, dtype=self.values.dtype)
    327 

TypeError: can't multiply sequence by non-int of type 'numpy.float64'
```

And the events.tsv file looks something like this:
```
onset   duration        trial_type      event_index     passage_num     sentence_num    topic_index
0.0     10      fixation        0       0       0       0
10.0    4       passage53       209     53      1       14
18.0    4       passage53       209     53      2       14
26.0    4       passage53       209     53      3       14
34.0    4       passage53       209     53      4       14
46.0    4       passage80       317     80      1       17
54.0    4       passage80       317     80      2       17
62.0    4       passage80       317     80      3       17
70.0    4       passage80       317     80      4       17
82.0    4       passage20       77      20      1       11
90.0    4       passage20       77      20      2       11
98.0    4       passage20       77      20      3       11
106.0   4       passage20       77      20      4       11
118.0   4       passage55       217     55      1       15
126.0   4       passage55       217     55      2       15
134.0   4       passage55       217     55      3       15
142.0   4       passage55       217     55      4       15
154.0   4       passage94       373     94      1       1
162.0   4       passage94       373     94      2       1
170.0   4       passage94       373     94      3       1
178.0   4       passage94       373     94      4       1
190.0   4       passage71       281     71      1       20
198.0   4       passage71       281     71      2       20
206.0   4       passage71       281     71      3       20
214.0   4       passage71       281     71      4       20
226.0   4       passage47       185     47      1       10
234.0   4       passage47       185     47      2       10
242.0   4       passage47       185     47      3       10
250.0   4       passage47       185     47      4       10
262.0   4       passage58       229     58      1       16
270.0   4       passage58       229     58      2       16
278.0   4       passage58       229     58      3       16
286.0   4       passage58       229     58      4       16
298.0   4       passage81       321     81      1       6
306.0   4       passage81       321     81      2       6
314.0   4       passage81       321     81      3       6
322.0   4       passage81       321     81      4       6
334.0   4       passage54       213     54      1       9
342.0   4       passage54       213     54      2       9
350.0   4       passage54       213     54      3       9
358.0   4       passage54       213     54      4       9
370.0   4       passage40       157     40      1       14
378.0   4       passage40       157     40      2       14
386.0   4       passage40       157     40      3       14
394.0   4       passage40       157     40      4       14
406.0   4       passage15       57      15      1       15
414.0   4       passage15       57      15      2       15
422.0   4       passage15       57      15      3       15
430.0   4       passage15       57      15      4       15
442.0   10      fixation        0       0       0       0
```

My guess is that the bug is here:
https://github.com/bids-standard/pybids/blob/a4bbc811e67df8ffbc186cca4c3c61e0992af09f/bids/variables/kollekshuns.py#L379

`sampling_rate` should only be `auto` in `merge_collections`, but it leaks out of this function and gets set as the sampling_rate of the actual collection. 

Resolving patch:
diff --git a/bids/variables/kollekshuns.py b/bids/variables/kollekshuns.py
--- a/bids/variables/kollekshuns.py
+++ b/bids/variables/kollekshuns.py
@@ -228,6 +228,9 @@ class BIDSRunVariableCollection(BIDSVariableCollection):
     def __init__(self, variables, sampling_rate=None):
         # Don't put the default value in signature because None is passed from
         # several places and we don't want multiple conflicting defaults.
+        if sampling_rate:
+            if isinstance(sampling_rate, str):
+                raise ValueError("Sampling rate must be numeric.")
         self.sampling_rate = sampling_rate or 10
         super(BIDSRunVariableCollection, self).__init__(variables)
 
@@ -376,6 +379,12 @@ def merge_collections(collections, force_dense=False, sampling_rate='auto'):
     variables = cls.merge_variables(variables, sampling_rate=sampling_rate)
 
     if isinstance(collections[0], BIDSRunVariableCollection):
+        if sampling_rate == 'auto':
+            rates = [var.sampling_rate for var in variables
+                     if isinstance(var, DenseRunVariable)]
+
+            sampling_rate = rates[0] if rates else None
+
         return cls(variables, sampling_rate)
 
     return cls(variables)


Test patch: