Skip to content

Commit

Permalink
Merge branch 'main' into pre
Browse files Browse the repository at this point in the history
  • Loading branch information
larsoner authored Mar 4, 2024
2 parents e923bf6 + 64901c4 commit 93f9dda
Show file tree
Hide file tree
Showing 64 changed files with 610 additions and 144 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
repos:
# Ruff mne
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.2
rev: v0.3.0
hooks:
- id: ruff
name: ruff lint mne
Expand Down
1 change: 1 addition & 0 deletions doc/api/preprocessing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Projections:
cortical_signal_suppression
create_ecg_epochs
create_eog_epochs
find_bad_channels_lof
find_bad_channels_maxwell
find_ecg_events
find_eog_events
Expand Down
1 change: 1 addition & 0 deletions doc/changes/devel/11234.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Detecting Bad EEG/MEG channels using the local outlier factor (LOF) algorithm in :func:`mne.preprocessing.find_bad_channels_lof`, by :newcontrib:`Velu Prabhakar Kumaravel`.
1 change: 1 addition & 0 deletions doc/changes/devel/12462.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`mne.epochs.make_metadata` now accepts strings as ``tmin`` and ``tmax`` parameter values, simplifying metadata creation based on time-varying events such as responses to a stimulus, by `Richard Höchenberger`_.
8 changes: 5 additions & 3 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

.. _Alex Gramfort: https://alexandre.gramfort.net

.. _Alex Kiefer: https://home.alex101.dev
.. _Alex Kiefer: https://home.alexk101.dev

.. _Alex Rockhill: https://github.com/alexrockhill/

Expand Down Expand Up @@ -208,7 +208,7 @@

.. _Henrich Kolkhorst: https://github.com/hekolk

.. _Hongjiang Ye: https://github.com/rubyyhj
.. _Hongjiang Ye: https://github.com/hongjiang-ye

.. _Hubert Banville: https://github.com/hubertjb

Expand Down Expand Up @@ -418,7 +418,7 @@

.. _Okba Bekhelifi: https://github.com/okbalefthanded

.. _Olaf Hauk: https://www.neuroscience.cam.ac.uk/directory/profile.php?olafhauk
.. _Olaf Hauk: https://neuroscience.cam.ac.uk/member/olafhauk

.. _Oleh Kozynets: https://github.com/OlehKSS

Expand Down Expand Up @@ -588,6 +588,8 @@

.. _Valerii Chirkov: https://github.com/vagechirkov

.. _Velu Prabhakar Kumaravel: https://github.com/vpKumaravel

.. _Victor Ferat: https://github.com/vferat

.. _Victoria Peterson: https://github.com/vpeterson
Expand Down
10 changes: 6 additions & 4 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,8 @@ def append_attr_meth_examples(app, what, name, obj, options, lines):
# Too slow
"https://speakerdeck.com/dengemann/",
"https://www.dtu.dk/english/service/phonebook/person",
# SSL problems sometimes
"http://ilabs.washington.edu",
]
linkcheck_anchors = False # saves a bit of time
linkcheck_timeout = 15 # some can be quite slow
Expand Down Expand Up @@ -1192,21 +1194,21 @@ def append_attr_meth_examples(app, what, name, obj, options, lines):
"carousel": [
dict(
title="Source Estimation",
text="Distributed, sparse, mixed-norm, beam\u00ADformers, dipole fitting, and more.", # noqa E501
text="Distributed, sparse, mixed-norm, beam\u00adformers, dipole fitting, and more.", # noqa E501
url="auto_tutorials/inverse/index.html",
img="sphx_glr_30_mne_dspm_loreta_008.gif",
alt="dSPM",
),
dict(
title="Machine Learning",
text="Advanced decoding models including time general\u00ADiza\u00ADtion.", # noqa E501
text="Advanced decoding models including time general\u00adiza\u00adtion.", # noqa E501
url="auto_tutorials/machine-learning/50_decoding.html",
img="sphx_glr_50_decoding_006.png",
alt="Decoding",
),
dict(
title="Encoding Models",
text="Receptive field estima\u00ADtion with optional smooth\u00ADness priors.", # noqa E501
text="Receptive field estima\u00adtion with optional smooth\u00adness priors.", # noqa E501
url="auto_tutorials/machine-learning/30_strf.html",
img="sphx_glr_30_strf_001.png",
alt="STRF",
Expand All @@ -1220,7 +1222,7 @@ def append_attr_meth_examples(app, what, name, obj, options, lines):
),
dict(
title="Connectivity",
text="All-to-all spectral and effective connec\u00ADtivity measures.", # noqa E501
text="All-to-all spectral and effective connec\u00adtivity measures.", # noqa E501
url="https://mne.tools/mne-connectivity/stable/auto_examples/mne_inverse_label_connectivity.html", # noqa E501
img="https://mne.tools/mne-connectivity/stable/_images/sphx_glr_mne_inverse_label_connectivity_001.png", # noqa E501
alt="Connectivity",
Expand Down
31 changes: 31 additions & 0 deletions doc/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -2450,6 +2450,37 @@ @article{TierneyEtAl2022
author = {Tierney, Tim M. and Mellor, Stephanie nd O'Neill, George C. and Timms, Ryan C. and Barnes, Gareth R.},
}

@article{KumaravelEtAl2022,
doi = {10.3390/s22197314},
url = {https://doi.org/10.3390/s22197314},
year = {2022},
month = sep,
publisher = {{MDPI} {AG}},
volume = {22},
number = {19},
pages = {7314},
author = {Velu Prabhakar Kumaravel and Marco Buiatti and Eugenio Parise and Elisabetta Farella},
title = {Adaptable and Robust {EEG} Bad Channel Detection Using Local Outlier Factor ({LOF})},
journal = {Sensors}
}

@article{BreunigEtAl2000,
author = {Breunig, Markus M. and Kriegel, Hans-Peter and Ng, Raymond T. and Sander, J\"{o}rg},
title = {LOF: Identifying Density-Based Local Outliers},
year = {2000},
issue_date = {June 2000},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {29},
number = {2},
url = {https://doi.org/10.1145/335191.335388},
doi = {10.1145/335191.335388},
journal = {SIGMOD Rec.},
month = {may},
pages = {93–104},
numpages = {12},
keywords = {outlier detection, database mining}
}

@article{OyamaEtAl2015,
title = {Dry phantom for magnetoencephalography —{Configuration}, calibration, and contribution},
Expand Down
2 changes: 1 addition & 1 deletion doc/sphinxext/unit_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def pass_error_to_sphinx(rawtext, text, lineno, inliner):
except ValueError:
return pass_error_to_sphinx(rawtext, text, lineno, inliner)
# input is well-formatted: proceed
node = nodes.Text("\u202F".join(parts))
node = nodes.Text("\u202f".join(parts))
return [node], []


Expand Down
1 change: 0 additions & 1 deletion examples/datasets/hf_sef_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

# %%


import os

import mne
Expand Down
1 change: 0 additions & 1 deletion examples/decoding/decoding_csp_eeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

# %%


import matplotlib.pyplot as plt
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
Expand Down
1 change: 0 additions & 1 deletion examples/decoding/decoding_csp_timefreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

# %%


import matplotlib.pyplot as plt
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
Expand Down
1 change: 0 additions & 1 deletion examples/decoding/ssd_spatial_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

# %%


import matplotlib.pyplot as plt

import mne
Expand Down
1 change: 0 additions & 1 deletion examples/io/elekta_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

# %%


import os

import mne
Expand Down
6 changes: 3 additions & 3 deletions examples/preprocessing/css.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ def subcortical_waveform(times):
labels=[postcenlab, hiplab],
data_fun=cortical_waveform,
)
stc.data[
np.where(np.isin(stc.vertices[0], hiplab.vertices))[0], :
] = subcortical_waveform(times)
stc.data[np.where(np.isin(stc.vertices[0], hiplab.vertices))[0], :] = (
subcortical_waveform(times)
)
evoked = simulate_evoked(fwd, stc, raw.info, cov, nave=15)

###############################################################################
Expand Down
1 change: 0 additions & 1 deletion examples/preprocessing/eog_artifact_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

# %%


import matplotlib.pyplot as plt
import numpy as np

Expand Down
171 changes: 171 additions & 0 deletions examples/preprocessing/epochs_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"""
.. _epochs-metadata:
===============================================================
Automated epochs metadata generation with variable time windows
===============================================================
When working with :class:`~mne.Epochs`, :ref:`metadata <tut-epochs-metadata>` can be
invaluable. There is an extensive tutorial on
:ref:`how it can be generated automatically <tut-autogenerate-metadata>`.
In the brief examples below, we will demonstrate different ways to bound the time
windows used to generate the metadata.
"""
# Authors: Richard Höchenberger <richard.hoechenberger@gmail.com>
#
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

# %%
# We will use data from an EEG recording during an Eriksen flanker task. For the
# purpose of demonstration, we'll only load the first 60 seconds of data.

import mne

data_dir = mne.datasets.erp_core.data_path()
infile = data_dir / "ERP-CORE_Subject-001_Task-Flankers_eeg.fif"

raw = mne.io.read_raw(infile, preload=True)
raw.crop(tmax=60).filter(l_freq=0.1, h_freq=40)

# %%
# Visualizing the events
# ^^^^^^^^^^^^^^^^^^^^^^
#
# All experimental events are stored in the :class:`~mne.io.Raw` instance as
# :class:`~mne.Annotations`. We first need to convert these to events and the
# corresponding mapping from event codes to event names (``event_id``). We then
# visualize the events.
all_events, all_event_id = mne.events_from_annotations(raw)
mne.viz.plot_events(events=all_events, event_id=all_event_id, sfreq=raw.info["sfreq"])


# %%
# As you can see, there are four types of ``stimulus`` and two types of ``response``
# events.
#
# Declaring "row events"
# ^^^^^^^^^^^^^^^^^^^^^^
#
# For the sake of this example, we will assume that during analysis our epochs will be
# time-locked to the stimulus onset events. Hence, we would like to create metadata with
# one row per ``stimulus``. We can achieve this by specifying all stimulus event names
# as ``row_events``.

row_events = [
"stimulus/compatible/target_left",
"stimulus/compatible/target_right",
"stimulus/incompatible/target_left",
"stimulus/incompatible/target_right",
]

# %%
# Specifying metadata time windows
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# Now, we will explore different ways of specifying the time windows around the
# ``row_events`` when generating metadata. Any events falling within the same time
# window will be added to the same row in the metadata table.
#
# Fixed time window
# ~~~~~~~~~~~~~~~~~
#
# A simple way to specify the time window extent is by specifying the time in seconds
# relative to the row event. In the following example, the time window spans from the
# row event (time point zero) up until three seconds later.

metadata_tmin = 0.0
metadata_tmax = 3.0

metadata, events, event_id = mne.epochs.make_metadata(
events=all_events,
event_id=all_event_id,
tmin=metadata_tmin,
tmax=metadata_tmax,
sfreq=raw.info["sfreq"],
row_events=row_events,
)

metadata

# %%
# This looks good at the first glance. However, for example in the 2nd and 3rd row, we
# have two responses listed (left and right). This is because the 3-second time window
# is obviously a bit too wide and captures more than one trial. While we could make it
# narrower, this could lead to a loss of events – if the window might become **too**
# narrow. Ultimately, this problem arises because the response time varies from trial
# to trial, so it's difficult for us to set a fixed upper bound for the time window.
#
# Fixed time window with ``keep_first``
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# One workaround is using the ``keep_first`` parameter, which will create a new column
# containing the first event of the specified type.

metadata_tmin = 0.0
metadata_tmax = 3.0
keep_first = "response" # <-- new

metadata, events, event_id = mne.epochs.make_metadata(
events=all_events,
event_id=all_event_id,
tmin=metadata_tmin,
tmax=metadata_tmax,
sfreq=raw.info["sfreq"],
row_events=row_events,
keep_first=keep_first, # <-- new
)

metadata

# %%
# As you can see, a new column ``response`` was created with the time of the first
# response event falling inside the time window. The ``first_response`` column specifies
# **which** response occurred first (left or right).
#
# Variable time window
# ~~~~~~~~~~~~~~~~~~~~
#
# Another way to address the challenge of variable time windows **without** the need to
# create new columns is by specifying ``tmin`` and ``tmax`` as event names. In this
# example, we use ``tmin=row_events``, because we want the time window to start
# with the time-locked event. ``tmax``, on the other hand, are the response events:
# The first response event following ``tmin`` will be used to determine the duration of
# the time window.

metadata_tmin = row_events
metadata_tmax = ["response/left", "response/right"]

metadata, events, event_id = mne.epochs.make_metadata(
events=all_events,
event_id=all_event_id,
tmin=metadata_tmin,
tmax=metadata_tmax,
sfreq=raw.info["sfreq"],
row_events=row_events,
)

metadata

# %%
# Variable time window (simplified)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# We can slightly simplify the above code: Since ``tmin`` shall be set to the
# ``row_events``, we can paass ``tmin=None``, which is a more convenient way to express
# ``tmin=row_events``. The resulting metadata looks the same as in the previous example.

metadata_tmin = None # <-- new
metadata_tmax = ["response/left", "response/right"]

metadata, events, event_id = mne.epochs.make_metadata(
events=all_events,
event_id=all_event_id,
tmin=metadata_tmin,
tmax=metadata_tmax,
sfreq=raw.info["sfreq"],
row_events=row_events,
)

metadata
1 change: 0 additions & 1 deletion examples/preprocessing/xdawn_denoising.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

# %%


from mne import Epochs, compute_raw_covariance, io, pick_types, read_events
from mne.datasets import sample
from mne.preprocessing import Xdawn
Expand Down
1 change: 0 additions & 1 deletion examples/visualization/eyetracking_plot_heatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
# :ref:`example data <eyelink-dataset>`: eye-tracking data recorded from SR research's
# ``'.asc'`` file format.


import matplotlib.pyplot as plt

import mne
Expand Down
Loading

0 comments on commit 93f9dda

Please sign in to comment.