initial commit
This commit is contained in:
605
mne/preprocessing/ssp.py
Normal file
605
mne/preprocessing/ssp.py
Normal file
@@ -0,0 +1,605 @@
|
||||
# Authors: The MNE-Python contributors.
|
||||
# License: BSD-3-Clause
|
||||
# Copyright the MNE-Python contributors.
|
||||
|
||||
import copy as cp
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .._fiff.pick import pick_types
|
||||
from .._fiff.reference import make_eeg_average_ref_proj
|
||||
from ..epochs import Epochs
|
||||
from ..proj import compute_proj_epochs, compute_proj_evoked
|
||||
from ..utils import _validate_type, logger, verbose, warn
|
||||
from .ecg import find_ecg_events
|
||||
from .eog import find_eog_events
|
||||
|
||||
|
||||
def _safe_del_key(dict_, key):
|
||||
"""Aux function.
|
||||
|
||||
Use this function when preparing rejection parameters
|
||||
instead of directly deleting keys.
|
||||
"""
|
||||
if key in dict_:
|
||||
del dict_[key]
|
||||
|
||||
|
||||
def _compute_exg_proj(
|
||||
mode,
|
||||
raw,
|
||||
raw_event,
|
||||
tmin,
|
||||
tmax,
|
||||
n_grad,
|
||||
n_mag,
|
||||
n_eeg,
|
||||
l_freq,
|
||||
h_freq,
|
||||
average,
|
||||
filter_length,
|
||||
n_jobs,
|
||||
ch_name,
|
||||
reject,
|
||||
flat,
|
||||
bads,
|
||||
avg_ref,
|
||||
no_proj,
|
||||
event_id,
|
||||
exg_l_freq,
|
||||
exg_h_freq,
|
||||
tstart,
|
||||
qrs_threshold,
|
||||
filter_method,
|
||||
iir_params,
|
||||
return_drop_log,
|
||||
copy,
|
||||
meg,
|
||||
verbose,
|
||||
):
|
||||
"""Compute SSP/PCA projections for ECG or EOG artifacts."""
|
||||
raw = raw.copy() if copy else raw
|
||||
del copy
|
||||
raw.load_data() # we will filter it later
|
||||
|
||||
if no_proj:
|
||||
projs = []
|
||||
else:
|
||||
projs = cp.deepcopy(raw.info["projs"])
|
||||
logger.info(f"Including {len(projs)} SSP projectors from raw file")
|
||||
|
||||
if avg_ref:
|
||||
eeg_proj = make_eeg_average_ref_proj(raw.info)
|
||||
projs.append(eeg_proj)
|
||||
|
||||
if raw_event is None:
|
||||
raw_event = raw
|
||||
|
||||
assert mode in ("ECG", "EOG") # internal function
|
||||
logger.info(f"Running {mode} SSP computation")
|
||||
if mode == "ECG":
|
||||
events, _, _ = find_ecg_events(
|
||||
raw_event,
|
||||
ch_name=ch_name,
|
||||
event_id=event_id,
|
||||
l_freq=exg_l_freq,
|
||||
h_freq=exg_h_freq,
|
||||
tstart=tstart,
|
||||
qrs_threshold=qrs_threshold,
|
||||
filter_length=filter_length,
|
||||
)
|
||||
else: # mode == 'EOG':
|
||||
events = find_eog_events(
|
||||
raw_event,
|
||||
event_id=event_id,
|
||||
l_freq=exg_l_freq,
|
||||
h_freq=exg_h_freq,
|
||||
filter_length=filter_length,
|
||||
ch_name=ch_name,
|
||||
tstart=tstart,
|
||||
)
|
||||
|
||||
# Check to make sure we actually got at least one usable event
|
||||
if events.shape[0] < 1:
|
||||
warn(f"No {mode} events found")
|
||||
return ([], events) + (([],) if return_drop_log else ())
|
||||
|
||||
logger.info("Computing projector")
|
||||
my_info = cp.deepcopy(raw.info)
|
||||
my_info["bads"] += bads
|
||||
|
||||
# Handler rejection parameters
|
||||
_validate_type(reject, (None, dict), "reject")
|
||||
_validate_type(flat, (None, dict), "flat")
|
||||
if reject is not None: # make sure they didn't pass None
|
||||
reject = reject.copy() # must make a copy or we modify default!
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg="grad",
|
||||
eeg=False,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(reject, "grad")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg="mag",
|
||||
eeg=False,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(reject, "mag")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg=False,
|
||||
eeg=True,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(reject, "eeg")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg=False,
|
||||
eeg=False,
|
||||
eog=True,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(reject, "eog")
|
||||
if flat is not None: # make sure they didn't pass None
|
||||
flat = flat.copy()
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg="grad",
|
||||
eeg=False,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(flat, "grad")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg="mag",
|
||||
eeg=False,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(flat, "mag")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg=False,
|
||||
eeg=True,
|
||||
eog=False,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(flat, "eeg")
|
||||
if (
|
||||
len(
|
||||
pick_types(
|
||||
my_info,
|
||||
meg=False,
|
||||
eeg=False,
|
||||
eog=True,
|
||||
ref_meg=False,
|
||||
exclude="bads",
|
||||
)
|
||||
)
|
||||
== 0
|
||||
):
|
||||
_safe_del_key(flat, "eog")
|
||||
|
||||
# exclude bad channels from projection
|
||||
# keep reference channels if compensation channels are present
|
||||
ref_meg = len(my_info["comps"]) > 0
|
||||
picks = pick_types(
|
||||
my_info, meg=True, eeg=True, eog=True, ecg=True, ref_meg=ref_meg, exclude="bads"
|
||||
)
|
||||
|
||||
raw.filter(
|
||||
l_freq,
|
||||
h_freq,
|
||||
picks=picks,
|
||||
filter_length=filter_length,
|
||||
n_jobs=n_jobs,
|
||||
method=filter_method,
|
||||
iir_params=iir_params,
|
||||
l_trans_bandwidth=0.5,
|
||||
h_trans_bandwidth=0.5,
|
||||
phase="zero-double",
|
||||
fir_design="firwin2",
|
||||
)
|
||||
|
||||
epochs = Epochs(
|
||||
raw,
|
||||
events,
|
||||
None,
|
||||
tmin,
|
||||
tmax,
|
||||
baseline=None,
|
||||
preload=True,
|
||||
picks=picks,
|
||||
reject=reject,
|
||||
flat=flat,
|
||||
proj=True,
|
||||
)
|
||||
|
||||
drop_log = epochs.drop_log
|
||||
if epochs.events.shape[0] < 1:
|
||||
warn("No good epochs found")
|
||||
return ([], events) + ((drop_log,) if return_drop_log else ())
|
||||
|
||||
if average:
|
||||
evoked = epochs.average()
|
||||
ev_projs = compute_proj_evoked(
|
||||
evoked, n_grad=n_grad, n_mag=n_mag, n_eeg=n_eeg, meg=meg
|
||||
)
|
||||
else:
|
||||
ev_projs = compute_proj_epochs(
|
||||
epochs, n_grad=n_grad, n_mag=n_mag, n_eeg=n_eeg, n_jobs=n_jobs, meg=meg
|
||||
)
|
||||
|
||||
for p in ev_projs:
|
||||
p["desc"] = mode + "-" + p["desc"]
|
||||
|
||||
projs.extend(ev_projs)
|
||||
logger.info("Done.")
|
||||
return (projs, events) + ((drop_log,) if return_drop_log else ())
|
||||
|
||||
|
||||
@verbose
|
||||
def compute_proj_ecg(
|
||||
raw,
|
||||
raw_event=None,
|
||||
tmin=-0.2,
|
||||
tmax=0.4,
|
||||
n_grad=2,
|
||||
n_mag=2,
|
||||
n_eeg=2,
|
||||
l_freq=1.0,
|
||||
h_freq=35.0,
|
||||
average=True,
|
||||
filter_length="10s",
|
||||
n_jobs=None,
|
||||
ch_name=None,
|
||||
reject=dict(grad=2000e-13, mag=3000e-15, eeg=50e-6, eog=250e-6), # noqa: B006
|
||||
flat=None,
|
||||
bads=(),
|
||||
avg_ref=False,
|
||||
no_proj=False,
|
||||
event_id=999,
|
||||
ecg_l_freq=5,
|
||||
ecg_h_freq=35,
|
||||
tstart=0.0,
|
||||
qrs_threshold="auto",
|
||||
filter_method="fir",
|
||||
iir_params=None,
|
||||
copy=True,
|
||||
return_drop_log=False,
|
||||
meg="separate",
|
||||
verbose=None,
|
||||
):
|
||||
"""Compute SSP (signal-space projection) vectors for ECG artifacts.
|
||||
|
||||
%(compute_proj_ecg)s
|
||||
|
||||
.. note:: Raw data will be loaded if it hasn't been preloaded already.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
raw : mne.io.Raw
|
||||
Raw input file.
|
||||
raw_event : mne.io.Raw or None
|
||||
Raw file to use for event detection (if None, raw is used).
|
||||
tmin : float
|
||||
Time before event in seconds.
|
||||
tmax : float
|
||||
Time after event in seconds.
|
||||
n_grad : int
|
||||
Number of SSP vectors for gradiometers.
|
||||
n_mag : int
|
||||
Number of SSP vectors for magnetometers.
|
||||
n_eeg : int
|
||||
Number of SSP vectors for EEG.
|
||||
l_freq : float | None
|
||||
Filter low cut-off frequency for the data channels in Hz.
|
||||
h_freq : float | None
|
||||
Filter high cut-off frequency for the data channels in Hz.
|
||||
average : bool
|
||||
Compute SSP after averaging. Default is True.
|
||||
filter_length : str | int | None
|
||||
Number of taps to use for filtering.
|
||||
%(n_jobs)s
|
||||
ch_name : str | None
|
||||
Channel to use for ECG detection (Required if no ECG found).
|
||||
reject : dict | None
|
||||
Epoch rejection configuration (see Epochs).
|
||||
flat : dict | None
|
||||
Epoch flat configuration (see Epochs).
|
||||
bads : list
|
||||
List with (additional) bad channels.
|
||||
avg_ref : bool
|
||||
Add EEG average reference proj.
|
||||
no_proj : bool
|
||||
Exclude the SSP projectors currently in the fiff file.
|
||||
event_id : int
|
||||
ID to use for events.
|
||||
ecg_l_freq : float
|
||||
Low pass frequency applied to the ECG channel for event detection.
|
||||
ecg_h_freq : float
|
||||
High pass frequency applied to the ECG channel for event detection.
|
||||
tstart : float
|
||||
Start artifact detection after tstart seconds.
|
||||
qrs_threshold : float | str
|
||||
Between 0 and 1. qrs detection threshold. Can also be "auto" to
|
||||
automatically choose the threshold that generates a reasonable
|
||||
number of heartbeats (40-160 beats / min).
|
||||
filter_method : str
|
||||
Method for filtering ('iir' or 'fir').
|
||||
iir_params : dict | None
|
||||
Dictionary of parameters to use for IIR filtering.
|
||||
See mne.filter.construct_iir_filter for details. If iir_params
|
||||
is None and method="iir", 4th order Butterworth will be used.
|
||||
copy : bool
|
||||
If False, filtering raw data is done in place. Defaults to True.
|
||||
return_drop_log : bool
|
||||
If True, return the drop log.
|
||||
|
||||
.. versionadded:: 0.15
|
||||
meg : str
|
||||
Can be ``'separate'`` (default) or ``'combined'`` to compute projectors
|
||||
for magnetometers and gradiometers separately or jointly.
|
||||
If ``'combined'``, ``n_mag == n_grad`` is required and the number of
|
||||
projectors computed for MEG will be ``n_mag``.
|
||||
|
||||
.. versionadded:: 0.18
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
%(projs)s
|
||||
ecg_events : ndarray
|
||||
Detected ECG events.
|
||||
drop_log : list
|
||||
The drop log, if requested.
|
||||
|
||||
See Also
|
||||
--------
|
||||
find_ecg_events
|
||||
create_ecg_epochs
|
||||
|
||||
Notes
|
||||
-----
|
||||
Filtering is applied to the ECG channel while finding events using
|
||||
``ecg_l_freq`` and ``ecg_h_freq``, and then to the ``raw`` instance
|
||||
using ``l_freq`` and ``h_freq`` before creation of the epochs used to
|
||||
create the projectors.
|
||||
"""
|
||||
return _compute_exg_proj(
|
||||
"ECG",
|
||||
raw,
|
||||
raw_event,
|
||||
tmin,
|
||||
tmax,
|
||||
n_grad,
|
||||
n_mag,
|
||||
n_eeg,
|
||||
l_freq,
|
||||
h_freq,
|
||||
average,
|
||||
filter_length,
|
||||
n_jobs,
|
||||
ch_name,
|
||||
reject,
|
||||
flat,
|
||||
bads,
|
||||
avg_ref,
|
||||
no_proj,
|
||||
event_id,
|
||||
ecg_l_freq,
|
||||
ecg_h_freq,
|
||||
tstart,
|
||||
qrs_threshold,
|
||||
filter_method,
|
||||
iir_params,
|
||||
return_drop_log,
|
||||
copy,
|
||||
meg,
|
||||
verbose,
|
||||
)
|
||||
|
||||
|
||||
@verbose
|
||||
def compute_proj_eog(
|
||||
raw,
|
||||
raw_event=None,
|
||||
tmin=-0.2,
|
||||
tmax=0.2,
|
||||
n_grad=2,
|
||||
n_mag=2,
|
||||
n_eeg=2,
|
||||
l_freq=1.0,
|
||||
h_freq=35.0,
|
||||
average=True,
|
||||
filter_length="10s",
|
||||
n_jobs=None,
|
||||
reject=dict(grad=2000e-13, mag=3000e-15, eeg=500e-6, eog=np.inf), # noqa: B006
|
||||
flat=None,
|
||||
bads=(),
|
||||
avg_ref=False,
|
||||
no_proj=False,
|
||||
event_id=998,
|
||||
eog_l_freq=1,
|
||||
eog_h_freq=10,
|
||||
tstart=0.0,
|
||||
filter_method="fir",
|
||||
iir_params=None,
|
||||
ch_name=None,
|
||||
copy=True,
|
||||
return_drop_log=False,
|
||||
meg="separate",
|
||||
verbose=None,
|
||||
):
|
||||
"""Compute SSP (signal-space projection) vectors for EOG artifacts.
|
||||
|
||||
%(compute_proj_eog)s
|
||||
|
||||
.. note:: Raw data must be preloaded.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
raw : mne.io.Raw
|
||||
Raw input file.
|
||||
raw_event : mne.io.Raw or None
|
||||
Raw file to use for event detection (if None, raw is used).
|
||||
tmin : float
|
||||
Time before event in seconds.
|
||||
tmax : float
|
||||
Time after event in seconds.
|
||||
n_grad : int
|
||||
Number of SSP vectors for gradiometers.
|
||||
n_mag : int
|
||||
Number of SSP vectors for magnetometers.
|
||||
n_eeg : int
|
||||
Number of SSP vectors for EEG.
|
||||
l_freq : float | None
|
||||
Filter low cut-off frequency for the data channels in Hz.
|
||||
h_freq : float | None
|
||||
Filter high cut-off frequency for the data channels in Hz.
|
||||
average : bool
|
||||
Compute SSP after averaging. Default is True.
|
||||
filter_length : str | int | None
|
||||
Number of taps to use for filtering.
|
||||
%(n_jobs)s
|
||||
reject : dict | None
|
||||
Epoch rejection configuration (see Epochs).
|
||||
flat : dict | None
|
||||
Epoch flat configuration (see Epochs).
|
||||
bads : list
|
||||
List with (additional) bad channels.
|
||||
avg_ref : bool
|
||||
Add EEG average reference proj.
|
||||
no_proj : bool
|
||||
Exclude the SSP projectors currently in the fiff file.
|
||||
event_id : int
|
||||
ID to use for events.
|
||||
eog_l_freq : float
|
||||
Low pass frequency applied to the E0G channel for event detection.
|
||||
eog_h_freq : float
|
||||
High pass frequency applied to the EOG channel for event detection.
|
||||
tstart : float
|
||||
Start artifact detection after tstart seconds.
|
||||
filter_method : str
|
||||
Method for filtering ('iir' or 'fir').
|
||||
iir_params : dict | None
|
||||
Dictionary of parameters to use for IIR filtering.
|
||||
See mne.filter.construct_iir_filter for details. If iir_params
|
||||
is None and method="iir", 4th order Butterworth will be used.
|
||||
ch_name : str | None
|
||||
If not None, specify EOG channel name.
|
||||
copy : bool
|
||||
If False, filtering raw data is done in place. Defaults to True.
|
||||
return_drop_log : bool
|
||||
If True, return the drop log.
|
||||
|
||||
.. versionadded:: 0.15
|
||||
meg : str
|
||||
Can be 'separate' (default) or 'combined' to compute projectors
|
||||
for magnetometers and gradiometers separately or jointly.
|
||||
If 'combined', ``n_mag == n_grad`` is required and the number of
|
||||
projectors computed for MEG will be ``n_mag``.
|
||||
|
||||
.. versionadded:: 0.18
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
%(projs)s
|
||||
eog_events: ndarray
|
||||
Detected EOG events.
|
||||
drop_log : list
|
||||
The drop log, if requested.
|
||||
|
||||
See Also
|
||||
--------
|
||||
find_eog_events
|
||||
create_eog_epochs
|
||||
|
||||
Notes
|
||||
-----
|
||||
Filtering is applied to the EOG channel while finding events using
|
||||
``eog_l_freq`` and ``eog_h_freq``, and then to the ``raw`` instance
|
||||
using ``l_freq`` and ``h_freq`` before creation of the epochs used to
|
||||
create the projectors.
|
||||
"""
|
||||
return _compute_exg_proj(
|
||||
"EOG",
|
||||
raw,
|
||||
raw_event,
|
||||
tmin,
|
||||
tmax,
|
||||
n_grad,
|
||||
n_mag,
|
||||
n_eeg,
|
||||
l_freq,
|
||||
h_freq,
|
||||
average,
|
||||
filter_length,
|
||||
n_jobs,
|
||||
ch_name,
|
||||
reject,
|
||||
flat,
|
||||
bads,
|
||||
avg_ref,
|
||||
no_proj,
|
||||
event_id,
|
||||
eog_l_freq,
|
||||
eog_h_freq,
|
||||
tstart,
|
||||
"auto",
|
||||
filter_method,
|
||||
iir_params,
|
||||
return_drop_log,
|
||||
copy,
|
||||
meg,
|
||||
verbose,
|
||||
)
|
||||
Reference in New Issue
Block a user