Skip to content

Commit

Permalink
Move to mne.stats.erp module
Browse files Browse the repository at this point in the history
  • Loading branch information
cbrnr committed Jul 11, 2024
1 parent 862c950 commit 112e320
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 49 deletions.
2 changes: 1 addition & 1 deletion doc/changes/devel/12707.newfeature.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Add :meth:`~mne.Epochs.compute_sme` to compute the analytical standardized measurement error (SME) as a data quality measure for ERP studies, by `Clemens Brunner`_.
Add :func:`~mne.stats.erp.compute_sme` to compute the analytical standardized measurement error (SME) as a data quality measure for ERP studies, by `Clemens Brunner`_.
43 changes: 0 additions & 43 deletions mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1282,49 +1282,6 @@ def _evoked_from_epoch_data(self, data, info, picks, n_events, kind, comment):

return evoked

def compute_sme(self, start=None, stop=None):
"""Compute standardized measurement error (SME).
The standardized measurement error :footcite:`LuckEtAl2021` can be used as a
universal measure of data quality in ERP studies.
Parameters
----------
start : int | float | None
Start time (in s) of the time window used for SME computation. If ``None``,
use the start of the epoch.
stop : int | float | None
Stop time (in s) of the time window used for SME computation. If ``None``,
use the end of the epoch.
Returns
-------
sme : array, shape (n_channels,)
SME in given time window for each channel.
Notes
-----
Currently, only the mean value in the given time window is supported, meaning
that the resulting SME is only valid in studies which quantify the amplitude of
an ERP component as the mean within the time window (as opposed to e.g. the
peak, which would require bootstrapping).
References
----------
.. footbibliography::
"""
_validate_type(start, ("numeric", None), "start", "int or float")
_validate_type(stop, ("numeric", None), "stop", "int or float")
start = self.tmin if start is None else start
stop = self.tmax if stop is None else stop
if start < self.tmin:
raise ValueError("start is out of bounds.")
if stop > self.tmax:
raise ValueError("stop is out of bounds.")

data = self.get_data(tmin=start, tmax=stop)
return data.mean(axis=2).std(axis=0) / np.sqrt(data.shape[0])

@property
def ch_names(self):
"""Channel names."""
Expand Down
2 changes: 2 additions & 0 deletions mne/stats/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __all__ = [
"bonferroni_correction",
"bootstrap_confidence_interval",
"combine_adjacency",
"compute_sme",
"f_mway_rm",
"f_oneway",
"f_threshold_mway_rm",
Expand All @@ -29,6 +30,7 @@ from .cluster_level import (
spatio_temporal_cluster_test,
summarize_clusters_stc,
)
from .erp import compute_sme
from .multi_comp import bonferroni_correction, fdr_correction
from .parametric import (
_parametric_ci,
Expand Down
47 changes: 47 additions & 0 deletions mne/stats/erp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import numpy as np

from mne.utils import _validate_type


def compute_sme(epochs, start=None, stop=None):
"""Compute standardized measurement error (SME).
The standardized measurement error :footcite:`LuckEtAl2021` can be used as a
universal measure of data quality in ERP studies.
Parameters
----------
start : int | float | None
Start time (in s) of the time window used for SME computation. If ``None``, use
the start of the epoch.
stop : int | float | None
Stop time (in s) of the time window used for SME computation. If ``None``, use
the end of the epoch.
Returns
-------
sme : array, shape (n_channels,)
SME in given time window for each channel.
Notes
-----
Currently, only the mean value in the given time window is supported, meaning that
the resulting SME is only valid in studies which quantify the amplitude of an ERP
component as the mean within the time window (as opposed to e.g. the peak, which
would require bootstrapping).
References
----------
.. footbibliography::
"""
_validate_type(start, ("numeric", None), "start", "int or float")
_validate_type(stop, ("numeric", None), "stop", "int or float")
start = epochs.tmin if start is None else start
stop = epochs.tmax if stop is None else stop
if start < epochs.tmin:
raise ValueError("start is out of bounds.")
if stop > epochs.tmax:
raise ValueError("stop is out of bounds.")

data = epochs.get_data(tmin=start, tmax=stop)
return data.mean(axis=2).std(axis=0) / np.sqrt(data.shape[0])
11 changes: 6 additions & 5 deletions mne/tests/test_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
from mne.event import merge_events
from mne.io import RawArray, read_raw_fif
from mne.preprocessing import maxwell_filter
from mne.stats.erp import compute_sme
from mne.utils import (
_dt_to_stamp,
_record_warnings,
Expand Down Expand Up @@ -5269,14 +5270,14 @@ def test_epochs_sme():
"""Test SME computation."""
raw, events, _ = _get_data()
epochs = Epochs(raw, events)
sme = epochs.compute_sme(start=0, stop=0.1)
sme = compute_sme(epochs, start=0, stop=0.1)
assert sme.shape == (376,)

with pytest.raises(TypeError, match="int or float"):
epochs.compute_sme("0", 0.1)
compute_sme(epochs, "0", 0.1)
with pytest.raises(TypeError, match="int or float"):
epochs.compute_sme(0, "0.1")
compute_sme(epochs, 0, "0.1")
with pytest.raises(ValueError, match="out of bounds"):
epochs.compute_sme(-1.2, 0.3)
compute_sme(epochs, -1.2, 0.3)
with pytest.raises(ValueError, match="out of bounds"):
epochs.compute_sme(-0.1, 0.8)
compute_sme(epochs, -0.1, 0.8)

0 comments on commit 112e320

Please sign in to comment.