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

[MRG] Fix: NaN problem in NiftiMapsMasker #1271

Merged
merged 7 commits into from
Nov 5, 2016

Conversation

KamalakerDadi
Copy link
Contributor

  • Add "ensure_finite" to ilearn.image.clean_img
  • Deal automatically with NaNs in NiftiMapsMasker

Attempts to Fix #1249

@@ -835,6 +836,10 @@ def clean_img(imgs, sessions=None, detrend=True, standardize=True,
t_r: float, optional
Repetition time, in second (sampling period).

ensure_finite: bool, optional
If True, the non-infinite values (NANs and infs) found in the images
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the infinite values (NANs and infs)

@KamalakerDadi
Copy link
Contributor Author

@darya-chyzhyk it would be great if you could look review the PR to see if NaN problem will be solved. Thanks

Copy link
Contributor

@AlexandreAbraham AlexandreAbraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should discuss the case of inifinity values before merging this PR. All NaN-related things are OK to me.

@@ -14,7 +14,7 @@
from .compat import _basestring, get_affine


def _safe_get_data(img):
def _safe_get_data(img, ensure_finite=False):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the description of the parameter in the docstring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its done.

@@ -835,6 +836,10 @@ def clean_img(imgs, sessions=None, detrend=True, standardize=True,
t_r: float, optional
Repetition time, in second (sampling period).

ensure_finite: bool, optional
If True, the infinite values (NANs and infs) found in the images
will be replaced by zeros.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, it is not about "infinite" values but "non-finite" values (because NaN is not an infinity). Second, you rely on np.nan_to_num that set NaNs to 0, Inf to a very big value and NegInf to a very small value. So your doc is not correct.

Although we agree that NaN = 0 in this case, I don't know what we should do with infinities... Maybe raise an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although we agree that NaN = 0 in this case, I don't know what we should do with infinities... Maybe raise an error?

Rather than raising an error. We do what we have done for NaN case. May be convert them equivalently to zeros or less risk value. What do you say ? Can you say we occur infinity errors too more often ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, it is not about "infinite" values but "non-finite" values (because NaN is not an infinity). Second, you rely on np.nan_to_num that set NaNs to 0, Inf to a very big value and NegInf to a very small value. So your doc is not correct.

I followed the same documentation which already written and exists for ensure_finite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are we decided upon ? what do we do with infinite values ?

@@ -106,6 +106,24 @@ def test_nifti_labels_masker():
get_affine(fmri11_img))


def test_nifti_labels_masker_with_nans():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't test infinities, which tells me that this PR is more about NaNs than infinities.

@KamalakerDadi
Copy link
Contributor Author

@aabadie Any hints how to fix this AppVeyor failure ?

@aabadie
Copy link
Contributor

aabadie commented Sep 20, 2016

@KamalakerDadi, I don't know. This seems weird and here for a long time in nilearn. Googling a bit I found other projects complaining about PyQt installation on appveyor. Have you tried this kind of appveyor configuration ?

@KamalakerDadi
Copy link
Contributor Author

KamalakerDadi commented Sep 20, 2016

Have you tried this kind of appveyor configuration ?

@aabadie Thanks, I haven't but I would try. Not immediately but later.

@KamalakerDadi
Copy link
Contributor Author

Can I have reviews please ?

@@ -782,7 +780,8 @@ def math_img(formula, **imgs):


def clean_img(imgs, sessions=None, detrend=True, standardize=True,
confounds=None, low_pass=None, high_pass=None, t_r=2.5):
confounds=None, low_pass=None, high_pass=None, t_r=2.5,
ensure_finite=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it default to False ? (just asking, I have no strong opinion on the question).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think agreement was not fill zeros automatically by default. Full discussion is in #282

# if infinity
data[:, 5, 5] = np.inf
nan_img = nibabel.Nifti1Image(data, np.eye(4))
clean_im = image.clean_img(nan_img, ensure_finite=True)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this behave if ensure_fininte==False ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think image will still have non-finite values and might break somewhere in the analysis.

With the same test code it behaves like this:

nilearn/signal.py:146: RuntimeWarning: invalid value encountered in subtract
  signals -= np.mean(signals, axis=0)
nilearn/signal.py:63: RuntimeWarning: invalid value encountered in less
  std[std < np.finfo(np.float).eps] = 1.  # avoid numerical problems


masker = NiftiLabelsMasker(labels_img, mask_img=mask_img)
sig = masker.fit_transform(fmri_img)
assert_equal(sig.shape, (length, n_regions))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't test whether sig is finite ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

mask_img = nibabel.Nifti1Image(mask_data, np.eye(4))

masker = NiftiMapsMasker(maps_img, mask_img=mask_img)
sig = masker.fit_transform(fmri_img)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its done below.

maps_mask = _utils.as_ndarray(maps_mask, dtype=np.bool)
else:
maps_mask = np.ones(maps_data.shape[:3], dtype=np.bool)
labels = np.arange(maps_data.shape[-1], dtype=np.int)

data = imgs.get_data()
data = _safe_get_data(imgs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensure_finite=True (?)

@KamalakerDadi
Copy link
Contributor Author

The way I am trying to fix this issue in particular NaN or infinite values in NiftiMapsMasker or NiftiLabelsMasker is by always keeping ensure_finite=True with function _safe_get_data(data, ensure_finite=True) in nilearn.regions.signal_extraction.py which is the heart of signal extraction for atlas maskers.

What I think would be instead of keeping ensure_finite=True always we give parameter option to atlas maskers and ask user to give it as True if they occur any error with non-finite values. Which I think would be safer. What do you think ? ping @AlexandreAbraham @GaelVaroquaux @bthirion

This idea came up following discussion in issue #282

@KamalakerDadi KamalakerDadi changed the title [Fix] NaN problem in NiftiMapsMasker [MRG] Fix: NaN problem in NiftiMapsMasker Oct 26, 2016
@KamalakerDadi
Copy link
Contributor Author

Any improvements or reviews on this please ?

if np.any(np.isnan(region_signals)):
ensure_finite = True
else:
ensure_finite = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should always have ensure_finite=True here: it costs as much to check if there is a problem as to fix it.

I'll push these changes directly in your branch, to save time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I've just realized that you are doing the ensure_finite on the signals, while it should really be done on the maps. I'll change that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I think we can remove this whole block ? Since signals that comes out are already replaced by zeros.

@GaelVaroquaux
Copy link
Member

GaelVaroquaux commented Nov 4, 2016 via email

@GaelVaroquaux
Copy link
Member

Done. Beware that I pushed changes to your branch. You need to pull.

@KamalakerDadi
Copy link
Contributor Author

This PR also has NiftiLabelsMasker related changes. Should I have been specific to Maps masker only ?

@GaelVaroquaux
Copy link
Member

This PR also has NiftiLabelsMasker related changes. Should I have been specific
to Maps masker only ?

Indeed.

The docstring was no longer valid. I have just fixed it.

@GaelVaroquaux GaelVaroquaux dismissed AlexandreAbraham’s stale review November 5, 2016 08:11

The changes requested have been performed.

@GaelVaroquaux
Copy link
Member

This is good to go. Merging.

@GaelVaroquaux GaelVaroquaux merged commit d4c248f into nilearn:master Nov 5, 2016
@KamalakerDadi
Copy link
Contributor Author

Thank you very much for your contribution and merging @GaelVaroquaux

@GaelVaroquaux
Copy link
Member

@KamalakerDadi, thanks for doing the work!

mrahim pushed a commit to mrahim/nilearn that referenced this pull request Dec 7, 2016
AlexandreAbraham added a commit to AlexandreAbraham/nilearn that referenced this pull request Aug 27, 2019
PR nilearn#1271 introduces this regression where ensure_finite=True modifies the data inplace instead of copying it (nan_to_num does an implicit copy). I preferred to get back to nan_to_num instead of just doing a copy because I believe that relying on numpy functions is safer and more future proof.
kchawla-pi pushed a commit that referenced this pull request Oct 9, 2019
* Remove inplace modification in signal.clean

PR #1271 introduces this regression where ensure_finite=True modifies the data inplace instead of copying it (nan_to_num does an implicit copy). I preferred to get back to nan_to_num instead of just doing a copy because I believe that relying on numpy functions is safer and more future proof.

* Do the replacement manually to be compliant with older numpy

* Added test to verify no inplace modification occurs when ensure_finite=True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NaN problem in NiftiMapsMasker
6 participants