Skip to content

Commit 99275fa

Browse files
Proj id and proj name access (#13261)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 8df70ae commit 99275fa

File tree

8 files changed

+48
-15
lines changed

8 files changed

+48
-15
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
User can set values for fields ``proj_id`` and ``proj_name`` in ``Info`` dict, by :newcontrib:`Laurent Le Mentec`.

doc/changes/names.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
.. _Larry Eisenman: https://github.com/lneisenman
167167
.. _Lau Møller Andersen: https://github.com/ualsbombe
168168
.. _Laura Gwilliams: https://lauragwilliams.github.io
169+
.. _Laurent Le Mentec: https://github.com/LaurentLM
169170
.. _Leonardo Barbosa: https://github.com/noreun
170171
.. _Leonardo Rochael Almeida: https://github.com/leorochael
171172
.. _Liberty Hamilton: https://github.com/libertyh

mne/_fiff/meas_info.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,10 +1173,10 @@ class Info(ValidatedDict, SetChannelsMixin, MontageMixin, ContainsMixin):
11731173
11741174
.. warning::
11751175
The only entries that should be manually changed by the user are:
1176-
``info['bads']``, ``info['description']``, ``info['device_info']``
1177-
``info['dev_head_t']``, ``info['experimenter']``,
1178-
``info['helium_info']``, ``info['line_freq']``, ``info['temp']``,
1179-
and ``info['subject_info']``.
1176+
``info['bads']``, ``info['description']``, ``info['device_info']``,
1177+
``info['proj_id']``, ``info['proj_name']``, ``info['dev_head_t']``,
1178+
``info['experimenter']``, ``info['helium_info']``,
1179+
``info['line_freq']``, ``info['temp']``, and ``info['subject_info']``.
11801180
11811181
All other entries should be considered read-only, though they can be
11821182
modified by various MNE-Python functions or methods (which have
@@ -1634,8 +1634,8 @@ class Info(ValidatedDict, SetChannelsMixin, MontageMixin, ContainsMixin):
16341634
"Please use methods inst.add_channels(), "
16351635
"inst.drop_channels(), and inst.pick() instead.",
16361636
"proc_history": "proc_history cannot be set directly.",
1637-
"proj_id": "proj_id cannot be set directly.",
1638-
"proj_name": "proj_name cannot be set directly.",
1637+
"proj_id": partial(_check_types, name="proj_id", types=(int, None), cast=int),
1638+
"proj_name": partial(_check_types, name="proj_name", types=(str, None)),
16391639
"projs": "projs cannot be set directly. "
16401640
"Please use methods inst.add_proj() and inst.del_proj() "
16411641
"instead.",
@@ -2206,7 +2206,7 @@ def read_meas_info(fid, tree, clean_bads=False, verbose=None):
22062206
description = tag.data
22072207
elif kind == FIFF.FIFF_PROJ_ID:
22082208
tag = read_tag(fid, pos)
2209-
proj_id = tag.data
2209+
proj_id = int(tag.data.item())
22102210
elif kind == FIFF.FIFF_PROJ_NAME:
22112211
tag = read_tag(fid, pos)
22122212
proj_name = tag.data
@@ -3009,6 +3009,21 @@ def _where_isinstance(values, kind):
30093009
return values[int(idx)]
30103010
elif len(idx) > 1:
30113011
raise RuntimeError(msg)
3012+
# proj_id
3013+
elif _check_isinstance(values, (int, type(None)), all) and key == "proj_id":
3014+
unique_values = set(values)
3015+
if len(unique_values) != 1:
3016+
logger.info("Found multiple proj_ids, using the first one.")
3017+
return list(unique_values)[0]
3018+
3019+
elif key == "experimenter" or key == "proj_name":
3020+
if _check_isinstance(values, (str, type(None)), all):
3021+
unique_values = set(values)
3022+
unique_values.discard(None)
3023+
if len(unique_values) == 1:
3024+
return list(unique_values)[0]
3025+
else:
3026+
return None
30123027
# other
30133028
else:
30143029
unique_values = set(values)
@@ -3018,7 +3033,7 @@ def _where_isinstance(values, kind):
30183033
logger.info("Found multiple StringIO instances. Setting value to `None`")
30193034
return None
30203035
elif isinstance(list(unique_values)[0], str):
3021-
logger.info("Found multiple filenames. Setting value to `None`")
3036+
logger.info(f"Found multiple {key}. Setting value to `None`")
30223037
return None
30233038
else:
30243039
raise RuntimeError(msg)
@@ -3498,7 +3513,7 @@ def anonymize_info(info, daysback=None, keep_his=False, verbose=None):
34983513
info["description"] = default_desc
34993514
with info._unlock():
35003515
if info["proj_id"] is not None:
3501-
info["proj_id"] = np.zeros_like(info["proj_id"])
3516+
info["proj_id"] = 0
35023517
if info["proj_name"] is not None:
35033518
info["proj_name"] = default_str
35043519
if info["utc_offset"] is not None:

mne/_fiff/tests/test_meas_info.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ def _test_anonymize_info(base_info, tmp_path):
637637
for lev in tp[:-1]:
638638
this = this[lev]
639639
this[tp[-1]] = default_str
640-
exp_info["proj_id"] = np.array([0])
640+
exp_info["proj_id"] = 0
641641
for key in ("sex", "id", "height", "weight"):
642642
exp_info["subject_info"][key] = 0
643643
exp_info["subject_info"]["his_id"] = str(default_subject_id)
@@ -798,7 +798,7 @@ def _complete_info(info):
798798
info["experimenter"] = "f"
799799
info["description"] = "g"
800800
with info._unlock():
801-
info["proj_id"] = np.ones(1, int)
801+
info["proj_id"] = 1
802802
info["proj_name"] = "h"
803803
info["utc_offset"] = "i"
804804
d = (1717707794, 2)
@@ -1217,6 +1217,7 @@ def test_info_bad():
12171217
info["line_freq"] = 50.0
12181218
info["bads"] = info["ch_names"][:1]
12191219
info["temp"] = ("whatever", 1.0)
1220+
12201221
with pytest.raises(RuntimeError, match=r"info\['temp'\]"):
12211222
info["bad_key"] = 1.0
12221223
for key, match in [("sfreq", r"inst\.resample"), ("chs", r"inst\.add_channels")]:
@@ -1277,3 +1278,15 @@ def test_tag_consistency():
12771278
assert call_set == call_names, "Mismatch between _call_dict and _call_dict_names"
12781279
# TODO: This was inspired by FIFF_DIG_STRING gh-13083, we should ideally add a test
12791280
# that those dig points can actually be read in correctly at some point.
1281+
1282+
1283+
def test_proj_id_entries():
1284+
"""Test that proj_id entries are the right type."""
1285+
info = create_info(5, 1000.0, "eeg")
1286+
info["proj_id"] = 123
1287+
# Boolean should be cast into an int
1288+
info["proj_id"] = True
1289+
with pytest.raises(TypeError, match="must be an instance"):
1290+
info["proj_id"] = "bad"
1291+
with pytest.raises(TypeError, match="must be an instance"):
1292+
info["proj_id"] = np.array([123])

mne/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def pytest_configure(config: pytest.Config):
195195
ignore:process .* is multi-threaded, use of fork/exec.*:DeprecationWarning
196196
# sklearn
197197
ignore:Python binding for RankQuantileOptions.*:RuntimeWarning
198+
ignore:.*The `disp` and `iprint` options of the L-BFGS-B solver.*:DeprecationWarning
198199
""" # noqa: E501
199200
for warning_line in warning_lines.split("\n"):
200201
warning_line = warning_line.strip()

mne/forward/tests/test_make_forward.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,10 +719,10 @@ def test_make_forward_dipole(tmp_path):
719719
# Make sure each coordinate is close to reference
720720
# NB tolerance should be set relative to snr of simulated evoked!
721721
assert_allclose(
722-
dip_fit.pos, dip_test.pos, rtol=0, atol=1.3e-2, err_msg="position mismatch"
722+
dip_fit.pos, dip_test.pos, rtol=0, atol=1.5e-2, err_msg="position mismatch"
723723
)
724724
assert dist < 1e-2 # within 1 cm
725-
assert corr > 0.985
725+
assert corr > 0.98
726726
assert gc_dist < 20 # less than 20 degrees
727727
assert amp_err < 10e-9 # within 10 nAm
728728

mne/preprocessing/tests/test_fine_cal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def test_compute_fine_cal(kind):
7777
angle_limit = 5
7878
gwoma = [66, 68]
7979
ggoma = [55, 150]
80-
ggwma = [60, 86]
80+
ggwma = [52, 86]
8181
sfs = [26, 27, 61, 63, 61, 63, 68, 70]
8282
cl3 = [0.6, 0.7]
8383
else:
@@ -243,7 +243,7 @@ def test_fine_cal_systems(system, tmp_path):
243243
int_order = 5
244244
corrs = (0.13, 0.0, 0.12)
245245
sfs = [4, 5, 125, 155]
246-
corr_tol = 0.34
246+
corr_tol = 0.38
247247
else:
248248
assert system == "triux", f"Unknown system {system}"
249249
raw = read_raw_fif(tri_fname)

mne/utils/tests/test_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from urllib.error import URLError
1414

1515
import pytest
16+
from flaky import flaky
1617

1718
import mne
1819
import mne.utils.config
@@ -173,6 +174,7 @@ def test_get_subjects_dir(tmp_path, monkeypatch):
173174
get_subjects_dir(raise_error=True)
174175

175176

177+
@flaky(max_runs=3)
176178
@pytest.mark.slowtest
177179
@requires_good_network
178180
def test_sys_info_check_outdated(monkeypatch):

0 commit comments

Comments
 (0)