Repository: mne-tools/mne-python

Issue:
A potential mismatch of times rescale in subtract_evoked
#### Describe the bug
In epochs analysis, a common operation is to compute induced response by subtracting the mean epochs. In MNE-python, it is computed by _detrend_offset_decim function in epochs sub-module.

However, the time scale operation seems use self._raw_times as time line instead of self.times.

The two values may be different if DECIM parameter is used when got the epochs, as the self._raw_times is kept unchange and self.times is recomputed according to DECIM parameter. If the mismatch happens, the program will crush immediately, and raise a ValueError says operands could be broadcast together with shapes x, y, x. (x means original times in self._raw_times, and y means new times in self.times)

#### Steps to reproduce
``` python
# %% Importing
import mne
from mne import io
from mne.datasets import sample

# %% Loading
data_path = sample.data_path()
raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'

tmin, tmax, event_id = -0.2, 0.5, 2

# Setup for reading the raw data
raw = io.read_raw_fif(raw_fname)
events = mne.find_events(raw, stim_channel='STI 014')

include = []

# Picks MEG channels
picks = mne.pick_types(raw.info, meg=True, eeg=False, eog=True,
                       stim=False, include=include, exclude='bads')
reject = None  # dict(grad=4000e-13, mag=4e-12, eog=150e-6)

# Load epochs
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks,
                    decim=10, # ***********Key to reproduce the bug.
                    baseline=(None, 0), reject=reject)


# %% Print shape of _raw_times and times
print('Shape of _raw_times is {}'.format(epochs._raw_times.shape))
print('Shape of times is {}'.format(epochs.times.shape))

# %% Bug occurs
induced_evoked = epochs.subtract_evoked().average()
```

[MWE]: https://en.wikipedia.org/wiki/Minimal_Working_Example
[built-in datasets]: https://mne.tools/dev/overview/datasets_index.html
[sample]: https://mne.tools/dev/overview/datasets_index.html#sample


#### Expected results
The induced_evoked is computed correctly.


#### Actual results
An error occurs as following:
```
Subtracting Evoked from Epochs
    The following channels are not included in the subtraction: EOG 061
[done]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~/Documents/RSVPshuju_analysis/Analysis/Visualize/crush.py in 
----> 34 induced_evoked = epochs.subtract_evoked().average()

~/anaconda3/lib/python3.7/site-packages/mne/epochs.py in average(self, picks, method)
    940         This would compute the trimmed mean.
    941         """
--> 942         return self._compute_aggregate(picks=picks, mode=method)
    943 
    944     @fill_doc

~/anaconda3/lib/python3.7/site-packages/mne/epochs.py in _compute_aggregate(self, picks, mode)
    989             data = np.zeros((n_channels, n_times))
    990             n_events = 0
--> 991             for e in self:
    992                 if np.iscomplexobj(e):
    993                     data = data.astype(np.complex128)

~/anaconda3/lib/python3.7/site-packages/mne/utils/mixin.py in __next__(self, return_event_id)
    326                     raise StopIteration  # signal the end properly
    327                 epoch_noproj = self._get_epoch_from_raw(self._current)
--> 328                 epoch_noproj = self._detrend_offset_decim(epoch_noproj)
    329                 epoch = self._project_epoch(epoch_noproj)
    330                 self._current += 1

 in _detrend_offset_decim(self, epoch, verbose)

~/anaconda3/lib/python3.7/site-packages/mne/epochs.py in _detrend_offset_decim(self, epoch, verbose)
    791         # handle offset
    792         if self._offset is not None:
--> 793             epoch += self._offset
    794 
    795         # Decimate if necessary (i.e., epoch not preloaded)

ValueError: operands could not be broadcast together with shapes (306,421) (306,43) (306,421)
```


Discussion:
Please read carefully the instructions in the bug report template, paying special attention to the parts that say "delete this section" or "replace this text with...". In particular, pay attention to the request that your code sample be *reproducible* so that others can copy/paste your code and have it work *without needing to edit it* (i.e., avoid placeholders like "some raw.fif file").
I confirm the bug. You can go around it for now by using preload=True in Epochs

Resolving patch:
diff --git a/mne/epochs.py b/mne/epochs.py
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -799,12 +799,13 @@ def _detrend_offset_decim(self, epoch, verbose=None):
         epoch[picks] = rescale(epoch[picks], self._raw_times, self.baseline,
                                copy=False, verbose=False)
 
+        # Decimate if necessary (i.e., epoch not preloaded)
+        epoch = epoch[:, self._decim_slice]
+
         # handle offset
         if self._offset is not None:
             epoch += self._offset
 
-        # Decimate if necessary (i.e., epoch not preloaded)
-        epoch = epoch[:, self._decim_slice]
         return epoch
 
     def iter_evoked(self, copy=False):


Test patch:
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
@@ -1609,6 +1609,14 @@ def test_subtract_evoked():
     data = zero_evoked.data
     assert_allclose(data, np.zeros_like(data), atol=1e-15)
 
+    # with decimation (gh-7854)
+    epochs3 = Epochs(raw, events[:10], event_id, tmin, tmax, picks=picks,
+                     decim=10, verbose='error')
+    data_old = epochs2.decimate(10, verbose='error').get_data()
+    data = epochs3.subtract_evoked().get_data()
+    assert_allclose(data, data_old)
+    assert_allclose(epochs3.average().data, 0., atol=1e-20)
+
 
 def test_epoch_eq():
     """Test epoch count equalization and condition combining."""