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

MRG: Add sessions as official suffix #1137

Merged
merged 16 commits into from May 27, 2023
Merged
2 changes: 1 addition & 1 deletion mne_bids/config.py
Expand Up @@ -140,7 +140,7 @@
# the extension)
ALLOWED_FILENAME_SUFFIX = [
'meg', 'markers', 'eeg', 'ieeg', 'T1w', 'FLASH', # datatype
'participants', 'scans',
'participants', 'scans', 'sessions',
'electrodes', 'optodes', 'channels', 'coordsystem', 'events', # sidecars
'headshape', 'digitizer', # meg-specific sidecars
'beh', 'physio', 'stim', # behavioral
Expand Down
27 changes: 27 additions & 0 deletions mne_bids/tests/data/tiny_bids/code/make_tiny_bids_dataset.py
Expand Up @@ -106,3 +106,30 @@
with open(dataset_description_json_path, "w", encoding="utf-8") as fout:
json.dump(ds_json, fout, indent=4)
fout.write("\n")

# %%
# Add *_sessions.tsv and *_sessions.json
sessions_tsv_bp = BIDSPath(
subject="01", suffix="sessions", extension=".tsv", root=tiny_bids_root
)
sessions_tsv_content = """session_id acq_time pathology
ses-eeg 2000-01-01T12:00:00.000000Z Parkinson's disease
"""

sessions_tsv_bp.fpath.write_text(sessions_tsv_content, encoding="utf-8")

sessions_json_bp = sessions_tsv_bp.copy().update(extension=".json")
sessions_json_content = """{
"acq_time":{
"Description":"date of the first acquistion of the session",
"Units":"date",
"TermURL":"https://tools.ietf.org/html/rfc3339#section-5.6"
},
"pathology":{
"Description":"Disease state of the subject when applicable",
}
}
"""
sessions_json_bp.fpath.write_text(sessions_json_content, encoding="utf-8")

# %%
8 changes: 8 additions & 0 deletions mne_bids/tests/data/tiny_bids/participants.json
Expand Up @@ -20,5 +20,13 @@
"L": "left",
"A": "ambidextrous"
}
},
"weight": {
"Description": "Body weight of the participant",
"Units": "kg"
},
"height": {
"Description": "Body height of the participant",
"Units": "m"
}
}
4 changes: 2 additions & 2 deletions mne_bids/tests/data/tiny_bids/participants.tsv
@@ -1,2 +1,2 @@
participant_id age sex hand
sub-01 29 F A
participant_id age sex hand weight height
sub-01 29 F A n/a n/a
@@ -0,0 +1,24 @@
{
"EEGCoordinateSystem": "CapTrak",
"EEGCoordinateUnits": "m",
"EEGCoordinateSystemDescription": "The X-axis goes from the left preauricular point (LPA) through the right preauricular point (RPA). The Y-axis goes orthogonally to the X-axis through the nasion (NAS). The Z-axis goes orthogonally to the XY-plane through the vertex of the head. This corresponds to a \"RAS\" orientation with the origin of the coordinate system approximately between the ears. See Appendix VIII in the BIDS specification.",
"AnatomicalLandmarkCoordinates": {
"NAS": [
-3.788238398497924e-18,
0.11309931478694205,
-3.0814879110195774e-33
],
"LPA": [
-0.09189697162389295,
3.078070254157709e-18,
0.0
],
"RPA": [
0.09240077493980713,
-3.094945043100789e-18,
-6.162975822039155e-33
]
},
"AnatomicalLandmarkCoordinateSystem": "CapTrak",
"AnatomicalLandmarkCoordinateUnits": "m"
}
@@ -0,0 +1,70 @@
name x y z impedance
Fp1 -0.03741270038437644 0.11102138145885607 0.04987088609474466 5.0
Fp2 0.02485529277372901 0.11470061734309513 0.051792653682490544 2.0
F7 -0.08409763256980056 0.0651383327975854 0.04885693263757247 4.0
F3 -0.06033321416227938 0.07429454777471324 0.1003425150886266 1.0
Fz -0.004358766283031327 0.08568143210491122 0.12205334790141567 5.0
F4 0.054248614498413714 0.08039407987521177 0.09908756593625902 0.0
F8 0.07985344457502894 0.06590997733368428 0.047504910890930334 0.0
FC5 -0.08856262271830136 0.03656538257252212 0.08223925927400842 0.0
FC1 -0.04102029132824396 0.04689188172302073 0.13593463220555657 5.0
FC2 0.03154947116645066 0.052194369449069106 0.13882908999751697 4.0
FC6 0.08262912998454677 0.041103054281419346 0.0874926634879591 0.0
T7 -0.09731968723468239 -0.0021423686071579805 0.050226741225281264 7.0
C3 -0.07560861330060216 0.00379803246039862 0.1234494784937915 7.0
Cz -0.006180417265753086 0.012315925271535114 0.15916621398101838 7.0
C4 0.06771780154881832 0.012607270994125496 0.12635776802248064 5.0
T8 0.09380794264378244 0.003856915183230266 0.05572229320000353 3.0
TP9 -0.08788277893731682 -0.044663525156301793 0.018366495084764893 2.0
CP5 -0.08573650192101132 -0.03761412179890887 0.08666886062797521 2.0
CP1 -0.04005140947629612 -0.03233107162343741 0.15014596318924497 1.0
CP2 0.031717794003817336 -0.0296088007148207 0.15221703399068814 0.0
CP6 0.08561136739245928 -0.02720456615478355 0.0902401015260155 0.0
TP10 0.08961349613268568 -0.039552949075016205 0.019334677566709556 6.0
P7 -0.07806326604115213 -0.07056190192615615 0.054534433604549515 2.0
P3 -0.05988675497167928 -0.07014191680061976 0.11264449281813038 3.0
Pz 0.0006598293180978853 -0.06804813714721136 0.1401607191300863 3.0
P4 0.05712134266236288 -0.06349053843971156 0.1161043631186527 3.0
P8 0.07981691958558523 -0.06052077913488906 0.06169955870397669 2.0
PO9 -0.05689512980123602 -0.10004211993311017 0.024158443841012435 0.0
O1 -0.02968467135335781 -0.11245008371099957 0.0698530819823185 0.0
Oz 0.0007596706595483696 -0.11571632630102933 0.07284087113946844 0.0
O2 0.03439028693906701 -0.10937515964204104 0.07138023552971996 1.0
PO10 0.061903361072981396 -0.09474806509500813 0.03333978523037232 1.0
AF7 -0.06827717522853415 0.08936527746345276 0.04754798622540827 0.0
AF3 -0.04262101269367339 0.10268157306800194 0.07886835797387867 0.0
AF4 0.03225018478101126 0.10529515184701174 0.08026648552783476 0.0
AF8 0.05956439040238648 0.09485953374400294 0.047945480521261415 0.0
F5 -0.07472092061394837 0.0711718724278273 0.07658228616101855 1.0
F1 -0.034810704300574445 0.07683856227323796 0.11751875341931364 0.0
F2 0.025336640790593185 0.0821915554814207 0.11870219411577368 1.0
F6 0.07170582263944832 0.07278864400463472 0.07387636924196503 2.0
FT9 -0.09501686722799514 0.0266282891146297 0.011193532499484526 4.0
FT7 -0.09514340411749506 0.033122249460160715 0.046783546930167653 4.0
FC3 -0.07076636427255573 0.04222010515235073 0.11253273493424133 5.0
FC4 0.06459420065117943 0.04794650194990631 0.11446098877470175 0.0
FT8 0.09119200553670907 0.03543463031800678 0.0495990840479741 0.0
FT10 0.0924565546557565 0.025823362649082977 0.014698781944416511 0.0
C5 -0.09271418986954857 -0.0008204160816824106 0.08669305289548136 0.0
C1 -0.041067570836697036 0.007252977347652117 0.14981945629789892 2.0
C2 0.031096077417169788 0.009822809030655128 0.15263556643976955 1.0
C6 0.08823411500163923 0.006781675055354489 0.09062978525129138 0.0
TP7 -0.09130133540418942 -0.03998047720710454 0.05072152381600354 0.0
CP3 -0.07051068609538193 -0.03805150971360316 0.12118907085589545 0.0
CPz -0.002804681696167738 -0.032506400711086986 0.15765098791790225 0.0
CP4 0.06857184159847507 -0.02861094380649944 0.12476974691488656 0.0
TP8 0.09015904987363602 -0.03087949475535255 0.05580695159940414 0.0
P5 -0.07198353757029069 -0.07069605341309865 0.08424402565103543 1.0
P1 -0.03160865255395789 -0.06888410750316204 0.13343172651379165 0.0
P2 0.029678055865445845 -0.06524160677056923 0.13633979302106938 0.0
P6 0.07231997488461793 -0.061210721352081214 0.08939618967378504 0.0
PO7 -0.05917554476252128 -0.09600963297712298 0.05901355471464929 0.0
PO3 -0.03295276056603997 -0.09836112961335748 0.09818535662931108 0.0
POz 0.00141250247182117 -0.09659898488597438 0.1092860795662283 2.0
PO4 0.03732555069518968 -0.09330847523481997 0.09871963971555255 0.0
PO8 0.061422359682971855 -0.08824937021900595 0.06730156974139692 0.0
ECG n/a n/a n/a n/a
HEOG n/a n/a n/a n/a
VEOG n/a n/a n/a n/a
GSR n/a n/a n/a n/a
Temperature n/a n/a n/a n/a
@@ -0,0 +1,11 @@
{
"sample": {
"Description": "The event onset time in number of sampling points."
},
"value": {
"Description": "The event code (also known as trigger code or event ID) associated with the event."
},
"trial_type": {
"Description": "The type, category, or name of the event."
}
}
10 changes: 10 additions & 0 deletions mne_bids/tests/data/tiny_bids/sub-01/sub-01_sessions.json
@@ -0,0 +1,10 @@
{
"acq_time":{
"Description":"date of the first acquistion of the session",
"Units":"date",
"TermURL":"https://tools.ietf.org/html/rfc3339#section-5.6"
},
"pathology":{
"Description":"Disease state of the subject when applicable",
}
}
2 changes: 2 additions & 0 deletions mne_bids/tests/data/tiny_bids/sub-01/sub-01_sessions.tsv
@@ -0,0 +1,2 @@
session_id acq_time pathology
ses-eeg 2000-01-01T12:00:00.000000Z Parkinson's disease
71 changes: 64 additions & 7 deletions mne_bids/tests/test_read.py
Expand Up @@ -49,7 +49,7 @@
_bids_path_minimal = BIDSPath(subject=subject_id, task=task)

# Get the MNE testing sample data - USA
data_path = testing.data_path(download=False)
data_path = testing.data_path(download=True)
hoechenberger marked this conversation as resolved.
Show resolved Hide resolved
raw_fname = op.join(data_path, 'MEG', 'sample',
'sample_audvis_trunc_raw.fif')

Expand All @@ -74,6 +74,7 @@ def fn(fname, *args, **kwargs):
raw = read_raw(fname, *args, **kwargs)
raw.info['line_freq'] = 60
return raw

return fn


Expand Down Expand Up @@ -580,6 +581,62 @@ def test_handle_scans_reading(tmp_path):
assert new_acq_time != raw_01.info['meas_date']


def test_handle_sessions_reading(tmp_path):
"""Test writing/reading and altering data from a BIDS sessions.tsv file."""
raw = _read_raw_fif(raw_fname)
suffix = "meg"

# write copy of raw with line freq of 60
# bids basename and fname
bids_path = BIDSPath(subject='01', session='01',
task='audiovisual', run='01',
datatype=suffix,
root=tmp_path)
bids_path = write_raw_bids(raw, bids_path, overwrite=True)
raw_01 = read_raw_bids(bids_path)

# create a sessions.tsv file based on the scans.tsv
# The acq_time in sessions.tsv always equals
# the acq_time of the first recording of that session
scans_path = BIDSPath(subject=bids_path.subject,
session=bids_path.session,
root=tmp_path,
suffix='scans', extension='.tsv')
scans_tsv = _from_tsv(scans_path)
sessions_path = BIDSPath(subject=bids_path.subject,
root=tmp_path,
suffix='sessions', extension='.tsv')

sessions_tsv = OrderedDict([('session_id', ['ses-01', '']),
('acq_time', [scans_tsv['acq_time'][0], ''])])
_to_tsv(sessions_tsv, sessions_path)

# find sessions.tsv file and alter the
# acquisition time to not have the optional microseconds
sessions_path = BIDSPath(subject=bids_path.subject,
root=tmp_path,
suffix='sessions', extension='.tsv')
sessions_tsv = _from_tsv(sessions_path)
acq_time_str = sessions_tsv['acq_time'][0]
acq_time = datetime.strptime(acq_time_str, '%Y-%m-%dT%H:%M:%S.%fZ')
acq_time = acq_time.replace(tzinfo=timezone.utc)
new_acq_time = acq_time_str.split('.')[0]
assert acq_time == raw_01.info['meas_date']
sessions_tsv['acq_time'][0] = new_acq_time
_to_tsv(sessions_tsv, sessions_path)

# now re-load the data and it should be different
# from the original date and the same as the newly altered date
sessions_02 = _from_tsv(sessions_path)
new_acq_time += '.0Z'
new_acq_time = datetime.strptime(new_acq_time,
'%Y-%m-%dT%H:%M:%S.%fZ')
ses2_acq_time = datetime.strptime(sessions_02['acq_time'][0],
'%Y-%m-%dT%H:%M:%S')
assert ses2_acq_time == new_acq_time
assert new_acq_time != raw_01.info['meas_date']


@requires_version('mne', '1.2') # tiny_bids contains GSR & temperature chans
@pytest.mark.filterwarnings(warning_str['channel_unit_changed'])
def test_handle_scans_reading_brainvision(tmp_path):
Expand Down Expand Up @@ -1255,9 +1312,9 @@ def test_channels_tsv_raw_mismatch(tmp_path):
raw.save(raw_path, overwrite=True)

with pytest.warns(
RuntimeWarning,
match='number of channels in the channels.tsv sidecar .* '
'does not match the number of channels in the raw data'
RuntimeWarning,
match='number of channels in the channels.tsv sidecar .* '
'does not match the number of channels in the raw data'
hoechenberger marked this conversation as resolved.
Show resolved Hide resolved
):
read_raw_bids(bids_path)

Expand Down Expand Up @@ -1288,9 +1345,9 @@ def test_channels_tsv_raw_mismatch(tmp_path):
raw.save(raw_path, overwrite=True)

with pytest.warns(
RuntimeWarning,
match=f'Cannot set "bad" status for the following channels, as '
f'they are missing in the raw data: {ch_name_orig}'
RuntimeWarning,
match=f'Cannot set "bad" status for the following channels, as '
f'they are missing in the raw data: {ch_name_orig}'
hoechenberger marked this conversation as resolved.
Show resolved Hide resolved
):
read_raw_bids(bids_path)

Expand Down