Skip to content

Commit

Permalink
MRG, FIX: Check reject_by_annotation in ica.plot_properties (#8103)
Browse files Browse the repository at this point in the history
* FIX: Check reject_by_annotation in ica.plot_properties

* ENH: Improve and add test for make_fixed_length_epochs

DOC: Update documentations

* ENH: remove unnecessary codes

* DOC: Use docdict for reject_by_annotation

* ENH: Catch convergence warning in test

* DOC: Update what's new

* Update latest.inc

Co-authored-by: Eric Larson <[email protected]>
  • Loading branch information
yh-luo and larsoner authored Aug 10, 2020
1 parent 421cb1b commit 9c7788a
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 57 deletions.
4 changes: 4 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ 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` and :meth:`mne.preprocessing.ICA.plot_properties` to reject bad data segments based on annotation by `Yu-Han Luo`_
Bug
~~~
Expand Down Expand Up @@ -293,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

0 comments on commit 9c7788a

Please sign in to comment.