Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add keep_masked_maps to NiftiMapsMasker #3732

Merged
merged 15 commits into from
Jul 8, 2023
23 changes: 21 additions & 2 deletions nilearn/maskers/nifti_maps_masker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ class _ExtractionFunctor:

func_name = 'nifti_maps_masker_extractor'

def __init__(self, _resampled_maps_img_, _resampled_mask_img_):
def __init__(self, _resampled_maps_img_, _resampled_mask_img_,
keep_masked_maps):
self._resampled_maps_img_ = _resampled_maps_img_
self._resampled_mask_img_ = _resampled_mask_img_
self.keep_masked_maps = keep_masked_maps

def __call__(self, imgs):
from ..regions import signal_extraction

return signal_extraction.img_to_signals_maps(
imgs, self._resampled_maps_img_,
mask_img=self._resampled_mask_img_)
mask_img=self._resampled_mask_img_,
keep_masked_maps=self.keep_masked_maps)


@_utils.fill_doc
Expand Down Expand Up @@ -74,6 +77,18 @@ class NiftiMapsMasker(BaseMasker, _utils.CacheMixin):
%(memory)s
%(memory_level)s
%(verbose0)s

keep_masked_maps : :obj:`bool`, optional
htwangtw marked this conversation as resolved.
Show resolved Hide resolved
If False, maps that lie completely outside the mask are dropped from
the output. If True, they are kept, meaning that maps that are
completely zero can occur in the output.
Default=True.

.. deprecated:: 0.9.2
The 'True' option for ``keep_masked_maps`` is deprecated.
The default value will change to 'False' in 0.13,
and the ``keep_masked_maps`` parameter will be removed in 0.15.

reports : :obj:`bool`, optional
If set to True, data is saved in order to produce a report.
Default=True.
Expand Down Expand Up @@ -121,6 +136,7 @@ def __init__(
t_r=None,
dtype=None,
resampling_target="data",
keep_masked_maps=True,
memory=Memory(location=None, verbose=0),
memory_level=0,
verbose=0,
Expand Down Expand Up @@ -178,6 +194,8 @@ def __init__(
"Set resampling_target to something else or provide a mask."
)

self.keep_masked_maps = keep_masked_maps

def generate_report(self, displayed_maps=10):
"""Generate an HTML report for the current ``NiftiMapsMasker`` object.

Expand Down Expand Up @@ -556,6 +574,7 @@ def transform_single_imgs(self, imgs, confounds=None, sample_mask=None):
imgs, _ExtractionFunctor(
self._resampled_maps_img_,
self._resampled_mask_img_,
self.keep_masked_maps,
),
# Pre-treatments
params,
Expand Down
34 changes: 32 additions & 2 deletions nilearn/regions/signal_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# Author: Philippe Gervais
# License: simplified BSD

import warnings

import numpy as np
from scipy import linalg, ndimage

Expand Down Expand Up @@ -376,7 +378,10 @@ def signals_to_img_labels(


@_utils.fill_doc
def img_to_signals_maps(imgs, maps_img, mask_img=None):
def img_to_signals_maps(
imgs, maps_img, mask_img=None,
keep_masked_maps=True
):
"""Extract region signals from image.

This function is applicable to regions defined by maps.
Expand All @@ -397,6 +402,17 @@ def img_to_signals_maps(imgs, maps_img, mask_img=None):
Every point outside the mask is considered
as background (i.e. outside of any region).

keep_masked_maps : :obj:`bool`, optional
If False, maps that lie completely outside the mask are dropped from
the output. If True, they are kept, meaning that maps that are
completely zero can occur in the output.
Default=True.

.. deprecated:: 0.9.2
The 'True' option for ``keep_masked_maps`` is deprecated.
The default value will change to 'False' in 0.13,
and the ``keep_masked_maps`` parameter will be removed in 0.15.

Returns
-------
region_signals : :class:`numpy.ndarray`
Expand Down Expand Up @@ -430,9 +446,23 @@ def img_to_signals_maps(imgs, maps_img, mask_img=None):
maps_data, maps_mask, labels = _trim_maps(
maps_data,
_safe_get_data(mask_img, ensure_finite=True),
keep_empty=True,
keep_empty=keep_masked_maps,
)
maps_mask = _utils.as_ndarray(maps_mask, dtype=bool)
if keep_masked_maps:
warnings.warn(
bthirion marked this conversation as resolved.
Show resolved Hide resolved
'Starting in version 0.15 the maps in "maps_img" '
'that are masked by "mask_img" will be removed from '
'output of "NiftiMapsMasker" signal extraction. '
'Until then, "keep_masked_labels=True" will produce '
'the old behavior. '
'The default behavior of "NiftiMapsMasker" will be '
'changed to "keep_masked_maps=False" in version 0.13 '
'and "keep_masked_maps" parameter will be removed '
'in version 0.15.',
DeprecationWarning,
stacklevel=2
)

data = _safe_get_data(imgs, ensure_finite=True)
region_signals = linalg.lstsq(maps_data[maps_mask, :], data[maps_mask, :])[
Expand Down
12 changes: 12 additions & 0 deletions nilearn/regions/tests/test_signal_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,18 @@ def test_signal_extraction_with_maps_and_labels(labeled_regions, fmri_img):
maps_img_r = signals_to_img_maps(maps_signals, maps_img, mask_img=mask_img)
assert maps_img_r.shape == SHAPE + (N_TIMEPOINTS,)

# apply img_to_signals_maps with a masking,
# containing only 3 regions, but
# not keeping the masked maps

maps_signals, maps_labels = img_to_signals_maps(
fmri_img, maps_img, mask_img=mask_img,
keep_masked_maps=False
)
# only 3 regions must be kept, others must be removed
assert maps_signals.shape == (N_TIMEPOINTS, 3)
assert len(maps_labels) == 3


def test_signal_extraction_nans_in_regions_are_replaced_with_zeros():
shape = (4, 5, 6)
Expand Down