Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream' into metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
hoechenberger committed Oct 30, 2023
2 parents bdc9b9f + 3add8f8 commit 626aede
Show file tree
Hide file tree
Showing 28 changed files with 99 additions and 57 deletions.
8 changes: 0 additions & 8 deletions .lgtm.yml

This file was deleted.

2 changes: 2 additions & 0 deletions doc/changes/devel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Enhancements
- Add support for writing forward solutions to HDF5 and convenience function :meth:`mne.Forward.save` (:gh:`12036` by `Eric Larson`_)
- Refactored internals of :func:`mne.read_annotations` (:gh:`11964` by `Paul Roujansky`_)
- Add support for drawing MEG sensors in :ref:`mne coreg` (:gh:`12098` by `Eric Larson`_)
- Bad channels are now colored gray in addition to being dashed when spatial colors are used in :func:`mne.viz.plot_evoked` and related functions (:gh:`12142` by `Eric Larson`_)
- By default MNE-Python creates matplotlib figures with ``layout='constrained'`` rather than the default ``layout='tight'`` (:gh:`12050`, :gh:`12103` by `Mathieu Scheltienne`_ and `Eric Larson`_)
- Enhance :func:`~mne.viz.plot_evoked_field` with a GUI that has controls for time, colormap, and contour lines (:gh:`11942` by `Marijn van Vliet`_)
- Add :class:`mne.viz.ui_events.UIEvent` linking for interactive colorbars, allowing users to link figures and change the colormap and limits interactively. This supports :func:`~mne.viz.plot_evoked_topomap`, :func:`~mne.viz.plot_ica_components`, :func:`~mne.viz.plot_tfr_topomap`, :func:`~mne.viz.plot_projs_topomap`, :meth:`~mne.Evoked.plot_image`, and :meth:`~mne.Epochs.plot_image` (:gh:`12057` by `Santeri Ruuskanen`_)
Expand Down Expand Up @@ -72,6 +73,7 @@ Bugs
- Fix :func:`~mne.viz.plot_volume_source_estimates` with :class:`~mne.VolSourceEstimate` which include a list of vertices (:gh:`12025` by `Mathieu Scheltienne`_)
- Add support for non-ASCII characters in Annotations, Evoked comments, etc when saving to FIFF format (:gh:`12080` by `Daniel McCloy`_)
- Correctly handle passing ``"eyegaze"`` or ``"pupil"`` to :meth:`mne.io.Raw.pick` (:gh:`12019` by `Scott Huberty`_)
- Fix bug with :func:`mne.time_frequency.Spectrum.plot` and related functions where bad channels were not marked (:gh:`12142` by `Eric Larson`_)
- Fix bug with :func:`~mne.viz.plot_raw` where changing ``MNE_BROWSER_BACKEND`` via :func:`~mne.set_config` would have no effect within a Python session (:gh:`12078` by `Santeri Ruuskanen`_)
- Improve handling of ``method`` argument in the channel interpolation function to support :class:`str` and raise helpful error messages (:gh:`12113` by `Mathieu Scheltienne`_)
- Fix combination of ``DIN`` event channels into a single synthetic trigger channel ``STI 014`` by the MFF reader of :func:`mne.io.read_raw_egi` (:gh:`12122` by `Mathieu Scheltienne`_)
Expand Down
2 changes: 1 addition & 1 deletion doc/development/governance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ BDFL
----

The Project will have a BDFL (Benevolent Dictator for Life), who is currently
Alexandre Gramfort. As Dictator, the BDFL has the authority to make all final
Daniel McCloy. As Dictator, the BDFL has the authority to make all final
decisions for The Project. As Benevolent, the BDFL, in practice, chooses to
defer that authority to the consensus of the community discussion channels and
the Steering Council (see below). It is expected, and in the past has been the
Expand Down
2 changes: 0 additions & 2 deletions doc/overview/people.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ Steering Council
* `Daniel McCloy`_
* `Denis Engemann`_
* `Eric Larson`_
* `Guillaume Favelier`_
* `Luke Bloy`_
* `Mainak Jas`_
* `Marijn van Vliet`_
* `Mathieu Scheltienne`_
Expand Down
2 changes: 1 addition & 1 deletion examples/preprocessing/eeg_bridging.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
electrode connects with the gel conducting signal from another electrode
"bridging" the two signals. This is undesirable because the signals from the
two (or more) electrodes are not as independent as they would otherwise be;
they are very similar to each other introducting additional
they are very similar to each other introducing additional
spatial smearing. An algorithm has been developed to detect electrode
bridging :footcite:`TenkeKayser2001`, which has been implemented in EEGLAB
:footcite:`DelormeMakeig2004`. Unfortunately, there is not a lot to be
Expand Down
7 changes: 1 addition & 6 deletions mne/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ def pytest_configure(config):
# Fixtures
for fixture in (
"matplotlib_config",
"close_all",
"check_verbose",
"qt_config",
"protect_config",
):
Expand Down Expand Up @@ -268,10 +266,7 @@ def matplotlib_config():
# functionality)
plt.ioff()
plt.rcParams["figure.dpi"] = 100
try:
plt.rcParams["figure.raise_window"] = False
except KeyError: # MPL < 3.3
pass
plt.rcParams["figure.raise_window"] = False

# Make sure that we always reraise exceptions in handlers
orig = cbook.CallbackRegistry
Expand Down
3 changes: 2 additions & 1 deletion mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4114,12 +4114,13 @@ def _concatenate_epochs(
event_id = deepcopy(out.event_id)
selection = out.selection
# offset is the last epoch + tmax + 10 second
shift = int((10 + tmax) * out.info["sfreq"])
shift = np.int64((10 + tmax) * out.info["sfreq"])
# Allow reading empty epochs (ToDo: Maybe not anymore in the future)
if out._allow_empty:
events_offset = 0
else:
events_offset = int(np.max(events[0][:, 0])) + shift
events_offset = np.int64(events_offset)
events_overflow = False
warned = False
for ii, epochs in enumerate(epochs_list[1:], 1):
Expand Down
12 changes: 9 additions & 3 deletions mne/io/bti/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ def read_int8(fid):

def read_uint16(fid):
"""Read unsigned 16bit integer from bti file."""
return _unpack_simple(fid, ">u2", np.uint16)
return _unpack_simple(fid, ">u2", np.uint32)


def read_int16(fid):
"""Read 16bit integer from bti file."""
return _unpack_simple(fid, ">i2", np.int16)
return _unpack_simple(fid, ">i2", np.int32)


def read_uint32(fid):
Expand Down Expand Up @@ -88,7 +88,13 @@ def read_double(fid):

def read_int16_matrix(fid, rows, cols):
"""Read 16bit integer matrix from bti file."""
return _unpack_matrix(fid, rows, cols, dtype=">i2", out_dtype=np.int16)
return _unpack_matrix(
fid,
rows,
cols,
dtype=">i2",
out_dtype=np.int32,
)


def read_float_matrix(fid, rows, cols):
Expand Down
17 changes: 16 additions & 1 deletion mne/io/ctf/res4.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def _read_ustring(fid, n_bytes):

def _read_int2(fid):
"""Read int from short."""
return np.fromfile(fid, ">i2", 1)[0]
return _auto_cast(np.fromfile(fid, ">i2", 1)[0])


def _read_int(fid):
Expand Down Expand Up @@ -208,6 +208,9 @@ def _read_res4(dsdir):
coil["area"] *= 1e-4
# convert to dict
chs = [dict(zip(chs.dtype.names, x)) for x in chs]
for ch in chs:
for key, val in ch.items():
ch[key] = _auto_cast(val)
res["chs"] = chs
for k in range(res["nchan"]):
res["chs"][k]["ch_name"] = res["ch_names"][k]
Expand All @@ -216,3 +219,15 @@ def _read_res4(dsdir):
_read_comp_coeff(fid, res)
logger.info(" res4 data read.")
return res


def _auto_cast(x):
# Upcast scalars
if isinstance(x, np.ScalarType):
if x.dtype.kind == "i":
if x.dtype != np.int64:
x = x.astype(np.int64)
elif x.dtype.kind == "f":
if x.dtype != np.float64:
x = x.astype(np.float64)
return x
6 changes: 3 additions & 3 deletions mne/io/edf/edf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ def _read_gdf_header(fname, exclude, include=None):
"Header information is incorrect for record length. "
"Default record length set to 1."
)
nchan = np.fromfile(fid, UINT32, 1)[0]
nchan = int(np.fromfile(fid, UINT32, 1)[0])
channels = list(range(nchan))
ch_names = [_edf_str(fid.read(16)).strip() for ch in channels]
exclude = _find_exclude_idx(ch_names, exclude, include)
Expand Down Expand Up @@ -1177,7 +1177,7 @@ def _read_gdf_header(fname, exclude, include=None):
fid.seek(etp)
etmode = np.fromfile(fid, UINT8, 1)[0]
if etmode in (1, 3):
sr = np.fromfile(fid, UINT8, 3)
sr = np.fromfile(fid, UINT8, 3).astype(np.uint32)
event_sr = sr[0]
for i in range(1, len(sr)):
event_sr = event_sr + sr[i] * 2 ** (i * 8)
Expand Down Expand Up @@ -1297,7 +1297,7 @@ def _read_gdf_header(fname, exclude, include=None):
"Default record length set to 1."
)

nchan = np.fromfile(fid, UINT16, 1)[0]
nchan = int(np.fromfile(fid, UINT16, 1)[0])
fid.seek(2, 1) # 2bytes reserved

# Channels (variable header)
Expand Down
6 changes: 3 additions & 3 deletions mne/io/egi/egi.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _read_header(fid):
)

def my_fread(*x, **y):
return np.fromfile(*x, **y)[0]
return int(np.fromfile(*x, **y)[0])

info = dict(
version=version,
Expand Down Expand Up @@ -57,8 +57,8 @@ def my_fread(*x, **y):
dict(
n_categories=0,
n_segments=1,
n_samples=np.fromfile(fid, ">i4", 1)[0],
n_events=np.fromfile(fid, ">i2", 1)[0],
n_samples=int(np.fromfile(fid, ">i4", 1)[0]),
n_events=int(np.fromfile(fid, ">i2", 1)[0]),
event_codes=[],
category_names=[],
category_lengths=[],
Expand Down
2 changes: 1 addition & 1 deletion mne/io/egi/egimff.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _read_mff_header(filepath):
# by what we need to (e.g., a sample rate of 500 means we can multiply
# by 1 and divide by 2 rather than multiplying by 500 and dividing by
# 1000)
numerator = signal_blocks["sfreq"]
numerator = int(signal_blocks["sfreq"])
denominator = 1000
this_gcd = math.gcd(numerator, denominator)
numerator = numerator // this_gcd
Expand Down
8 changes: 4 additions & 4 deletions mne/io/nihon/nihon.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def _read_nihon_header(fname):
t_datablock["address"] = t_data_address

fid.seek(t_data_address + 0x26)
t_n_channels = np.fromfile(fid, np.uint8, 1)[0]
t_n_channels = np.fromfile(fid, np.uint8, 1)[0].astype(np.int64)
t_datablock["n_channels"] = t_n_channels

t_channels = []
Expand All @@ -219,14 +219,14 @@ def _read_nihon_header(fname):
t_datablock["channels"] = t_channels

fid.seek(t_data_address + 0x1C)
t_record_duration = np.fromfile(fid, np.uint32, 1)[0]
t_record_duration = np.fromfile(fid, np.uint32, 1)[0].astype(np.int64)
t_datablock["duration"] = t_record_duration

fid.seek(t_data_address + 0x1A)
sfreq = np.fromfile(fid, np.uint16, 1)[0] & 0x3FFF
t_datablock["sfreq"] = sfreq
t_datablock["sfreq"] = sfreq.astype(np.int64)

t_datablock["n_samples"] = int(t_record_duration * sfreq / 10)
t_datablock["n_samples"] = np.int64(t_record_duration * sfreq // 10)
t_controlblock["datablocks"].append(t_datablock)
controlblocks.append(t_controlblock)
header["controlblocks"] = controlblocks
Expand Down
2 changes: 1 addition & 1 deletion mne/io/nsx/nsx.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def _get_hdr_info(fname, stim_channel=True, eog=None, misc=None):

stim_channel_idxs, _ = _check_stim_channel(stim_channel, ch_names)

nchan = nsx_info["channel_count"]
nchan = int(nsx_info["channel_count"])
logger.info("Setting channel info structure...")
chs = list()
pick_mask = np.ones(len(ch_names))
Expand Down
13 changes: 7 additions & 6 deletions mne/preprocessing/realign.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ def realign_raw(raw, other, t_raw, t_other, verbose=None):
The second raw instance. It will be resampled to match ``raw``.
t_raw : array-like, shape (n_events,)
The times of shared events in ``raw`` relative to ``raw.times[0]`` (0).
Typically these could be events on some TTL channel like
``find_events(raw)[:, 0] - raw.first_samp``.
Typically these could be events on some TTL channel such as::
find_events(raw)[:, 0] / raw.info["sfreq"] - raw.first_time
t_other : array-like, shape (n_events,)
The times of shared events in ``other`` relative to ``other.times[0]``.
%(verbose)s
Expand Down Expand Up @@ -92,11 +93,11 @@ def realign_raw(raw, other, t_raw, t_other, verbose=None):
logger.info(f"Cropping {zero_ord:0.3f} s from the start of raw")
raw.crop(zero_ord, None)
t_raw -= zero_ord
else: # need to crop start of other to match raw
t_crop = zero_ord / first_ord
elif zero_ord < 0: # need to crop start of other to match raw
t_crop = -zero_ord / first_ord
logger.info(f"Cropping {t_crop:0.3f} s from the start of other")
other.crop(-t_crop, None)
t_other += t_crop
other.crop(t_crop, None)
t_other -= t_crop

# 3. Resample data using the first-order term
nan_ch_names = [
Expand Down
2 changes: 2 additions & 0 deletions mne/report/tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,8 @@ def test_survive_pickle(tmp_path):
def test_manual_report_2d(tmp_path, invisible_fig):
"""Simulate user manually creating report by adding one file at a time."""
pytest.importorskip("sklearn")
pytest.importorskip("pandas")

from sklearn.exceptions import ConvergenceWarning

r = Report(title="My Report")
Expand Down
2 changes: 1 addition & 1 deletion mne/surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ def _call_old(self, rr, n_jobs):

def _fread3(fobj):
"""Read 3 bytes and adjust."""
b1, b2, b3 = np.fromfile(fobj, ">u1", 3)
b1, b2, b3 = np.fromfile(fobj, ">u1", 3).astype(np.int64)
return (b1 << 16) + (b2 << 8) + b3


Expand Down
14 changes: 11 additions & 3 deletions mne/time_frequency/tests/test_spectrum.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from contextlib import nullcontext
from functools import partial

import matplotlib.pyplot as plt
import numpy as np
import pytest
from matplotlib.colors import same_color
from numpy.testing import assert_allclose, assert_array_equal

from mne import Annotations, create_info, make_fixed_length_epochs
Expand Down Expand Up @@ -449,8 +449,16 @@ def test_plot_spectrum(kind, array, request):
data, freqs = spectrum.get_data(return_freqs=True)
Klass = SpectrumArray if kind == "raw" else EpochsSpectrumArray
spectrum = Klass(data=data, info=spectrum.info, freqs=freqs)
spectrum.info["bads"] = spectrum.ch_names[:1] # one grad channel
spectrum.plot(average=True, amplitude=True, spatial_colors=True)
spectrum.plot(average=False, amplitude=False, spatial_colors=False)
spectrum.plot(average=True, amplitude=False, spatial_colors=False)
n_grad = sum(ch_type == "grad" for ch_type in spectrum.get_channel_types())
for amp, sc in ((True, True), (False, False)):
fig = spectrum.plot(average=False, amplitude=amp, spatial_colors=sc, exclude=())
lines = fig.axes[0].lines[2:] # grads, ignore two vlines
assert len(lines) == n_grad
bad_color = "0.5" if sc else "r"
n_bad = sum(same_color(line.get_color(), bad_color) for line in lines)
assert n_bad == 1
spectrum.plot_topo()
spectrum.plot_topomap()
plt.close("all")
3 changes: 1 addition & 2 deletions mne/utils/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import os
import re
from builtins import input # no-op here but facilitates testing
from collections.abc import Sequence
from difflib import get_close_matches
from importlib import import_module
from importlib.metadata import version
Expand Down Expand Up @@ -542,7 +541,7 @@ def __instancecheck__(cls, other):
"path-like": path_like,
"int-like": (int_like,),
"callable": (_Callable(),),
"array-like": (Sequence, np.ndarray),
"array-like": (list, tuple, set, np.ndarray),
}


Expand Down
1 change: 1 addition & 0 deletions mne/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def set_memmap_min_size(memmap_min_size):
"MNE_DATASETS_FNIRS", # mne-nirs
"MNE_NIRS", # mne-nirs
"MNE_KIT2FIFF", # mne-kit-gui
"MNE_ICALABEL", # mne-icalabel
)


Expand Down
5 changes: 5 additions & 0 deletions mne/utils/tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ def test_validate_type():
_validate_type(1, "int-like")
with pytest.raises(TypeError, match="int-like"):
_validate_type(False, "int-like")
_validate_type([1, 2, 3], "array-like")
_validate_type((1, 2, 3), "array-like")
_validate_type({1, 2, 3}, "array-like")
with pytest.raises(TypeError, match="array-like"):
_validate_type("123", "array-like") # a string is not array-like


def test_check_range():
Expand Down
8 changes: 5 additions & 3 deletions mne/viz/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,15 +679,17 @@ def _plot_lines(
_handle_spatial_colors(
colors, info, idx, this_type, psd, ax, sphere
)
bad_color = (0.5, 0.5, 0.5)
else:
if isinstance(_spat_col, (tuple, str)):
col = [_spat_col]
else:
col = ["k"]
bad_color = "r"
colors = col * len(idx)
for i in bad_ch_idx:
if i in idx:
colors[idx.index(i)] = "r"
for i in bad_ch_idx:
if i in idx:
colors[idx.index(i)] = bad_color

if zorder == "std":
# find the channels with the least activity
Expand Down
7 changes: 7 additions & 0 deletions mne/viz/tests/test_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import pytest
from matplotlib import gridspec
from matplotlib.collections import PolyCollection
from matplotlib.colors import same_color
from mpl_toolkits.axes_grid1.parasite_axes import HostAxes # spatial_colors
from numpy.testing import assert_allclose

Expand Down Expand Up @@ -134,6 +135,12 @@ def test_plot_evoked():
amplitudes = _get_amplitudes(fig)
assert len(amplitudes) == len(default_picks)
assert evoked.proj is False
assert evoked.info["bads"] == ["MEG 2641", "EEG 004"]
eeg_lines = fig.axes[2].lines
n_eeg = sum(ch_type == "eeg" for ch_type in evoked.get_channel_types())
assert len(eeg_lines) == n_eeg == 4
n_bad = sum(same_color(line.get_color(), "0.5") for line in eeg_lines)
assert n_bad == 1
# Test a click
ax = fig.get_axes()[0]
line = ax.lines[0]
Expand Down
Loading

0 comments on commit 626aede

Please sign in to comment.