Skip to content

Commit

Permalink
Merge pull request #35 from juaml/enh/falff
Browse files Browse the repository at this point in the history
[MARKER]: (f)ALFF
  • Loading branch information
synchon authored Dec 19, 2022
2 parents ade3974 + 08154f9 commit 49b17f9
Show file tree
Hide file tree
Showing 13 changed files with 1,367 additions and 4 deletions.
12 changes: 8 additions & 4 deletions docs/builtin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,14 @@ Available
- Calculate regional homogeneity over spheres placed on coordinates
- Done
- 0.0.1

* - :class:`junifer.markers.AmplitudeLowFrequencyFluctuationParcels`
- Calculate (f)ALFF and aggregate using parcellations
- Done
- 0.0.1
* - :class:`junifer.markers.AmplitudeLowFrequencyFluctuationSpheres`
- Calculate (f)ALFF and aggregate using spheres placed on coordinates
- Done
- 0.0.1

Planned
~~~~~~~
Expand All @@ -189,9 +196,6 @@ Planned
* - Connectedness
- Compute connectedness
- :gh:`34`
* - ALFF and (f)ALFF
- Detect amplitude of low-frequency fluctuation (ALFF) for resting-state fMRI
- :gh:`35`
* - Permutation entropy, Range entropy, Multiscale entropy and Hurst exponent
- Calculate Permutation entropy, Range entropy, Multiscale entropy and Hurst exponent
- :gh:`61`
Expand Down
2 changes: 2 additions & 0 deletions docs/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Enhancements

- Implement :class:`junifer.markers.ReHoParcels` and :class:`junifer.markers.ReHoSpheres` markers (:gh:`36` by `Synchon Mandal`_).

- Implement :class:`junifer.markers.AmplitudeLowFrequencyFluctuationParcels` and :class:`junifer.markers.AmplitudeLowFrequencyFluctuationSpheres` markers (:gh:`35` by `Fede Raimondo`_).

Bugs
~~~~

Expand Down
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@
"class",
"objects",
"Engine",
"positive",
"negative",
}
# numpydoc_validation_checks = {
# "all",
Expand Down
3 changes: 3 additions & 0 deletions junifer/api/res/afni/3dRSFC
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

run_afni_docker.sh 3dRSFC "$@"
4 changes: 4 additions & 0 deletions junifer/markers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
from .parcel_aggregation import ParcelAggregation
from .sphere_aggregation import SphereAggregation
from .reho import ReHoParcels, ReHoSpheres
from .falff import (
AmplitudeLowFrequencyFluctuationParcels,
AmplitudeLowFrequencyFluctuationSpheres,
)
7 changes: 7 additions & 0 deletions junifer/markers/falff/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Provide imports for falff sub-package."""

# Authors: Federico Raimondo <[email protected]>
# License: AGPL

from .falff_parcels import AmplitudeLowFrequencyFluctuationParcels
from .falff_spheres import AmplitudeLowFrequencyFluctuationSpheres
180 changes: 180 additions & 0 deletions junifer/markers/falff/falff_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""Provide abstract class for computing fALFF."""

# Authors: Federico Raimondo <[email protected]>
# Amir Omidvarnia <[email protected]>
# Kaustubh R. Patil <[email protected]>
# License: AGPL

from typing import Dict, List, Optional

from abc import abstractmethod

from ..base import BaseMarker
from .falff_estimator import AmplitudeLowFrequencyFluctuationEstimator
from ...utils.logging import raise_error


class AmplitudeLowFrequencyFluctuationBase(BaseMarker):
"""Base class for (fractional) Amplitude Low Frequency Fluctuation.
Parameters
----------
fractional : bool
Whether to compute fractional ALFF.
highpass : positive float
Highpass cutoff frequency.
lowpass : positive float
Lowpass cutoff frequency.
tr : positive float, optional
The Repetition Time of the BOLD data. If None, will extract
the TR from NIFTI header (default None).
use_afni : bool, optional
Whether to use AFNI for computing. If None, will use AFNI only
if available (default None).
name : str, optional
The name of the marker. If None, it will use the class name
(default None).
Notes
-----
The `tr` parameter is crucial for the correctness of fALFF/ALFF
computation. If a dataset is correctly preprocessed, the TR should be
extracted from the NIFTI without any issue. However, it has been
reported that some preprocessed data might not have the correct TR in
the NIFTI header.
"""

_EXT_DEPENDENCIES = [
{
"name": "afni",
"optional": True,
"commands": ["3dRSFC", "3dAFNItoNIFTI"],
},
]

def __init__(
self,
fractional: bool,
highpass: float,
lowpass: float,
tr: Optional[float] = None,
use_afni: Optional[bool] = None,
name: Optional[str] = None,
) -> None:
if highpass <= 0:
raise_error("Highpass must be positive")
if lowpass <= 0:
raise_error("Lowpass must be positive")
if highpass >= lowpass:
raise_error("Highpass must be lower than lowpass")
self.highpass = highpass
self.lowpass = lowpass
self.tr = tr
self.use_afni = use_afni
self.fractional = fractional

# Create a name based on the class name if none is provided
if name is None:
suffix = "_fractional" if fractional else ""
name = f"{self.__class__.__name__}{suffix}"
super().__init__(on="BOLD", name=name)

def get_valid_inputs(self) -> List[str]:
"""Get valid data types for input.
Returns
-------
list of str
The list of data types that can be used as input for this marker.
"""
return ["BOLD"]

def get_output_type(self, input_type: str) -> str:
"""Get output type.
Parameters
----------
input_type : str
The data type input to the marker.
Returns
-------
str
The storage type output by the marker.
"""
return "table"

def compute(
self,
input: Dict[str, Dict],
extra_input: Optional[Dict] = None,
) -> Dict:
"""Compute.
Parameters
----------
input : dict
A single input from the pipeline data object in which to compute
the marker.
extra_input : dict, optional
The other fields in the pipeline data object. Useful for accessing
other data kind that needs to be used in the computation. For
example, the functional connectivity markers can make use of the
confounds if available (default None).
Returns
-------
dict
The computed result as dictionary. This will be either returned
to the user or stored in the storage by calling the store method
with this as a parameter. The dictionary has the following keys:
* ``data`` : the actual computed values as a numpy.ndarray
* ``columns`` : the column labels for the computed values as a list
* ``row_names`` (if more than one row is present in data): "scan"
"""
if self.use_afni is None:
raise_error(
"Parameter `use_afni` must be set to True or False in order "
"to compute this marker. It is currently set to None (default "
"behaviour). This is intended to be for auto-detection. In "
"order for that to happen, please call the `validate` method "
"before calling the `compute` method."
)

estimator = AmplitudeLowFrequencyFluctuationEstimator()

alff, falff = estimator.fit_transform(
use_afni=self.use_afni,
input_data=input,
highpass=self.highpass,
lowpass=self.lowpass,
tr=self.tr,
)
post_data = falff if self.fractional else alff

post_input = {
"data": post_data,
"path": None,
}

out = self._postprocess(post_input)

return out

@abstractmethod
def _postprocess(self, input: Dict) -> Dict:
"""Postprocess the output of the estimator.
Parameters
----------
input : dict
The output of the estimator. It must have the following
"""
raise_error(
"_postprocess must be implemented", klass=NotImplementedError
)
Loading

0 comments on commit 49b17f9

Please sign in to comment.