Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write optical density data in SNIRF format #511

Merged
merged 10 commits into from
May 3, 2023

Conversation

florin-pop
Copy link
Collaborator

@florin-pop florin-pop commented Apr 28, 2023

Fixes #456

Write optical density data in SNIRF format.

Example usage:

# %%
import os
import mne
import snirf

from mne.io import read_raw_nirx, read_raw_snirf
from mne_nirs.io import write_raw_snirf
from mne.preprocessing.nirs import optical_density
from numpy.testing import assert_allclose


# %%
# Import raw NIRS data from vendor
# --------------------------------
#
# First we import some example data recorded with a NIRX device.


fnirs_data_folder = mne.datasets.fnirs_motor.data_path()
fnirs_raw_dir = os.path.join(fnirs_data_folder, 'Participant-1')
raw_intensity = read_raw_nirx(fnirs_raw_dir).load_data()

# %%
# Convert to optical density

raw_od = optical_density(raw_intensity)

# %%
# Write data as SNIRF
# -------------------
#
# Now we can write this data back to disk in the SNIRF format.

write_raw_snirf(raw_od, 'test_raw.snirf')


# %%
# Read back SNIRF file
# --------------------
# 
# Next we can read back the snirf file.

snirf_intensity = read_raw_snirf('test_raw.snirf')


# %%
# Compare files
# -------------
# 
# Finally we can compare the data of the original to the SNIRF format and
# ensure that the values are the same.

assert_allclose(raw_od.get_data(), snirf_intensity.get_data())

snirf_intensity.plot(n_channels=30, duration=300, show_scrollbars=False)


# %%
# Validate SNIRF File
# -------------------
#
# To validate that a file complies with the SNIRF standard you should use the
# official SNIRF validator from the Boston University Neurophotonics Center
# called ``snirf``. Detailed instructions for this program can be found at
# https://github.com/BUNPC/pysnirf2. Below we demonstrate that the files created
# by MNE-NIRS are compliant with the specification.

result = snirf.validateSnirf('test_raw.snirf')
assert result.is_valid()
result.display()

Contributed with ❤️ by AE Studio

@codecov
Copy link

codecov bot commented Apr 28, 2023

Codecov Report

Merging #511 (6d4f625) into main (975b44a) will increase coverage by 0.02%.
The diff coverage is 97.72%.

@@            Coverage Diff             @@
##             main     #511      +/-   ##
==========================================
+ Coverage   95.44%   95.47%   +0.02%     
==========================================
  Files          69       69              
  Lines        2791     2830      +39     
  Branches      400      403       +3     
==========================================
+ Hits         2664     2702      +38     
  Misses         63       63              
- Partials       64       65       +1     
Impacted Files Coverage Δ
mne_nirs/io/snirf/tests/test_snirf.py 97.60% <96.77%> (-0.21%) ⬇️
mne_nirs/io/snirf/_snirf.py 95.94% <100.00%> (+0.14%) ⬆️
mne_nirs/statistics/tests/test_glm_type.py 100.00% <100.00%> (ø)

@rob-luke
Copy link
Member

rob-luke commented May 2, 2023

Great PR @florin-pop. I made some suggestions that I think will simplify the code and slightly boost test coverage. Could you please check if they actually work? And add a changelog entry then ping us for a review/merge. Thanks!

mne_nirs/io/snirf/_snirf.py Outdated Show resolved Hide resolved
mne_nirs/io/snirf/_snirf.py Outdated Show resolved Hide resolved
mne_nirs/io/snirf/_snirf.py Show resolved Hide resolved
mne_nirs/io/snirf/tests/test_snirf.py Show resolved Hide resolved
@florin-pop florin-pop marked this pull request as ready for review May 2, 2023 06:37
@florin-pop
Copy link
Collaborator Author

florin-pop commented May 2, 2023

@larsoner I noticed that after you merged mne-tools/mne-python#11665 this line started failing in CI

with pytest.raises(RuntimeWarning, match='could not be picked'):

Should we replace it with:
with pytest.raises(ValueError, match='could not be picked'):
?

@florin-pop
Copy link
Collaborator Author

I was thinking of doing something like:

    if check_version(mne.__version__, '1.4.0'):
        expected_pick_failure = ValueError
    else:
        expected_pick_failure = Warning
    with pytest.raises(expected_pick_failure, match='could not be picked'):
        assert len(res.copy().pick(picks=["S1_D1 hbr", "S1_D1 XXX"])) == 1

but I'm not sure check_version works as expected

>>> import mne
>>> from mne.utils import check_version
>>> print(mne.__version__)
1.4.0.dev136+g263114e32
>>> print(check_version(mne.__version__, '1.4.0', return_version=True))
(False, None)

@larsoner
Copy link
Member

larsoner commented May 2, 2023

I would use mne.fixes._compare_version since it behaves as you'd expect/need here, and the pattern I usually use is:

if _compare_version(mne.__version__, '>=', '1.4'):
    ctx = pytest.raises(ValueError, match='...')
else:
    ctx = pytest.warns(RuntimeWarning, match='...')
with ctx:
    ...

@rob-luke
Copy link
Member

rob-luke commented May 3, 2023

Tests are green. Thanks @florin-pop @larsoner

@rob-luke rob-luke merged commit 1756f1c into mne-tools:main May 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Write optical density data in SNIRF format
3 participants