Skip to content
This repository has been archived by the owner on Apr 2, 2020. It is now read-only.

First level residuals #410

Merged
merged 39 commits into from
Jan 13, 2020
Merged

Conversation

jdkent
Copy link
Contributor

@jdkent jdkent commented Nov 20, 2019

Continuation of PR #298

@codecov
Copy link

codecov bot commented Nov 20, 2019

Codecov Report

Merging #410 into master will increase coverage by 0.03%.
The diff coverage is 94.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #410      +/-   ##
==========================================
+ Coverage   92.66%   92.69%   +0.03%     
==========================================
  Files          40       40              
  Lines        4514     4588      +74     
==========================================
+ Hits         4183     4253      +70     
- Misses        331      335       +4
Impacted Files Coverage Δ
nistats/regression.py 77.31% <100%> (+0.72%) ⬆️
nistats/tests/test_first_level_model.py 100% <100%> (ø) ⬆️
nistats/tests/test_regression.py 100% <100%> (ø) ⬆️
nistats/first_level_model.py 83.12% <85.18%> (+0.18%) ⬆️
nistats/tests/test_reporting.py 96.82% <0%> (ø) ⬆️
nistats/tests/test_glm_reporter.py 97.93% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 755039f...44b0e85. Read the comment docs.

@jdkent jdkent changed the title [WIP] First level residuals First level residuals Nov 20, 2019
@jdkent
Copy link
Contributor Author

jdkent commented Nov 20, 2019

I believe this is ready for review, @Gilles86, could you take a look to make sure I didn't fundamentally alter your pull request?

@Gilles86
Copy link
Contributor

Very cool. Will try to have a look this week.

nistats/first_level_model.py Show resolved Hide resolved
nistats/first_level_model.py Show resolved Hide resolved
nistats/first_level_model.py Show resolved Hide resolved
nistats/first_level_model.py Show resolved Hide resolved
nistats/first_level_model.py Outdated Show resolved Hide resolved
nistats/regression.py Outdated Show resolved Hide resolved
@kchawla-pi
Copy link
Collaborator

Hi @jdkent thanks for looking after this Feature.

@kchawla-pi
Copy link
Collaborator

@bthirion I know we avoid using @property when we can. The functions residuals & predictions will really serve better as properties instead of methods.

@bthirion
Copy link
Member

bthirion commented Dec 1, 2019

@bthirion I know we avoid using @property when we can. The functions residuals & predictions will really serve better as properties instead of methods.

Which is why the setattr_on_read decorator is used. I think that this is OK.

@jdkent
Copy link
Contributor Author

jdkent commented Dec 2, 2019

Except for the codecov patch error, I think this is ready for review. I personally find patch coverage to be a rather noisy measure of a pull request, I have a very lenient setting in one of my own projects. But if the patch diff is important to fix, I can add a couple new tests so that "more lines" are covered for this pull request.

@kchawla-pi
Copy link
Collaborator

Which is why the setattr_on_read decorator is used. I think that this is OK.

Why is that preferable to a property in this case?
A property can be repeatedly used as an an attribute, simplifying access for users.
setattr_on_read makes the method usable as an attribute only once, & if that's the case then we don't need decorator at all.
This will create confusion for users, if they access the method as an attribute once & then watch it fail the second time.
If I am missing something here, explain it to me.
If not, we should make these properties.

@kchawla-pi
Copy link
Collaborator

kchawla-pi commented Dec 18, 2019

@bthirion best we wrap this one up before holidays.
@jeromedockes What's your opinion about the setattr_on_read and properties discussion we're having here

Copy link
Contributor

@Gilles86 Gilles86 left a comment

Choose a reason for hiding this comment

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

Hey guys,

Great work. I toyed around a bit with this branch. For me it seems to work super well! Super nice to quickly make a nifti with predicted time series that you can view in Python without resorting to command line FSL stuff... :)

This will create confusion for users, if they access the method as an attribute once & then watch it fail the second time.

This does not seem to happen for me. Can you give an example when this happens?

One final thing: I had to struggle for 20 minutes to understand why the predicted time series were scaled completely off compared to real fMRI data. It turns out that FirstLevelModel standardizes the data across time by default. I have to say I personally don't like that, because if you don't know this, it's very hard to understand to know what's going on... Any thoughts about this?

-Gilles

nistats/regression.py Outdated Show resolved Hide resolved
@jeromedockes
Copy link
Member

@jeromedockes What's your opinion about the setattr_on_read and properties discussion we're having here

No strong opinion. using setattr_on_read is fine. it is a caching mechanism so it could also be an option to use the firstlevelobject's joblib memory for this. it would also be perfectly fine (perhaps best) to just have a get_residuals() function and let the user hold a reference to the result if they want.

is this going to be showcased in an example?

@kchawla-pi
Copy link
Collaborator

Okay setattr_on_read is fine then.

It turns out that FirstLevelModel standardizes the data across time by default. I have to say I personally don't like that, because if you don't know this, it's very hard to understand to know what's going on... Any thoughts about this?

That would be beyond the purview of this PR I think.
You should file an issue, and we can look into that before or after the freeze.

@kchawla-pi
Copy link
Collaborator

This seems nearly ready.
@bthirion any further comments? Anything about @Gilles86 FLM time normalized comment?
@jdkent Attend to the missing docstrings and too may blank lines, and if there are no more objections, we can merge.

@jeromedockes
Copy link
Member

This seems nearly ready.

still thinking about the upcoming merge into nilearn: if this were nilearn we would probably ask for this to be shown in an example

@Gilles86
Copy link
Contributor

Gilles86 commented Dec 18, 2019 via email

@jeromedockes
Copy link
Member

also making that error message : https://github.com/nistats/nistats/pull/410/files#r359257721 a bit more verbose would be great -- if that is confusing for developers it certainly will be for users. but I can also do it in another PR if necessary

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.

We're getting close thanks ! I'm mostly asking for a bit more pedagogy in the examples.

from nistats.reporting import get_clusters_table
from nistats.first_level_model import FirstLevelModel
import pandas as pd
import numpy as np
Copy link
Member

Choose a reason for hiding this comment

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

If you don't mind, could you split the import so that novel users are not overwhelmed by the ten initial lines of imports.

cluster_threshold=20).set_index('Cluster ID', drop=True)
table.head()

masker = input_data.NiftiSpheresMasker(table.loc[range(1, 7), ['X', 'Y', 'Z']].values)
Copy link
Member

Choose a reason for hiding this comment

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

Can the corresponding locations be plotted ? Otherwise, this looks a bit cryptic
Also, you probably want to explain what you're doing here.


Parameters
----------
attribute : str
Copy link
Member

Choose a reason for hiding this comment

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

attribute is defined within a fixed dictionary of values I guess ? This should be provided if the function is meant to be used publicly.

Copy link
Contributor Author

@jdkent jdkent Jan 9, 2020

Choose a reason for hiding this comment

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

I believe this should really be a private method (I've changed it to be a private method), but I added additional documentation and an internal check to be explicit anyway.

This comment was marked as outdated.

This comment was marked as resolved.

nistats/first_level_model.py Show resolved Hide resolved
@@ -583,6 +586,96 @@ def compute_contrast(self, contrast_def, stat_type=None,

return outputs if output_type == 'all' else output

def _get_voxelwise_model_attribute(self, attribute, result_as_time_series=True):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Private methods and functions should not have default args. Only public ones should. It's best to explicitly supply the arg for each parameter of private functions and methods.

@@ -276,6 +276,7 @@ def __init__(self, theta, Y, model, wY, wresid, cov=None, dispersion=1.,
dispersion, nuisance)
self.wY = wY
self.wresid = wresid
self.wdesign = model.wdesign

@setattr_on_read
def resid(self):
Copy link
Collaborator

Choose a reason for hiding this comment

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

@bthirion We should deprecate this and replace it with residuals in a separate PR, and others like it.
Or I should ask JD Kent to revert the changes I asked him to make and rename the resid mehotd in this PR back to resid so it stay consistent.
I like the former idea, renaming all uses of resid to residuals.
Does Nilearn have resid or residuals?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, let's do it in a sperate PR.
I'm not aware of resid in nilear? We should go for residuals

Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't think Nilearn has any resid method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so we will keep the method as resid for this pull request, I will not change this

Davidson and MacKinnon 15.2 p 662
"""
return self.resid(Y) * positive_reciprocal(np.sqrt(self.dispersion))
raise ValueError('minimize_memory should be set to False to make residuals or predictions.')
Copy link
Collaborator

@kchawla-pi kchawla-pi Jan 9, 2020

Choose a reason for hiding this comment

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

@bthirion @jdkent @Gilles86
I'm still confused about the suitability of this, and other such methods. It feels like logL(), resid() and norm_resid() are in effect, traps; waiting for unsuspecting users to call them and watch their programs fail.

If these methods do not have anything to say, then we should simply remove all of them, or convert this into a message rather than an error.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed.

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 believe the motivation for this was to provide a hint to the user on how to get residuals from their model instead of an attribute error, but I see this check is handled here. I'm fine with removing those methods.

@kchawla-pi
Copy link
Collaborator

This seems ready to merge. Anything else @bthirion ?

@kchawla-pi
Copy link
Collaborator

@jdkent Add whats_new entry and also update your name in the .mailmap .

@kchawla-pi
Copy link
Collaborator

Phenomenal work @jdkent and @Gilles86 ! Thanks to both of you!!

@kchawla-pi kchawla-pi merged commit 839f8c9 into nilearn:master Jan 13, 2020
@Gilles86
Copy link
Contributor

Thank you!

Nistats is one of my favorite python neuroimaging packages. Keep up the good work!

kchawla-pi added a commit to kchawla-pi/nistats that referenced this pull request Jan 13, 2020
…me-resid

* 'master' of https://github.com/nistats/nistats:
  First level residuals (nilearn#410)
  Replace numpy's deprecated dec with pytest's skipif (nilearn#423)
kchawla-pi added a commit to bthirion/nistats that referenced this pull request Jan 14, 2020
…ove-reporting

* 'master' of https://github.com/nistats/nistats:
  Prevent duplication & insertion of reports from damaging website layout (nilearn#429)
  Fix whatsnew and the website's News/updates section (nilearn#428)
  GLM Reports: Computed masks are used if there is no user supplied one (nilearn#424)
  First level residuals (nilearn#410)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants