Skip to content
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

MRG, FIX: Check reject_by_annotation in ica.plot_properties #8103

Merged
merged 7 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ Changelog

- Speed up :func:`mne.stats.summarize_clusters_stc` using Numba by `Yu-Han Luo`_

- Add ``reject_by_annotation=True`` to :func:`mne.make_fixed_length_epochs` to reject bad epochs based on annotation by `Yu-Han Luo`_

- Add ``reject_by_annotation=True`` to :meth:`mne.preprocessing.ICA.plot_properties` to reject bad data segments based on annotation by `Yu-Han Luo`_

Bug
~~~

Expand Down Expand Up @@ -291,6 +295,8 @@ Bug

- Fix bug in :meth:`mne.preprocessing.ICA.plot_properties` where time series plot doesn't start at the proper tmin by `Teon Brooks`_

- Fix bug with :meth:`mne.preprocessing.ICA.plot_properties` where a :class:`mne.io.Raw` object with annotations would lead to an error by `Yu-Han Luo`_

API
~~~

Expand Down
5 changes: 1 addition & 4 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,10 +425,7 @@ def compute_raw_covariance(raw, tmin=0, tmax=None, tstep=0.2, reject=None,
method equals 'auto' or is a list of str. Defaults to False.

.. versionadded:: 0.12
reject_by_annotation : bool
Whether to reject based on annotations. If True (default), epochs
overlapping with segments whose description begins with ``'bad'`` are
rejected. If False, no rejection based on annotations is performed.
%(reject_by_annotation_epochs)s

.. versionadded:: 0.14
%(rank_None)s
Expand Down
18 changes: 9 additions & 9 deletions mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2002,10 +2002,7 @@ class Epochs(BaseEpochs):
warn, if 'ignore' it will proceed silently. Note.
If none of the event ids are found in the data, an error will be
automatically generated irrespective of this parameter.
reject_by_annotation : bool
Whether to reject based on annotations. If True (default), epochs
overlapping with segments whose description begins with ``'bad'`` are
rejected. If False, no rejection based on annotations is performed.
%(reject_by_annotation_epochs)s
metadata : instance of pandas.DataFrame | None
A :class:`pandas.DataFrame` specifying metadata about each epoch.
If given, ``len(metadata)`` must equal ``len(events)``. The DataFrame
Expand Down Expand Up @@ -3277,8 +3274,8 @@ def average_movements(epochs, head_pos=None, orig_sfreq=None, picks=None,


@verbose
def make_fixed_length_epochs(raw, duration=1.,
preload=False, verbose=None):
def make_fixed_length_epochs(raw, duration=1., preload=False,
reject_by_annotation=True, verbose=None):
"""Divide continuous raw data into equal-sized consecutive epochs.

Parameters
Expand All @@ -3288,6 +3285,9 @@ def make_fixed_length_epochs(raw, duration=1.,
duration : float
Duration of each epoch in seconds. Defaults to 1.
%(preload)s
%(reject_by_annotation_epochs)s

.. versionadded:: 0.21.0
%(verbose)s

Returns
Expand All @@ -3301,6 +3301,6 @@ def make_fixed_length_epochs(raw, duration=1.,
"""
events = make_fixed_length_events(raw, 1, duration=duration)
delta = 1. / raw.info['sfreq']
return Epochs(raw, events, event_id=[1], tmin=0.,
tmax=duration - delta,
verbose=verbose, baseline=None)
return Epochs(raw, events, event_id=[1], tmin=0, tmax=duration - delta,
baseline=None, preload=preload,
reject_by_annotation=reject_by_annotation, verbose=verbose)
8 changes: 2 additions & 6 deletions mne/preprocessing/ecg.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ def find_ecg_events(raw, event_id=999, ch_name=None, tstart=0.0,
return_ecg : bool
Return ecg channel if synthesized. Defaults to False. If True and
and ecg exists this will yield None.
reject_by_annotation : bool
Whether to omit data that is annotated as bad.
%(reject_by_annotation_all)s

.. versionadded:: 0.18
%(verbose)s
Expand Down Expand Up @@ -329,10 +328,7 @@ def create_ecg_epochs(raw, ch_name=None, event_id=999, picks=None, tmin=-0.5,
When ECG is synthetically created (after picking), should it be added
to the epochs? Must be False when synthetic channel is not used.
Defaults to False.
reject_by_annotation : bool
Whether to reject based on annotations. If True (default), epochs
overlapping with segments whose description begins with ``'bad'`` are
rejected. If False, no rejection based on annotations is performed.
%(reject_by_annotation_epochs)s

.. versionadded:: 0.14.0
%(verbose)s
Expand Down
6 changes: 1 addition & 5 deletions mne/preprocessing/eog.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,7 @@ def create_eog_epochs(raw, ch_name=None, event_id=998, picks=None, tmin=-0.5,
interval is used. If None, no correction is applied.
preload : bool
Preload epochs or not.
reject_by_annotation : bool
Whether to reject based on annotations. If True (default), segments
whose description begins with ``'bad'`` are not used for finding
artifacts and epochs overlapping with them are rejected. If False, no
rejection based on annotations is performed.
%(reject_by_annotation_epochs)s

.. versionadded:: 0.14.0
thresh : float
Expand Down
24 changes: 9 additions & 15 deletions mne/preprocessing/ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,7 @@ def fit(self, inst, picks=None, start=None, stop=None, decim=None,
tstep : float
Length of data chunks for artifact rejection in seconds.
It only applies if ``inst`` is of type Raw.
reject_by_annotation : bool
Whether to omit bad segments from the data before fitting. If True,
annotated segments with a description that starts with 'bad' are
omitted. Has no effect if ``inst`` is an Epochs or Evoked object.
Defaults to True.
%(reject_by_annotation_raw)s

.. versionadded:: 0.14.0
%(verbose_meth)s
Expand Down Expand Up @@ -986,8 +982,7 @@ def score_sources(self, inst, target=None, score_func='pearsonr',
Low pass frequency.
h_freq : float
High pass frequency.
reject_by_annotation : bool
If True, data annotated as bad will be omitted. Defaults to True.
%(reject_by_annotation_all)s

.. versionadded:: 0.14.0
%(verbose_meth)s
Expand Down Expand Up @@ -1196,8 +1191,7 @@ def find_bads_ecg(self, inst, ch_name=None, threshold=None, start=None,
threshold components will be masked and the z-score will
be recomputed until no supra-threshold component remains.
Defaults to 'ctps'.
reject_by_annotation : bool
If True, data annotated as bad will be omitted. Defaults to True.
%(reject_by_annotation_all)s

.. versionadded:: 0.14.0
measure : 'zscore' | 'correlation'
Expand Down Expand Up @@ -1316,8 +1310,7 @@ def find_bads_ref(self, inst, ch_name=None, threshold=3.0, start=None,
Low pass frequency.
h_freq : float
High pass frequency.
reject_by_annotation : bool
If True, data annotated as bad will be omitted. Defaults to True.
%(reject_by_annotation_all)s
method : 'together' | 'separate'
Method to use to identify reference channel related components.
Defaults to ``'together'``. See notes.
Expand Down Expand Up @@ -1446,8 +1439,7 @@ def find_bads_eog(self, inst, ch_name=None, threshold=3.0, start=None,
Low pass frequency.
h_freq : float
High pass frequency.
reject_by_annotation : bool
If True, data annotated as bad will be omitted. Defaults to True.
%(reject_by_annotation_all)s

.. versionadded:: 0.14.0
measure : 'zscore' | 'correlation'
Expand Down Expand Up @@ -1749,12 +1741,14 @@ def plot_components(self, picks=None, ch_type=None, res=64,
@copy_function_doc_to_method_doc(plot_ica_properties)
def plot_properties(self, inst, picks=None, axes=None, dB=True,
plot_std=True, topomap_args=None, image_args=None,
psd_args=None, figsize=None, show=True, reject='auto'):
psd_args=None, figsize=None, show=True, reject='auto',
reject_by_annotation=True):
return plot_ica_properties(self, inst, picks=picks, axes=axes,
dB=dB, plot_std=plot_std,
topomap_args=topomap_args,
image_args=image_args, psd_args=psd_args,
figsize=figsize, show=show, reject=reject)
figsize=figsize, show=show, reject=reject,
reject_by_annotation=reject_by_annotation)

@copy_function_doc_to_method_doc(plot_ica_sources)
def plot_sources(self, inst, picks=None, start=None,
Expand Down
17 changes: 16 additions & 1 deletion mne/tests/test_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from mne import (Epochs, Annotations, read_events, pick_events, read_epochs,
equalize_channels, pick_types, pick_channels, read_evokeds,
write_evokeds, create_info, make_fixed_length_events,
combine_evoked)
make_fixed_length_epochs, combine_evoked)
from mne.baseline import rescale
from mne.fixes import rfft, rfftfreq
from mne.preprocessing import maxwell_filter
Expand Down Expand Up @@ -3019,4 +3019,19 @@ def test_pick_types_reject_flat_keys():
assert sorted(epochs.flat.keys()) == ['grad', 'mag']


@testing.requires_testing_data
def test_make_fixed_length_epochs():
"""Test dividing raw data into equal-sized consecutive epochs."""
raw = read_raw_fif(raw_fname, preload=True)
epochs = make_fixed_length_epochs(raw, duration=1, preload=True)
# Test Raw with annotations
annot = Annotations(onset=[0], duration=[5], description=['BAD'])
raw_annot = raw.set_annotations(annot)
epochs_annot = make_fixed_length_epochs(raw_annot, duration=1.0,
preload=True)
assert len(epochs) > 10
assert len(epochs_annot) > 10
assert len(epochs) > len(epochs_annot)


run_tests_if_main()
6 changes: 1 addition & 5 deletions mne/time_frequency/psd.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,7 @@ def psd_welch(inst, fmin=0, fmax=np.inf, tmin=None, tmax=None, n_fft=256,
proj : bool
Apply SSP projection vectors. If inst is ndarray this is not used.
%(n_jobs)s
reject_by_annotation : bool
Whether to omit bad segments from the data while computing the
PSD. If True, annotated segments with a description that starts
with 'bad' are omitted. Has no effect if ``inst`` is an Epochs or
Evoked object. Defaults to True.
%(reject_by_annotation_raw)s

.. versionadded:: 0.15.0
average : str | None
Expand Down
18 changes: 18 additions & 0 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@
End time of the raw data to use in seconds (cannot exceed data duration).
"""


# Reject by annotation
docdict['reject_by_annotation_all'] = """
reject_by_annotation : bool
Whether to omit bad segments from the data before fitting. If ``True``
(default), annotated segments whose description begins with ``'bad'`` are
omitted. If ``False``, no rejection based on annotations is performed.
"""
docdict['reject_by_annotation_epochs'] = """
reject_by_annotation : bool
Whether to reject based on annotations. If ``True`` (default), epochs
overlapping with segments whose description begins with ``'bad'`` are
rejected. If ``False``, no rejection based on annotations is performed.
"""
docdict['reject_by_annotation_raw'] = docdict['reject_by_annotation_all'] + """
Has no effect if ``inst`` is not a :class:`mne.io.Raw` object."""


# General plotting
docdict["show"] = """
show : bool
Expand Down
24 changes: 17 additions & 7 deletions mne/viz/ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ def _get_psd_label_and_std(this_psd, dB, ica, num_std):
@fill_doc
def plot_ica_properties(ica, inst, picks=None, axes=None, dB=True,
plot_std=True, topomap_args=None, image_args=None,
psd_args=None, figsize=None, show=True, reject='auto'):
psd_args=None, figsize=None, show=True, reject='auto',
reject_by_annotation=True):
"""Display component properties.

Properties include the topography, epochs image, ERP/ERF, power
Expand Down Expand Up @@ -309,6 +310,9 @@ def plot_ica_properties(ica, inst, picks=None, axes=None, dB=True,
If None, no rejection is applied. The default is 'auto',
which applies the rejection parameters used when fitting
the ICA object.
%(reject_by_annotation_raw)s

.. versionadded:: 0.21.0

Returns
-------
Expand Down Expand Up @@ -390,12 +394,18 @@ def plot_ica_properties(ica, inst, picks=None, axes=None, dB=True,

# break up continuous signal into segments
from ..epochs import make_fixed_length_epochs
inst_rejected = make_fixed_length_epochs(inst_rejected,
duration=2.,
verbose=False,
preload=True)
inst = make_fixed_length_epochs(inst, duration=2., verbose=False,
preload=True)
inst_rejected = make_fixed_length_epochs(
inst_rejected,
duration=2,
preload=True,
reject_by_annotation=reject_by_annotation,
verbose=False)
inst = make_fixed_length_epochs(
inst,
duration=2,
preload=True,
reject_by_annotation=reject_by_annotation,
verbose=False)
kind = "Segment"
else:
drop_inds = None
Expand Down
6 changes: 1 addition & 5 deletions mne/viz/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,11 +604,7 @@ def plot_raw_psd(raw, fmin=0, fmax=np.inf, tmin=None, tmax=None, proj=False,
n_overlap : int
The number of points of overlap between blocks. The default value
is 0 (no overlap).
reject_by_annotation : bool
Whether to omit bad segments from the data while computing the
PSD. If True, annotated segments with a description that starts
with 'bad' are omitted. Has no effect if ``inst`` is an Epochs or
Evoked object. Defaults to True.
%(reject_by_annotation_raw)s
%(plot_psd_picks_good_data)s
ax : instance of Axes | None
Axes to plot into. If None, axes will be created.
Expand Down
19 changes: 19 additions & 0 deletions mne/viz/tests/test_ica.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,25 @@ def test_plot_ica_properties():
ica.plot_properties(epochs)
plt.close('all')

# Test Raw with annotations
annot = Annotations(onset=[1], duration=[1], description=['BAD'])
raw_annot = _get_raw(preload=True).set_annotations(annot)

with pytest.warns(UserWarning, match='did not converge'):
ica.fit(raw_annot)
# drop bad data segments
ica.plot_properties(raw_annot)
# don't drop
ica.plot_properties(raw_annot, reject_by_annotation=False)
# fitting with bad data
with pytest.warns(UserWarning, match='did not converge'):
ica.fit(raw_annot, reject_by_annotation=False)
# drop bad data when plotting
ica.plot_properties(raw_annot)
# don't drop bad data when plotting
ica.plot_properties(raw_annot, reject_by_annotation=False)
plt.close('all')


@requires_sklearn
def test_plot_ica_sources():
Expand Down