-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
ENH Enable common average reference projection for ECOG, DBS, and SEEG data #10656
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Using a common average projection currently only works for EEG data. It should also be available for intracranial data. This commit tries to solve it.
|
Hello! 👋 Thanks for opening your first pull request here! ❤️ We will try to get back to you soon. 🚴🏽♂️ |
mne/io/proj.py
Outdated
|
|
||
| @verbose | ||
| def make_eeg_average_ref_proj(info, activate=True, verbose=None): | ||
| def make_eeg_average_ref_proj(info, activate=True, ch_dict=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have gone with ch_type=('eeg',) as API and
default parameter
would that do the job for you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thank you for the quick review @agramfort! I changed it in this commit 😊
Suggestion by @agramfort.
mne/io/proj.py
Outdated
|
|
||
| ch_dict = {**{type_: True for type_ in ch_type}, | ||
| 'meg': False, 'ref_meg': False} | ||
| eeg_sel = pick_types(info, **ch_dict, exclude='bads') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can reuse the private function _picks_to_idx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @agramfort. I don't think I understand this private function though. How can I include it?
eeg_sel = _picks_to_idx(info, ch_type, none='data', exclude='bads', allow_empty=False, with_ref_meg=False)???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would go for none='eeg'
|
@moritz-gerster there are some failing tests: style test: functionality tests: Let us know if you need help understanding the problems or finding the right fix. |
|
@drammock thank you for your help! 😊
I did update the docstring now but it seems there are still some doc tests failing. I don't understand why.
I don't know how to fix this... Any help would be appreciated! In the meantime, I will keep trying to find out myself. |
mne/io/proj.py
Outdated
| activate : bool | ||
| If True projections are activated. | ||
| ch_type : tuple | ||
| List of channel types to use for reference projection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@moritz-gerster can you list the valid channel types. I would say this makes sense for EEG, iEEG and ECoG?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, @agramfort but also for DBS! We use the DBS leads to measure local field potentials. So it should be possible for the mne types eeg, seeg, dbs, and ecog.
This means that on line 172 of the file mne-python/mne/io/reference.py Lines 315 to 317 in b74d004
you'll need to figure out why that warning isn't triggered anymore when that test is run. |
|
@moritz-gerster it was a bit more difficult than what I though. I pushed here. Let me know what you think. |
agramfort
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok so we still need to sort out the referencing with different channel types. One way I see to test for now is to get a proj for each channel type and use add_proj method to add both. Would this do the job for you @moritz-gerster ?
mne/__init__.py
Outdated
| @@ -1,3 +1,4 @@ | |||
| import bullshit | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know this package ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not coming back earlier, I am currently busy but I think I can come back to this next week!
In the meantime, I took a programming workshop and the instructor helped me to get my local version of mne running in my own virtual environment. To test that it works we added an error by importing bullshit :D I'll remove that of course.
Dear @agramfort, Yes that sounds good. I am a little lost now though: What exactly should I change in the code? |
|
I think the idea would be to allow |
@larsoner thanks for the hint. I am not sure though if this would solve my problem. I would like to apply an average reference projector to my combined LFP+ECOG electrodes. I don't want a separate reference projector for the ECOG and LFP electrodes respectively. I need a common average reference across both electrode types. |
|
Ahh okay. The separate average reference case can actually be solved easily by the user looping over N types and passing them as arguments one by one to get N separate projectors. So how about if they pass a list of N types, it makes one projector that jointly average references all of those types. In other words, you still add support for list of types here, but in that case it produces a single projector. |
@larsoner how do I average all these ch_type specific projections that I added to yield a single projection? |
You would not average the projectors together, you would create a single projector that includes all the channels (of all of the given types together). Make sense? |
|
@moritz-gerster do you plan to work on this soon-ish? If not, then someone else might be able to push this over the finish line |
|
Dear @larsoner, I am not sure. It is on my list but I cannot start working on it in the next four weeks. Also, even if I would come back I am not sure if I am proficient enough to finish it. If someone else would start working on that I'd be very happy! |
|
@agramfort this one will hopefully come back green or close to it. Ready for review/merge from my end |
agramfort
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a nitpick
@moritz-gerster can you check if it works for you before we merge?
Co-authored-by: Alexandre Gramfort <alexandre.gramfort@m4x.org>
Yes, I'll check tomorrow! |
|
Dear @larsoner and @agramfort, I looked into the code and the projection now works! Thank you very much!! There was some behavior of the code I found surprising though and it took me quite some time to understand it. I am not sure if this is a bug or on purpose. I guess this is not limited to intracranial data but to reference projections in general: To test whether I can easily switch between the CAR projection and the monopolar reference, I plotted ECoG spectra side by side. I used raw_CAR, _ = mne.set_eeg_reference(raw, ref_channels='average', copy=True, projection=True, ch_type='ecog')
psd_CAR, freqs = mne.time_frequency.psd_welch(raw_CAR, proj=True)
psd, freqs = mne.time_frequency.psd_welch(raw_CAR, proj=False)However, the spectra looked identical. Only if I used raw_CAR, _ = mne.set_eeg_reference(raw, ref_channels='average', copy=True, projection=True, ch_type='ecog')
raw_CAR.apply_proj()
psd_CAR, freqs = mne.time_frequency.psd_welch(raw_CAR)
psd, freqs = mne.time_frequency.psd_welch(raw)the expected behavior occurred. The documentation of psd_welch (I know its deprecated) states
The same is written in the new method compute_psd. Does that mean only SSP projections should be used for plotting spectra but not reference projections? |
|
since you used projection=True you need raw_CAR.apply_proj() as otherwise
the projection
is not immediately applied. If you use projection=False then it's
immediate. It's
independent from the plotting code that takes the data as they are.
ok?
… Message ID: ***@***.***>
|
|
ok @agramfort. For me this was confusing because I thought the great benefit of the projection is that I can easily turn it on- and off during plotting etc. while just having a single |
|
🎉 Congrats on merging your first pull request! 🥳 Looking forward to seeing more from you in the future! 💪 |
|
Thx @moritz-gerster @larsoner 🙏🙏 |
I agree that what you suggest should have worked, although since psd_welch is deprecated and we're about to release 1.2 we may not fix it. Can you try this instead? If this is broken we will definitely fix. raw_CAR, _ = mne.set_eeg_reference(raw, ref_channels='average', copy=True,
projection=True, ch_type='ecog')
for proj in (False, True):
raw_CAR.compute_psd(proj=proj).plot() |
|
Dear @drammock, I tested it for |
Summary
Using a common average projection currently only works for EEG data. It should also be available for intracranial data. This PR tries to solve it.
What is the problem?
The method set_eeg_reference works, when using a projection, only for EEG data. When working with intracranial data such as ECoG it breaks because it does not find any EEG channels:
What is the fix/enhancement?
With this PR, it is possible to specify the type of channels used for the reference projection. It is straightforward to implement since set_eeg_reference has the argument "ch_type" implemented already.