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

[ENH] Add new returns to NiftiLabelsMasker's transform output #3761

Merged
merged 28 commits into from Nov 29, 2023

Conversation

mtorabi59
Copy link
Contributor

Addresses #3085 .

Changes proposed in this pull request:

  • add new outputs to NiftiLabelsMasker transform_single_imgs besides region_signals, including: region_ids and region_img

@github-actions
Copy link
Contributor

👋 @mtorabi59 Thanks for creating a PR!

Until this PR is ready for review, you can include the [WIP] tag in its title, or leave it as a github draft.

Please make sure it is compliant with our contributing guidelines. In particular, be sure it checks the boxes listed below.

  • PR has an interpretable title.
  • PR links to Github issue with mention Closes #XXXX (see our documentation on PR structure)
  • Code is PEP8-compliant (see our documentation on coding style)
  • Changelog or what's new entry in doc/changes/latest.rst (see our documentation on PR structure)

For new features:

  • There is at least one unit test per new function / class (see our documentation on testing)
  • The new feature is demoed in at least one relevant example.

For bug fixes:

  • There is at least one test that would fail under the original bug conditions.

We will review it as quick as possible, feel free to ping us with questions if needed.

@mtorabi59
Copy link
Contributor Author

Hi,

I want to add new outputs to transform_single_imgs as we discussed, but this causes some tests to fail because the function is now returning more than one output. What is the best way to handle this?

@mtorabi59
Copy link
Contributor Author

Also, I can add region_names as another output too, which is different from region_ids. Or it can now be obtained by labels[region_ids[i]] for the signal in region_signals[:, i].

@ymzayek
Copy link
Member

ymzayek commented Jun 16, 2023

@mtorabi59 it's a breaking change so we would have to go with a deprecation. However, I need to brush up a bit on the issue. I'm wondering if it would make sense to instead add the new returns as attributes to the NiftiLabelsMasker class.

@mtorabi59
Copy link
Contributor Author

mtorabi59 commented Jun 16, 2023

@mtorabi59 it's a breaking change so we would have to go with a deprecation. However, I need to brush up a bit on the issue. I'm wondering if it would make sense to instead add the new returns as attributes to the NiftiLabelsMasker class.

@ymzayek I think that makes sense. because they are not depending on fmri_img given to transform_single_imgs. I can do that👍

@mtorabi59
Copy link
Contributor Author

I face a problem here because I'm adding a new output to img_to_signals_labels but in this line it expects only 2 outputs. Should I combine the two last outputs, e.g. put them in a dict, or change BaseMasker?

@bthirion
Copy link
Member

Actually, we should strive to have always the same outputs for a given function. Using a dictionary is a bit cheating and will make it harder to find bugs I'm afraid. Isn't there a way to change systematically the output of this function ?

@mtorabi59
Copy link
Contributor Author

Actually, we should strive to have always the same outputs for a given function. Using a dictionary is a bit cheating and will make it harder to find bugs I'm afraid. Isn't there a way to change systematically the output of this function ?

Yes, I agree. Do you mean the output of img_to_signals_labels or _filter_and_extract? could you clarify what you mean by systematically changing it?

@bthirion
Copy link
Member

I just mean that this function is called from many different places in the codebase. So the change may be quite important.

@mtorabi59
Copy link
Contributor Author

So I added a optional argument called return_masked_atlas to img_to_signals_labels. If True, it will also return masked_atlas or masked_labels_img. Then, this will be used to set the new attribute of NiftiLabelsMasker called region_atlas_.

@codecov
Copy link

codecov bot commented Aug 8, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (c16146a) 91.81% compared to head (50e57fc) 91.82%.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3761   +/-   ##
=======================================
  Coverage   91.81%   91.82%           
=======================================
  Files         144      144           
  Lines       16173    16187   +14     
  Branches     3360     3364    +4     
=======================================
+ Hits        14850    14864   +14     
  Misses        781      781           
  Partials      542      542           
Flag Coverage Δ
macos-latest_3.10_test_plotting 91.65% <100.00%> (?)
macos-latest_3.11_test_plotting 91.65% <100.00%> (?)
macos-latest_3.12_test_plotting 91.65% <100.00%> (?)
macos-latest_3.8_test_plotting 91.61% <100.00%> (?)
macos-latest_3.9_test_plotting 91.61% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.10_test_plotting 91.65% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.11_test_plotting 91.65% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.12_test_plotting 91.65% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.12_test_pre 91.65% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.8_test_min 69.19% <100.00%> (+0.02%) ⬆️
ubuntu-latest_3.8_test_plot_min 91.37% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.8_test_plotting 91.61% <100.00%> (+<0.01%) ⬆️
ubuntu-latest_3.9_test_plotting 91.61% <100.00%> (+<0.01%) ⬆️
windows-latest_3.10_test_plotting 91.62% <100.00%> (+<0.01%) ⬆️
windows-latest_3.11_test_plotting 91.62% <100.00%> (?)
windows-latest_3.12_test_plotting 91.62% <100.00%> (?)
windows-latest_3.8_test_plotting 91.58% <100.00%> (+<0.01%) ⬆️
windows-latest_3.9_test_plotting 91.59% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mtorabi59
Copy link
Contributor Author

For this error from documentation builder:
ERROR: Unknown target name: "region_ids".
Do I need to add the new attributes somewhere for documentation?

@Remi-Gau
Copy link
Collaborator

looks like sphinx is trying to build a hyperlink

not sure why it is happening there and not for other places in the code base where we have this

@Remi-Gau
Copy link
Collaborator

For this error from documentation builder: ERROR: Unknown target name: "region_ids". Do I need to add the new attributes somewhere for documentation?

FIXED by this commit.

c911019

The name of the attribute with a trailing underscore was not the problem.
The issue came from having attribute names in the following description wihtout backticks around it.

    this_is_ok_ : Niimg-like object
        Something like this will not be ok --> this_is_ok_
        But this is is fine --> ``this_is_ok_``

nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/maskers/nifti_labels_masker.py Show resolved Hide resolved
nilearn/maskers/nifti_labels_masker.py Show resolved Hide resolved
@mtorabi59
Copy link
Contributor Author

Is the documentation error from my part? I don't find the error message referring to the line that is causing this:/

doc/changes/latest.rst Outdated Show resolved Hide resolved
nilearn/maskers/nifti_labels_masker.py Outdated Show resolved Hide resolved
keep_masked_labels=self.keep_masked_labels,
mask_img=self.mask_img,
)
return signals, (labels, masked_labels_img)
Copy link
Member

Choose a reason for hiding this comment

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

And here since this is a private function we can just add the new return without making part of a tuple

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ymzayek no, the tuple is because _filter_and_extract gets only two inputs and we didn't want to change that.

Copy link
Member

Choose a reason for hiding this comment

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

Ok and not wanting to change _filter_and_extract is because it is called by other maskers not dealt with in this PR? I think we can leave it like this for this PR but just noting that it seems reasonable to me to change its return behavior if other maskers will be changed to support returning a masked image as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ymzayek yes we have to change it's behavior in future

nilearn/maskers/tests/test_nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/maskers/tests/test_nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/maskers/tests/test_nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

@mtorabi59 thanks for the updates! I want to give it another full review but seems like it's good to go

Comment on lines 273 to 285
background_label : number, default=0
Number representing background in labels_img.
background_label : number, optional
Number representing background in labels_img. Default=0.

order : :obj:`str`, default="F"
Ordering of output array ("C" or "F").
order : :obj:`str`, optional
Ordering of output array ("C" or "F"). Default="F".

strategy : :obj:`str`, default="mean"
strategy : :obj:`str`, optional
The name of a valid function to reduce the region with.
Must be one of: sum, mean, median, minimum, maximum, variance,
standard_deviation.
standard_deviation. Default="mean".
%(keep_masked_labels)s
Copy link
Member

Choose a reason for hiding this comment

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

Here we shouldn't see a diff. The default values were moved up to the type definition in another PR so maybe this is a result of incorrect merge conflict resolution

nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

Minor stuff

nilearn/maskers/nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/maskers/nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/maskers/nifti_labels_masker.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/tests/test_signal_extraction.py Outdated Show resolved Hide resolved
@ymzayek ymzayek changed the title Add new outputs to labels_masker [ENH] Add new returns to NiftiLabelsMasker's transform output Nov 13, 2023
Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

@mtorabi59 can you make the following changes?

nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/regions/signal_extraction.py Outdated Show resolved Hide resolved
nilearn/maskers/tests/test_nifti_labels_masker.py Outdated Show resolved Hide resolved
Copy link
Member

@bthirion bthirion left a comment

Choose a reason for hiding this comment

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

The PR LGTM. @ymzayek proposed an additional test thatw e could add.

Copy link
Member

@ymzayek ymzayek left a comment

Choose a reason for hiding this comment

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

Thanks @mtorabi59 ! LGTM

@mtorabi59
Copy link
Contributor Author

Thanks @mtorabi59 ! LGTM

Thank you @ymzayek !

Copy link
Member

@bthirion bthirion left a comment

Choose a reason for hiding this comment

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

LGTM, thx.

@ymzayek
Copy link
Member

ymzayek commented Nov 29, 2023

Merging 🚀

@ymzayek ymzayek merged commit 115631c into nilearn:main Nov 29, 2023
32 checks passed
@Remi-Gau
Copy link
Collaborator

@mtorabi59
I need to check but I think that some files that were on this PR were not "blackified" yet.
So before you get back to work on the maskers some more I will probably lint those files.

Comment on lines +119 to +145
region_ids_ : dict
A dictionary containing the region ids corresponding
to each column in region_signal.
The region id corresponding to ``region_signal[:,i]``
is ``region_ids_[i]``.
``region_ids_['background']`` is the background label.

.. versionadded:: 0.11.0.dev

region_names_ : dict
A dictionary containing the region names corresponding
to each column in region_signal.
The region names correspond to the labels provided
in labels in input.
The region name corresponding to ``region_signal[:,i]``
is ``region_names_[i]``.

.. versionadded:: 0.11.0.dev

region_atlas_ : Niimg-like object
Regions definition as labels.
The labels correspond to the indices in ``region_ids_``.
The region in ``region_atlas_`` that takes the value ``region_ids_[i]``
is used to compute the signal in ``region_signal[:,i]``.

.. versionadded:: 0.11.0.dev

Copy link
Collaborator

Choose a reason for hiding this comment

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

@mtorabi59
Random question on this PR: why was it chosen for those attributes to have a trailing underscore?

Why couldn't we have:

  • region_names
  • region_atlas
  • region_ids

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 it means that these quantities are estimated, i.e. fit-/data-dependent.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ha. I guess we should probably mention that somewhere in our style guide, because it is clearly not obvious.

Copy link
Member

Choose a reason for hiding this comment

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

It's an sklearn convention, but you're right.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I am fine just pointing to the scikit learn doc: https://scikit-learn.org/stable/developers/develop.html#estimated-attributes

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.

None yet

5 participants