Validated new algorithm for ICA MEG reference channel method#7212
Validated new algorithm for ICA MEG reference channel method#7212jshanna100 wants to merge 13 commits intomne-tools:masterfrom
Conversation
… correlation thesholds instead of Z score
|
do we have an MNE dataset that allows to illustrate the benefit of this code in an example? |
larsoner
left a comment
There was a problem hiding this comment.
As for datasets, it looks like we use gradient compensation with this dataset:
raw_fname = op.join(data_path, 'MEG', 'bst_resting',
'subj002_spontaneous_20111102_01_AUX.ds')
Can you see if there are some artifacts that can be removed with your method?
If not, maybe one of the other brainstorm datasets?
|
Regarding datasets, I've tried all of the brainstorm, as well as the BTI Phantom. Unfortunately, they're all too clean. There may be an inherent selection bias problem, here: The method focusses on intermittent external noise which isn't corrected by fixed weights solutions, but presumably a dataset which has such intermittent noise would be considered not suitable as an example dataset. |
agramfort
left a comment
There was a problem hiding this comment.
It would be really nice to have an example to show how to use this from raw data. Worse case can we use a simulation if we don't have an adapted public dataset we can use?
mne/preprocessing/ica.py
Outdated
| stop=None, l_freq=None, h_freq=None, | ||
| reject_by_annotation=True, verbose=None): | ||
| stop=None, l_freq=None, h_freq=None, method='together', | ||
| reject_by_annotation=True, bad_measure="zscore", |
There was a problem hiding this comment.
bad_measure -> measure? function already has bads in its name
There was a problem hiding this comment.
Sure. Should I also extend this option to EOG and ECG?
|
I'll see what I can do with simulated data. There should be a good solution there, somewhere... |
…arameter expanded to EOG/ECG
Codecov Report
@@ Coverage Diff @@
## master #7212 +/- ##
==========================================
- Coverage 89.77% 89.76% -0.02%
==========================================
Files 445 445
Lines 79873 79897 +24
Branches 12773 12781 +8
==========================================
+ Hits 71703 71716 +13
- Misses 5372 5379 +7
- Partials 2798 2802 +4 |
|
Hopefully these cover all the issues in review. The example should follow in a few days. |
|
Artemis data has a reference array that we are currently not using (certainly not effectively) that might be useful for an example. There is 1 second hpi phantom run in the
hpis are at 140hz, 150hz, 160hz everything else is 'noise'. @jshanna100 if that looks promising, I may be able to find subject who I can share, either offline or via some |
|
@jshanna100 any chance to get to this in the next week or two to make it into the 0.20 release? |
|
Possibly within two weeks, definitely not in one. Sorry, a bunch of deadlines with work threw me off track here! |
|
@larsoner @agramfort doing simulations of external noise that also manifest in the reference channels will require also making small modifications to forward and simulate. I already did these a while ago for doing the simulations I needed to validate the methods. They are contained in the refsim branch on my MNE fork. Should we go forward with this? Alternatively, we have a real MEG dataset that demonstrates the technique very well. I'm not sure how conservative you are with adding new datasets... |
Real data would be nicer than simulations I think. You could take your data file and crop it to some suitable length (30 sec? 60 sec?) to demonstrate the method's utility, then we add it as a new |
|
Here is an example of what's needed at the MNE end: |
|
Let me know what you think. The dataset is unfortunately fairly large at around 90MB - also the two full ICA decompositions that run in the example do not get done so quickly. I spent some time trying to get it down as much as I could, but the "together" algorithm does not seem to function well with very short stretches of data. I was able to reduce it by a lot however by downsampling to 100Hz, and throwing out every other magnetometer. |
Have you tried |
and This is going to be our longest-running example, and memory > 1.5 GB is typically problematic because it can cause CircleCI to crash on a full run. Okay if I take a look and see if there is some way to speed it up? and |
mne/preprocessing/ica.py
Outdated
| Method to use to identify reference channel related components. | ||
| Defaults to "together." See notes. | ||
|
|
||
| .. versionadded:: 0.20 |
| measure : {'zscore', "cor"} | ||
| Which method to use for finding outliers. "zscore" (default) is | ||
| the iterated Z-scoring method, and "cor" is an absolute raw | ||
| correlation threshold with a range of 0 to 1. |
|
One of the two ICA decompositions is actually superfluous, so that now nearly cuts it in half. Picard does not converge, even with the default number of iterations. Presently I have it set at the default "fastica." Feel free to have a go at speeding it up. |
|
Style run failed |
|
Hmm. It's passing on my side. What could be causing the discrepancy? |
| # external magnetic noise | ||
| ref_comps = ica_ref.get_sources(raw_sep) | ||
| for c in ref_comps.ch_names: # they need to have REF_ prefix to be recognised | ||
| ref_comps.rename_channels({c:"REF_" + c}) |
There was a problem hiding this comment.
For example this is definitely E231 missing whitespace after :
Did you forget to push? Does make flake actually run on your machine?
I see |
|
Oh, sorry, I see now I forgot to stage the new plot_find_ref_artifacts.py. Here it comes... |
|
I can't push commits because edits by maintainers are not allowed. I also can't seem to open a PR to your repo for some reason, so you can cherry-pick this if you want |
|
That's about a five-fold speed up on my machine. |
|
@jshanna100 in the future you can do: And it would have preserved the history instead of the lines becoming yours. Not a problem here really but can be useful / easier than manual copy-paste + commit of the changes. |
mne/preprocessing/ica.py
Outdated
| ---------- | ||
| .. footbibliography:: | ||
|
|
||
| .. versionadded:: 0.18 |
There was a problem hiding this comment.
versionadded should stay in Notes section, not move to References
.circleci/config.yml
Outdated
| python -c "import mne; print(mne.datasets.limo.data_path(subject=1, update_path=True))"; | ||
| fi; | ||
| if [[ $(cat $FNAME | grep -x ".*datasets.*megref_noise.*" | wc -l) -gt 0 ]]; then | ||
| python -c "import mne; print(mne.datasets.megref_noise.data_path(update_path=True))"; |
There was a problem hiding this comment.
These two lines must be wrong because CircleCI has the download status:
Looks like megref_noise -> refmeg_noise
Can you check the output and see if it's otherwise how you want it to look?
The topomaps look a bit crazy but that's a separate plotting bug I think (feel free to open an issue), not something you changed here
There was a problem hiding this comment.
The first plot now looks more cramped than it did before, but I could live with it. Otherwise it seems fine.
I noticed the topomaps as well. My guess is that this is somehow related to throwing out every other channel to reduce the size of the dataset.
| year = {1989} | ||
| } | ||
|
|
||
| @article{HannaEtAl2020, |
| .. _ex-megnoise_processing: | ||
|
|
||
| ==================================== | ||
| Find MEG reference channel artefacts |
There was a problem hiding this comment.
throughout the rest of the docstrings / documentation we use the "artifact" spelling variant (not "artefact"); please change to be consistent.
|
|
||
| Use ICA decompositions of MEG reference channels to remove intermittent noise. | ||
|
|
||
| Many MEG systems have an array of reference channels which are used to remove |
There was a problem hiding this comment.
| Many MEG systems have an array of reference channels which are used to remove | |
| Many MEG systems have an array of reference channels which are used to detect |
| Use ICA decompositions of MEG reference channels to remove intermittent noise. | ||
|
|
||
| Many MEG systems have an array of reference channels which are used to remove | ||
| external magnetic noise. However, standard removal techniques often fail when |
There was a problem hiding this comment.
| external magnetic noise. However, standard removal techniques often fail when | |
| external magnetic noise. However, standard techniques that use reference | |
| channels to remove noise from standard channels often fail when |
|
|
||
| Many MEG systems have an array of reference channels which are used to remove | ||
| external magnetic noise. However, standard removal techniques often fail when | ||
| noise is intermittent. This technique often succeeds where the standard |
There was a problem hiding this comment.
| noise is intermittent. This technique often succeeds where the standard | |
| noise is intermittent. The technique described here (using ICA on the reference | |
| channels) often succeeds where the standard |
mne/preprocessing/ica.py
Outdated
| is similar to an EOG/ECG, with reference components replacing the | ||
| EOG/ECG channels. Recommended procedure is to perform ICA separately | ||
| on reference channels, extract them using .get_sources(), and then | ||
| append them to the inst using .add_channels(), preferably with the |
There was a problem hiding this comment.
| append them to the inst using .add_channels(), preferably with the | |
| append them to the inst using :meth:`~mne.io.Raw.add_channels`, | |
| preferably with the |
mne/preprocessing/ica.py
Outdated
| EOG/ECG channels. Recommended procedure is to perform ICA separately | ||
| on reference channels, extract them using .get_sources(), and then | ||
| append them to the inst using .add_channels(), preferably with the | ||
| prefix REF_ICA so that they can be automatically detected. |
There was a problem hiding this comment.
| prefix REF_ICA so that they can be automatically detected. | |
| prefix ``REF_ICA`` so that they can be automatically detected. |
mne/preprocessing/ica.py
Outdated
| prefix REF_ICA so that they can be automatically detected. | ||
|
|
||
| Thresholding in both cases is based on adaptive z-scoring: | ||
| The above threshold components will be masked and the z-score will be |
There was a problem hiding this comment.
| The above threshold components will be masked and the z-score will be | |
| The above-threshold components will be masked and the z-score will be |
mne/preprocessing/ica.py
Outdated
| recomputed until no supra-threshold component remains. | ||
|
|
||
| Validation and further documentation for this technique can be found | ||
| in :footcite:`HannaEtAl2020` |
There was a problem hiding this comment.
| in :footcite:`HannaEtAl2020` | |
| in :footcite:`HannaEtAl2020`. |
mne/preprocessing/ica.py
Outdated
| If True, data annotated as bad will be omitted. Defaults to True. | ||
|
|
||
| .. versionadded:: 0.14.0 | ||
| measure : {'zscore', "cor"} |
There was a problem hiding this comment.
| measure : {'zscore', "cor"} | |
| measure : 'zscore' | 'cor' |
|
Thanks @drammock, the raw.plot in particular looks much better now. |
|
CircleCI failure is unrelated. Last idea: |
|
Coverage actually looks okay so I think we can ignore that one, too |
|
Commit updated on |
|
I'm away from my computer for the day. I've tried again enabling contributions. If it works, feel free to try any changes. Otherwise I'll add the plot tomorrow. |
Does not seem to work unfortunately |
|
I opened #7810, will merge if CIs come back happy and everything looks okay. Thanks in advance @jshanna100 ! |
This revists the use of ICA decompositions on reference channel data to remove noise from the main channels. This was first implemented in #5807. Adding a 2nd algorithm was discussed in #5959, but ultimately postponed until the method was validated. A validation using simulations has now been successfully carried out, and the results will be published shortly in the Journal of Neuroscience Methods (preprint: https://arxiv.org/abs/2001.03397).
This PR has the following changes: