Skip to content

Commit 915269d

Browse files
committed
ENH: Add support for alternative age units
1 parent a01b1c8 commit 915269d

File tree

1 file changed

+60
-18
lines changed

1 file changed

+60
-18
lines changed

nibabies/utils/bids.py

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import warnings
1212
from pathlib import Path
1313

14+
SUPPORTED_AGE_UNITS = ('weeks', 'months', 'years')
15+
1416

1517
def write_bidsignore(deriv_dir):
1618
# TODO: Port to niworkflows
@@ -237,26 +239,22 @@ def parse_bids_for_age_months(
237239

238240

239241
def _get_age_from_tsv(
240-
bids_tsv: Path, level: ty.Literal['session', 'participant'], key: str
242+
bids_tsv: Path,
243+
level: ty.Literal['session', 'participant'],
244+
key: str,
241245
) -> int | None:
242246
import pandas as pd
243247

244248
df = pd.read_csv(str(bids_tsv), sep='\t')
245249
age_col = None
246-
# prefer explicit "age_months" over "age"
247-
for c in ('age_months', 'age'):
248-
if c in df.columns:
249-
age_col = c
250+
251+
for column in ('age_weeks', 'age_months', 'age_years', 'age'):
252+
if column in df.columns:
253+
age_col = column
250254
break
251255

252-
if age_col == 'age':
253-
# verify age is in months
254-
bids_json = bids_tsv.with_suffix('.json')
255-
if not _verify_age_json(bids_json):
256-
warnings.warn(
257-
f'Could not verify age column is in months for file: {bids_tsv}',
258-
stacklevel=1,
259-
)
256+
if age_col is None:
257+
return
260258

261259
# find the relevant row
262260
if level == 'session':
@@ -268,13 +266,57 @@ def _get_age_from_tsv(
268266
# extract age value from row
269267
age = int(df.loc[mask, age_col].values[0])
270268
except Exception: # noqa: BLE001
271-
age = None
272-
return age
269+
return
273270

271+
if age_col == 'age':
272+
# verify age is in months
273+
bids_json = bids_tsv.with_suffix('.json')
274+
age_units = _get_age_units(bids_json)
275+
if age_units is False:
276+
warnings.warn(
277+
f'Could not verify age units for file: {bids_tsv}',
278+
stacklevel=1,
279+
)
280+
age_units = 'months'
281+
else:
282+
age_units = age_col.split('_')[-1]
283+
284+
age_months = age_to_months(age, units=age_units)
285+
return age_months
274286

275-
def _verify_age_json(bids_json: Path) -> bool:
287+
288+
def _get_age_units(bids_json: Path) -> ty.Literal['weeks', 'months', 'years', False]:
276289
try:
277290
data = json.loads(bids_json.read_text())
278-
return data['age']['Units'].lower() == 'months'
279-
except Exception: # noqa: BLE001
291+
except json.JSONDecodeError:
280292
return False
293+
294+
# See if the unit is listed
295+
units = data.get('age', {}).get('Units')
296+
for unit in units:
297+
if unit.lower() in SUPPORTED_AGE_UNITS:
298+
return unit
299+
return False
300+
301+
302+
def age_to_months(age: int, units: ty.Literal['weeks', 'months', 'years']) -> int:
303+
"""
304+
Convert a given age, in either "weeks", "months", or "years", into months.
305+
306+
>>> age_to_months(1, 'years')
307+
12
308+
>>> age_to_months(2, 'weeks')
309+
0
310+
>>> age_to_months(3, 'weeks')
311+
1
312+
>>> age_to_months(8, 'months')
313+
8
314+
"""
315+
WEEKS_TO_MONTH = 0.230137
316+
YEARS_TO_MONTH = 12
317+
318+
if units == 'weeks':
319+
age = round(age * WEEKS_TO_MONTH)
320+
elif units == 'years':
321+
age *= YEARS_TO_MONTH
322+
return age

0 commit comments

Comments
 (0)