Skip to content

Commit

Permalink
BUG: Avoid warning in report.add_ica (#11232)
Browse files Browse the repository at this point in the history
  • Loading branch information
larsoner authored Oct 9, 2022
1 parent f55c5ea commit 0a0e00f
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 16 deletions.
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Enhancements
- Add ``unscale`` option to :func:`mne.head_to_mri` to facilitate working with surrogate MRI data (:gh:`11185` by `Eric Larson`_)
- Add ``encoding`` parameter to :func:`mne.io.read_raw_edf` and :func:`mne.io.read_raw_bdf` to support custom (non-UTF8) annotation channel encodings (:gh:`11154` by `Clemens Brunner`_)
- :class:`mne.preprocessing.ICA` gained a new method, :meth:`~mne.preprocessing.ICA.get_explained_variance_ratio`, that allows the retrieval of the proportion of variance explained by ICA components (:gh:`11141` by `Richard Höchenberger`_)
- Add ``on_baseline`` to :meth:`mne.preprocessing.ICA.apply`, :meth:`mne.preprocessing.ICA.plot_overlay`, and :func:`mne.viz.plot_ica_overlay` to allow reapplying baseline correction after applying ICA (:gh:`11232` by `Eric Larson`_)
- Add config option ``MNE_REPR_HTML`` to disable HTML repr in notebook environments (:gh:`11159` by `Clemens Brunner`_)

Bugs
Expand Down Expand Up @@ -76,6 +77,7 @@ Bugs
- Fix bug in :meth:`mne.preprocessing.ICA.find_bads_muscle` where epochs caused an error when passed as the ``inst`` (:gh:`11197` by `Alex Rockhill`_)
- Fix bug in readers where EEG coordinates were assumed to be in head coordinates but no fiducial points were present. Estimated fiducial locations will now be added automatically to reflect the assumption of locations being in the head coordinate frame (:gh:`11212` by `Stefan Appelhoff`_ and `Eric Larson`_)
- The duration of raw data sometimes wasn't displayed correctly in Jupyter notebooks by omitting fractions of a second. We now always round up to the next full second so a duration of less than 1 second will not be displayed as a duration of zero anymore (:gh:`11203` by `Richard Höchenberger`_)
- Fix bug in :meth:`mne.Report.add_ica` and where baselines were not reapplied to the data when ``inst`` is Epochs or Evoked (:gh:`11232` by `Eric Larson`_)

API changes
~~~~~~~~~~~
Expand Down
33 changes: 24 additions & 9 deletions mne/preprocessing/ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
copy_function_doc_to_method_doc, _pl, warn, Bunch,
_check_preload, _check_compensation_grade, fill_doc,
_check_option, _PCA, int_like, _require_version,
_check_all_same_channel_names)
_check_all_same_channel_names, _check_on_missing,
_on_missing)

from ..fixes import _safe_svd
from ..filter import filter_data
Expand Down Expand Up @@ -1896,7 +1897,7 @@ def find_bads_eog(self, inst, ch_name=None, threshold=3.0, start=None,

@verbose
def apply(self, inst, include=None, exclude=None, n_pca_components=None,
start=None, stop=None, verbose=None):
start=None, stop=None, *, on_baseline='warn', verbose=None):
"""Remove selected components from the signal.
Given the unmixing matrix, transform the data,
Expand Down Expand Up @@ -1927,6 +1928,7 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
stop : int | float | None
Last sample to not include. If float, data will be interpreted as
time in seconds. If None, data will be used to the last sample.
%(on_baseline_ica)s
%(verbose)s
Returns
Expand Down Expand Up @@ -1963,15 +1965,26 @@ def apply(self, inst, include=None, exclude=None, n_pca_components=None,
_check_compensation_grade(self.info, inst.info, 'ICA', kind,
ch_names=self.ch_names)

_check_on_missing(on_baseline, 'on_baseline', extras=('reapply',))
reapply_baseline = False
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.')
if on_baseline == 'reapply':
reapply_baseline = True
else:
msg = (
'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.'
)
_on_missing(on_baseline, msg, 'on_baseline')

logger.info(f'Applying ICA to {kind} instance')
return meth(**kwargs)
out = meth(**kwargs)
if reapply_baseline:
out.apply_baseline(inst.baseline)
return out

def _check_exclude(self, exclude):
if exclude is None:
Expand Down Expand Up @@ -2220,10 +2233,12 @@ def plot_scores(self, scores, exclude=None, labels=None, axhline=None,

@copy_function_doc_to_method_doc(plot_ica_overlay)
def plot_overlay(self, inst, exclude=None, picks=None, start=None,
stop=None, title=None, show=True, n_pca_components=None):
stop=None, title=None, show=True, n_pca_components=None,
*, on_baseline='warn', verbose=None):
return plot_ica_overlay(self, inst=inst, exclude=exclude, picks=picks,
start=start, stop=stop, title=title, show=show,
n_pca_components=n_pca_components)
n_pca_components=n_pca_components,
on_baseline=on_baseline, verbose=verbose)

@verbose
def _check_n_pca_components(self, _n_pca_comp, verbose=None):
Expand Down
2 changes: 1 addition & 1 deletion mne/report/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,7 @@ def _add_ica_overlay(
else: # Epochs
inst_ = inst.average()

fig = ica.plot_overlay(inst=inst_, show=False)
fig = ica.plot_overlay(inst=inst_, show=False, on_baseline='reapply')
del inst_
tight_layout(fig=fig)
_constrain_fig_resolution(
Expand Down
9 changes: 8 additions & 1 deletion mne/report/tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,14 +790,21 @@ def test_manual_report_2d(tmp_path, invisible_fig):
with pytest.raises(RuntimeError, match='not preloaded'):
r.add_ica(ica=ica, title='ica', inst=raw)
r.add_ica(
ica=ica, title='my ica with inst',
ica=ica, title='my ica with raw inst',
inst=raw.copy().load_data(),
picks=[0],
ecg_evoked=ica_ecg_evoked,
eog_evoked=ica_eog_evoked,
ecg_scores=ica_ecg_scores,
eog_scores=ica_eog_scores
)
epochs_baseline = epochs_without_metadata.copy().load_data()
epochs_baseline.apply_baseline((None, 0))
r.add_ica(
ica=ica, title='my ica with epochs inst',
inst=epochs_baseline,
picks=[0],
)
r.add_covariance(cov=cov, info=raw_fname, title='my cov')
r.add_forward(forward=fwd_fname, title='my forward', subject='sample',
subjects_dir=subjects_dir)
Expand Down
4 changes: 2 additions & 2 deletions mne/utils/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,9 +977,9 @@ def _suggest(val, options, cutoff=0.66):
return ' Did you mean one of %r?' % (options,)


def _check_on_missing(on_missing, name='on_missing'):
def _check_on_missing(on_missing, name='on_missing', *, extras=()):
_validate_type(on_missing, str, name)
_check_option(name, on_missing, ['raise', 'warn', 'ignore'])
_check_option(name, on_missing, ['raise', 'warn', 'ignore'] + list(extras))


def _on_missing(on_missing, msg, name='on_missing', error_klass=None):
Expand Down
9 changes: 9 additions & 0 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,15 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
.. versionadded:: 0.12
"""

docdict['on_baseline_ica'] = """
on_baseline : str
How to handle baseline-corrected epochs or evoked data.
Can be ``'raise'`` to raise an error, ``'warn'`` (default) to emit a
warning, ``'ignore'`` to ignore, or "reapply" to reapply the baseline
after applying ICA.
.. versionadded:: 1.2
"""
docdict['on_defects'] = """
on_defects : 'raise' | 'warn' | 'ignore'
What to do if the surface is found to have topological defects.
Expand Down
10 changes: 7 additions & 3 deletions mne/viz/ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,9 +842,10 @@ def plot_ica_scores(ica, scores, exclude=None, labels=None, axhline=None,
return fig


@fill_doc
@verbose
def plot_ica_overlay(ica, inst, exclude=None, picks=None, start=None,
stop=None, title=None, show=True, n_pca_components=None):
stop=None, title=None, show=True, n_pca_components=None,
*, on_baseline='warn', verbose=None):
"""Overlay of raw and cleaned signals given the unmixing matrix.
This method helps visualizing signal quality and artifact rejection.
Expand Down Expand Up @@ -874,6 +875,8 @@ def plot_ica_overlay(ica, inst, exclude=None, picks=None, start=None,
%(n_pca_components_apply)s
.. versionadded:: 0.22
%(on_baseline_ica)s
%(verbose)s
Returns
-------
Expand Down Expand Up @@ -922,7 +925,8 @@ def plot_ica_overlay(ica, inst, exclude=None, picks=None, start=None,
inst.info['comps'] = [] # can be safely disabled
inst.pick_channels([inst.ch_names[p] for p in picks])
evoked_cln = ica.apply(inst.copy(), exclude=exclude,
n_pca_components=n_pca_components)
n_pca_components=n_pca_components,
on_baseline=on_baseline)
fig = _plot_ica_overlay_evoked(evoked=inst, evoked_cln=evoked_cln,
title=title, show=show)

Expand Down

0 comments on commit 0a0e00f

Please sign in to comment.