Skip to content

Commit

Permalink
More unit tests, physio/metric wrapper optimization, doc fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
maestroque committed Aug 21, 2024
1 parent b0f4110 commit 10509bd
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"contributions": [
"code",
"ideas",
"infra",
"docs",
"bug",
"review"
]
Expand Down
10 changes: 5 additions & 5 deletions phys2denoise/metrics/cardiac.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def _cardiac_metrics(
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded cardiac signal
metrics : "hbi", "hr", "hrv", string
Cardiac metric(s) to calculate.
Expand Down Expand Up @@ -164,7 +164,7 @@ def heart_rate(data, fs=None, peaks=None, window=6, central_measure="mean", **kw
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded cardiac signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -234,7 +234,7 @@ def heart_rate_variability(
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded cardiac signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -298,7 +298,7 @@ def heart_beat_interval(
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded cardiac signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -358,7 +358,7 @@ def cardiac_phase(data, slice_timings, n_scans, t_r, fs=None, peaks=None, **kwar
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded cardiac signal
slice_timings : 1D array_like
Slice times, in seconds.
Expand Down
10 changes: 5 additions & 5 deletions phys2denoise/metrics/chest_belt.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def respiratory_variance_time(
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -118,7 +118,7 @@ def respiratory_pattern_variability(data, window, **kwargs):
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory signal
window : int
Window length in samples.
Expand Down Expand Up @@ -166,7 +166,7 @@ def env(data, fs=None, window=10, **kwargs):
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -250,7 +250,7 @@ def respiratory_variance(data, fs=None, window=6, **kwargs):
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory signal
fs : float, optional if data is a physutils.Physio
Sampling rate of `data` in Hz
Expand Down Expand Up @@ -315,7 +315,7 @@ def respiratory_phase(data, n_scans, slice_timings, t_r, fs=None, **kwargs):
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory signal
n_scans
Number of volumes in the imaging run.
Expand Down
8 changes: 6 additions & 2 deletions phys2denoise/metrics/multimodal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from ..due import due
from .cardiac import cardiac_phase
from .chest_belt import respiratory_phase
from .utils import return_physio_or_metric


@due.dcite(references.GLOVER_2000)
@return_physio_or_metric()
@physio.make_operation()
def retroicor(
data,
Expand All @@ -26,7 +28,7 @@ def retroicor(
Parameters
----------
data : Physio_like
data : physutils.Physio, np.ndarray, or array-like object
Object containing the timeseries of the recorded respiratory or cardiac signal
t_r : float
Imaging sample rate, in seconds.
Expand Down Expand Up @@ -138,4 +140,6 @@ def retroicor(
data._computed_metrics["retroicor_regressors"] = dict(
metrics=retroicor_regressors, phase=phase
)
return data, retroicor_regressors, phase
retroicor_regressors = dict(metrics=retroicor_regressors, phase=phase)

return data, retroicor_regressors
25 changes: 23 additions & 2 deletions phys2denoise/metrics/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Miscellaneous utility functions for metric calculation."""
import functools
import inspect
import logging

import numpy as np
Expand Down Expand Up @@ -353,7 +354,6 @@ def return_physio_or_metric(*, return_physio=True):
"""

def determine_return_type(func):
metrics_with_lags = ["respiratory_variance_time"]
convolved_metrics = [
"respiratory_variance",
"heart_rate",
Expand All @@ -364,8 +364,15 @@ def determine_return_type(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
physio, metric = func(*args, **kwargs)
default_args = get_default_args(func)
if isinstance(args[0], Physio):
has_lags = True if func.__name__ in metrics_with_lags else False
if "lags" in kwargs:
has_lags = True if len(kwargs["lags"]) > 1 else False
elif "lags" in default_args:
has_lags = True if len(default_args["lags"]) > 1 else False
else:
has_lags = False

is_convolved = True if func.__name__ in convolved_metrics else False

physio._computed_metrics[func.__name__] = Metric(
Expand All @@ -389,6 +396,20 @@ def wrapper(*args, **kwargs):
return determine_return_type


def get_default_args(func):
# Get the signature of the function
sig = inspect.signature(func)

# Extract default values for each parameter
defaults = {
k: v.default
for k, v in sig.parameters.items()
if v.default is not inspect.Parameter.empty
}

return defaults


class Metric:
def __init__(self, name, data, args, has_lags=False, is_convolved=False):
self.name = name
Expand Down
39 changes: 39 additions & 0 deletions phys2denoise/tests/test_rvt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import peakdet
from physutils import io, physio

from phys2denoise.metrics.chest_belt import respiratory_variance_time

Expand All @@ -23,3 +24,41 @@ def test_respiratory_variance_time(fake_phys):
)
assert r is not None
assert len(r) == 18750


def test_respiratory_variance_time(fake_phys):
phys = peakdet.Physio(fake_phys, fs=62.5)
phys = peakdet.operations.filter_physio(phys, cutoffs=3, method="lowpass")
phys = peakdet.operations.peakfind_physio(phys)

# TODO: Change to a simpler call once physutils are
# integrated to peakdet/prep4phys
r = respiratory_variance_time(
phys.data, fs=phys.fs, peaks=phys.peaks, troughs=phys.troughs
)
assert r is not None
assert len(r) == 18750


def test_respiratory_variance_time_physio_obj(fake_phys):
phys = peakdet.Physio(fake_phys, fs=62.5)
phys = peakdet.operations.filter_physio(phys, cutoffs=3, method="lowpass")
phys = peakdet.operations.peakfind_physio(phys)

# TODO: Change to a simpler call once physutils are
# integrated to peakdet/prep4phys
physio_obj = physio.Physio(phys.data, phys.fs)
physio_obj._metadata["peaks"] = phys.peaks
physio_obj._metadata["troughs"] = phys.troughs
physio_obj = respiratory_variance_time(physio_obj)

assert (
physio_obj.history[0][0]
== "phys2denoise.metrics.chest_belt.respiratory_variance_time"
)
assert physio_obj.computed_metrics["respiratory_variance_time"].ndim == 2
assert physio_obj.computed_metrics["respiratory_variance_time"].shape == (18750, 4)
assert physio_obj.computed_metrics["respiratory_variance_time"].has_lags == True

# assert r is not None
# assert len(r) == 18750

0 comments on commit 10509bd

Please sign in to comment.