Average reference as projector is recommended for inverse modeling in Documentation but is in fact mandatory

  • MNE version: 1.6.0
  • operating system: Windows 10

Hello,

Context: I want to compute functional connectivity on source estimations. To do this, i made a rather classical preprocessing pipeline with automatic epoch rejection, ICA etc that will be followed by forward modeling and the inverse problem resolving in order to obtain the source activities.

Problem: I would like to rereference my data during in the preprocessing steps with an average reference (it is recommended for inverse modeling) but i would like to directly apply the rereference during preprocessing and not use a projection.

=> But it is not possible with MNE inverse modeling as shown here:

And I am not sure to understand why.

According to the documentation: Setting the EEG reference ā€” MNE 1.6.1 documentation

=> It is strongly recommended to use average reference to have a ā€˜balancedā€™ forward error and to use it as a projection because it will adapt if channels are dropped afterwards.

But in practice it is mandatory or it will raises a ValueError. Maybe it is more of a documentation accuracy issue but even so I donā€™t understand why the function make it mandatory to have a projection.
A lot of time applying a rereference at some point during the preprocessing is useful and there are ways to make sure that no other channels has been dropped after the rereferencing so why a projection is mandatory for inverse modeling ? Is there a way to cleanly bypass this limitation ?

Thx you in advance for your help

1 Like

Hello, I think you can work with an applied average reference projection until you hit the inverse modeling steps, and then you may be able to simply add the average reference as a projector again (without applying it). This should work around this limitation.

I suspect this limitation exists because after applying the average reference projection, there is currently no way for MNE to know that an average reference has been applied and whether itā€™s still valid. To my knowledge, all MNE will remember is that a ā€œcustomā€ reference has been applied. By adding an average reference projector again, you make things explicit.

I hope this helps a little!

Richard

1 Like

I guess it is the cleanest way to pass this check, thx for the tips

1 Like

If I click through to the page you linked to, I donā€™t actually find that quote about having a ā€œbalancedā€ forward error. What I do see is this:

If you plan to perform source modeling (either with EEG or combined EEG/MEG data), it is strongly recommended to use the average-reference-as-projection approach.

it goes on to say that

an average reference projector adapts if channels are dropped, ensuring that the signal will always be zero-mean when the source modeling is performed. In contrast, applying an average reference by the traditional subtraction method offers no such guarantee.

ā€¦and:

For these reasons, when performing inverse imaging, MNE-Python will raise a ValueError if there are EEG channels present and something other than an average reference strategy has been specified.

So I think the documentation is pretty clear that what is recommended is the projector method of average referencing (when you want to do inverse modeling). You say that you would like to directly apply the average reference during preprocessing, but you donā€™t tell us why that is necessary / why a projector will not work for you. Is there some reason why you need to apply the reference during preprocessing?

If I click through to the page you linked to, I donā€™t actually find that quote about having a ā€œbalancedā€ forward error.

About the forward error spreading, I was referring to this sentence:

It is important to use an average reference because using a specific reference sensor (or even an average of a few sensors) spreads the forward model error from the reference sensor(s) into all sensors, effectively amplifying the importance of the reference sensor(s) when computing source estimates.


Now for the documentation:

So I think the documentation is pretty clear that what is recommended is the projector method of average referencing (when you want to do inverse modeling).

ā€¦and:

For these reasons, when performing inverse imaging, MNE-Python will raise a ValueError if there are EEG channels present and something other than an average reference strategy has been specified.

Yep, the documentation is pretty clear on what is recommended but not on what raises an error.
In fact if you apply average rereferencing on your data it will raise an error even if you used the average reference strategy like written on the paragraph above.
This is surely because as @richard suggested, MNE consider that it is a ā€˜custom referenceā€™ and not an average one because MNE has no means of knowing that the average rereference is still valid. So:

So the documentation could clarify that it will raise an error if a rereference has been applied even if it is an average rereferencing. To not raise an error you need to not apply rereferencing at all and have a average rereferencing projector ready.

You say that you would like to directly apply the average reference during preprocessing, but you donā€™t tell us why that is necessary / why a projector will not work for you. Is there some reason why you need to apply the reference during preprocessing?

I want to apply an average rereferencing during the preprocessing steps because i have 64 electrodes and an automatic epoch rejection program based on the amplitude/covariance/PSD of the epochs

1 Like

Fair point about the docs not being clear between applied average references and average reference projectors in terms of when the error will be raised. If you have time to open a pull request to improve the docs, we would appreciate it!

ok. FYI the built-in peak-to-peak and flat-channel epoch rejection mechanisms will automatically take into account any projectors (without permanently applying them) before deciding whether an epoch should be rejected. But if youā€™re using a custom function for deciding which epochs to keep, then you would indeed need to apply the projectors.

You could also use an average reference projector, then do something like referenced_epochs = epochs.copy().apply_proj() and then run your custom drop function on referenced_epochs but then go back to epochs once youā€™ve determined which epoch indices to keep/drop).

Soon, it will be even easier once [WIP] [ENH] Add ability to reject epochs using callables by withmywoessner Ā· Pull Request #12195 Ā· mne-tools/mne-python Ā· GitHub is merged and released ā€” then you will be able to use a custom reject function, and get the benefit of projectors getting automatically accounted for behind the scenes.

1 Like

Fair point about the docs not being clear between applied average references and average reference projectors in terms of when the error will be raised. If you have time to open a pull request to improve the docs, we would appreciate it!

Yep, i will do a PR, it might help people with a similar case

ok. FYI the built-in peak-to-peak and flat-channel epoch rejection mechanisms will automatically take into account any projectors (without permanently applying them) before deciding whether an epoch should be rejected. But if youā€™re using a custom function for deciding which epochs to keep, then you would indeed need to apply the projectors.

I am indeed using custom functions for epoch rejection but the future feature you shown would come in very handy.

You could also use an average reference projector, then do something like referenced_epochs = epochs.copy().apply_proj() and then run your custom drop function on referenced_epochs but then go back to epochs once youā€™ve determined which epoch indices to keep/drop).

It is an option but in my case, because i am applying automatic rejection with custom functions before and after applying ICA, i would need to do this swap 2 times. So ā€˜trickingā€™ MNE by recreating an average rereference projector is not the most distinguished solution but looks to be the cleanest way in term of code.

1 Like