Repository: mne-tools/mne-python

Issue:
BUG: problem with crop / decimation
I'm encountering some weird behaviors, but I'm unclear what they're due to:

```
import numpy as np
from mne.epochs import concatenate_epochs, EpochsArray
from mne.io.meas_info import create_info
from mne.decoding import GeneralizationAcrossTime


def make_epochs():
    n_trials = 100
    data = np.random.randn(n_trials, 64, 1946)
    times = np.linspace(-1.8, 2., data.shape[2])
    sfreq = len(times) / np.ptp([times[0], times[-1]])
    chan_names = ['eeg_%s' % chan for chan in range(64)]
    chan_types = ['eeg' for ii in range(64)]
    events = np.hstack((np.zeros(n_trials / 2), np.ones(n_trials / 2)))
    events = np.c_[np.cumsum(np.ones_like(events)) * sfreq, events, events]
    info = create_info(chan_names, sfreq, chan_types)
    epochs = EpochsArray(data, info, events=events, tmin=-1.8,
                         event_id=dict(a=0, b=1), verbose=False)
    return epochs

# BUG 1
epochs = make_epochs()
len(epochs)  # ok
epochs_ = concatenate_epochs((epochs['a'], epochs['b']))
len(epochs_)  # why not ok??
```

gives 

```
   1196         if not self._bad_dropped:
-> 1197             raise RuntimeError('Since bad epochs have not been dropped, the '
   1198                                'length of the Epochs is not known. Load the '
   1199                                'Epochs with preload=True, or call '

RuntimeError: Since bad epochs have not been dropped, the length of the Epochs is not known. Load the Epochs with preload=True, or call Epochs.drop_bad_epochs(). To find the number of events in the Epochs, use len(Epochs.events).
```

```
# BUG 2
gat = GeneralizationAcrossTime()
epochs = make_epochs()
epochs.crop(-.200, .700)
gat.fit(epochs)  # ok

epochs = make_epochs()
epochs.decimate(10)
gat.fit(epochs)  # ok

epochs = make_epochs()
epochs.decimate(10)
epochs.crop(-.200, .700)
gat.fit(epochs)  # ok

epochs = make_epochs()
epochs.crop(-.200, .700)
epochs.decimate(10)
gat.fit(epochs)  # why not ok??

```

gives

```
/home/jrking/mne-python/mne/decoding/time_gen.pyc in <genexpr>((train_slices_chunk,))
    162                                   X[..., f(train_slices_chunk)],
    163                                   y, train_slices_chunk, cv)
--> 164                        for train_slices_chunk in splits)
    165         # Unpack estimators into time slices X folds list of lists.
    166         self.estimators_ = sum(out, list())
```



Discussion:
What erros do you get? Is this related to #2356?

I agree these should be doable, and made into tests to make sure everything is working properly.

> What erros do you get?

I updated the post.

> Is this related to #2356?

I'm not sure, but I think these are separate issues.

First bug -- it looks like somehow the concatenation function is not recognizing/passing on that bad epochs have been dropped, and probably needs to set a parameter appropriately.

If the splits are in time, that last one could actually be due to time rounding/indexing errors, or it could be something different.

fixed first bug in :

https://github.com/mne-tools/mne-python/commit/1cc037e71d723350f3beb54f9a545d56cc4e6683

for the second one it's something in GAT I suspect.

thanks @agramfort! For the second bug, it's actually a crop / decimate issue too: 

```
epochs = make_epochs()
epochs.crop(-.200, .700)
times = [epochs.times[-1]]
epochs.decimate(10)
times.append(epochs.times[-1])
print times
```

gives

```
[0.6990750256937307, 1.9878725590955804]
```

I guess you should probably update _raw_times, or compute the decimated time differently than what you currently do ('epochs.times = epochs._raw_times[epochs._decim_slice]').

I'm not sure what _raw_times is, so I'll let you fix the bug ;)

I can look into it tomorrow


Resolving patch:
diff --git a/mne/epochs.py b/mne/epochs.py
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -1366,6 +1366,7 @@ def crop(self, tmin=None, tmax=None, copy=False):
         this_epochs.tmin = this_epochs.times[tidx[0]]
         this_epochs.tmax = this_epochs.times[tidx[-1]]
         this_epochs.times = this_epochs.times[tmask]
+        this_epochs._raw_times = this_epochs._raw_times[tmask]
         this_epochs._data = this_epochs._data[:, :, tmask]
         return this_epochs
 
@@ -1809,7 +1810,7 @@ def __init__(self, data, info, events, tmin=0, event_id=None,
             raise ValueError('Data must be a 3D array of shape (n_epochs, '
                              'n_channels, n_samples)')
 
-        if len(info['ch_names']) != np.shape(data)[1]:
+        if len(info['ch_names']) != data.shape[1]:
             raise ValueError('Info and data must have same number of '
                              'channels.')
         if data.shape[0] != len(events):
diff --git a/mne/simulation/evoked.py b/mne/simulation/evoked.py
--- a/mne/simulation/evoked.py
+++ b/mne/simulation/evoked.py
@@ -75,17 +75,27 @@ def generate_noise_evoked(evoked, cov, iir_filter=None, random_state=None):
     noise : evoked object
         an instance of evoked
     """
-    from scipy.signal import lfilter
     noise = copy.deepcopy(evoked)
-    noise_cov = pick_channels_cov(cov, include=noise.info['ch_names'])
+    noise.data = _generate_noise(evoked.info, cov, iir_filter, random_state,
+                                 evoked.data.shape[1])[0]
+    return noise
+
+
+def _generate_noise(info, cov, iir_filter, random_state, n_samples, zi=None):
+    """Helper to create spatially colored and temporally IIR-filtered noise"""
+    from scipy.signal import lfilter
+    noise_cov = pick_channels_cov(cov, include=info['ch_names'])
     rng = check_random_state(random_state)
-    n_channels = np.zeros(noise.info['nchan'])
-    n_samples = evoked.data.shape[1]
+    mu_channels = np.zeros(info['nchan'])
     c = np.diag(noise_cov.data) if noise_cov['diag'] else noise_cov.data
-    noise.data = rng.multivariate_normal(n_channels, c, n_samples).T
+    noise = rng.multivariate_normal(mu_channels, c, n_samples).T
     if iir_filter is not None:
-        noise.data = lfilter([1], iir_filter, noise.data, axis=-1)
-    return noise
+        if zi is None:
+            zi = np.zeros((info['nchan'], len(iir_filter) - 1))
+        noise, zf = lfilter([1], iir_filter, noise, axis=-1, zi=zi)
+    else:
+        zf = None
+    return noise, zf
 
 
 def add_noise_evoked(evoked, noise, snr, tmin=None, tmax=None):


Test patch:
diff --git a/mne/beamformer/tests/test_rap_music.py b/mne/beamformer/tests/test_rap_music.py
--- a/mne/beamformer/tests/test_rap_music.py
+++ b/mne/beamformer/tests/test_rap_music.py
@@ -13,6 +13,7 @@
 import mne
 from mne.datasets import testing
 from mne.beamformer import rap_music
+from mne.utils import run_tests_if_main
 
 
 data_path = testing.data_path(download=False)
@@ -147,3 +148,5 @@ def test_rap_music_simulated():
     dipoles, residual = rap_music(sim_evoked, forward_surf_ori, noise_cov,
                                   n_dipoles=n_dipoles, return_residual=True)
     _check_dipoles(dipoles, forward_fixed, stc, evoked, residual)
+
+run_tests_if_main()
diff --git a/mne/simulation/tests/test_evoked.py b/mne/simulation/tests/test_evoked.py
--- a/mne/simulation/tests/test_evoked.py
+++ b/mne/simulation/tests/test_evoked.py
@@ -10,12 +10,12 @@
 import warnings
 
 from mne.datasets import testing
-from mne import read_label, read_forward_solution
+from mne import (read_label, read_forward_solution, pick_types_forward,
+                 read_evokeds, read_cov)
 from mne.time_frequency import morlet
 from mne.simulation import generate_sparse_stc, generate_evoked
-from mne import read_cov
 from mne.io import Raw
-from mne import pick_types_forward, read_evokeds
+from mne.utils import run_tests_if_main
 
 warnings.simplefilter('always')
 
@@ -79,3 +79,5 @@ def test_simulate_evoked():
     stc_bad.vertices[0][0] = mv + 1
     assert_raises(RuntimeError, generate_evoked, fwd, stc_bad,
                   evoked_template, cov, snr, tmin=0.0, tmax=0.2)
+
+run_tests_if_main()
diff --git a/mne/tests/test_epochs.py b/mne/tests/test_epochs.py
--- a/mne/tests/test_epochs.py
+++ b/mne/tests/test_epochs.py
@@ -833,6 +833,15 @@ def test_crop():
     assert_array_equal(data2, data_normal[:, :, tmask])
     assert_array_equal(data3, data_normal[:, :, tmask])
 
+    # test time info is correct
+    epochs = EpochsArray(np.zeros((1, 1, 1000)), create_info(1, 1000., 'eeg'),
+                         np.ones((1, 3), int), tmin=-0.2)
+    epochs.crop(-.200, .700)
+    last_time = epochs.times[-1]
+    with warnings.catch_warnings(record=True):  # not LP filtered
+        epochs.decimate(10)
+    assert_allclose(last_time, epochs.times[-1])
+
 
 def test_resample():
     """Test of resample of epochs