diff --git a/doc/whats_new.rst b/doc/whats_new.rst index 42a42a725..f495df826 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -56,6 +56,7 @@ Detailed list of changes - When anonymizing the date of a recording, MNE-BIDS will no longer error during `~mne_bids.write_raw_bids` if passing a `~mne.io.Raw` instance to ``empty_room``, by `Daniel McCloy`_ (:gh:`1270`) - Dealing with alphanumeric ``sub`` entity labels is now fixed for :func:`~mne_bids.write_raw_bids`, by `Aaron Earle-Richardson`_ (:gh:`1291`) +- When processing subject_info data that MNE Python imports as numpy arrays with only one item, MNE-BIDS now unpacks these, resulting in a correct participants.tsv, by `Thomas Hartmann`_ (:gh:`1310`) ⚕️ Code health ^^^^^^^^^^^^^^ diff --git a/mne_bids/tests/test_write.py b/mne_bids/tests/test_write.py index 63485cda2..e1f369744 100644 --- a/mne_bids/tests/test_write.py +++ b/mne_bids/tests/test_write.py @@ -4187,3 +4187,48 @@ def test_write_evt_metadata(_bids_validate, tmp_path): for cur_col in event_metadata.columns: assert cur_col in events_tsv assert cur_col in events_json + + +# XXX: Remove once MNE-Python <1.9 is no longer supported +@testing.requires_testing_data +def test_write_bids_with_age_weight_info(tmp_path, monkeypatch): + """Test writing participant.tsv when using np.arrays for weight and height.""" + bids_root = tmp_path / "bids" + raw_fname = data_path / "MEG" / "sample" / "sample_audvis_trunc_raw.fif" + raw = _read_raw_fif(raw_fname) + # disable MNE-Python 1.9+ validation for the duration of this test + if "subject_info" in getattr(mne.Info, "_attributes", {}): + monkeypatch.setitem( + mne.Info._attributes, + "subject_info", + lambda val, **kwargs: val, + ) + raw.info["subject_info"] = { + "weight": np.array([75.0]), + "height": np.array([180.0]), + } + + bids_path = _bids_path.copy().update(root=bids_root, datatype="meg", run=1) + write_raw_bids(raw, bids_path=bids_path) + bids_path = _bids_path.copy().update(root=bids_root, datatype="meg", run=2) + write_raw_bids(raw, bids_path=bids_path) + + # Test that we get a value error when we have more than one item + raw.info["subject_info"] = { + "weight": np.array([75.0, 10.2]), + "height": np.array([180.0]), + } + + bids_path = _bids_path.copy().update(root=bids_root, datatype="meg", run=3) + assert_array_equal(raw.info["subject_info"]["weight"], [75.0, 10.2]) + with pytest.raises(ValueError, match="more than one element"): + write_raw_bids(raw, bids_path=bids_path) + + # Test that scalar data is handled correctly + raw.info["subject_info"] = { + "weight": 75.0, + "height": np.array([180.0]), + } + + bids_path = _bids_path.copy().update(root=bids_root, datatype="meg", run=3) + write_raw_bids(raw, bids_path=bids_path) diff --git a/mne_bids/write.py b/mne_bids/write.py index b7cf34d02..500d300ff 100644 --- a/mne_bids/write.py +++ b/mne_bids/write.py @@ -511,6 +511,29 @@ def _participants_tsv(raw, subject_id, fname, overwrite=False): } ) + # XXX: Remove once MNE-Python <1.9 is no longer supported + # Make sure that all entries to data are lists that + # contain scalars (i.e. not further lists). Fix if possible + for key in data.keys(): + cur_value = data[key] + + # Check if all values are scalars + new_value = [] + for cur_item in cur_value: + if isinstance(cur_item, list | tuple | np.ndarray): + if len(cur_item) == 1: + new_value.append(cur_item[0]) + else: + raise ValueError( + f"Value for key {key} is a list with more " + f"than one element. This is not supported. " + f"Got: {cur_value}." + ) + else: + new_value.append(cur_item) + + data[key] = new_value + if os.path.exists(fname): orig_data = _from_tsv(fname) # whether the new data exists identically in the previous data