Integrate physutils - Physio object usage#54
Conversation
|
Currently tests are failing, because the |
m-miedema
left a comment
There was a problem hiding this comment.
The main thing that I think we need to discuss is checking for peaks/troughs where appropriate in the Physio object. I'm not sure the best way to do this (and we've talked about maintaining functionality for non-Physio object time-series, so we'll need to think about that in this context too). Many parameters e.g. TR could equally be associated with a Physio object, so we need a clear plan that checks for parameters not supplied explicitly but allows flexibility for a non-Physio implementation.
| sample_rate : float | ||
| Physio sample rate, in Hertz. | ||
| data : Physio_like | ||
| Object containing the timeseries of the recorded respiratory or cardiac signal |
There was a problem hiding this comment.
I would suggest including a bit more information here, especially that the object should also contain associated meta-data.
There was a problem hiding this comment.
(I think it's worth a discussion where we talk about advantages of forcing some parameters to be in the Physio object vs. explicitly specified before we go too far down this road!)
|
I believe the support for Physio and non-Physio inputs has been completed. @m-miedema @me-pic let me know your thoughts The unit tests now need updating. |
|
In CRF, iCRF and RRF there is this line: time_stamps = np.arange(0, time_length, 1 / samplerate)Since samplerate is in seconds, shouldn't it be time_stamps = np.arange(0, time_length, samplerate)Do you prefer changing the doc to Hz or is it more convinient in seconds? |
|
Also in general, shouldn't we opt in a single way to define sample rate? Is there any reason to not have everything in Hz? |
| assert rrf_arr.size == pred_len | ||
|
|
||
|
|
||
| @mark.xfail |
There was a problem hiding this comment.
Not sure why this was marked as xfail, but now it's fixed and it passes.
|
@m-miedema metrics calculations are already integrated with the |
|
Currently blocked by physiopy/physutils#4, due to important physio object fix (release needed): 0c324c5 and 2b0deb9 |
|
@smoia @me-pic @m-miedema I added a wrapper to sort out the returns (only metric or physio with metric), feel free to review.
Also, the following changes were made to deps:
|
|
I believe the Metric class will make it much easier for the pydra tasks to handle computing and exporting metrics |
| install_requires = | ||
| numpy >=1.9.3 | ||
| matplotlib >=3.1.1 | ||
| numpy >=1.9.3, <2 |
There was a problem hiding this comment.
Can you open an issue to state we need to refactor code to handle numpy >=2.0, please?
(It's good to leave it like this for the moment, though, thanks!)
| pytest >=5.3 | ||
| pytest-cov | ||
| peakdet | ||
| peakdet>=0.5.0 |
There was a problem hiding this comment.
With the physutils integration, do we still need peakdet?
There was a problem hiding this comment.
yes, there is a unit test with peak finding
phys2denoise/metrics/utils.py
Outdated
| """ | ||
|
|
||
| def determine_return_type(func): | ||
| metrics_with_lags = ["respiratory_variance_time"] |
There was a problem hiding this comment.
We did not implemented it yet, but technically all metrics could have lags - and all metrics could be convolved. Is this a way to check the type of output you get?
There was a problem hiding this comment.
Could you instead make a check on the provided arguments to the function, to check whether convolution and/or lag happened?
There was a problem hiding this comment.
I'll change to detect lagged metrics, since it's dependent on the argument. However convolution is now only done in some metrics without being specified in the arguments and can't be detected easily if you don't read the code. Also you can't deduce that the metric is convolved from the dimensions of the metric output.
I can open an issue about it if you want
There was a problem hiding this comment.
An issue sounds good to me, you're right that we can improve transparency and flexibility for those operations as we build up the toolbox. If you don't have time I can open one, let me know your preference @maestroque :)
| deep breaths," Neuroimage, vol. 204, 2020. | ||
| """ | ||
|
|
||
| def _respiratory_pattern_variability(data, window): |
There was a problem hiding this comment.
Now that you can either input a physio object or a numpy array, do we still need to duplicate code for this application?
There was a problem hiding this comment.
That's true the relevant line in env() could be changed as such:
env_arr = (
pd.Series(data.data)
.rolling(window=window, center=True)
.apply(respiratory_pattern_variability, args=(window,), kwargs=dict(return_physio=False))
)However we should also include support for pandas.core.series.Series types in the load_physio function for that to work, which also impacts the other module. Still, I think it'd be better to open an issue about it, because it complicates the PRs
Wdyty @smoia
phys2denoise/metrics/chest_belt.py
Outdated
| @return_physio_or_metric() | ||
| @physio.make_operation() | ||
| def respiratory_variance_time( | ||
| data, fs=None, peaks=None, troughs=None, lags=(0, 4, 8, 12), **kwargs |
There was a problem hiding this comment.
| data, fs=None, peaks=None, troughs=None, lags=(0, 4, 8, 12), **kwargs | |
| data, peaks=None, troughs=None, fs=None, lags=(0, 4, 8, 12), **kwargs |
There was a problem hiding this comment.
It would be good to have peaks and troughs first when possible (and, in general, avoid changing the order of inputs as much as possible), but if it makes things too complicated with the wrapper, then no problem.
smoia
left a comment
There was a problem hiding this comment.
Couple of notes for the moment!
phys2denoise/metrics/chest_belt.py
Outdated
| @return_physio_or_metric() | ||
| @physio.make_operation() | ||
| def respiratory_variance_time( | ||
| data, fs=None, peaks=None, troughs=None, lags=(0, 4, 8, 12), **kwargs |
There was a problem hiding this comment.
It would be good to have peaks and troughs first when possible (and, in general, avoid changing the order of inputs as much as possible), but if it makes things too complicated with the wrapper, then no problem.
phys2denoise/metrics/utils.py
Outdated
| """ | ||
|
|
||
| def determine_return_type(func): | ||
| metrics_with_lags = ["respiratory_variance_time"] |
There was a problem hiding this comment.
Could you instead make a check on the provided arguments to the function, to check whether convolution and/or lag happened?
phys2denoise/metrics/chest_belt.py
Outdated
| troughs found by peakdet algorithm | ||
| samplerate: float | ||
| sample rate in hz of respiratory belt | ||
| data : Physio_like |
There was a problem hiding this comment.
Could we change this to physutils.Physio, np.ndarray, or array-like object instead?
That's also possible now, right?
| fluctuations in fMRI”, NeuroImage, vol.31, pp. 1536-1548, 2006. | ||
| """ | ||
| timestep = 1 / samplerate | ||
| if isinstance(data, physio.Physio): |
There was a problem hiding this comment.
I was that you repeat this few following lines often in different metrics - if that is the case, I would suggest to make it a function on its own or even better to add it to the decorator you created. If you make it a function, the return can be done within the metric function itself (without the wrapper), if you move this to the decorator (better), you can revert to the original metric functions, and deal with the input and returns of the metrics within the wrapper!
There was a problem hiding this comment.
I tried to think of a cleaner way, but tbh the error handling cannot be abstracted efficiently, because there are some differences between functions using fs, peaks, troughs and combinations of them. Effectively, if an abstraction would be made it would almost have to be a separate handle case for each function. It could be done by parsing the argument name list in the wrapper but I don't think it can be much cleaner than what exists
|
@smoia please recheck when you find some time |
| function should give an error.""" | ||
| arr = np.array(short_arr) | ||
| with pytest.raises(Exception) as e_info: | ||
| with pytest.raises(IndexError): |
There was a problem hiding this comment.
I get the expected logger warning here, so the test fails. I'm guessing this one is what you were referring to as the failed catch here? If so, I'm wondering what your suggestion is for handling it to catch the warning instead.
There was a problem hiding this comment.
Yes that's the one. It's not detected because it's being handled. I'll just add a check that the subsequent warning is raised
There was a problem hiding this comment.
It's a bit convoluted though and it's pending a full integration of loguru in this package as well. I'd say we merge and document in the issue/PR #53
There was a problem hiding this comment.
I did a quick fix and it detects the expected warning text (as in the peakdet warning assertion tests I did), however the full integration of loguru needs to be adressed: Relevant note
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #54 +/- ##
==========================================
+ Coverage 47.04% 53.85% +6.81%
==========================================
Files 7 8 +1
Lines 321 596 +275
==========================================
+ Hits 151 321 +170
- Misses 170 275 +105
|
|
Now, CI passes online as well, because of the |
Proposed Changes
Physioobject for the metric functionsChange Type
bugfix(+0.0.1)minor(+0.1.0)major(+1.0.0)refactoring(no version update)test(no version update)infrastructure(no version update)documentation(no version update)otherChecklist before review