Repository: mne-tools/mne-python

Issue:
RFC: Re-applying baseline correction after ICA
Skimming over some Study Template analysis results with @agramfort, we found that some of our data appeared to be not baseline-corrected – some traces clearly didn't have a zero-mean during the baseline period.

So I started digging and figured that we had **indeed** applied baseline correction to our data; yet after cleaning the Epochs via ICA, the baseline signals of some channels had started floating away from zero.

Now this may not come as a surprise to some of you, but I have to admit it caught me off-guard. I'd never seen this behavior before with the data I'd worked with so far, and I've failed to reproduce a similarly visible effect using our `sample` data.

However, looking through the [ICA tutorial](https://mne.tools/dev/auto_tutorials/preprocessing/plot_40_artifact_correction_ica.html#fitting-and-plotting-the-ica-solution), where we clean Raw data, one can see the essence of this effect too – albeit with Raw and not Epochs (lower panel):


![](https://mne.tools/dev/_images/sphx_glr_plot_40_artifact_correction_ica_010.png)

I'd like to propose and discuss the following changes and enhancements to deal with this behavior:

- Clearly state in the docs that baseline correction should be re-applied to Epochs and Evoked data after applying ICA
- `viz.plot_ica_overlay()` should automatically re-apply baseline correction to the cleaned signal if an Evoked was passed
    - for this, we'd need to add an `Evoked.baseline` attribute, which gets inherited from the parent `Epochs.baseline`. This has been on my agenda for a while anyway in order to improve provenance, so I'd might take a shot at this anyway.
- We could even go so far as to auto-reapply baseline correction to Epochs and Evoked data as part of `ICA.apply()`, so users wouldn't have to do this themselves. This behavior should be toggle-able using a new kwarg.



Discussion:
I think this is because of `pca_mean_`. Basically whatever mean value you have for each channel gets subtracted, then ICA unmixing/selection/mixing, then `pca_mean_` gets added back to the data. If you set `ica.pca_mean_[:] = 0`, does this problem go away?

I'm not actually 100% sure what the correct approach is here, but I don't like the fact that applying ICA imposes a DC shift in the data during an interval where the DC value of each channel is zero. This seems in theory incorrect, and in practice potentially dangerous.
> . If you set `ica.pca_mean_[:] = 0`, does this problem go away?

Yes this indeed fixes the problem.

Before:
<img width="300" alt="Screen Shot 2021-03-10 at 13 31 37" src="https://user-images.githubusercontent.com/2046265/110630052-2ed17d00-81a5-11eb-96be-b787aec59d30.png">

After setting `ica.pca_mean_[:] = 0`:
<img width="300" alt="Screen Shot 2021-03-10 at 13 33 04" src="https://user-images.githubusercontent.com/2046265/110630089-3b55d580-81a5-11eb-905e-4a482d66beae.png">

I wonder if we should have a `center=True` (default) argument that says whether or not to use the empirical estimate of the mean for each channel to center the data. And I'm not 100% sure it makes sense to keep the default at `True`, we might want to (by deprecation) change it to `False`. If data that has been high-pass filtered (raw, or epochs based on high-passed raw data without baseline correction), then you probably don't want to do it since the high-passing should already center the data for you. If data that has been baseline corrected (epochs), it's probably not doing the right thing since the DC levels have been altered by the baselining, so your attempt to center with `pca_mean_` will be wrong (like this example).

Probably @agramfort has thought about this more than I have, though...
> If data that has been high-pass filtered (raw, or epochs based on high-passed raw data without baseline correction), then you probably don't want to do it since the high-passing should already center the data for you. If data that has been baseline corrected (epochs), it's probably not doing the right thing

If that's the case, we could simply decide to only center `if inst.baseline is not None`, I suppose? Would work right away with Epochs, and would require implementation of the `baseline` attribute for Evoked
> If that's the case, we could simply decide to only center `if inst.baseline is not None`, I suppose? 

... plus a check whether the data has been high-passed, of course …
That seems reasonable to me, and we could still pair it with a `center='auto' | True | False` where `'auto'` centers if it makes sense to do so (data not high-passed, not baseline corrected), where `center` defaults to `True` in 0.23 and goes to `'auto'` in 0.24 via deprecation. But let's wait to hear if @agramfort sees this as conceptually correct.

In the meantime you can work on the evoked baseline attribute again if you want :)
@larsoner Would a similar issue be expected to appear with SSP too?
SSP is a linear spatial operator, so if all channels have zero mean (during some interval) all channels will have zero mean after the operation (in that same interval). This is because the mean (over that interval) is also a linear operator.

To demonstrate how ICA violates this, let's say we have two channels of data for one 3-sample epoch that that each already have zero mean over time, for example `[[1, 0, -1], [0, 0, 0]]`. If the empirical means estimated by ICA during fitting happened to be, say -1 and 0, ICA will:

1. Subtract the empirical estimated means, giving `[[2, 1, 0], [0, 0, 0]]`
2. Apply the ICA operator, which is just some linear spatial operator. Let's pick one that just swaps data between channels, `[[0, 1], [1, 0]]` (ICA in practice will do something different like unmix-exclude-mix, but this gives a simple example to play with). So now our data after the ICA operator is `[[0, 0, 0], [2, 1, 0]]`.
3. Add the means back, giving `[[-1, -1, -1], [2, 1, 0]]`. 

Now our previously-zero-mean channels have means `-1` and `1`, which is not good. Let's call this operation `B = ICA(A)`. If we apply this operation to identical data, that will (trivially) have `B = ICA(A)` having means `-1` and `1`, too.

And this is where you can see that the `ICA` operation above is not linear. We know that `B + B = ICA(A) + ICA(A)` but that this means `2B = ICA(A) + ICA(A)`. We know that since `B` has means `-1` and `1`, `2B` will have means over time of `-2` and `2`. For linearity to hold, we need that `2B = ICA(A) + ICA(A) = ICA(A + A) = ICA(2A)`, but unfortunately `ICA(2A)` will go from `[[2, 0, -2], [0, 0, 0]]` to start, through step 1 to `[[3, 0, -1], [0, 0, 0]]`, through step 2 to `[[0, 0, 0], [3, 0, -1]]`, through step 3 to `[[-1, -1, -1], [3, 0, -1]]`, which has means `-1` and `2/3`, which do not equal `-2` and `2`. Hence our ICA operation is not a linear operator, which I think it is meant to be...

If we remove the mean subtraction at the beginning and mean re-addition at the end, ICA will be a linear spatial operator (just like SSP) in the end. To me this is starting to seem like the correct option.
I fail to see why ICA should estimate means that are not equal to the sample means, in which case your example doesn't work.
> I fail to see why ICA should estimate means that are not equal to the sample means, in which case your example doesn't work.

Indeed the example is contrived but there are cases where it can happen. Simple examples like estimating / fitting on some or all segments of raw and applying to epochs, or estimating/applying on different segments of raw data, or estimating/applying on epochs/raw, etc. will all have this problem to varying degrees, depending on the data characteristics. The severity of the problem is going to depend on the difference in the means of the data it's fit on and the data it's being applied to. Having baseline corrected epochs is a particularly problematic case (@hoechenberger's above). The key is that the estimated sample mean from PCA needs to accurately reflect the true underlying mean of the data you're applying the ICA to.

So IMO the particularly problematic case is estimation on raw, then application on baseline corrected epochs. I think if you fit and apply to the same epochs (baseline corrected or not), or fit and apply to raw, or fit to raw and apply to non-baseline-corrected epochs (assuming you have enough raw samples, etc.) and then optionally baseline correct, things are probably okay.
I agree, but this isn't really an ICA problem, but it happens because we've added an explicit zero-meaning step. I'm not even sure why we're doing this, because PCA could be computed on the covariance matrix, but we're probably using SVD. So I guess what I'm trying to say is that we probably shouldn't make this the default and leave centering to the user (but of course highlight this in the docs).
btw I just ask Arnaud Delorme and EEGLAB also does the centering.

Now if it turns out to help we could add an `assume_centered` book param in
_ICA class
to possibly skip centering.

More soon

>

I've created the following MWE to check for interactions between

- baselining and not baselining the epochs we remove ICs from
- baselining and not baselining epochs before ICA **decomposition**
- centering and not centering the signal via `ICA.pca_mean_`

```python
import os
import itertools
import mne
from mne.preprocessing import ICA

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
event_id = {'auditory/left': 1, 'auditory/right': 2, 'visual/left': 3,
            'visual/right': 4, 'face': 5, 'button': 32}

raw = (mne.io.read_raw_fif(sample_data_raw_file, preload=True)
       .filter(l_freq=1., h_freq=None))
events = mne.find_events(raw, stim_channel='STI 014')

for baseline_data, baseline_ica, center_pca in itertools.product([True, False],
                                                                 [True, False],
                                                                 [True, False]):

    epochs_data = mne.Epochs(raw, events, tmin=-0.2, tmax=0.5,
                             event_id=event_id, preload=True, baseline=None)
    epochs_ica = epochs_data.copy()

    if baseline_data:
        epochs_data.apply_baseline()
    if baseline_ica:
        epochs_ica.apply_baseline()

    ica = ICA(n_components=15, random_state=97)
    ica.fit(epochs_ica)
    if not center_pca:
        ica.pca_mean_[:] = 0

    evoked = epochs_data.average()
    fig = ica.plot_overlay(evoked, exclude=[0])
    title = (f'data_'
             f'{"baselined" if baseline_data else "not_baselined"}-'
             f'ica_'
             f'{"baselined" if baseline_ica else "not_baselined"}-'
             f'{"centered" if center_pca else "not_centered"}')
    fig.suptitle(title)
    fig.tight_layout()
    fig.savefig(f'{title}.png')
```

![data_baselined-ica_baselined-centered](https://user-images.githubusercontent.com/2046265/110768732-c7c0d080-8257-11eb-9f96-3a3570e58205.png)
![data_baselined-ica_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110768738-c8f1fd80-8257-11eb-9914-9dc393ec1f05.png)
![data_baselined-ica_not_baselined-centered](https://user-images.githubusercontent.com/2046265/110768739-c8f1fd80-8257-11eb-94b2-d07b150260ba.png)
![data_baselined-ica_not_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110768741-c98a9400-8257-11eb-97aa-ae0e62691e80.png)
![data_not_baselined-ica_baselined-centered](https://user-images.githubusercontent.com/2046265/110768745-c98a9400-8257-11eb-9aa3-721b11feb62c.png)
![data_not_baselined-ica_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110768748-ca232a80-8257-11eb-870c-22e239ecb7fc.png)
![data_not_baselined-ica_not_baselined-centered](https://user-images.githubusercontent.com/2046265/110768750-ca232a80-8257-11eb-8f62-199b03f93a64.png)
![data_not_baselined-ica_not_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110768751-cabbc100-8257-11eb-884e-5a6ce287d7eb.png)

Since the effect is most pronounced for EEG data, here are the EEG plots again:

![data_baselined-ica_baselined-centered](https://user-images.githubusercontent.com/2046265/110770065-2aff3280-8259-11eb-83f7-abd3b92dbc3e.png)
![data_baselined-ica_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110770069-2b97c900-8259-11eb-8281-d6cbacc7f2db.png)
![data_baselined-ica_not_baselined-centered](https://user-images.githubusercontent.com/2046265/110770073-2c305f80-8259-11eb-9d0e-4647d8122562.png)
![data_baselined-ica_not_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110770074-2cc8f600-8259-11eb-8c11-3b8ad79703cf.png)
![data_not_baselined-ica_baselined-centered](https://user-images.githubusercontent.com/2046265/110770075-2cc8f600-8259-11eb-9627-8125f6614b92.png)
![data_not_baselined-ica_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110770078-2d618c80-8259-11eb-9ff2-a2e58ef75eff.png)
![data_not_baselined-ica_not_baselined-centered](https://user-images.githubusercontent.com/2046265/110770081-2d618c80-8259-11eb-8eed-d56b6c760686.png)
![data_not_baselined-ica_not_baselined-not_centered](https://user-images.githubusercontent.com/2046265/110770082-2d618c80-8259-11eb-89a9-b55513ed9e8e.png)

What we're now doing in the Study Template is we don't apply baseline correction to anything until **after** the data has been cleaned. The results we get that way look sane, as far as I can tell.
ICA cannot handle DC well, so it is recommended to high-pass the data (cutoff e.g. 0.5Hz or 1Hz). This means that baselining could even have detrimental effects, i.e. if all concatenated epochs already have zero mean, baseline-correcting might introduce an offset.
we converge to the same thing.

Do not baseline epochs passed to ICA.

Apply baseline only after doing ICA cleaning.

No need to enable not centering the PCA step.

>

I will update the docs accordingly. 

Resolving patch:
diff --git a/mne/preprocessing/ica.py b/mne/preprocessing/ica.py
--- a/mne/preprocessing/ica.py
+++ b/mne/preprocessing/ica.py
@@ -200,11 +200,6 @@ class ICA(ContainsMixin):
         and to ``500`` for ``'infomax'`` or ``'picard'``. The actual number of
         iterations it took :meth:`ICA.fit` to complete will be stored in the
         ``n_iter_`` attribute.
-
-        .. versionchanged:: 0.23
-            Version 0.23 introduced ``'auto'`` settings for maximum iterations
-             as specified above. With version 0.24 ``'auto'`` will be the new
-            default, replacing the current ``max_iter=200``.
     allow_ref_meg : bool
         Allow ICA on MEG reference channels. Defaults to False.
 
@@ -263,6 +258,19 @@ class ICA(ContainsMixin):
 
     Notes
     -----
+    .. versionchanged:: 0.23
+        Version 0.23 introduced the ``max_iter='auto'`` settings for maximum
+        iterations. With version 0.24 ``'auto'`` will be the new
+        default, replacing the current ``max_iter=200``.
+
+    .. versionchanged:: 0.23
+        Warn if `~mne.Epochs` were baseline-corrected.
+
+    .. note:: If you intend to clean fit ICA on `~mne.Epochs`, it is
+              recommended to high-pass filter, but **not** baseline correct the
+              data for good ICA performance. A warning will be emitted
+              otherwise.
+
     A trailing ``_`` in an attribute name signifies that the attribute was
     added to the object during fitting, consistent with standard scikit-learn
     practice.
@@ -475,7 +483,7 @@ def fit(self, inst, picks=None, start=None, stop=None, decim=None,
         Parameters
         ----------
         inst : instance of Raw or Epochs
-            Raw measurements to be decomposed.
+            The data to be decomposed.
         %(picks_good_data_noref)s
             This selection remains throughout the initialized ICA solution.
         start : int | float | None
@@ -521,6 +529,17 @@ def fit(self, inst, picks=None, start=None, stop=None, decim=None,
             Returns the modified instance.
         """
         _validate_type(inst, (BaseRaw, BaseEpochs), 'inst', 'Raw or Epochs')
+
+        if np.isclose(inst.info['highpass'], 0.):
+            warn('The data has not been high-pass filtered. For good ICA '
+                 'performance, it should be high-pass filtered (e.g., with a '
+                 '1.0 Hz lower bound) before fitting ICA.')
+
+        if isinstance(inst, BaseEpochs) and inst.baseline is not None:
+            warn('The epochs you passed to ICA.fit() were baseline-corrected. '
+                 'However, we suggest to fit ICA only on data that has been '
+                 'high-pass filtered, but NOT baseline-corrected.')
+
         picks = _picks_to_idx(inst.info, picks, allow_empty=False,
                               with_ref_meg=self.allow_ref_meg)
         _check_for_unsupported_ica_channels(
@@ -1530,16 +1549,16 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
               start=None, stop=None, verbose=None):
         """Remove selected components from the signal.
 
-        Given the unmixing matrix, transform data,
-        zero out components, and inverse transform the data.
+        Given the unmixing matrix, transform the data,
+        zero out all excluded components, and inverse-transform the data.
         This procedure will reconstruct M/EEG signals from which
         the dynamics described by the excluded components is subtracted.
-        The data is processed in place.
 
         Parameters
         ----------
         inst : instance of Raw, Epochs or Evoked
-            The data to be processed. The instance is modified inplace.
+            The data to be processed (i.e., cleaned). It will be modified
+            in-place.
         include : array_like of int
             The indices referring to columns in the ummixing matrix. The
             components to be kept.
@@ -1559,6 +1578,19 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
         -------
         out : instance of Raw, Epochs or Evoked
             The processed data.
+
+        Notes
+        -----
+        .. note:: Applying ICA may introduce a DC shift. If you pass
+                  baseline-corrected `~mne.Epochs` or `~mne.Evoked` data,
+                  the baseline period of the cleaned data may not be of
+                  zero mean anymore. If you require baseline-corrected
+                  data, apply baseline correction again after cleaning
+                  via ICA. A warning will be emitted to remind you of this
+                  fact if you pass baseline-corrected data.
+
+        .. versionchanged:: 0.23
+            Warn if instance was baseline-corrected.
         """
         _validate_type(inst, (BaseRaw, BaseEpochs, Evoked), 'inst',
                        'Raw, Epochs, or Evoked')
@@ -1575,6 +1607,14 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
             kwargs.update(evoked=inst)
         _check_compensation_grade(self.info, inst.info, 'ICA', kind,
                                   ch_names=self.ch_names)
+
+        if isinstance(inst, (BaseEpochs, Evoked)):
+            if getattr(inst, 'baseline', None) is not None:
+                warn('The data you passed to ICA.apply() was '
+                     'baseline-corrected. Please note that ICA can introduce '
+                     'DC shifts, therefore you may wish to consider '
+                     'baseline-correcting the cleaned data again.')
+
         logger.info(f'Applying ICA to {kind} instance')
         return meth(**kwargs)
 
diff --git a/tutorials/preprocessing/plot_40_artifact_correction_ica.py b/tutorials/preprocessing/plot_40_artifact_correction_ica.py
--- a/tutorials/preprocessing/plot_40_artifact_correction_ica.py
+++ b/tutorials/preprocessing/plot_40_artifact_correction_ica.py
@@ -64,12 +64,13 @@
 #
 #     If you want to perform ICA with *no* dimensionality reduction (other than
 #     the number of Independent Components (ICs) given in ``n_components``, and
-#     any subsequent exclusion of ICs you specify in ``ICA.exclude``), pass
-#     ``n_pca_components=None`` (this is the default value).
+#     any subsequent exclusion of ICs you specify in ``ICA.exclude``), simply
+#     pass ``n_components``.
 #
 #     However, if you *do* want to reduce dimensionality, consider this
-#     example: if you have 300 sensor channels and you set
-#     ``n_pca_components=None`` and ``n_components=50``, then the the first 50
+#     example: if you have 300 sensor channels and you set ``n_components=50``
+#     during instantiation and pass ``n_pca_components=None`` to
+#     `~mne.preprocessing.ICA.apply`, then the the first 50
 #     PCs are sent to the ICA algorithm (yielding 50 ICs), and during
 #     reconstruction `~mne.preprocessing.ICA.apply` will use the 50 ICs
 #     plus PCs number 51-300 (the full PCA residual). If instead you specify
@@ -79,9 +80,8 @@
 #
 #     **If you have previously been using EEGLAB**'s ``runica()`` and are
 #     looking for the equivalent of its ``'pca', n`` option to reduce
-#     dimensionality via PCA before the ICA step, set ``n_components=n``
-#     during initialization and pass ``n_pca_components=n`` to
-#     `~mne.preprocessing.ICA.apply`.
+#     dimensionality, set ``n_components=n`` during initialization and pass
+#     ``n_pca_components=n`` to `~mne.preprocessing.ICA.apply`.
 #
 # MNE-Python implements three different ICA algorithms: ``fastica`` (the
 # default), ``picard``, and ``infomax``. FastICA and Infomax are both in fairly
@@ -207,6 +207,12 @@
 #     just continuous `~mne.io.Raw` objects), or only use every Nth
 #     sample by passing the ``decim`` parameter to ``ICA.fit()``.
 #
+#     .. note:: `~mne.Epochs` used for fitting ICA should not be
+#               baseline-corrected. Because cleaning the data via ICA may
+#               introduce DC offsets, we suggest to baseline correct your data
+#               **after** cleaning (and not before), should you require
+#               baseline correction.
+#
 # Now we're ready to set up and fit the ICA. Since we know (from observing our
 # raw data) that the EOG and ECG artifacts are fairly strong, we would expect
 # those artifacts to be captured in the first few dimensions of the PCA
@@ -488,9 +494,11 @@
     # remove trailing `.` from channel names so we can set montage
     raw.rename_channels(mapping)
     raw.set_montage('standard_1005')
+    # high-pass filter
+    raw_filt = raw.copy().load_data().filter(l_freq=1., h_freq=None)
     # fit ICA
     ica = ICA(n_components=30, max_iter='auto', random_state=97)
-    ica.fit(raw)
+    ica.fit(raw_filt)
     raws.append(raw)
     icas.append(ica)
 
@@ -587,7 +595,7 @@
 
 
 ###############################################################################
-# Compute ICA components on epochs
+# Compute ICA components on Epochs
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 #
 # ICA is now fit to epoched MEG data instead of the raw data.
@@ -604,6 +612,11 @@
 # - 1-30 Hz band-pass filter
 # - epoching -0.2 to 0.5 seconds with respect to events
 # - rejection based on peak-to-peak amplitude
+#
+# Note that we don't baseline correct the epochs here – we'll do this after
+# cleaning with ICA is completed. Baseline correction before ICA is not
+# recommended by the MNE-Python developers, as it doesn't guarantee optimal
+# results.
 
 filt_raw.pick_types(meg=True, eeg=False, exclude='bads', stim=True).load_data()
 filt_raw.filter(1, 30, fir_design='firwin')
@@ -612,8 +625,9 @@
 reject = dict(grad=4000e-13, mag=4e-12)
 # create longer and more epochs for more artifact exposure
 events = mne.find_events(filt_raw, stim_channel='STI 014')
+# don't baseline correct epochs
 epochs = mne.Epochs(filt_raw, events, event_id=None, tmin=-0.2, tmax=0.5,
-                    reject=reject)
+                    reject=reject, baseline=None)
 
 ###############################################################################
 # Fit ICA model using the FastICA algorithm, detect and plot components


Test patch:
diff --git a/mne/preprocessing/tests/test_ica.py b/mne/preprocessing/tests/test_ica.py
--- a/mne/preprocessing/tests/test_ica.py
+++ b/mne/preprocessing/tests/test_ica.py
@@ -84,7 +84,7 @@ def test_ica_full_data_recovery(method):
     picks = pick_types(raw.info, meg=True, stim=False, ecg=False,
                        eog=False, exclude='bads')[:10]
     epochs = Epochs(raw, events[:4], event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), preload=True)
+                    baseline=None, preload=True)
     evoked = epochs.average()
     n_channels = 5
     data = raw._data[:n_channels].copy()
@@ -165,6 +165,35 @@ def test_ica_simple(method):
     assert amari_distance < 0.1
 
 
+def test_warnings():
+    """Test that ICA warns on certain input data conditions."""
+    raw = read_raw_fif(raw_fname).crop(0, 5).load_data()
+    events = read_events(event_name)
+    epochs = Epochs(raw, events=events, baseline=None, preload=True)
+    ica = ICA(n_components=2, max_iter=1, method='infomax', random_state=0)
+
+    # not high-passed
+    epochs.info['highpass'] = 0.
+    with pytest.warns(RuntimeWarning, match='should be high-pass filtered'):
+        ica.fit(epochs)
+
+    # baselined
+    epochs.info['highpass'] = 1.
+    epochs.baseline = (epochs.tmin, 0)
+    with pytest.warns(RuntimeWarning, match='epochs.*were baseline-corrected'):
+        ica.fit(epochs)
+
+    # cleaning baseline-corrected data
+    epochs.info['highpass'] = 1.
+    epochs.baseline = None
+    ica.fit(epochs)
+
+    epochs.baseline = (epochs.tmin, 0)
+    with pytest.warns(RuntimeWarning, match='consider baseline-correcting.*'
+                                            'again'):
+        ica.apply(epochs)
+
+
 @requires_sklearn
 @pytest.mark.parametrize('n_components', (None, 0.9999, 8, 9, 10))
 @pytest.mark.parametrize('n_pca_components', [8, 9, 0.9999, 10])
@@ -177,6 +206,7 @@ def test_ica_noop(n_components, n_pca_components, tmpdir):
     info = create_info(10, 1000., 'eeg')
     raw = RawArray(data, info)
     raw.set_eeg_reference()
+    raw.info['highpass'] = 1.0  # fake high-pass filtering
     assert np.linalg.matrix_rank(raw.get_data()) == 9
     kwargs = dict(n_components=n_components, verbose=True)
     if isinstance(n_components, int) and \
@@ -423,7 +453,7 @@ def test_ica_core(method, n_components, noise_cov, n_pca_components):
     raw.del_proj()
     del picks
     epochs = Epochs(raw, events[:4], event_id, tmin, tmax,
-                    baseline=(None, 0), preload=True)
+                    baseline=None, preload=True)
 
     # test essential core functionality
 
@@ -1079,6 +1109,10 @@ def test_bad_channels(method, allow_ref_meg):
     data = rng.rand(100, len(chs), 50)
     epochs = EpochsArray(data, info)
 
+    # fake high-pass filtering
+    raw.info['highpass'] = 1.0
+    epochs.info['highpass'] = 1.0
+
     n_components = 0.9
     data_chs = list(_DATA_CH_TYPES_SPLIT + ('eog',))
     if allow_ref_meg:
@@ -1122,7 +1156,7 @@ def test_eog_channel(method):
     picks = pick_types(raw.info, meg=True, stim=True, ecg=False,
                        eog=True, exclude='bads')
     epochs = Epochs(raw, events, event_id, tmin, tmax, picks=picks,
-                    baseline=(None, 0), preload=True, proj=False)
+                    baseline=None, preload=True, proj=False)
     n_components = 0.9
     ica = ICA(n_components=n_components, method=method)
     # Test case for MEG and EOG data. Should have EOG channel
@@ -1187,7 +1221,8 @@ def test_ica_ctf():
     events = make_fixed_length_events(raw, 99999)
     for comp in [0, 1]:
         raw.apply_gradient_compensation(comp)
-        epochs = Epochs(raw, events, None, -0.2, 0.2, preload=True)
+        epochs = Epochs(raw, events=events, tmin=-0.2, tmax=0.2, baseline=None,
+                        preload=True)
         evoked = epochs.average()
 
         # test fit
@@ -1209,7 +1244,8 @@ def test_ica_ctf():
         ica.fit(raw)
     _assert_ica_attributes(ica)
     raw.apply_gradient_compensation(1)
-    epochs = Epochs(raw, events, None, -0.2, 0.2, preload=True)
+    epochs = Epochs(raw, events=events, tmin=-0.2, tmax=0.2, baseline=None,
+                    preload=True)
     evoked = epochs.average()
     for inst in [raw, epochs, evoked]:
         with pytest.raises(RuntimeError, match='Compensation grade of ICA'):
@@ -1312,7 +1348,8 @@ def test_ica_eeg(fname, grade):
     picks_all = []
     picks_all.extend(picks_meg)
     picks_all.extend(picks_eeg)
-    epochs = Epochs(raw, events, None, -0.1, 0.1, preload=True, proj=False)
+    epochs = Epochs(raw, events=events, tmin=-0.1, tmax=0.1, baseline=None,
+                    preload=True, proj=False)
     evoked = epochs.average()
 
     for picks in [picks_meg, picks_eeg, picks_all]:
@@ -1449,7 +1486,8 @@ def test_ica_ch_types(ch_type):
     raw = RawArray(data, info)
     events = make_fixed_length_events(raw, 99999, start=0, stop=0.3,
                                       duration=0.1)
-    epochs = Epochs(raw, events, None, -0.1, 0.1, preload=True, proj=False)
+    epochs = Epochs(raw, events=events, tmin=-0.1, tmax=0.1, baseline=None,
+                    preload=True, proj=False)
     evoked = epochs.average()
     # test fit
     method = 'infomax'
diff --git a/mne/viz/tests/test_ica.py b/mne/viz/tests/test_ica.py
--- a/mne/viz/tests/test_ica.py
+++ b/mne/viz/tests/test_ica.py
@@ -119,6 +119,7 @@ def test_plot_ica_properties():
     """Test plotting of ICA properties."""
     raw = _get_raw(preload=True).crop(0, 5)
     raw.add_proj([], remove_existing=True)
+    raw.info['highpass'] = 1.0  # fake high-pass filtering
     events = make_fixed_length_events(raw)
     picks = _get_picks(raw)[:6]
     pick_names = [raw.ch_names[k] for k in picks]
@@ -304,6 +305,7 @@ def test_plot_ica_sources():
 def test_plot_ica_overlay():
     """Test plotting of ICA cleaning."""
     raw = _get_raw(preload=True)
+    raw.info['highpass'] = 1.0  # fake high-pass filtering
     picks = _get_picks(raw)
     ica = ICA(noise_cov=read_cov(cov_fname), n_components=2, random_state=0)
     # can't use info.normalize_proj here because of how and when ICA and Epochs
@@ -325,6 +327,7 @@ def test_plot_ica_overlay():
     # smoke test for CTF
     raw = read_raw_fif(raw_ctf_fname)
     raw.apply_gradient_compensation(3)
+    raw.info['highpass'] = 1.0  # fake high-pass filtering
     picks = pick_types(raw.info, meg=True, ref_meg=False)
     ica = ICA(n_components=2, )
     ica.fit(raw, picks=picks)
diff --git a/mne/viz/tests/test_topomap.py b/mne/viz/tests/test_topomap.py
--- a/mne/viz/tests/test_topomap.py
+++ b/mne/viz/tests/test_topomap.py
@@ -585,6 +585,12 @@ def test_plot_topomap_nirs_ica(fnirs_epochs):
     from mne.preprocessing import ICA
     fnirs_epochs = fnirs_epochs.load_data().pick(picks='hbo')
     fnirs_epochs = fnirs_epochs.pick(picks=range(30))
+
+    # fake high-pass filtering and hide the fact that the epochs were
+    # baseline corrected
+    fnirs_epochs.info['highpass'] = 1.0
+    fnirs_epochs.baseline = None
+
     ica = ICA().fit(fnirs_epochs)
     fig = ica.plot_components()
     assert len(fig[0].axes) == 20