Skip to content

Commit 02b2553

Browse files
Merge branch 'enh-opm-grouping-final-fix' of https://github.com/PragnyaKhandelwal/mne-python into enh-opm-grouping-final-fix
2 parents c24a2ea + a02b69d commit 02b2553

27 files changed

Lines changed: 486 additions & 94 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
It is now possible to concatenate raw objects with :func:`mne.concatenate_raws` as long as they inherit from :class:`~mne.io.BaseRaw`, even if their specific types differ (e.g., :class:`~mne.io.Raw` and :class:`~mne.io.RawArray`), by `Clemens Brunner`_.

doc/changes/dev/13855.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed incorrect label orientation for nodes in the 0–90° polar range (the
2+
12–3 o'clock quadrant) of the connectivity circle plot, by
3+
:newcontrib:`Pavel Popov`.

doc/changes/dev/13856.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an indexing bug in fNIRS support in :meth:`mne.io.BaseRaw.interpolate_bads` (and related methods) that could errantly use incorrect donor channels, by :newcontrib:`Kalle Makela`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add dark theme support (``theme="light"``, ``"dark"``, or ``"auto"``) to the ``'matplotlib'`` browser backend used by :meth:`mne.io.Raw.plot`, :meth:`mne.Epochs.plot`, and :meth:`mne.preprocessing.ICA.plot_sources`, by `Clemens Brunner`_.

doc/changes/names.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
.. _Jukka Nenonen: https://www.linkedin.com/pub/jukka-nenonen/28/b5a/684
173173
.. _Jussi Nurminen: https://github.com/jjnurminen
174174
.. _Kaisu Lankinen: http://bishoplab.berkeley.edu/Kaisu.html
175+
.. _Kalle Makela: https://github.com/Kallemakela
175176
.. _Katarina Slama: https://github.com/katarinaslama
176177
.. _Katia Al-Amir: https://github.com/katia-sentry
177178
.. _Kay Robbins: https://github.com/VisLab
@@ -257,6 +258,7 @@
257258
.. _Paul Pasler: https://github.com/ppasler
258259
.. _Paul Roujansky: https://github.com/paulroujansky
259260
.. _Pavel Navratil: https://github.com/navrpa13
261+
.. _Pavel Popov: https://github.com/paavalipopov
260262
.. _Peter Molfese: https://github.com/pmolfese
261263
.. _Phillip Alday: https://palday.bitbucket.io
262264
.. _Pierre Ablin: https://pierreablin.com

doc/conf.py

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
import mne
2929
import mne.html_templates._templates
30-
from mne.tests.test_docstring_parameters import error_ignores
3130
from mne.utils import (
3231
linkcode_resolve,
3332
run_subprocess,
@@ -432,42 +431,17 @@
432431
"pooch.HTTPDownloader",
433432
}
434433
numpydoc_validate = True
435-
numpydoc_validation_checks = {"all"} | set(error_ignores)
436-
numpydoc_validation_exclude = { # set of regex
437-
# dict subclasses
438-
r"\.clear",
439-
r"\.get$",
440-
r"\.copy$",
441-
r"\.fromkeys",
442-
r"\.items",
443-
r"\.keys",
444-
r"\.move_to_end",
445-
r"\.pop",
446-
r"\.popitem",
447-
r"\.setdefault",
448-
r"\.update",
449-
r"\.values",
450-
# list subclasses
451-
r"\.append",
452-
r"\.count",
453-
r"\.extend",
454-
r"\.index",
455-
r"\.insert",
456-
r"\.remove",
457-
r"\.sort",
458-
# we currently don't document these properly (probably okay)
459-
r"\.__getitem__",
460-
r"\.__contains__",
461-
r"\.__hash__",
462-
r"\.__mul__",
463-
r"\.__sub__",
464-
r"\.__add__",
465-
r"\.__iter__",
466-
r"\.__div__",
467-
r"\.__neg__",
468-
# copied from sklearn
469-
r"mne\.utils\.deprecated",
470-
}
434+
try:
435+
import tomllib
436+
# TODO VERSION: Can be removed once Python 3.11 is required
437+
except Exception:
438+
pass
439+
else:
440+
pyproject_path = Path(__file__).parent.parent / "pyproject.toml"
441+
pyproject = tomllib.loads(pyproject_path.read_text("utf-8"))
442+
pyproject_nv = pyproject["tool"]["numpydoc_validation"]
443+
numpydoc_validation_checks = set(pyproject_nv["checks"])
444+
numpydoc_validation_exclude = set(pyproject_nv["exclude"])
471445

472446

473447
# -- Sphinx-gallery configuration --------------------------------------------

doc/development/contributing.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -607,9 +607,12 @@ dependency
607607
bugfix
608608
For bug fixes. Can change code behavior with no deprecation period.
609609
apichange
610-
Code behavior changes that require a deprecation period.
610+
Changes to existing code behavior that require a deprecation period. Can include the
611+
addition of new features only if existing code behavior is changed alongside this.
611612
newfeature
612-
For new features.
613+
For new features that do not change existing code behavior. If existing code
614+
behavior is changed alongside the addition of these features, the changes should
615+
instead be labelled as ``apichange``.
613616
other
614617
For changes that don't fit into any of the above categories, e.g.,
615618
internal refactorings.

mne/bem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def _check_complete_surface(surf, copy=False, incomplete="raise", extra=""):
314314
fewer = (fewer[:80] + ["..."]) if len(fewer) > 80 else fewer
315315
fewer = ", ".join(str(f) for f in fewer)
316316
msg = (
317-
f"Surface {_bem_surf_name[surf['id']]} has topological defects: "
317+
f"Surface {_bem_surf_name[surf['id']].strip()} has topological defects: "
318318
f"{len(fewer)} / {len(surf['rr'])} vertices have fewer than three "
319319
f"neighboring triangles [{fewer}]{extra}"
320320
)

mne/channels/interpolation.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,15 +278,19 @@ def _interpolate_bads_nirs(inst, exclude=(), verbose=None):
278278
dist = pdist(locs3d)
279279
dist = squareform(dist)
280280

281-
for bad in picks_bad:
282-
dists_to_bad = dist[bad]
281+
for bad_raw_idx in picks_bad:
282+
# `bad_raw_idx` is the index of the bad channel in `inst`
283+
# `bad_dist_idx` is the index of the bad channel in `dist`
284+
bad_dist_idx = np.where(picks_nirs == bad_raw_idx)[0][0]
285+
dists_to_bad = dist[bad_dist_idx].copy()
283286
# Ignore distances to self
284287
dists_to_bad[dists_to_bad == 0] = np.inf
285288
# Ignore distances to other bad channels
286289
dists_to_bad[bads_mask] = np.inf
287290
# Find closest remaining channels for same frequency
288-
closest_idx = np.argmin(dists_to_bad) + (bad % 2)
289-
inst._data[bad] = inst._data[closest_idx]
291+
closest_dist_idx = np.argmin(dists_to_bad) + (bad_dist_idx % 2)
292+
closest_raw_idx = picks_nirs[closest_dist_idx]
293+
inst._data[bad_raw_idx] = inst._data[closest_raw_idx]
290294

291295
# TODO: this seems like a bug because it does not respect reset_bads
292296
inst.info["bads"] = [ch for ch in inst.info["bads"] if ch in exclude]

mne/channels/tests/test_interpolation.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from numpy.testing import assert_allclose, assert_array_equal
1111

1212
import mne.channels.channels
13-
from mne import Epochs, pick_channels, pick_types, read_events
13+
from mne import Epochs, create_info, pick_channels, pick_types, read_events
1414
from mne._fiff.constants import FIFF
1515
from mne._fiff.proj import _has_eeg_average_ref_proj
1616
from mne.channels import make_dig_montage, make_standard_montage
@@ -333,6 +333,44 @@ def test_interpolation_nirs():
333333
assert raw_haemo.info["bads"] == []
334334

335335

336+
def test_interpolation_nirs_reordered_picks():
337+
"""Test NIRS interpolation uses the closest donor in raw channel space."""
338+
ch_names = [
339+
"S1_D1 760",
340+
"S1_D1 850",
341+
"S2_D2 760",
342+
"S2_D2 850",
343+
"S3_D3 760",
344+
"S3_D3 850",
345+
"S10_D10 760",
346+
"S10_D10 850",
347+
]
348+
info = create_info(ch_names, sfreq=1.0, ch_types=["fnirs_cw_amplitude"] * 8)
349+
pair_positions = {
350+
"S1_D1": (0.009, 0.0, 0.0),
351+
"S2_D2": (0.010, 0.0, 0.0),
352+
"S3_D3": (0.030, 0.0, 0.0),
353+
"S10_D10": (0.040, 0.0, 0.0),
354+
}
355+
for idx, ch in enumerate(info["chs"]):
356+
pair = ch["ch_name"].rsplit(" ", 1)[0]
357+
ch["loc"][:3] = pair_positions[pair]
358+
ch["loc"][9] = 760.0 if idx % 2 == 0 else 850.0
359+
data = np.arange(len(ch_names), dtype=float).reshape(-1, 1)
360+
data = np.repeat(data, 5, axis=1)
361+
raw = RawArray(data, info, verbose=False)
362+
raw.info["bads"] = ["S2_D2 760", "S2_D2 850"]
363+
364+
raw.interpolate_bads(
365+
method=dict(fnirs="nearest"), origin=(0.0, 0.0, 0.0), verbose=False
366+
)
367+
368+
# Bad S2_D2 should copy from the nearest good pair, S1_D1.
369+
picks_bad = pick_channels(raw.ch_names, ["S2_D2 760", "S2_D2 850"], exclude=[])
370+
picks_want = pick_channels(raw.ch_names, ["S1_D1 760", "S1_D1 850"], exclude=[])
371+
assert_allclose(raw.get_data(picks=picks_bad), raw.get_data(picks=picks_want))
372+
373+
336374
@testing.requires_testing_data
337375
def test_interpolation_ecog():
338376
"""Test interpolation for ECoG."""

0 commit comments

Comments
 (0)