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

Allow notch filtering of epochs #12403

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ MNE-Python is maintained by a community of scientists and research labs. The pro

Users and contributors to MNE-Python are expected to follow our [code of conduct](https://github.com/mne-tools/.github/blob/main/CODE_OF_CONDUCT.md).

The [contributing guide](https://mne.tools/dev/development/contributing.html) has details on the preferred contribution workflow
The [contributing guide](https://mne.tools/stable/development/contributing.html) has details on the preferred contribution workflow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should actually still point to the dev page because it stays most up-to-date (updated roughly daily), and so is the best guide for current practice

Suggested change
The [contributing guide](https://mne.tools/stable/development/contributing.html) has details on the preferred contribution workflow
The [contributing guide](https://mne.tools/dev/development/contributing.html) has details on the preferred contribution workflow

and the recommended system configuration for a smooth contribution/development experience.
111 changes: 111 additions & 0 deletions mne/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,117 @@ def savgol_filter(self, h_freq, verbose=None):
)
return self

@verbose
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",
skip_by_annotation=("edge", "bad_acq_skip"),
verbose=None,
):
"""Notch filter a subset of channels.

Parameters
----------
freqs : float | array of float | None
Specific frequencies to filter out from data, e.g.,
``np.arange(60, 241, 60)`` in the US or ``np.arange(50, 251, 50)``
in Europe. ``None`` can only be used with the mode
``'spectrum_fit'``, where an F test is used to find sinusoidal
components.
%(picks_all_data)s
%(filter_length_notch)s
notch_widths : float | array of float | None
Width of each stop band (centred at each freq in freqs) in Hz.
If None, ``freqs / 200`` is used.
trans_bandwidth : float
Width of the transition band in Hz.
Only used for ``method='fir'`` and ``method='iir'``.
%(n_jobs_fir)s
%(method_fir)s
%(iir_params)s
mt_bandwidth : float | None
The bandwidth of the multitaper windowing function in Hz.
Only used in 'spectrum_fit' mode.
p_value : float
P-value to use in F-test thresholding to determine significant
sinusoidal components to remove when ``method='spectrum_fit'`` and
``freqs=None``. Note that this will be Bonferroni corrected for the
number of frequencies, so large p-values may be justified.
%(phase)s
%(fir_window)s
%(fir_design)s
%(pad_fir)s
The default is ``'reflect_limited'``.

.. versionadded:: 0.15
%(skip_by_annotation)s
%(verbose)s

Returns
-------
inst : instance of Epochs or Evoked
The object with the filtering applied.

See Also
--------
mne.io.Raw.filter

Notes
-----
Applies a zero-phase notch filter to the channels selected by
"picks". By default the data of the Epochs or Evoked object is modified inplace.

The Epochs or Evoked object has to have the data loaded e.g. with ``preload=True``
or ``self.load_data()``.

.. note:: If n_jobs > 1, more memory is required as
``len(picks) * n_times`` additional time points need to
be temporarily stored in memory.

For details, see :func:`mne.filter.notch_filter`.
"""
fs = float(self.info["sfreq"])
picks = _picks_to_idx(self.info, picks, exclude=(), none="data_or_ica")
_check_preload(self, "raw.notch_filter")
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)):
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
def filter(
self,
Expand Down
114 changes: 0 additions & 114 deletions mne/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
_check_resamp_noop,
_resamp_ratio_len,
_resample_stim_channels,
notch_filter,
resample,
)
from ..html_templates import _get_html_template
Expand All @@ -96,7 +95,6 @@
_file_like,
_get_argvalues,
_get_stim_channel,
_pl,
_scale_dataframe_data,
_stamp_to_dt,
_time_mask,
Expand Down Expand Up @@ -1143,118 +1141,6 @@ def filter(
verbose=verbose,
)

@verbose
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",
skip_by_annotation=("edge", "bad_acq_skip"),
verbose=None,
):
"""Notch filter a subset of channels.

Parameters
----------
freqs : float | array of float | None
Specific frequencies to filter out from data, e.g.,
``np.arange(60, 241, 60)`` in the US or ``np.arange(50, 251, 50)``
in Europe. ``None`` can only be used with the mode
``'spectrum_fit'``, where an F test is used to find sinusoidal
components.
%(picks_all_data)s
%(filter_length_notch)s
notch_widths : float | array of float | None
Width of each stop band (centred at each freq in freqs) in Hz.
If None, ``freqs / 200`` is used.
trans_bandwidth : float
Width of the transition band in Hz.
Only used for ``method='fir'`` and ``method='iir'``.
%(n_jobs_fir)s
%(method_fir)s
%(iir_params)s
mt_bandwidth : float | None
The bandwidth of the multitaper windowing function in Hz.
Only used in 'spectrum_fit' mode.
p_value : float
P-value to use in F-test thresholding to determine significant
sinusoidal components to remove when ``method='spectrum_fit'`` and
``freqs=None``. Note that this will be Bonferroni corrected for the
number of frequencies, so large p-values may be justified.
%(phase)s
%(fir_window)s
%(fir_design)s
%(pad_fir)s
The default is ``'reflect_limited'``.

.. versionadded:: 0.15
%(skip_by_annotation)s
%(verbose)s

Returns
-------
raw : instance of Raw
The raw instance with filtered data.

See Also
--------
mne.filter.notch_filter
mne.io.Raw.filter

Notes
-----
Applies a zero-phase notch filter to the channels selected by
"picks". By default the data of the Raw object is modified inplace.

The Raw object has to have the data loaded e.g. with ``preload=True``
or ``self.load_data()``.

.. note:: If n_jobs > 1, more memory is required as
``len(picks) * n_times`` additional time points need to
be temporarily stored in memory.

For details, see :func:`mne.filter.notch_filter`.
"""
fs = float(self.info["sfreq"])
picks = _picks_to_idx(self.info, picks, exclude=(), none="data_or_ica")
_check_preload(self, "raw.notch_filter")
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)):
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
def resample(
self,
Expand Down