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] ENH: add skip_by_annotation to notch_filter #11388

Merged
merged 1 commit into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Enhancements
~~~~~~~~~~~~
- Added ability to read stimulus durations from SNIRF files when using :func:`mne.io.read_raw_snirf` (:gh:`11397` by `Robert Luke`_)
- Add :meth:`mne.Info.save` to save an :class:`mne.Info` object to a fif file (:gh:`11401` by `Alex Rockhill`_)
- Add support for ``skip_by_annotation`` in :func:`mne.io.Raw.notch_filter` (:gh:`11388` by `Mainak Jas`_)

Bugs
~~~~
Expand Down
10 changes: 1 addition & 9 deletions mne/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1935,15 +1935,7 @@ def filter(self, l_freq, h_freq, picks=None, filter_length='auto',
%(phase)s
%(fir_window)s
%(fir_design)s
skip_by_annotation : str | list of str
If a string (or list of str), any annotation segment that begins
with the given string will not be included in filtering, and
segments on either side of the given excluded annotated segment
will be filtered separately (i.e., as independent signals).
The default (``('edge', 'bad_acq_skip')`` will separately filter
any segments that were concatenated by :func:`mne.concatenate_raws`
or :meth:`mne.io.Raw.append`, or separated during acquisition.
To disable, provide an empty list. Only used if ``inst`` is raw.
%(skip_by_annotation)s
.. versionadded:: 0.16.
%(pad_fir)s
Expand Down
28 changes: 19 additions & 9 deletions mne/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
copy_function_doc_to_method_doc, _validate_type,
_check_preload, _get_argvalues, _check_option,
_build_data_frame, _convert_times, _scale_dataframe_data,
_check_time_format, _arange_div, TimeMixin, repr_html)
_check_time_format, _arange_div, TimeMixin, repr_html,
_pl)
from ..defaults import _handle_default
from ..viz import plot_raw, _RAW_CLIP_DEF
from ..event import find_events, concatenate_events
Expand Down Expand Up @@ -987,7 +988,9 @@ def notch_filter(self, freqs, picks=None, filter_length='auto',
notch_widths=None, trans_bandwidth=1.0, n_jobs=None,
method='fir', iir_params=None, mt_bandwidth=None,
p_value=0.05, phase='zero', fir_window='hamming',
fir_design='firwin', pad='reflect_limited', verbose=None):
fir_design='firwin', pad='reflect_limited',
skip_by_annotation=('edge', 'bad_acq_skip'),
verbose=None):
"""Notch filter a subset of channels.
Parameters
Expand Down Expand Up @@ -1024,6 +1027,7 @@ def notch_filter(self, freqs, picks=None, filter_length='auto',
The default is ``'reflect_limited'``.
.. versionadded:: 0.15
%(skip_by_annotation)s
%(verbose)s
Returns
Expand Down Expand Up @@ -1053,13 +1057,19 @@ def notch_filter(self, freqs, picks=None, filter_length='auto',
fs = float(self.info['sfreq'])
picks = _picks_to_idx(self.info, picks, exclude=(), none='data_or_ica')
_check_preload(self, 'raw.notch_filter')
self._data = notch_filter(
self._data, fs, freqs, filter_length=filter_length,
notch_widths=notch_widths, trans_bandwidth=trans_bandwidth,
method=method, iir_params=iir_params, mt_bandwidth=mt_bandwidth,
p_value=p_value, picks=picks, n_jobs=n_jobs, copy=False,
phase=phase, fir_window=fir_window, fir_design=fir_design,
pad=pad)
onsets, ends = _annotations_starts_stops(
self, skip_by_annotation, invert=True)
logger.info('Filtering raw data in %d contiguous segment%s'
% (len(onsets), _pl(onsets)))
for si, (start, stop) in enumerate(zip(onsets, ends)):
self._data = notch_filter(
self._data[:, start:stop], fs, freqs,
filter_length=filter_length, notch_widths=notch_widths,
trans_bandwidth=trans_bandwidth, method=method,
iir_params=iir_params, mt_bandwidth=mt_bandwidth,
p_value=p_value, picks=picks, n_jobs=n_jobs, copy=False,
phase=phase, fir_window=fir_window, fir_design=fir_design,
pad=pad)
return self

@verbose
Expand Down
10 changes: 10 additions & 0 deletions mne/tests/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,16 @@ def test_annotation_filtering(first_samp):
raws_concat_stop = raws_concat.copy().filter(skip_by_annotation='edge',
**kwargs_stop)
assert_allclose(raws_zero[0][0], raws_concat_stop[0][0], atol=1e-14)

# test notch_filtering
raw_notch = concatenate_raws([raws_concat.copy(), raws_concat.copy()])
raw_notch.annotations.append(7. + raw_notch._first_time, 1., 'foo_notch')
with catch_logging() as log:
raw_notch.notch_filter(60., fir_design='firwin',
skip_by_annotation='foo_notch', verbose='info')
log = log.getvalue()
assert '1 contiguous segment' in log

# one last test: let's cut out a section entirely:
# here the 1-3 second window should be skipped
raw = raws_concat.copy()
Expand Down
12 changes: 12 additions & 0 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3224,6 +3224,18 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
Side length of each subplot in inches.
"""

docdict['skip_by_annotation'] = """
skip_by_annotation : str | list of str
If a string (or list of str), any annotation segment that begins
with the given string will not be included in filtering, and
segments on either side of the given excluded annotated segment
will be filtered separately (i.e., as independent signals).
The default (``('edge', 'bad_acq_skip')`` will separately filter
any segments that were concatenated by :func:`mne.concatenate_raws`
or :meth:`mne.io.Raw.append`, or separated during acquisition.
To disable, provide an empty list. Only used if ``inst`` is raw.
"""

docdict['skip_by_annotation_maxwell'] = """
skip_by_annotation : str | list of str
If a string (or list of str), any annotation segment that begins
Expand Down