[BUG]: PatternDataGrabber does not respect the "format" parameter required for BOLD_confounds data type #158

Merged
synchon merged 8 commits from fix/confounds-format-pattern-dg into main 2022-12-22 13:47:19 +00:00
7 changed files with 83 additions and 1 deletions

View file

@ -30,3 +30,6 @@ Bugs
API changes 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`_).

View file

@ -107,6 +107,7 @@ class JuselessUCLA(PatternDataGrabber):
datadir=datadir, datadir=datadir,
patterns=patterns, patterns=patterns,
replacements=replacements, replacements=replacements,
confounds_format="fmriprep",
) )
def get_elements(self) -> List: def get_elements(self) -> List:

View file

@ -86,4 +86,5 @@ class DataladAOMICID1000(PatternDataladDataGrabber):
uri=uri, uri=uri,
patterns=patterns, patterns=patterns,
replacements=replacements, replacements=replacements,
confounds_format="fmriprep",
) )

View file

@ -116,6 +116,7 @@ class DataladAOMICPIOP1(PatternDataladDataGrabber):
uri=uri, uri=uri,
patterns=patterns, patterns=patterns,
replacements=replacements, replacements=replacements,
confounds_format="fmriprep",
) )
def get_item(self, subject: str, task: str) -> Dict: def get_item(self, subject: str, task: str) -> Dict:

View file

@ -113,6 +113,7 @@ class DataladAOMICPIOP2(PatternDataladDataGrabber):
uri=uri, uri=uri,
patterns=patterns, patterns=patterns,
replacements=replacements, replacements=replacements,
confounds_format="fmriprep",
) )
def get_elements(self) -> List: def get_elements(self) -> List:

View file

@ -7,7 +7,7 @@
import re import re
from pathlib import Path from pathlib import Path
from typing import Dict, List, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
import numpy as np import numpy as np
@ -17,6 +17,10 @@ from .base import BaseDataGrabber
from .utils import validate_patterns, validate_replacements from .utils import validate_patterns, validate_replacements
# Accepted formats for confounds specification
_CONFOUNDS_FORMATS = ("fmriprep", "adhoc")
@register_datagrabber @register_datagrabber
class PatternDataGrabber(BaseDataGrabber): class PatternDataGrabber(BaseDataGrabber):
"""Concrete implementation for data grabbing using patterns. """Concrete implementation for data grabbing using patterns.
@ -35,6 +39,9 @@ class PatternDataGrabber(BaseDataGrabber):
Replacements in the patterns for each item in the "element" tuple. Replacements in the patterns for each item in the "element" tuple.
datadir : str or pathlib.Path datadir : str or pathlib.Path
The directory where the data is / will be stored. 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__( def __init__(
@ -43,6 +50,7 @@ class PatternDataGrabber(BaseDataGrabber):
patterns: Dict[str, str], patterns: Dict[str, str],
replacements: Union[List[str], str], replacements: Union[List[str], str],
datadir: Union[str, Path], datadir: Union[str, Path],
confounds_format: Optional[str] = None,
) -> None: ) -> None:
# Validate patterns # Validate patterns
validate_patterns(types=types, patterns=patterns) validate_patterns(types=types, patterns=patterns)
@ -52,6 +60,14 @@ class PatternDataGrabber(BaseDataGrabber):
# Validate replacements # Validate replacements
validate_replacements(replacements=replacements, patterns=patterns) 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) super().__init__(types=types, datadir=datadir)
logger.debug("Initializing PatternDataGrabber") logger.debug("Initializing PatternDataGrabber")
logger.debug(f"\tpatterns = {patterns}") logger.debug(f"\tpatterns = {patterns}")
@ -184,7 +200,21 @@ class PatternDataGrabber(BaseDataGrabber):
f"Cannot access {t_type} for {element}: " f"Cannot access {t_type} for {element}: "
f"File {t_out} does not exist" f"File {t_out} does not exist"
) )
# Update path for the element
out[t_type] = {"path": t_out} out[t_type] = {"path": t_out}
# 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 return out
def get_elements(self) -> List: def get_elements(self) -> List:

View file

@ -259,3 +259,48 @@ def test_PatternDataGrabber(tmp_path: Path) -> None:
assert out1["func"]["path"] == out2["func"]["path"] assert out1["func"]["path"] == out2["func"]["path"]
assert out1["anat"]["path"] == out2["anat"]["path"] assert out1["anat"]["path"] == out2["anat"]["path"]
assert out1["vbm"]["path"] != out2["vbm"]["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")