Skip to content

Commit 1a72423

Browse files
authored
Merge pull request #2 from smoia/add-pydra
A few mods stemming from PR convo
2 parents 1cd7539 + 94b711c commit 1a72423

File tree

5 files changed

+131
-69
lines changed

5 files changed

+131
-69
lines changed

physutils/io.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import os.path as op
1010

1111
import numpy as np
12-
from bids import BIDSLayout
1312
from loguru import logger
1413

1514
from physutils import physio
@@ -28,7 +27,7 @@ def load_from_bids(
2827
suffix="physio",
2928
):
3029
"""
31-
Load physiological data from BIDS-formatted directory
30+
Load physiological data from BIDS-formatted directory.
3231
3332
Parameters
3433
----------
@@ -50,6 +49,12 @@ def load_from_bids(
5049
data : :class:`physutils.Physio`
5150
Loaded physiological data
5251
"""
52+
try:
53+
from bids import BIDSLayout
54+
except ImportError:
55+
raise ImportError(
56+
"To use BIDS-based feature, pybids must be installed. Install manually or with `pip install physutils[bids]`"
57+
)
5358

5459
# check if file exists and is in BIDS format
5560
if not op.exists(bids_path):

physutils/tasks.py

Lines changed: 40 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,48 @@
1-
import logging
2-
from functools import wraps
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
33

4-
from bids import BIDSLayout
5-
from loguru import logger
4+
"""Helper class for holding physiological data and associated metadata information."""
65

7-
from physutils.io import load_from_bids, load_physio
8-
from physutils.physio import Physio
6+
import logging
97

10-
LGR = logging.getLogger(__name__)
11-
LGR.setLevel(logging.DEBUG)
8+
from .io import load_from_bids, load_physio
9+
from .physio import Physio
10+
from .utils import is_bids_directory
1211

13-
try:
14-
import pydra
12+
# from loguru import logger
1513

16-
pydra_imported = True
14+
try:
15+
from pydra import task
1716
except ImportError:
18-
pydra_imported = False
19-
20-
21-
def mark_task(pydra_imported=pydra_imported):
22-
def decorator(func):
23-
if pydra_imported:
24-
# If the decorator exists, apply it
25-
@wraps(func)
26-
def wrapped_func(*args, **kwargs):
27-
logger.debug(f"Creating pydra task for {func.__name__}")
28-
return pydra.mark.task(func)(*args, **kwargs)
17+
from .utils import task
2918

30-
return wrapped_func
31-
# Otherwise, return the original function
32-
return func
3319

34-
return decorator
20+
LGR = logging.getLogger(__name__)
21+
LGR.setLevel(logging.DEBUG)
3522

3623

37-
def is_bids_directory(directory):
38-
try:
39-
# Attempt to create a BIDSLayout object
40-
_ = BIDSLayout(directory)
41-
return True
42-
except Exception as e:
43-
# Catch other exceptions that might indicate the directory isn't BIDS compliant
44-
logger.error(
45-
f"An error occurred while trying to load {directory} as a BIDS Layout object: {e}"
46-
)
47-
return False
24+
@task
25+
def generate_physio(
26+
input_file: str, mode="auto", fs=None, bids_parameters=dict(), col_physio_type=None
27+
) -> Physio:
28+
"""
29+
Load a physio object from either a BIDS directory or an exported physio object.
4830
31+
Parameters
32+
----------
33+
input_file : str
34+
Path to input file
35+
mode : 'auto', 'physio', or 'bids', optional
36+
Mode to operate with
37+
fs : None, optional
38+
Set or force set sapmling frequency (Hz).
39+
bids_parameters : dictionary, optional
40+
Dictionary containing BIDS parameters
41+
col_physio_type : int or None, optional
42+
Object to pick up in a BIDS array of physio objects.
4943
50-
@mark_task(pydra_imported=pydra_imported)
51-
def transform_to_physio(
52-
input_file: str, mode="physio", fs=None, bids_parameters=dict(), bids_channel=None
53-
) -> Physio:
54-
if not pydra_imported:
55-
LGR.warning(
56-
"Pydra is not installed, thus transform_to_physio is not available as a pydra task. Using the function directly"
57-
)
58-
LGR.debug(f"Loading physio object from {input_file}")
59-
if not fs:
60-
fs = None
44+
"""
45+
LGR.info(f"Loading physio object from {input_file}")
6146

6247
if mode == "auto":
6348
if input_file.endswith((".phys", ".physio", ".1D", ".txt", ".tsv", ".csv")):
@@ -66,20 +51,20 @@ def transform_to_physio(
6651
mode = "bids"
6752
else:
6853
raise ValueError(
69-
"Could not determine mode automatically, please specify mode"
54+
"Could not determine input mode automatically. Please specify it manually."
7055
)
7156
if mode == "physio":
72-
if fs is not None:
73-
physio_obj = load_physio(input_file, fs=fs, allow_pickle=True)
74-
else:
75-
physio_obj = load_physio(input_file, allow_pickle=True)
57+
physio_obj = load_physio(input_file, fs=fs, allow_pickle=True)
7658

7759
elif mode == "bids":
7860
if bids_parameters is {}:
7961
raise ValueError("BIDS parameters must be provided when loading from BIDS")
8062
else:
8163
physio_array = load_from_bids(input_file, **bids_parameters)
82-
physio_obj = physio_array[bids_channel]
64+
physio_obj = (
65+
physio_array[col_physio_type] if col_physio_type else physio_array
66+
)
8367
else:
84-
raise ValueError(f"Invalid transform_to_physio mode: {mode}")
68+
raise ValueError(f"Invalid generate_physio mode: {mode}")
69+
8570
return physio_obj

physutils/tests/test_tasks.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
from physutils.tests.utils import create_random_bids_structure
88

99

10-
def test_transform_to_physio_phys_file():
11-
"""Test transform_to_physio task."""
10+
def test_generate_physio_phys_file():
11+
"""Test generate_physio task."""
1212
physio_file = os.path.abspath("physutils/tests/data/ECG.phys")
13-
task = tasks.transform_to_physio(input_file=physio_file, mode="physio")
13+
task = tasks.generate_physio(input_file=physio_file, mode="physio")
1414
assert task.inputs.input_file == physio_file
1515
assert task.inputs.mode == "physio"
1616
assert task.inputs.fs is None
@@ -23,8 +23,8 @@ def test_transform_to_physio_phys_file():
2323
assert physio_obj.data.shape == (44611,)
2424

2525

26-
def test_transform_to_physio_bids_file():
27-
"""Test transform_to_physio task."""
26+
def test_generate_physio_bids_file():
27+
"""Test generate_physio task."""
2828
create_random_bids_structure("physutils/tests/data", recording_id="cardiac")
2929
bids_parameters = {
3030
"subject": "01",
@@ -34,7 +34,7 @@ def test_transform_to_physio_bids_file():
3434
"recording": "cardiac",
3535
}
3636
bids_dir = os.path.abspath("physutils/tests/data/bids-dir")
37-
task = tasks.transform_to_physio(
37+
task = tasks.generate_physio(
3838
input_file=bids_dir,
3939
mode="bids",
4040
bids_parameters=bids_parameters,
@@ -53,7 +53,7 @@ def test_transform_to_physio_bids_file():
5353
assert isinstance(physio_obj, physio.Physio)
5454

5555

56-
def test_transform_to_physio_auto():
56+
def test_generate_physio_auto():
5757
create_random_bids_structure("physutils/tests/data", recording_id="cardiac")
5858
bids_parameters = {
5959
"subject": "01",
@@ -63,7 +63,7 @@ def test_transform_to_physio_auto():
6363
"recording": "cardiac",
6464
}
6565
bids_dir = os.path.abspath("physutils/tests/data/bids-dir")
66-
task = tasks.transform_to_physio(
66+
task = tasks.generate_physio(
6767
input_file=bids_dir,
6868
mode="auto",
6969
bids_parameters=bids_parameters,
@@ -82,9 +82,9 @@ def test_transform_to_physio_auto():
8282
assert isinstance(physio_obj, physio.Physio)
8383

8484

85-
def test_transform_to_physio_auto_error(caplog):
85+
def test_generate_physio_auto_error(caplog):
8686
bids_dir = os.path.abspath("physutils/tests/data/non-bids-dir")
87-
task = tasks.transform_to_physio(
87+
task = tasks.generate_physio(
8888
input_file=bids_dir,
8989
mode="auto",
9090
bids_channel="cardiac",

physutils/utils.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
"""Helper class for holding physiological data and associated metadata information."""
5+
6+
import logging
7+
from functools import wraps
8+
9+
from loguru import logger
10+
11+
LGR = logging.getLogger(__name__)
12+
LGR.setLevel(logging.DEBUG)
13+
14+
15+
def task(func):
16+
"""
17+
Fake task decorator to import when pydra is not installed/used.
18+
19+
Parameters
20+
----------
21+
func: function
22+
Function to run the wrapper around
23+
24+
Returns
25+
-------
26+
function
27+
"""
28+
29+
@wraps(func)
30+
def wrapper(*args, **kwargs):
31+
return func(*args, **kwargs)
32+
LGR.debug(
33+
"Pydra is not installed, thus generate_physio is not available as a pydra task. Using the function directly"
34+
)
35+
36+
return wrapper
37+
38+
39+
def is_bids_directory(path_to_dir):
40+
"""
41+
Check if a directory is a BIDS compliant directory.
42+
43+
Parameters
44+
----------
45+
path_to_dir : os.path or str
46+
Path to (supposed) BIDS directory
47+
48+
Returns
49+
-------
50+
bool
51+
True if the given path is a BIDS directory, False is not.
52+
"""
53+
try:
54+
from bids import BIDSLayout
55+
except ImportError:
56+
raise ImportError(
57+
"To use BIDS-based feature, pybids must be installed. Install manually or with `pip install physutils[bids]`"
58+
)
59+
try:
60+
# Attempt to create a BIDSLayout object
61+
_ = BIDSLayout(path_to_dir)
62+
return True
63+
except Exception as e:
64+
# Catch other exceptions that might indicate the directory isn't BIDS compliant
65+
logger.error(
66+
f"An error occurred while trying to load {path_to_dir} as a BIDS Layout object: {e}"
67+
)
68+
return False

setup.cfg

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ install_requires =
2424
matplotlib
2525
numpy >=1.9.3
2626
loguru
27-
pydra
28-
pybids
2927
tests_require =
3028
pytest >=3.6
3129
test_suite = pytest
@@ -34,6 +32,10 @@ packages = find:
3432
include_package_data = True
3533

3634
[options.extras_require]
35+
pydra =
36+
pydra
37+
bids =
38+
pybids
3739
doc =
3840
sphinx >=2.0
3941
sphinx-argparse
@@ -49,6 +51,8 @@ test =
4951
scipy
5052
pytest >=5.3
5153
pytest-cov
54+
%(pydra)s
55+
%(bids)s
5256
%(style)s
5357
devtools =
5458
pre-commit

0 commit comments

Comments
 (0)