Mne-python: ICA.plot_overlay() and baseline correction

Created on 23 Aug 2020  ·  14Comments  ·  Source: mne-tools/mne-python

Describe the bug

@agramfort pointed out that in ICA overlay plots (i.e., visualization of Evoked before and after artifact removal via ICA), the displayed cleaned Evoked isn't baseline-corrected, even if the input was baseline-corrected.

Screenshot 2020-08-23 at 17 54 34

I've tracked this down to an issue in mne.viz.ica.plot_ica_overlay()

https://github.com/mne-tools/mne-python/blob/4c44bd6ae90dd4d5c6c2e925d93f1b2fbf7bda6a/mne/viz/ica.py#L791-L793

The root of our problem is that evoked_cln is not baseline-corrected before it is being submitted to _plot_ica_overlay_evoked(). This in itself could be tolerated; however, since our input data (called inst here) was already baseline-corrected (by us, before invoking plot_overlay()), we end up with an overlay of a baseline-corrected and a non-baseline-corrected Evoked.

Expected results

By applying baseline correction to evoked_cln before plotting, I get the expected result:

     evoked_cln = ica.apply(inst.copy(), exclude=exclude) 
     evoked_cln.apply_baseline()
     fig = _plot_ica_overlay_evoked(evoked=inst, evoked_cln=evoked_cln, 
                                    title=title, show=show) 

Screenshot 2020-08-23 at 17 55 05

So now I thought: neat, that'll be an easy fix! Just check if the passed Evoked instance has been baseline-corrected; and if yes, also baseline-correct the cleaned Evoked.

But there is no Evoked.baseline attribute as there is for Epochs. Therefore it seems to be impossible to find out whether an Evoked has been baseline-corrected or not.

Proposed fix

I suggest we

  • add a .baseline attribute to mne.Evoked, which is inherited from the Epochs used for averaging
  • add a check to mne.viz.ica.plot_ica_overlay():

    • if inst.baseline is set, apply the same baseline correction to evoked_cln,

    • otherwise, do not apply baseline correction to evoked_cln

Alternatively we could also consider adding this check to ICA.apply(): if the passed Evoked has been baseline-corrected, apply the same baseline correction to the cleaned Evoked before returning it.

Thoughts?

cc @cbrnr

Most helpful comment

I'd add a baseline attribute to be consistent with epochs.

All 14 comments

Of course one could also argue that the current behavior is, technically, absolutely correct: You pass in an Evoked, remove some ICs, and plot the result without altering it any further (e.g., through baseline correction).

Considering this, we should at least add a baseline kwarg to plot_ica_overlay(), which would apply baseline correction to the input and to the cleaned data before plotting.

(However, I personally feel it's kind of misleading comparing an Evoked after baseline correction with one that was not baseline-corrected)

I'd add a baseline attribute to be consistent with epochs.

Just to get some more opinions on the underlying issue I'm trying to solve, I'd like to hear what regular users of ICA think. Pinging @cbrnr @sappelhoff @SophieHerbst @dnacombo:

I have a baseline-corrected dataset (red traces in the image below); applying ICA to remove some components introduces an offset in some channels of the cleaned data (black traces)
Screenshot 2020-08-23 at 17 54 34

Applying baseline-correction to the cleaned data seems to "fix" this:
Screenshot 2020-08-23 at 17 55 05
However I've never seen anyone doing this, nor have I ever done this myself – I never had the impression it was necessary. Have you ever seen anything like this?

I usually highpass filter my data, perform ICA, and then make epochs, which I then baseline-correct :thinking:

So I don't baseline correct prior to ICA and haven't encountered this problem. But if the offsets are created through the component removal, then maybe that's to be expected and doing a second baseline correction is in fact a second baseline correction, and not a fix of the first baseline correction?

I'd add a baseline attribute to be consistent with epochs.

sounds reasonable to me :+1:

What I did here was:

  • bandpass filter (1.0–40.0 Hz)
  • epoch & baseline correct
  • run ICA on those epochs

Then I go back to the raw data,

  • highpass filter with a low cutoff (0.3 Hz)
  • epoch & baseline correct
  • ICA.apply(epoch)

@sappelhoff

I usually highpass filter my data, perform ICA, and then make epochs, which I then baseline-correct 🤔

So you run ICA on the continuous data?

So you run ICA on the continuous data?

yes, downsampled to 250Hz (with appropriate anti-aliasing filter)

I later apply the ICA solution to the raw, unfiltered data and then proceed to preprocess that ICA cleaned data.

it goes a bit like this:

  1. load each subj-task raw data (3 files per subj) and inspect visually, marking noisy segments and bad channels, and saving as .txt (for reproducibility)
  2. load all task raw data per subject and concatenate into one file as preparation for ICA
  3. ICA cleaning workflow for each concatenated file (one per subject)

    1. ICA-specific preprocessing (applied to a copy [!] of the data)



      1. highpass data at 1Hz


      2. downsample data to 250Hz (applying an appropriate anti-aliasing filter before)



    2. run ICA on all good (! drop bad) EEG (! not EOG etc.) channels and drop bad time segments prior to fitting (see step 1.)

    3. use EOG and ECG channels to automatically identify stereotypical artifacts (blinks, eye-movements, heartbeat)

    4. manually inspect all thusly identitfied ICA components, double checking the automatically marked artifacts and correcting/changing them. Finally, saving the to-be-rejected component indices as .txt

    5. apply ICA to raw data (not yet filtered or downsampled, see step 3a, we worked on a copy so far. now apply to original)

  4. Steps applied to ICA cleaned data
  5. filter between 0.1 and 40Hz (FIR one-pass, zero-phase, non-causal bandpass filter)
  6. interpolate all bad channels (spherical spline approach)
  7. re-reference to average

So you run ICA on the continuous data?

yes

Wouldn't that cause ICA also to pick up all "un-interesting" artifacts that occur between trials, e.g. when a participant blinks, swallows, moves?

Also I would like to point out that the order at least of the first few channels is preserved:
Screenshot 2020-08-23 at 17 54 34

I also use a procedure similar to what @sappelhoff described - or at least I don't run ICA on baseline-corrected epoched data. Technically, I use non-baseline-corrected epochs (which contain the activity of interest), but I don't use MNE epochs but basically cut my own epochs based on the raw continuous data.

@hoechenberger it's true that doing baseline correction if you already high
pass filtered the data
is unnecessary. Can you remove this extra baseline correction to see how it
behaves?

>

Wouldn't that cause ICA also to pick up all "un-interesting" artifacts that occur between trials, e.g. when a participant blinks, swallows, moves?

to some extend yes, ... I do cut out block breaks and then I simply trust that blinks, horizontals, and cardiac ICs are clear enough that I can reject them and I don't care about all other ICs and what they do or don't capture --> I use ICA exclusively for preprocessing/cleaning)

@hoechenberger

I've done ICA mostly with EEG data.
From raw EEG data, I usually

  • re-reference to average,

    • downsample,

    • filter,

    • epoch,

    • baseline subtract,

    • run ICA,

    • remove comps,

    • plot

And they're all aligned. I don't recall having had this issue before.
Perhaps something to do with the re-referencing, which is not applied to MEG? on EEG signals, not rereferencing has a strange effect. You get a (usually first) component that sort of captures the average ref.

I cannot reproduce this issue anymore. Which is good, I suppose 😅👍

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hoechenberger picture hoechenberger  ·  43Comments

kingjr picture kingjr  ·  35Comments

cbrnr picture cbrnr  ·  34Comments

choldgraf picture choldgraf  ·  42Comments

hoechenberger picture hoechenberger  ·  56Comments