Skip to content

Commit

Permalink
Address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
pvandyken committed Jan 24, 2024
1 parent 7b4c7a6 commit b0359b9
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 30 deletions.
31 changes: 27 additions & 4 deletions docs/bids_app/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ The value of `filters` should be a dictionary where each key corresponds to a BI
the bold component would match any paths under the `func/` datatype folder, with the suffix `bold` and the extension `.nii.gz`.

```
sub-xxx/.../func/ent1-xxx_ent2-xxx_..._bold.nii.gz
sub-xxx/.../func/sub-xxx_ses-xxx_..._bold.nii.gz
```
* [`boolean`](#bool): constrains presence or absence of the entity without restricting its value. `False` requires that the entity be **absent**, while `True` requires that the entity be **present**, regardless of value.
* [`boolean`](#bool): constrains presence or absence of the entity without restricting its value. `False` requires that the entity be **absent**, while `True` requires the entity to be **present**, regardless of value.
```yaml
pybids_inputs:
derivs:
Expand All @@ -38,9 +38,12 @@ The value of `filters` should be a dictionary where each key corresponds to a BI
desc: True
acquisition: False
```
The above example maps all paths in the `func/` datatype folder that have a `_desc-` entity but do not have the `_acq-` entity.
The above example selects all paths in the `func/` datatype folder that have a `_desc-` entity but do not have the `_acq-` entity.

* [`list`](#list): Specify multiple string or boolean filters. Any path matching any one of the filters will be selected. Using `False` as one of the filters allows the entity to optionally be absent in addition to matching one of the string filters. Using `True` along with text is redundant, as `True` will cause any value to be selected. Using `True` with `False` is equivalent to not providing the filter at all.

These filters:

```yaml
pybids_inputs:
derivs:
Expand All @@ -51,7 +54,19 @@ The value of `filters` should be a dictionary where each key corresponds to a BI
- MP2RAGE
```
would select all of the following paths:
```
sub-001/ses-1/anat/sub-001_ses-001_acq-MPRAGE_run-1_T1w.nii.gz
sub-001/ses-1/anat/sub-001_ses-001_acq-MP2RAGE_run-1_T1w.nii.gz
sub-001/ses-1/anat/sub-001_ses-001_run-1_T1w.nii.gz
```


* To use regex for filtering, use an additional subkey set either to [`match`](#re.match) or [`search`](#re.search), depending on which regex method you wish to use. This key may be set to any one of the above items (`str`, `bool`, or `list`). Only one such key may be used.

These filters:

```yaml
pybids_inputs:
derivs:
Expand All @@ -62,8 +77,16 @@ The value of `filters` should be a dictionary where each key corresponds to a BI
match: MP2?RAGE
```
would select all of the following paths:
```
sub-001/ses-1/anat/sub-001_ses-001_acq-MPRAGE_run-1_T1.nii.gz
sub-001/ses-1/anat/sub-001_ses-001_acq-MP2RAGE_run-1_t1w.nii.gz
sub-001/ses-1/anat/sub-001_ses-001_acq-MPRAGE_run-1_qT1w.nii.gz
```

````{note}
`match` and `search` are both filtering methods. In addition to these, `get` is also a valid filtering method and may be used as the subkey for a filter. However, this is equivalent to directly providing the desired filter without a subkey:
`match` and `search` are both _filtering methods_. In addition to these, `get` is also a valid filtering method and may be used as the subkey for a filter. However, this is equivalent to directly providing the desired filter without a subkey:
```yaml
pybids_inputs:
Expand Down
28 changes: 15 additions & 13 deletions snakebids/core/_querying.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@ def add_filter(
Converts a list of values to include or exclude into Pybids compatible filters.
Exclusion filters are appropriately formatted as regex. Raises an exception if
both include and exclude are stipulated
both include and exclude are stipulated.
_Postfilter is modified in-place
PostFilter is modified in-place.
Parameters
----------
key
Name of entity to be filtered
inclusions
Values to include, values not found in this list will be excluded, by
default None
default ``None``
exclusions
Values to exclude, only values not found in this list will be included, by
default None
default ``None``
Raises
------
Expand Down Expand Up @@ -143,9 +143,9 @@ def prefilters(self) -> FilterMap:

@ft.cached_property
def get(self) -> CompiledFilter:
"""The combination pre- and post- filters for indexing pybids via ``.get()``.
"""The combination of pre- and post- filters for indexing pybids via ``.get()``.
Includes pre-filters not annotated for regex match or search and all inclusion
Includes pre-filters not annotated for regex querying and all inclusion
post-filters. Empty post-filters are replaced with Query.ANY. This allows valid
paths to be found and processed later. Post-filters are not applied when an
equivalent prefilter is present
Expand All @@ -171,7 +171,7 @@ def get(self) -> CompiledFilter:
def search(self) -> CompiledFilter:
"""Pre-filters for indexing pybids via ``.get(regex_search=True)``.
As with :func:`_UnifiedFilter.get`, but only prefilters labelled for regex
As with :prop:`UnifiedFilter.get`, but only prefilters labelled for regex
matching using ``search:`` or ``match:``.
Raises
Expand Down Expand Up @@ -251,10 +251,10 @@ def get_matching_files(
"dataset."
)
raise PybidsError(msg) from err
else:
if search is not None:
return [p for p in get if p in search]
return get

if search is not None:
return [p for p in get if p in search]
return get


@attrs.define
Expand All @@ -279,8 +279,8 @@ class _TooFewKeysError(FilterSpecError):
@override
def get_config_error(self, component_name: str) -> ConfigError:
msg = (
f"Filter '{self.entity}' for component '{component_name}' was specified a "
f"dict but was not given any keys. {self.requirement} Got: {self.value}"
f"Filter '{self.entity}' for component '{component_name}' was specified as "
f"a dict but was not given any keys. {self.requirement} Got: {self.value}"
)
return ConfigError(msg)

Expand Down Expand Up @@ -352,6 +352,8 @@ def _compile_filt(filt: Iterable[str | bool]):
result[key] = _compile_filt(itx.always_iterable(f))

if filt_type == "match":
# pybids only does search, so surround string filters with position anchors
# to simulate match
result[key] = [
f"^(?:{filt})$" if isinstance(filt, str) else filt
for filt in result[key]
Expand Down
15 changes: 2 additions & 13 deletions snakebids/core/input_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,10 @@ def _get_component(
Parameters
----------
bids_layout : BIDSLayout
bids_layout
Layout from pybids for accessing the BIDS dataset to grab paths
pybids_inputs : dict
component
Dictionary indexed by modality name, specifying the filters and
wildcards for each pybids input.
Expand All @@ -539,11 +539,6 @@ def _get_component(
postfilters
Filters to component after delineation
Yields
------
BidsComponent
One BidsComponent is yielded for each modality described by ``pybids_inputs``.
Raises
------
ConfigError
Expand All @@ -554,12 +549,6 @@ def _get_component(
filters = UnifiedFilter(component, postfilters or {})

if "custom_path" in component:
# a custom path was specified for this input, skip pybids:
# get input_wildcards by parsing path for {} entries (using a set
# to get unique only)
# get zip_lists by using glob_wildcards (but need to modify
# to deal with multiple wildcards

path = component["custom_path"]
zip_lists = _parse_custom_path(path, filters=filters)
return BidsComponent(name=input_name, path=path, zip_lists=zip_lists)
Expand Down

0 comments on commit b0359b9

Please sign in to comment.