Skip to content
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/dev/14205.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ``meanlogratio`` baselining mode to :meth:`mne.time_frequency.EpochsTFR.apply_baseline` and related functions, by :newcontrib:`Virginie van Wassenhove`.
1 change: 1 addition & 0 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@
.. _Victor Férat: https://github.com/vferat
.. _Victoria Peterson: https://github.com/vpeterson
.. _Vincent Gao: https://github.com/gaoflow
.. _Virginie van Wassenhove: https://brainthemind.com/virginie-van-wassenhove
.. _Wei Xu: https://github.com/psyxw
.. _Will Turner: https://bootstrapbill.github.io
.. _Wouter Kroot: https://github.com/WouterKroot
Expand Down
9 changes: 9 additions & 0 deletions doc/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,15 @@ @unpublished{KingEtAl2018
note = {hal-01848442}
}

@article{KinleyEtAl2026,
author={Kinley, Isaac and Roberts, Reece P and Meltzer, Jed A and Addis, Donna Rose},
title={Spectral change or Jensen gap? Log-ratio baseline correction for time-frequency M/EEG is negatively biased},
journal={Journal of Neuroscience Methods},
pages={110826},
year={2026},
publisher={Elsevier}
}

@article{KnuutilaEtAl1993,
author = {Knuutila, Jukka E. T. and Ahonen, Antti I. and Hämäläinen, Matti S. and Kajola, Matti J. and Laine, P. P. and Lounasmaa, Olli V. and Parkkonen, Lauri T. and Simola, Juha T. A. and Tesche, Claudia D.},
doi = {10.1109/20.281163},
Expand Down
37 changes: 21 additions & 16 deletions mne/baseline.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ def _log_rescale(baseline, mode="mean"):
_check_option(
"mode",
mode,
["logratio", "ratio", "zscore", "mean", "percent", "zlogratio"],
[
"logratio",
"ratio",
"zscore",
"mean",
"percent",
"zlogratio",
"meanlogratio",
],
)
msg = f"Applying baseline correction (mode: {mode})"
else:
Expand All @@ -35,21 +43,7 @@ def rescale(data, times, baseline, mode="mean", copy=True, picks=None, verbose=N
times : 1D array
Time instants is seconds.
%(baseline_rescale)s
mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio'
Perform baseline correction by

- subtracting the mean of baseline values ('mean')
- dividing by the mean of baseline values ('ratio')
- dividing by the mean of baseline values and taking the log
('logratio')
- subtracting the mean of baseline values followed by dividing by
the mean of baseline values ('percent')
- subtracting the mean of baseline values and dividing by the
standard deviation of baseline values ('zscore')
- dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
('zlogratio')

%(baseline_mode)s
copy : bool
Whether to return a new instance or modify in place.
picks : list of int | None
Expand All @@ -60,6 +54,10 @@ def rescale(data, times, baseline, mode="mean", copy=True, picks=None, verbose=N
-------
data_scaled: array
Array of same shape as data after rescaling.

References
----------
.. footbibliography::
"""
if copy:
data = data.copy()
Expand Down Expand Up @@ -114,6 +112,13 @@ def fun(d, m):
d /= m
np.log10(d, out=d)

elif mode == "meanlogratio":

def fun(d, m):
d /= m
np.log10(d, out=d)
d -= np.mean(d[..., imin:imax], axis=-1, keepdims=True)

elif mode == "percent":

def fun(d, m):
Expand Down
40 changes: 10 additions & 30 deletions mne/minimum_norm/time_frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,21 +220,7 @@ def source_band_induced_power(
If a is None the beginning of the data is used and if b is None then b
is set to the end of the interval. If baseline is equal to (None, None)
all the time interval is used.
baseline_mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio'
Perform baseline correction by

- subtracting the mean of baseline values ('mean')
- dividing by the mean of baseline values ('ratio')
- dividing by the mean of baseline values and taking the log
('logratio')
- subtracting the mean of baseline values followed by dividing by
the mean of baseline values ('percent')
- subtracting the mean of baseline values and dividing by the
standard deviation of baseline values ('zscore')
- dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
('zlogratio')

%(baseline_mode_mn)s
pca : bool
If True, the true dimension of data is estimated before running
the time-frequency transforms. It reduces the computation times
Expand All @@ -258,6 +244,10 @@ def source_band_induced_power(
(n_vertices, n_frequencies, n_samples) if label=None or label=label.
For lists of one or more labels, the induced power estimate has shape
(n_labels, n_frequencies, n_samples).

References
----------
.. footbibliography::
""" # noqa: E501
_check_option("method", method, INVERSE_METHODS)

Expand Down Expand Up @@ -648,21 +638,7 @@ def source_induced_power(
and if b is None then b is set to the end of the interval.
If baseline is equal to (None, None) all the time
interval is used.
baseline_mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio'
Perform baseline correction by

- subtracting the mean of baseline values ('mean')
- dividing by the mean of baseline values ('ratio')
- dividing by the mean of baseline values and taking the log
('logratio')
- subtracting the mean of baseline values followed by dividing by
the mean of baseline values ('percent')
- subtracting the mean of baseline values and dividing by the
standard deviation of baseline values ('zscore')
- dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
('zlogratio')

%(baseline_mode_mn)s
pca : bool
If True, the true dimension of data is estimated before running
the time-frequency transforms. It reduces the computation times
Expand Down Expand Up @@ -692,6 +668,10 @@ def source_induced_power(
plv : array
The phase-locking value array with shape (n_sources, n_freqs,
n_samples). Only returned if ``return_plv=True``.

References
----------
.. footbibliography::
""" # noqa: E501
_check_option("method", method, INVERSE_METHODS)
_check_ori(pick_ori, inverse_operator["source_ori"], inverse_operator["src"])
Expand Down
3 changes: 2 additions & 1 deletion mne/time_frequency/tests/test_tfr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,8 @@ def test_tfr_copy(average_tfr):


@pytest.mark.parametrize(
"mode", ("mean", "ratio", "logratio", "percent", "zscore", "zlogratio")
"mode",
("mean", "ratio", "logratio", "meanlogratio", "percent", "zscore", "zlogratio"),
)
def test_tfr_apply_baseline(average_tfr, mode):
"""Test TFR baselining."""
Expand Down
25 changes: 8 additions & 17 deletions mne/time_frequency/tfr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1793,26 +1793,17 @@ def apply_baseline(self, baseline, mode="mean", verbose=None):
%(baseline_rescale)s

How baseline is computed is determined by the ``mode`` parameter.
mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio'
Perform baseline correction by

- subtracting the mean of baseline values ('mean')
- dividing by the mean of baseline values ('ratio')
- dividing by the mean of baseline values and taking the log
('logratio')
- subtracting the mean of baseline values followed by dividing by
the mean of baseline values ('percent')
- subtracting the mean of baseline values and dividing by the
standard deviation of baseline values ('zscore')
- dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
('zlogratio')
%(baseline_mode)s
%(verbose)s

Returns
-------
%(inst_tfr)s
The modified instance.

References
----------
.. footbibliography::
"""
self._baseline = _check_baseline(baseline, times=self.times, sfreq=self.sfreq)
rescale(self.data, self.times, self.baseline, mode, copy=False, verbose=verbose)
Expand Down Expand Up @@ -1950,7 +1941,7 @@ def plot(
%(baseline_rescale)s

How baseline is computed is determined by the ``mode`` parameter.
%(mode_tfr_plot)s
%(baseline_mode)s
%(dB_tfr_plot)s
%(combine_tfr_plot)s

Expand Down Expand Up @@ -2216,7 +2207,7 @@ def plot_joint(
%(baseline_rescale)s

How baseline is computed is determined by the ``mode`` parameter.
%(mode_tfr_plot)s
%(baseline_mode)s
%(dB_tfr_plot)s
%(yscale_tfr_plot)s
%(vlim_tfr_plot_joint)s
Expand Down Expand Up @@ -2514,7 +2505,7 @@ def plot_topo(
%(baseline_rescale)s

How baseline is computed is determined by the ``mode`` parameter.
%(mode_tfr_plot)s
%(baseline_mode)s
%(tmin_tmax_psd)s
%(fmin_fmax_tfr)s
%(vmin_vmax_tfr_plot_topo)s
Expand Down
48 changes: 31 additions & 17 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,37 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
2. Subtract this mean from the **entire** ``Evoked``.

"""
_baseline_mode_desc = """\
Perform baseline correction by:

``"mean"``
Subtracting the mean of baseline values
``"ratio"``
Dividing by the mean of baseline values
``"logratio"``
Dividing by the mean of baseline values and taking the log
``"meanlogratio"``
Dividing by the mean of baseline values, taking the log and then
subtracting the mean (:footcite:`KinleyEtAl2026`)

.. note:: this baseline mode has not been tested at the source-level!
``"percent"``
Subtracting the mean of baseline values followed by dividing by
the mean of baseline values
``"zscore"``
Subtracting the mean of baseline values and dividing by the
standard deviation of baseline values
``"zlogratio"``
Dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
"""

docdict["baseline_mode"] = f"""\
mode : 'mean' | 'ratio' | 'logratio' | 'meanlogratio' | 'percent' | 'zscore' | 'zlogratio'
{_baseline_mode_desc}""" # noqa: E501
docdict["baseline_mode_mn"] = f"""\
baseline_mode : 'mean' | 'ratio' | 'logratio' | 'meanlogratio' | 'percent' | 'zscore' | 'zlogratio'
{_baseline_mode_desc}""" # noqa: E501

docdict["baseline_report"] = f"""{_baseline_rescale_base}
Correction is applied in the following way **to each channel:**
Expand Down Expand Up @@ -2825,23 +2856,6 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
* 'sum' : Sum of PSFs/CTFs across vertices.
"""

docdict["mode_tfr_plot"] = """
mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio'
Perform baseline correction by

- subtracting the mean of baseline values ('mean') (default)
- dividing by the mean of baseline values ('ratio')
- dividing by the mean of baseline values and taking the log
('logratio')
- subtracting the mean of baseline values followed by dividing by
the mean of baseline values ('percent')
- subtracting the mean of baseline values and dividing by the
standard deviation of baseline values ('zscore')
- dividing by the mean of baseline values, taking the log, and
dividing by the standard deviation of log baseline values
('zlogratio')
"""

docdict["montage"] = """
montage : None | str | DigMontage
A montage containing channel positions. If a string or
Expand Down
19 changes: 5 additions & 14 deletions mne/viz/topomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2065,20 +2065,7 @@ def plot_tfr_topomap(
"b (s)". If a is None the beginning of the data is used and if b is
None then b is set to the end of the interval. If baseline is equal to
(None, None) the whole time interval is used.
mode : 'mean' | 'ratio' | 'logratio' | 'percent' | 'zscore' | 'zlogratio' | None
Perform baseline correction by

- subtracting the mean baseline power ('mean')
- dividing by the mean baseline power ('ratio')
- dividing by the mean baseline power and taking the log ('logratio')
- subtracting the mean baseline power followed by dividing by the
mean baseline power ('percent')
- subtracting the mean baseline power and dividing by the standard
deviation of the baseline power ('zscore')
- dividing by the mean baseline power, taking the log, and dividing
by the standard deviation of the baseline power ('zlogratio')

If None no baseline correction is applied.
%(baseline_mode)s
%(sensors_topomap)s
%(show_names_topomap)s
%(mask_evoked_topomap)s
Expand Down Expand Up @@ -2119,6 +2106,10 @@ def plot_tfr_topomap(
-------
fig : matplotlib.figure.Figure
The figure containing the topography.

References
----------
.. footbibliography::
""" # noqa: E501
import matplotlib.pyplot as plt

Expand Down
Loading
Loading