Skip to content

Commit

Permalink
Merge branch 'main' into pre-commit-ci-update-config
Browse files Browse the repository at this point in the history
  • Loading branch information
sappelhoff authored Nov 20, 2023
2 parents b0232b4 + 781fc57 commit c0d3201
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 28 deletions.
1 change: 1 addition & 0 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ jobs:
python -c 'import mne_bids; print(mne_bids.__version__)'
python -c 'import nibabel; print(nibabel.__version__)'
python -c 'import pybv; print(pybv.__version__)'
python -c 'import eeglabio; print(eeglabio.__version__)'
python -c 'import pymatreader; print(pymatreader.__version__)'
python -c 'import matplotlib; print(matplotlib.__version__)'
python -c 'import pandas; print(pandas.__version__)'
Expand Down
13 changes: 9 additions & 4 deletions doc/_static/versions.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
[
{
"name": "0.14 (devel)",
"name": "0.15 (devel)",
"version": "dev",
"url": "https://mne.tools/mne-bids/dev/"
},
{
"name": "0.13 (stable)",
"version": "dev",
"name": "0.14 (stable)",
"version": "stable",
"url": "https://mne.tools/mne-bids/v0.14/"
},
{
"name": "0.13",
"version": "0.13",
"url": "https://mne.tools/mne-bids/v0.13/"
},
{
"name": "0.12",
"version": "stable",
"version": "0.12",
"url": "https://mne.tools/mne-bids/v0.12/"
},
{
Expand Down
3 changes: 2 additions & 1 deletion doc/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Optional:

* ``nibabel`` (>=3.2.1, for processing MRI data)
* ``pybv`` (>=0.7.5, for writing BrainVision data)
* ``pymatreader`` (>=0.0.30 , for operations with EEGLAB data)
* ``eeglabio`` (>=0.0.2, for writing EEGLAB data)
* ``pymatreader`` (>=0.0.30, for other operations with EEGLAB data)
* ``matplotlib`` (>=3.4.0, for using the interactive data inspector)
* ``pandas`` (>=1.2.4, for generating event statistics)
* ``EDFlib-Python`` (>=1.0.6, for writing EDF data)
Expand Down
9 changes: 3 additions & 6 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
What's new?
===========

.. _changes_0_14:
.. _changes_0_15:

Version 0.14 (unreleased)
Version 0.15 (unreleased)
-------------------------

👩🏽‍💻 Authors
Expand All @@ -21,7 +21,6 @@ The following authors contributed for the first time. Thank you so much! 🤩

The following authors had contributed before. Thank you for sticking around! 🤘

* `Richard Höchenberger`_
* `Stefan Appelhoff`_

Detailed list of changes
Expand All @@ -45,9 +44,7 @@ Detailed list of changes
🪲 Bug fixes
^^^^^^^^^^^^

- Fix reading when the channel order differs between ``*_channels.tsv`` and the raw data file, which would previously throw an error, by `Richard Höchenberger`_ (:gh:`1171`)
- Fix bug with writing crosstalk and calibration files when subject is ``"emptyroom"``, by `Eric Larson`_ (:gh:`1189`)
- Make ``recording`` entity available for :func:`mne_bids.get_entity_vals`, by `Stefan Appelhoff`_ (:gh:`1182`)
- nothing yet

:doc:`Find out what was new in previous releases <whats_new_previous_releases>`

Expand Down
31 changes: 31 additions & 0 deletions doc/whats_new_previous_releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@
What was new in previous releases?
==================================

.. _changes_0_14:

Version 0.14 (2023-11-16)
-------------------------

👩🏽‍💻 Authors
~~~~~~~~~~~~~~~

The following authors contributed:

* `Eric Larson`_
* `Laetitia Fesselier`_
* `Mathieu Scheltienne`_
* `Richard Höchenberger`_
* `Stefan Appelhoff`_

Detailed list of changes
~~~~~~~~~~~~~~~~~~~~~~~~

🚀 Enhancements
^^^^^^^^^^^^^^^

- Enable exporting to the EEGLAB data format (``.set``), by `Laetitia Fesselier`_ and `Stefan Appelhoff`_ (:gh:`1187`)

🪲 Bug fixes
^^^^^^^^^^^^

- Fix reading when the channel order differs between ``*_channels.tsv`` and the raw data file, which would previously throw an error, by `Richard Höchenberger`_ (:gh:`1171`)
- Fix bug with writing crosstalk and calibration files when subject is ``"emptyroom"``, by `Eric Larson`_ (:gh:`1189`)
- Make ``recording`` entity available for :func:`mne_bids.get_entity_vals`, by `Stefan Appelhoff`_ (:gh:`1182`)

.. _changes_0_13:

Version 0.13 (2023-08-21)
Expand Down
2 changes: 1 addition & 1 deletion mne_bids/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""MNE software for easily interacting with BIDS compatible datasets."""

__version__ = "0.14.dev0"
__version__ = "0.15.dev0"
from mne_bids import commands
from mne_bids.report import make_report
from mne_bids.path import (
Expand Down
1 change: 1 addition & 0 deletions mne_bids/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
BIDS_VERSION = "1.7.0"

PYBV_VERSION = "0.7.3"
EEGLABIO_VERSION = "0.0.2"

DOI = """https://doi.org/10.21105/joss.01896"""

Expand Down
8 changes: 4 additions & 4 deletions mne_bids/tests/test_dig.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def test_dig_pixels(tmp_path):
op.join(bids_root, "sub-01", "ses-01", bids_path.datatype), exist_ok=True
)
raw = _load_raw()
raw.pick_types(eeg=True)
raw.pick(["eeg"])
raw.del_proj()
raw.set_channel_types({ch: "ecog" for ch in raw.ch_names})

Expand Down Expand Up @@ -145,7 +145,7 @@ def test_dig_template(tmp_path):
bids_path = _bids_path.copy().update(root=bids_root, datatype=datatype)
for coord_frame in BIDS_STANDARD_TEMPLATE_COORDINATE_SYSTEMS:
raw = _load_raw()
raw.pick_types(eeg=True)
raw.pick(["eeg"])
bids_path.update(space=coord_frame)
montage = raw.get_montage()
pos = montage.get_positions()
Expand Down Expand Up @@ -251,7 +251,7 @@ def test_template_to_head():
# test all coordinate frames
raw = _load_raw()
raw.set_montage(None)
raw.pick_types(eeg=True)
raw.pick(["eeg"])
raw.drop_channels(raw.ch_names[3:])
montage = mne.channels.make_dig_montage(
ch_pos={
Expand Down Expand Up @@ -365,7 +365,7 @@ def test_convert_montage():
def test_electrodes_io(tmp_path):
"""Ensure only electrodes end up in *_electrodes.json."""
raw = _load_raw()
raw.pick_types(eeg=True, stim=True) # we don't need meg channels
raw.pick(["eeg", "stim"]) # we don't need meg channels
bids_root = tmp_path / "bids1"
bids_path = _bids_path.copy().update(root=bids_root, datatype="eeg")
write_raw_bids(raw=raw, bids_path=bids_path)
Expand Down
33 changes: 26 additions & 7 deletions mne_bids/tests/test_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#
# License: BSD-3-Clause
import sys
import inspect
import os
import os.path as op
from glob import glob
Expand Down Expand Up @@ -58,7 +59,12 @@
from mne_bids.sidecar_updates import _update_sidecar, update_sidecar_json
from mne_bids.path import _find_matching_sidecar, _parse_ext
from mne_bids.pick import coil_type
from mne_bids.config import REFERENCES, BIDS_COORD_FRAME_DESCRIPTIONS, PYBV_VERSION
from mne_bids.config import (
REFERENCES,
BIDS_COORD_FRAME_DESCRIPTIONS,
PYBV_VERSION,
EEGLABIO_VERSION,
)

base_path = op.join(op.dirname(mne.__file__), "io")
subject_id = "01"
Expand Down Expand Up @@ -147,6 +153,7 @@ def fn(fname, *args, **kwargs):

# parametrization for testing converting file formats for EEG/iEEG
test_converteeg_data = [
("EEGLAB", "EEGLAB", "test_raw.set", _read_raw_eeglab), # noqa
(
"Persyst",
"BrainVision",
Expand Down Expand Up @@ -563,7 +570,7 @@ def test_fif(_bids_validate, tmp_path):
bids_path.update(root=bids_root)
raw = _read_raw_fif(raw_fname)
raw.load_data()
raw2 = raw.pick_types(meg=False, eeg=True, stim=True, eog=True, ecg=True)
raw2 = raw.pick(["eeg", "stim", "eog", "ecg"])
raw2.save(bids_root / "test-raw.fif", overwrite=True)
raw2 = mne.io.Raw(op.join(bids_root, "test-raw.fif"), preload=False)
events = mne.find_events(raw2)
Expand Down Expand Up @@ -617,7 +624,15 @@ def test_fif(_bids_validate, tmp_path):
raw2, events2, event_id=event_id, tmin=-0.2, tmax=0.5, preload=True
)
assert_array_almost_equal(raw.get_data(), raw2.get_data())
assert_array_almost_equal(epochs.get_data(), epochs2.get_data(), decimal=4)
kwargs = dict()
# XXX: remove logic once support for mne<1.6 is dropped
if "copy" in inspect.getfullargspec(epochs.get_data).kwonlyargs:
kwargs["copy"] = False
assert_array_almost_equal(
epochs.get_data(**kwargs),
epochs2.get_data(**kwargs),
decimal=4,
)
_bids_validate(bids_root)

# write the same data but pretend it is empty room data:
Expand Down Expand Up @@ -3319,6 +3334,7 @@ def test_sidecar_encoding(_bids_validate, tmp_path):
def test_convert_eeg_formats(dir_name, format, fname, reader, tmp_path):
"""Test conversion of EEG/iEEG manufacturer fmt to BrainVision/EDF."""
pytest.importorskip("pybv", PYBV_VERSION)
pytest.importorskip("eeglabio", EEGLABIO_VERSION)
bids_root = tmp_path / format
raw_fname = data_path / dir_name / fname

Expand All @@ -3327,7 +3343,7 @@ def test_convert_eeg_formats(dir_name, format, fname, reader, tmp_path):

raw = reader(raw_fname)
# drop 'misc' type channels when exporting
raw = raw.pick_types(eeg=True)
raw = raw.pick(["eeg"])
kwargs = dict(
raw=raw, format=format, bids_path=bids_path, overwrite=True, verbose=False
)
Expand Down Expand Up @@ -3357,11 +3373,13 @@ def test_convert_eeg_formats(dir_name, format, fname, reader, tmp_path):
):
bids_output_path = write_raw_bids(**kwargs)
else:
with pytest.warns(RuntimeWarning, match="Converting data files to EDF format"):
with pytest.warns(
RuntimeWarning, match=f"Converting data files to {format} format"
):
bids_output_path = write_raw_bids(**kwargs)

# channel units should stay the same
raw2 = read_raw_bids(bids_output_path)
raw2 = read_raw_bids(bids_output_path, extra_params=dict(preload=True))
assert all(
[
ch1["unit"] == ch2["unit"]
Expand Down Expand Up @@ -3404,6 +3422,7 @@ def test_convert_eeg_formats(dir_name, format, fname, reader, tmp_path):
def test_format_conversion_overwrite(dir_name, format, fname, reader, tmp_path):
"""Test that overwrite works when format is passed to write_raw_bids."""
pytest.importorskip("pybv", PYBV_VERSION)
pytest.importorskip("eeglabio", EEGLABIO_VERSION)
bids_root = tmp_path / format
raw_fname = data_path / dir_name / fname

Expand All @@ -3412,7 +3431,7 @@ def test_format_conversion_overwrite(dir_name, format, fname, reader, tmp_path):

raw = reader(raw_fname)
# drop 'misc' type channels when exporting
raw = raw.pick_types(eeg=True)
raw = raw.pick(["eeg"])
kwargs = dict(raw=raw, format=format, bids_path=bids_path, verbose=False)

with warnings.catch_warnings():
Expand Down
34 changes: 29 additions & 5 deletions mne_bids/write.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,22 @@ def _write_raw_edf(raw, bids_fname, overwrite):
raw.export(bids_fname, overwrite=overwrite)


def _write_raw_eeglab(raw, bids_fname, overwrite):
"""Store data as EEGLAB.
Parameters
----------
raw : mne.io.Raw
Raw data to save.
bids_fname : str
The output filename.
overwrite : bool
Whether to overwrite an existing file or not.
"""
assert str(bids_fname).endswith(".set")
raw.export(bids_fname, overwrite=overwrite)


@verbose
def make_dataset_description(
*,
Expand Down Expand Up @@ -1467,14 +1483,14 @@ def write_raw_bids(
``source`` column of ``scans.tsv``. By default, this information
is not stored.
format : 'auto' | 'BrainVision' | 'EDF' | 'FIF'
format : 'auto' | 'BrainVision' | 'EDF' | 'FIF' | 'EEGLAB'
Controls the file format of the data after BIDS conversion. If
``'auto'``, MNE-BIDS will attempt to convert the input data to BIDS
without a change of the original file format. A conversion to a
different file format (BrainVision, EDF, or FIF) will only take place
when the original file format lacks some necessary features. Conversion
can be forced to BrainVision or EDF for (i)EEG, and to FIF for MEG
data.
different file format will then only take place if the original file
format lacks some necessary features.
Conversion may be forced to BrainVision, EDF, or EEGLAB for (i)EEG,
and to FIF for MEG data.
symlink : bool
Instead of copying the source files, only create symbolic links to
preserve storage space. This is only allowed when not anonymizing the
Expand Down Expand Up @@ -1716,6 +1732,8 @@ def write_raw_bids(
ext = ".vhdr"
elif format == "EDF":
ext = ".edf"
elif format == "EEGLAB":
ext = ".set"
elif format == "FIF":
ext = ".fif"
else:
Expand Down Expand Up @@ -2029,6 +2047,9 @@ def write_raw_bids(
elif format == "EDF" and bids_path.datatype in ["ieeg", "eeg"]:
convert = True
bids_path.update(extension=".edf")
elif format == "EEGLAB" and bids_path.datatype in ["ieeg", "eeg"]:
convert = True
bids_path.update(extension=".set")
elif format == "FIF" and bids_path.datatype == "meg":
convert = True
bids_path.update(extension=".fif")
Expand Down Expand Up @@ -2089,6 +2110,9 @@ def write_raw_bids(
elif bids_path.datatype in ["eeg", "ieeg"] and format == "EDF":
warn("Converting data files to EDF format")
_write_raw_edf(raw, bids_path.fpath, overwrite=overwrite)
elif bids_path.datatype in ["eeg", "ieeg"] and format == "EEGLAB":
warn("Converting data files to EEGLAB format")
_write_raw_eeglab(raw, bids_path.fpath, overwrite=overwrite)
else:
warn("Converting data files to BrainVision format")
bids_path.update(suffix=bids_path.datatype, extension=".vhdr")
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ include_package_data = True
full =
nibabel >= 3.2.1
pybv >= 0.7.5
eeglabio >= 0.0.2
pymatreader >= 0.0.30
matplotlib >= 3.4.0
pandas >= 1.2.4
Expand Down
1 change: 1 addition & 0 deletions test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ numpy>=1.20.2
scipy>=1.6.3
nibabel>=3.2.1
pybv>=0.7.5
eeglabio>=0.0.2
pymatreader>=0.0.30
matplotlib>=3.4.0
pandas>=1.2.4
Expand Down

0 comments on commit c0d3201

Please sign in to comment.