From a745879e3ba73de818a34cd695d5aa673b6a5816 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Wed, 21 Dec 2022 16:05:21 +0100 Subject: [PATCH 1/8] update: add support for `confounds_format` argument in PatternDataGrabber() --- junifer/datagrabber/pattern.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/junifer/datagrabber/pattern.py b/junifer/datagrabber/pattern.py index 48a452d29..09f7cbf2e 100644 --- a/junifer/datagrabber/pattern.py +++ b/junifer/datagrabber/pattern.py @@ -7,7 +7,7 @@ import re from pathlib import Path -from typing import Dict, List, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union import numpy as np @@ -35,6 +35,9 @@ class PatternDataGrabber(BaseDataGrabber): Replacements in the patterns for each item in the "element" tuple. datadir : str or pathlib.Path The directory where the data is / will be stored. + confounds_format : {"fmriprep", "adhoc"}, optional + The format of the confounds for the dataset (default None). + """ def __init__( @@ -43,6 +46,7 @@ class PatternDataGrabber(BaseDataGrabber): patterns: Dict[str, str], replacements: Union[List[str], str], datadir: Union[str, Path], + confounds_format: Optional[str] = None, ) -> None: # Validate patterns validate_patterns(types=types, patterns=patterns) @@ -58,6 +62,7 @@ class PatternDataGrabber(BaseDataGrabber): logger.debug(f"\treplacements = {replacements}") self.patterns = patterns self.replacements = replacements + self.confounds_format = confounds_format @property def skip_file_check(self) -> bool: @@ -184,7 +189,12 @@ class PatternDataGrabber(BaseDataGrabber): f"Cannot access {t_type} for {element}: " f"File {t_out} does not exist" ) + # Update path for the element out[t_type] = {"path": t_out} + # Update confounds format (if provided) + if self.confounds_format is not None: + out[t_type].update({"format": self.confounds_format}) + return out def get_elements(self) -> List: -- 2.52.0 From a397f57b18690da4682963d414c103a115f369d0 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Wed, 21 Dec 2022 16:06:29 +0100 Subject: [PATCH 2/8] update: add `confounds_format` parameter for DataladAOMICPIOP1() --- junifer/datagrabber/aomic/piop1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/junifer/datagrabber/aomic/piop1.py b/junifer/datagrabber/aomic/piop1.py index abce1404e..d38feea67 100644 --- a/junifer/datagrabber/aomic/piop1.py +++ b/junifer/datagrabber/aomic/piop1.py @@ -116,6 +116,7 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber): uri=uri, patterns=patterns, replacements=replacements, + confounds_format="fmriprep", ) def get_item(self, subject: str, task: str) -> Dict: -- 2.52.0 From 8b487f23ecd89b85f2691a004838b537970b9a2d Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Wed, 21 Dec 2022 17:13:38 +0100 Subject: [PATCH 3/8] update: improve confounds_format check in PatternDataGrabber() and only add "format" to "BOLD_confounds" --- junifer/datagrabber/pattern.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/junifer/datagrabber/pattern.py b/junifer/datagrabber/pattern.py index 09f7cbf2e..ee5ef2a55 100644 --- a/junifer/datagrabber/pattern.py +++ b/junifer/datagrabber/pattern.py @@ -17,6 +17,10 @@ from .base import BaseDataGrabber from .utils import validate_patterns, validate_replacements +# Accepted formats for confounds specification +_CONFOUNDS_FORMATS = ("fmriprep", "adhoc") + + @register_datagrabber class PatternDataGrabber(BaseDataGrabber): """Concrete implementation for data grabbing using patterns. @@ -56,13 +60,20 @@ class PatternDataGrabber(BaseDataGrabber): # Validate replacements validate_replacements(replacements=replacements, patterns=patterns) + # Validate confounds format + if confounds_format and confounds_format not in _CONFOUNDS_FORMATS: + raise_error( + "Invalid value for `confounds_format`, should be one of " + f"{_CONFOUNDS_FORMATS}." + ) + self.confounds_format = confounds_format + super().__init__(types=types, datadir=datadir) logger.debug("Initializing PatternDataGrabber") logger.debug(f"\tpatterns = {patterns}") logger.debug(f"\treplacements = {replacements}") self.patterns = patterns self.replacements = replacements - self.confounds_format = confounds_format @property def skip_file_check(self) -> bool: @@ -191,8 +202,17 @@ class PatternDataGrabber(BaseDataGrabber): ) # Update path for the element out[t_type] = {"path": t_out} - # Update confounds format (if provided) - if self.confounds_format is not None: + # Update confounds format for BOLD_confounds + # (if found in the datagrabber) + if t_type == "BOLD_confounds": + if not self.confounds_format: + raise_error( + "`confounds_format` needs to be one of " + f"{_CONFOUNDS_FORMATS}, None provided. " + "As the datagrabber used specifies " + "'BOLD_confounds', None is invalid." + ) + # Set the format out[t_type].update({"format": self.confounds_format}) return out -- 2.52.0 From 00e45217d991eb7e25812ecb1cfd7897b05a2097 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Wed, 21 Dec 2022 17:14:46 +0100 Subject: [PATCH 4/8] update: add tests for confounds_format of PatternDataGrabber() --- junifer/datagrabber/tests/test_pattern.py | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/junifer/datagrabber/tests/test_pattern.py b/junifer/datagrabber/tests/test_pattern.py index 8d2ec5bc8..41043f6c9 100644 --- a/junifer/datagrabber/tests/test_pattern.py +++ b/junifer/datagrabber/tests/test_pattern.py @@ -259,3 +259,48 @@ def test_PatternDataGrabber(tmp_path: Path) -> None: assert out1["func"]["path"] == out2["func"]["path"] assert out1["anat"]["path"] == out2["anat"]["path"] assert out1["vbm"]["path"] != out2["vbm"]["path"] + + +def test_pattern_data_grabber_confounds_format_error_on_init() -> None: + """Test PatterDataGrabber confounds format error on initialisation.""" + with pytest.raises( + ValueError, match="Invalid value for `confounds_format`" + ): + PatternDataGrabber( + types=["func"], + patterns={"func": "func/{subject}.nii"}, + replacements=["subject"], + datadir="/tmp", + confounds_format="foobar", + ) + + +def test_pattern_data_grabber_confounds_format_error_on_fetch( + tmp_path: Path, +) -> None: + """Test PatterDataGrabber confounds format error on fetching. + + Parameters + ---------- + tmp_path : pathlib.Path + The path to the test directory. + + """ + # Create test directory path + tmpdir = tmp_path / "pattern_dg_test" + # Create final test directory + (tmpdir / "func" / "confounds").mkdir(exist_ok=True, parents=True) + # Create test confound file + (tmpdir / "func" / "confounds" / "sub-001.nii").touch() + # Initialise datagrabber + datagrabber = PatternDataGrabber( + types=["BOLD_confounds"], + patterns={"BOLD_confounds": "func/confounds/{subject}.nii"}, + replacements=["subject"], + datadir=tmpdir, + ) + # Check error on fetch + with pytest.raises( + ValueError, match="As the datagrabber used specifies 'BOLD_confounds'" + ): + datagrabber.get_item(subject="sub-001") -- 2.52.0 From bd51aa406d5fb68c0f1dbe1f76eba112e22bbb46 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 22 Dec 2022 13:54:18 +0100 Subject: [PATCH 5/8] update: add `confounds_format` parameter for DataladAOMICPIOP2() --- junifer/datagrabber/aomic/piop2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/junifer/datagrabber/aomic/piop2.py b/junifer/datagrabber/aomic/piop2.py index 7fe2212fb..aeae58cb3 100644 --- a/junifer/datagrabber/aomic/piop2.py +++ b/junifer/datagrabber/aomic/piop2.py @@ -113,6 +113,7 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber): uri=uri, patterns=patterns, replacements=replacements, + confounds_format="fmriprep", ) def get_elements(self) -> List: -- 2.52.0 From 1bc771813a84df9b014274e34f0caada061c3b6a Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 22 Dec 2022 13:54:30 +0100 Subject: [PATCH 6/8] update: add `confounds_format` parameter for DataladAOMICID1000() --- junifer/datagrabber/aomic/id1000.py | 1 + 1 file changed, 1 insertion(+) diff --git a/junifer/datagrabber/aomic/id1000.py b/junifer/datagrabber/aomic/id1000.py index 2266ff04b..14a50d3ce 100644 --- a/junifer/datagrabber/aomic/id1000.py +++ b/junifer/datagrabber/aomic/id1000.py @@ -86,4 +86,5 @@ class DataladAOMICID1000(PatternDataladDataGrabber): uri=uri, patterns=patterns, replacements=replacements, + confounds_format="fmriprep", ) -- 2.52.0 From e4efee1579f329f8e98a807767a2d166a5adbcc0 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 22 Dec 2022 13:54:49 +0100 Subject: [PATCH 7/8] update: add `confounds_format` parameter for JuselessUCLA() --- junifer/configs/juseless/datagrabbers/ucla.py | 1 + 1 file changed, 1 insertion(+) diff --git a/junifer/configs/juseless/datagrabbers/ucla.py b/junifer/configs/juseless/datagrabbers/ucla.py index 443a1257f..cca0957fd 100644 --- a/junifer/configs/juseless/datagrabbers/ucla.py +++ b/junifer/configs/juseless/datagrabbers/ucla.py @@ -107,6 +107,7 @@ class JuselessUCLA(PatternDataGrabber): datadir=datadir, patterns=patterns, replacements=replacements, + confounds_format="fmriprep", ) def get_elements(self) -> List: -- 2.52.0 From 905b2f9d22bd9e8febab7107e22fb4800e902207 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 22 Dec 2022 14:25:40 +0100 Subject: [PATCH 8/8] chore: update latest.inc --- docs/changes/latest.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changes/latest.inc b/docs/changes/latest.inc index 34176bcf1..7da87db84 100644 --- a/docs/changes/latest.inc +++ b/docs/changes/latest.inc @@ -30,3 +30,6 @@ Bugs API changes ~~~~~~~~~~~ + +- Add ``confounds_format`` parameter to :class:`junifer.datagrabber.PatternDataGrabber` constructor for + improved handling of confounds specified via ``BOLD_confounds`` data type (:gh:`158` by `Synchon Mandal`_). \ No newline at end of file -- 2.52.0