Skip to content

Proj id and proj name access #13261

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

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8985f70
user access to proj_id and proj_name
LaurentLM May 23, 2025
ec964f5
updated class documentation
LaurentLM May 23, 2025
71b1ecd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 23, 2025
e40061b
Added tests and contrib info
LaurentLM May 25, 2025
da5dd52
Merge branch 'proj_id-and-proj_name-access' of https://github.com/Lau…
LaurentLM May 25, 2025
6ac6dc7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 25, 2025
079d7a2
Attempt to fix ndarray error
LaurentLM May 25, 2025
0e6778a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 25, 2025
9eeb532
fix for ndarrays
LaurentLM May 25, 2025
5212596
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 25, 2025
25e0a0f
fix formatting
LaurentLM May 25, 2025
e43df52
using isinstance to check proj_id type
LaurentLM May 25, 2025
c576799
initialise infor['prij_id'] as int instead of nd array. Conversion to…
LaurentLM Jun 3, 2025
a5f20fb
updated type conversion
LaurentLM Jun 3, 2025
ffb5835
Merge branch 'main' into proj_id-and-proj_name-access
LaurentLM Jun 3, 2025
b0e1a72
changed ndarray to int as per allowed types for info['proj_id']
LaurentLM Jun 3, 2025
94d85dd
Merge branch 'main' into proj_id-and-proj_name-access
LaurentLM Jun 4, 2025
883534e
fixed tests and merge
LaurentLM Jun 4, 2025
6c6bd85
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 4, 2025
7d94a82
update in tests and _merge_info_values
LaurentLM Jun 4, 2025
7b148c0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 4, 2025
78a3b5d
changed merging behaviour for project_id
LaurentLM Jun 5, 2025
a825c18
Merge branch 'proj_id-and-proj_name-access' of https://github.com/Lau…
LaurentLM Jun 5, 2025
a8609c8
updated merging of proj_id
LaurentLM Jun 5, 2025
9a4527f
Update mne/_fiff/meas_info.py
LaurentLM Jun 5, 2025
6f7673d
Added info message as ped Eric's suggestion
LaurentLM Jun 5, 2025
8c3eb38
improved merging messages. Fix bug when merging experimenters.
LaurentLM Jun 6, 2025
686afa6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/devel/13261.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
User can set values for fields ``proj_id`` and ``proj_name`` in ``Info`` dict, by :newcontrib:`Laurent Le Mentec`.
1 change: 1 addition & 0 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
.. _Larry Eisenman: https://github.com/lneisenman
.. _Lau Møller Andersen: https://github.com/ualsbombe
.. _Laura Gwilliams: https://lauragwilliams.github.io
.. _Laurent Le Mentec: https://github.com/LaurentLM
.. _Leonardo Barbosa: https://github.com/noreun
.. _Leonardo Rochael Almeida: https://github.com/leorochael
.. _Liberty Hamilton: https://github.com/libertyh
Expand Down
33 changes: 24 additions & 9 deletions mne/_fiff/meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,10 +1173,10 @@ class Info(ValidatedDict, SetChannelsMixin, MontageMixin, ContainsMixin):

.. warning::
The only entries that should be manually changed by the user are:
``info['bads']``, ``info['description']``, ``info['device_info']``
``info['dev_head_t']``, ``info['experimenter']``,
``info['helium_info']``, ``info['line_freq']``, ``info['temp']``,
and ``info['subject_info']``.
``info['bads']``, ``info['description']``, ``info['device_info']``,
``info['proj_id']``, ``info['proj_name']``, ``info['dev_head_t']``,
``info['experimenter']``, ``info['helium_info']``,
``info['line_freq']``, ``info['temp']``, and ``info['subject_info']``.

All other entries should be considered read-only, though they can be
modified by various MNE-Python functions or methods (which have
Expand Down Expand Up @@ -1634,8 +1634,8 @@ class Info(ValidatedDict, SetChannelsMixin, MontageMixin, ContainsMixin):
"Please use methods inst.add_channels(), "
"inst.drop_channels(), and inst.pick() instead.",
"proc_history": "proc_history cannot be set directly.",
"proj_id": "proj_id cannot be set directly.",
"proj_name": "proj_name cannot be set directly.",
"proj_id": partial(_check_types, name="proj_id", types=(int, None), cast=int),
"proj_name": partial(_check_types, name="proj_name", types=(str, None)),
"projs": "projs cannot be set directly. "
"Please use methods inst.add_proj() and inst.del_proj() "
"instead.",
Expand Down Expand Up @@ -2206,7 +2206,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
description = tag.data
elif kind == FIFF.FIFF_PROJ_ID:
tag = read_tag(fid, pos)
proj_id = tag.data
proj_id = int(tag.data.item())
elif kind == FIFF.FIFF_PROJ_NAME:
tag = read_tag(fid, pos)
proj_name = tag.data
Expand Down Expand Up @@ -3009,6 +3009,21 @@ def _where_isinstance(values, kind):
return values[int(idx)]
elif len(idx) > 1:
raise RuntimeError(msg)
# proj_id
elif _check_isinstance(values, (int, type(None)), all) and key == "proj_id":
unique_values = set(values)
if len(unique_values) != 1:
logger.info("Found multiple proj_ids, using the first one.")
return list(unique_values)[0]

elif key == "experimenter" or key == "proj_name":
if _check_isinstance(values, (str, type(None)), all):
unique_values = set(values)
unique_values.discard(None)
if len(unique_values) == 1:
return list(unique_values)[0]
else:
return None
# other
else:
unique_values = set(values)
Expand All @@ -3018,7 +3033,7 @@ def _where_isinstance(values, kind):
logger.info("Found multiple StringIO instances. Setting value to `None`")
return None
elif isinstance(list(unique_values)[0], str):
logger.info("Found multiple filenames. Setting value to `None`")
logger.info(f"Found multiple {key}. Setting value to `None`")
return None
else:
raise RuntimeError(msg)
Expand Down Expand Up @@ -3498,7 +3513,7 @@ def anonymize_info(info, daysback=None, keep_his=False, verbose=None):
info["description"] = default_desc
with info._unlock():
if info["proj_id"] is not None:
info["proj_id"] = np.zeros_like(info["proj_id"])
info["proj_id"] = 0
if info["proj_name"] is not None:
info["proj_name"] = default_str
if info["utc_offset"] is not None:
Expand Down
17 changes: 15 additions & 2 deletions mne/_fiff/tests/test_meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ def _test_anonymize_info(base_info, tmp_path):
for lev in tp[:-1]:
this = this[lev]
this[tp[-1]] = default_str
exp_info["proj_id"] = np.array([0])
exp_info["proj_id"] = 0
for key in ("sex", "id", "height", "weight"):
exp_info["subject_info"][key] = 0
exp_info["subject_info"]["his_id"] = str(default_subject_id)
Expand Down Expand Up @@ -798,7 +798,7 @@ def _complete_info(info):
info["experimenter"] = "f"
info["description"] = "g"
with info._unlock():
info["proj_id"] = np.ones(1, int)
info["proj_id"] = 1
info["proj_name"] = "h"
info["utc_offset"] = "i"
d = (1717707794, 2)
Expand Down Expand Up @@ -1217,6 +1217,7 @@ def test_info_bad():
info["line_freq"] = 50.0
info["bads"] = info["ch_names"][:1]
info["temp"] = ("whatever", 1.0)

with pytest.raises(RuntimeError, match=r"info\['temp'\]"):
info["bad_key"] = 1.0
for key, match in [("sfreq", r"inst\.resample"), ("chs", r"inst\.add_channels")]:
Expand Down Expand Up @@ -1277,3 +1278,15 @@ def test_tag_consistency():
assert call_set == call_names, "Mismatch between _call_dict and _call_dict_names"
# TODO: This was inspired by FIFF_DIG_STRING gh-13083, we should ideally add a test
# that those dig points can actually be read in correctly at some point.


def test_proj_id_entries():
"""Test that proj_id entries are the right type."""
info = create_info(5, 1000.0, "eeg")
info["proj_id"] = 123
# Boolean should be cast into an int
info["proj_id"] = True
with pytest.raises(TypeError, match="must be an instance"):
info["proj_id"] = "bad"
with pytest.raises(TypeError, match="must be an instance"):
info["proj_id"] = np.array([123])