Skip to content

Commit e6b9673

Browse files
committed
Add preliminary load_from_bids function
1 parent e7c66b4 commit e6b9673

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

physutils/io.py

+116
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,129 @@
88
import os.path as op
99

1010
import numpy as np
11+
from bids import BIDSLayout, BIDSValidator
1112
from loguru import logger
1213

1314
from physutils import physio
1415

1516
EXPECTED = ["data", "fs", "history", "metadata"]
1617

1718

19+
def load_from_bids(
20+
bids_path,
21+
subject,
22+
session=None,
23+
task=None,
24+
run=None,
25+
extension="tsv.gz",
26+
suffix="physio",
27+
):
28+
"""
29+
Load physiological data from BIDS-formatted directory
30+
31+
Parameters
32+
----------
33+
bids_path : str
34+
Path to BIDS-formatted directory
35+
subject : str
36+
Subject identifier
37+
session : str
38+
Session identifier
39+
task : str
40+
Task identifier
41+
run : str
42+
Run identifier
43+
suffix : str
44+
Suffix of file to load
45+
46+
Returns
47+
-------
48+
data : :class:`physutils.Physio`
49+
Loaded physiological data
50+
"""
51+
_supported_columns = [
52+
"cardiac",
53+
"respiratory",
54+
"trigger",
55+
"rsp",
56+
"ppg",
57+
"tr",
58+
"time",
59+
]
60+
validator = BIDSValidator()
61+
62+
# check if file exists and is in BIDS format
63+
if not op.exists(bids_path):
64+
raise FileNotFoundError(f"Provided path {bids_path} does not exist")
65+
if not validator.is_bids(bids_path):
66+
raise ValueError(f"Provided path {bids_path} is not a BIDS directory")
67+
68+
layout = BIDSLayout(bids_path)
69+
bids_file = layout.get(
70+
subject=subject,
71+
session=session,
72+
task=task,
73+
run=run,
74+
suffix=suffix,
75+
extension=extension,
76+
)[0]
77+
if len(bids_file) == 0:
78+
raise FileNotFoundError(
79+
f"No files found for subject {subject}, session {session}, task {task}, run {run}"
80+
)
81+
if len(bids_file) > 1:
82+
raise ValueError(
83+
f"Multiple files found for subject {subject}, session {session}, task {task}, run {run}"
84+
)
85+
86+
config_file = bids_file.get_metadata()
87+
fs = config_file["SamplingFrequency"]
88+
t_start = config_file["StartTime"] # noqa
89+
columns = config_file["Columns"]
90+
91+
physio_objects = {}
92+
data = np.loadtxt(op.join(bids_file.dirname, bids_file.path))
93+
94+
if "time" in columns:
95+
idx_0 = np.argmax(data[:, columns.index("time")] >= 0)
96+
else:
97+
idx_0 = 0
98+
logger.warning(
99+
"No time column found in file. Assuming data starts at the beginning of the file"
100+
)
101+
102+
for col in columns:
103+
if col not in _supported_columns:
104+
raise ValueError(
105+
f"Column {col} is not supported. Supported columns are {_supported_columns}"
106+
)
107+
if col in ["cardiac", "ppg", "ecg"]:
108+
physio_type = "cardiac"
109+
if col in ["respiratory", "rsp"]:
110+
physio_type = "respiratory"
111+
if col in ["trigger", "tr"]:
112+
physio_type = "trigger"
113+
if col in ["time"]:
114+
continue
115+
116+
if physio_type == "cardiac" or "respiratory":
117+
physio_objects[physio_type] = physio.Physio(
118+
data[idx_0:][columns.index(col)],
119+
fs=fs,
120+
history=[physio._get_call(exclude=[])],
121+
)
122+
physio_objects[physio_type]._physio_type = physio_type
123+
physio_objects[physio_type].label = bids_file.filename.split(".")[
124+
0
125+
].replace("_physio", "")
126+
127+
if physio_type == "trigger":
128+
# TODO: Implement trigger loading using the MRI data object
129+
logger.warning("Trigger loading not yet implemented")
130+
131+
return physio_objects
132+
133+
18134
def load_physio(data, *, fs=None, dtype=None, history=None, allow_pickle=False):
19135
"""
20136
Returns `Physio` object with provided data

physutils/physio.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,12 @@ def new_physio_like(
220220

221221
if suppdata is None:
222222
suppdata = ref_physio._suppdata if copy_suppdata else None
223-
223+
224224
label = ref_physio.label if copy_label else None
225225
physio_type = ref_physio.physio_type if copy_physio_type else None
226-
computed_metrics = list(ref_physio.computed_metrics) if copy_computed_metrics else []
226+
computed_metrics = (
227+
list(ref_physio.computed_metrics) if copy_computed_metrics else []
228+
)
227229

228230
# make new class
229231
out = ref_physio.__class__(

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ install_requires =
2626
numpy >=1.9.3
2727
scipy
2828
loguru
29+
pybids
2930
tests_require =
3031
pytest >=3.6
3132
test_suite = pytest

0 commit comments

Comments
 (0)