Repository: mne-tools/mne-python

Issue:
Q: default value of baseline in Epochs
What is the motivation for the default value of `baseline=(None, 0)` in `Epochs` constructor? This subtracts the value of the first time point in each epoch.

I can imagine mainly for visualization purposes. However, this can be quite problematic for analysis if one doesn't know about this default behaviour. This was my case. I assumed it uses "raw" data and my whole analysis is based on it, which is now wrong.
From the API perspective, I would assume there is no baseline correction unless specified.


Discussion:
put baseline=None then

baseline defaults to (None, 0) as if tmin is negative in ERP/ERF study
is subtracts the mean during baseline

>

The `tmin=0` case is common enough that it might warrant a warning. People will probably never actually want a one-sample baseline like this. And if they do we can in the warning tell them to use `baseline=(0., 0.)` to avoid the warning.
shall we raise an error with tmin=0 and baseline=(None, 0) ?

Sure, that's probably even better than a warning. @milan-rybar do you want to try a PR for this?
> baseline defaults to (None, 0) as if tmin is negative in ERP/ERF study is subtracts the mean during baseline

I don't think about MNE as a tool primary for ERP/ERF study. Thus, I believe default values shouldn't be based on one particular case of all possible analysis. Especially, when `Epochs` is such a core class. It would make more sense to remove default values for `tmin`, `tmax`, and set `baseline=None`. This way, the user is directly forced to provide desired values and it doesn't do anything unexpected in the background.


> Sure, that's probably even better than a warning. @milan-rybar do you want to try a PR for this?

I agree with rasing an error for the combination of `tmin=0` and `baseline=(None, 0)`. I can do PR for this.

problem is backward compatibility. I would break a lot of code if we change
this so the deprecation cost is to be considered.

>


Resolving patch:
diff --git a/mne/epochs.py b/mne/epochs.py
--- a/mne/epochs.py
+++ b/mne/epochs.py
@@ -1588,6 +1588,11 @@ def _check_baseline(baseline, tmin, tmax, sfreq):
         if not isinstance(baseline, tuple) or len(baseline) != 2:
             raise ValueError('`baseline=%s` is an invalid argument, must be '
                              'a tuple of length 2 or None' % str(baseline))
+        # check default value of baseline and `tmin=0`
+        if baseline == (None, 0) and tmin == 0:
+            raise ValueError('Baseline interval is only one sample. Use '
+                             '`baseline=(0, 0)` if this is desired.')
+
         baseline_tmin, baseline_tmax = baseline
         tstep = 1. / float(sfreq)
         if baseline_tmin is None:
diff --git a/mne/minimum_norm/time_frequency.py b/mne/minimum_norm/time_frequency.py
--- a/mne/minimum_norm/time_frequency.py
+++ b/mne/minimum_norm/time_frequency.py
@@ -510,7 +510,8 @@ def compute_source_psd(raw, inverse_operator, lambda2=1. / 9., method="dSPM",
     n_fft = int(n_fft)
     duration = ((1. - overlap) * n_fft) / raw.info['sfreq']
     events = make_fixed_length_events(raw, 1, tmin, tmax, duration)
-    epochs = Epochs(raw, events, 1, 0, (n_fft - 1) / raw.info['sfreq'])
+    epochs = Epochs(raw, events, 1, 0, (n_fft - 1) / raw.info['sfreq'],
+                    baseline=None)
     out = compute_source_psd_epochs(
         epochs, inverse_operator, lambda2, method, fmin, fmax,
         pick_ori, label, nave, pca, inv_split, bandwidth, adaptive, low_bias,


Test patch:
diff --git a/mne/beamformer/tests/test_dics.py b/mne/beamformer/tests/test_dics.py
--- a/mne/beamformer/tests/test_dics.py
+++ b/mne/beamformer/tests/test_dics.py
@@ -85,9 +85,9 @@ def _simulate_data(fwd):
     noise = random.randn(*raw._data.shape) * 1e-14
     raw._data += noise
 
-    # Define a single epoch
+    # Define a single epoch (weird baseline but shouldn't matter)
     epochs = mne.Epochs(raw, [[0, 0, 1]], event_id=1, tmin=0,
-                        tmax=raw.times[-1], preload=True)
+                        tmax=raw.times[-1], baseline=(0., 0.), preload=True)
     evoked = epochs.average()
 
     # Compute the cross-spectral density matrix
diff --git a/mne/beamformer/tests/test_lcmv.py b/mne/beamformer/tests/test_lcmv.py
--- a/mne/beamformer/tests/test_lcmv.py
+++ b/mne/beamformer/tests/test_lcmv.py
@@ -670,7 +670,7 @@ def test_lcmv_ctf_comp():
     raw = mne.io.read_raw_ctf(raw_fname, preload=True)
 
     events = mne.make_fixed_length_events(raw, duration=0.2)[:2]
-    epochs = mne.Epochs(raw, events, tmin=0., tmax=0.2)
+    epochs = mne.Epochs(raw, events, tmin=-0.1, tmax=0.2)
     evoked = epochs.average()
 
     with pytest.warns(RuntimeWarning,
diff --git a/mne/decoding/tests/test_transformer.py b/mne/decoding/tests/test_transformer.py
--- a/mne/decoding/tests/test_transformer.py
+++ b/mne/decoding/tests/test_transformer.py
@@ -80,7 +80,7 @@ def test_scaler():
     pytest.raises(ValueError, Scaler, None, None)
     pytest.raises(ValueError, scaler.fit, epochs, y)
     pytest.raises(ValueError, scaler.transform, epochs)
-    epochs_bad = Epochs(raw, events, event_id, 0, 0.01,
+    epochs_bad = Epochs(raw, events, event_id, 0, 0.01, baseline=None,
                         picks=np.arange(len(raw.ch_names)))  # non-data chs
     scaler = Scaler(epochs_bad.info, None)
     pytest.raises(ValueError, scaler.fit, epochs_bad.get_data(), y)
diff --git a/mne/io/tests/test_compensator.py b/mne/io/tests/test_compensator.py
--- a/mne/io/tests/test_compensator.py
+++ b/mne/io/tests/test_compensator.py
@@ -68,7 +68,8 @@ def make_evoked(fname, comp):
             raw.apply_gradient_compensation(comp)
         picks = pick_types(raw.info, meg=True, ref_meg=True)
         events = np.array([[0, 0, 1]], dtype=np.int)
-        evoked = Epochs(raw, events, 1, 0, 20e-3, picks=picks).average()
+        evoked = Epochs(raw, events, 1, 0, 20e-3, picks=picks,
+                        baseline=None).average()
         return evoked
 
     def compensate_mne(fname, comp):
diff --git a/mne/io/tests/test_meas_info.py b/mne/io/tests/test_meas_info.py
--- a/mne/io/tests/test_meas_info.py
+++ b/mne/io/tests/test_meas_info.py
@@ -153,8 +153,7 @@ def test_info():
     event_id, tmin, tmax = 1, -0.2, 0.5
     events = read_events(event_name)
     event_id = int(events[0, 2])
-    epochs = Epochs(raw, events[:1], event_id, tmin, tmax, picks=None,
-                    baseline=(None, 0))
+    epochs = Epochs(raw, events[:1], event_id, tmin, tmax, picks=None)
 
     evoked = epochs.average()
 
@@ -507,7 +506,7 @@ def test_anonymize():
 
     # Test instance method
     events = read_events(event_name)
-    epochs = Epochs(raw, events[:1], 2, 0., 0.1)
+    epochs = Epochs(raw, events[:1], 2, 0., 0.1, baseline=None)
 
     assert not any(_is_anonymous(raw))
     raw.anonymize()
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
@@ -361,7 +361,8 @@ def test_decim():
     pytest.raises(ValueError, epochs.decimate, 2, offset=2)
     for this_offset in range(decim):
         epochs = Epochs(raw, events, event_id,
-                        tmin=-this_offset / raw.info['sfreq'], tmax=tmax)
+                        tmin=-this_offset / raw.info['sfreq'], tmax=tmax,
+                        baseline=None)
         idx_offsets = np.arange(decim) + this_offset
         for offset, idx_offset in zip(np.arange(decim), idx_offsets):
             expected_times = epochs.times[idx_offset::decim]
@@ -587,7 +588,7 @@ def test_epochs_baseline():
         expected = data.copy()
         assert_array_equal(epochs_data[0], expected)
         # the baseline period (1 sample here)
-        epochs.apply_baseline((None, 0))
+        epochs.apply_baseline((0, 0))
         expected[0] = [0, 1]
         if preload:
             assert_allclose(epochs_data[0][0], expected[0])