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: Drop missing model inputs #183

Merged
merged 18 commits into from Oct 2, 2019

Conversation

adelavega
Copy link
Collaborator

@adelavega adelavega commented Sep 24, 2019

Fixes #176

  • Behavior is toggled by CLI flag (--drop-missing). By default, it crashes.
  • In FirstLevelModel, empty columns are detected in design matrix.
  • In higher level models, dropping missing inputs is handled by MergeAll.

@adelavega
Copy link
Collaborator Author

adelavega commented Sep 24, 2019

Also, I'm wondering how to handle Nones with traits, as they will fail at the OutputSpec right now (and will also fail the Input spec on the next model).

@codecov-io
Copy link

codecov-io commented Sep 25, 2019

Codecov Report

Merging #183 into master will decrease coverage by 0.3%.
The diff coverage is 67.56%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #183      +/-   ##
=========================================
- Coverage    76.7%   76.4%   -0.31%     
=========================================
  Files          18      18              
  Lines         996    1017      +21     
  Branches      171     177       +6     
=========================================
+ Hits          764     777      +13     
- Misses        146     150       +4     
- Partials       86      90       +4
Flag Coverage Δ
#ds003 76.4% <67.56%> (-0.31%) ⬇️
Impacted Files Coverage Δ
fitlins/workflows/base.py 60.43% <ø> (ø) ⬆️
fitlins/interfaces/bids.py 74.1% <ø> (+0.52%) ⬆️
fitlins/cli/run.py 80% <100%> (+0.18%) ⬆️
fitlins/viz/reports.py 89.55% <100%> (+0.48%) ⬆️
fitlins/interfaces/abstract.py 100% <100%> (ø) ⬆️
fitlins/interfaces/nistats.py 81.75% <50%> (-5.45%) ⬇️
fitlins/interfaces/utils.py 82.53% <78.57%> (+0.08%) ⬆️

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 8a41735...b78e34d. Read the comment docs.

if v != 0 and n not in all_regressors]
for row in contrast['weights']])
if missing:
weights = None
Copy link
Collaborator

@effigies effigies Sep 25, 2019

Choose a reason for hiding this comment

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

If we continue, then the contrast list is just shorter, and you don't need to deal with Nones.

Suggested change
weights = None
continue

Would this work? We'll need to make sure that there isn't some other place where we're separately generating a list that should match 1-1, but no longer would.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Was this not a sufficient place to block the production contrasts involving missing regressors?

@adelavega
Copy link
Collaborator Author

I have to test more, but this seems to have worked for me.

The main related issue is that if only one image per run remains, a second level moel can't be run, and it will crash.

This is the related issue: neuroscout/neuroscout#641
Seemed like it got more complex, but maybe in the meantime we can have the 2nd level model output the input if it only received one (instead of trying to run a model).

@adelavega
Copy link
Collaborator Author

If I'm deciphering correctly, as far as fitlins is concerned, we need to add pass through for random effects models, and implemented fixed effect models as well?

The rest of the issue is more about the spec and how to specific random vs fixed effects.

@effigies
Copy link
Collaborator

Yes... I need to re-read that, but it's also time to make dinner.

@adelavega
Copy link
Collaborator Author

Sounds more appealing, tbh.

@adelavega
Copy link
Collaborator Author

adelavega commented Sep 25, 2019

Allright, cool, this is ready for review. We can handle the fixed/random stuff in another PR, I think. That may end up being more complicated. Well at least the spec stuff, we could probably bang out the pass-through fairly quickly (wonder if that should be the default option + a warning, and if it needs a CLI flag).

Copy link
Collaborator

@effigies effigies left a comment

Choose a reason for hiding this comment

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

Some very preliminary comments. I'll get back to this this afternoon.

output_spec = DynamicTraitedSpec

def __init__(self, fields=None):
super(MergeAll, self).__init__()
def __init__(self, fields=None, **kwargs):
Copy link
Collaborator

Choose a reason for hiding this comment

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

drop_missing is relevant to the purpose of this interface in the overall workflow, but isn't very obvious in terms of functionality when considered on its own. What if we invert it to a check_lengths kwarg and just save it as an instance variable?

def __init__(self, fields=None, check_lengths=True):
    super(MergeAll, self).__init__()
    self._check_lengths = check_lengths
    ...

Then you can drop all of the input spec changes.

if v != 0 and n not in all_regressors]
for row in contrast['weights']])
if missing:
weights = None
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was this not a sufficient place to block the production contrasts involving missing regressors?

@adelavega
Copy link
Collaborator Author

I reverted the commit about pass through since it seems we need to implement FEMA for that.

@adelavega adelavega changed the title Skip missing model inputs ENH: Drop missing model inputs Sep 30, 2019
@adelavega
Copy link
Collaborator Author

@effigies if the tests pass, and you approve, this should be ready.

i'll tackle adding FEMA contrasts in another PR (that might stay open for a while, since it will be blocked by nistats + spec changes)

@adelavega
Copy link
Collaborator Author

The main conflict is where to put the drop_missing input spec for what I think is now called DesignMatrix? I'm guessing this change should go into DesignMatrixInterface?

@effigies
Copy link
Collaborator

I think it needs to go in both DesignMatrix and FirstLevelModel, right? I'll review this now.

Copy link
Collaborator

@effigies effigies left a comment

Choose a reason for hiding this comment

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

Hmm. Looks like you're not using drop_missing in FLM, so I guess it all just falls out of the design matrix?

fitlins/cli/run.py Outdated Show resolved Hide resolved
fitlins/data/full_report.tpl Outdated Show resolved Hide resolved
@@ -37,6 +43,9 @@ def _run_interface(self, runtime):
import nibabel as nb
from nistats import design_matrix as dm
info = self.inputs.session_info
drop_missing = self.inputs.drop_missing
Copy link
Collaborator

Choose a reason for hiding this comment

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

bool(Undefined) == False.

Suggested change
drop_missing = self.inputs.drop_missing
drop_missing = bool(self.inputs.drop_missing)

@@ -37,6 +43,9 @@ def _run_interface(self, runtime):
import nibabel as nb
from nistats import design_matrix as dm
info = self.inputs.session_info
drop_missing = self.inputs.drop_missing
if not isdefined(drop_missing):
drop_missing = False
Copy link
Collaborator

Choose a reason for hiding this comment

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

Then remove these lines.

fitlins/interfaces/nistats.py Outdated Show resolved Hide resolved
fitlins/interfaces/nistats.py Outdated Show resolved Hide resolved
contrast_metadata.append(
{'contrast': name,
'stat': contrast_type,
**out_ents}
)
maps = flm.compute_contrast(
weights, contrast_type, output_type='all')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think it just made sense to me to do that right before looping over it. No functional difference.

fitlins/interfaces/utils.py Outdated Show resolved Hide resolved
fitlins/workflows/base.py Outdated Show resolved Hide resolved
@adelavega
Copy link
Collaborator Author

I think only in DesignMatrix as that's where the errors are thrown. For higher levels they are thrown in MergeAll. I'm in the process of running a NS model with these new changes.

@adelavega
Copy link
Collaborator Author

I'm getting an error I don't understand:

Exception occurred in traits notification handler for object:
bold_file = <undefined>
contrast_info = <undefined>
design_matrix = <undefined>
mask_file = <undefined>
smoothing_fwhm = <undefined>
, trait: trait_added, old value: <undefined>, new value: drop_missing
Traceback (most recent call last):
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/trait_notifiers.py",
line 591, in _dispatch_change_event
    self.dispatch(handler, *args)
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/trait_notifiers.py",
line 553, in dispatch
    handler(*args)
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/nipype/pipeline/engine/nodes
.py", line 1045, in _set_mapnode_input
    setattr(self._interface.inputs, name, newvalue)
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/trait_notifiers.py",
line 400, in __call__
    handle_exception(object, trait_name, old, new)
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/trait_notifiers.py",
line 180, in _handle_exception
    raise excp
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/trait_notifiers.py",
line 394, in __call__
    self.handler(*args)
  File "/opt/miniconda-latest/envs/neuro/lib/python3.6/site-packages/traits/has_traits.py", line
1168, in _trait_added_changed
    if (trait.type == "delegate") and (name[-6:] != "_items"):

@effigies
Copy link
Collaborator

Are you reusing a working directory?

@adelavega
Copy link
Collaborator Author

No, I'm not. This doesn't seem to happen on master fwiw.

@adelavega
Copy link
Collaborator Author

Ah, I see what it is, drop_missing was going to the first level model, whereas it should to design matrix now.

@adelavega
Copy link
Collaborator Author

I set drop_missing as a kwarg for DesignMatrix and took it out of the input_spec. Does that make sense to you?

@effigies
Copy link
Collaborator

I set drop_missing as a kwarg for DesignMatrix and took it out of the input_spec. Does that make sense to you?

From the nipype perspective, the contents of the outputs will not purely depend on the contents of the inputs, so you might get a situation where you re-run, adding or removing a --drop-missing flag, and the outputs from the previous run get reused, rather than recomputed. I think I would prefer it to be an input.

You can still keep the DesignMatrix(drop_missing=drop_missing), though. That will set the trait in the input spec.

@adelavega
Copy link
Collaborator Author

Okay, sure, then what I don't understand is why we don't do the same for MergeAll. Shouldn't that also be as an input then?

@effigies
Copy link
Collaborator

Yeah, strictly speaking, probably. It doesn't change the outputs, but instead changes the error condition, so it's a little less problematic. Also by the time things show up there, they will have had to go through DesignMatrix. So... whichever you want.

adelavega and others added 3 commits September 30, 2019 21:39
Co-Authored-By: Chris Markiewicz <effigies@gmail.com>
Co-Authored-By: Chris Markiewicz <effigies@gmail.com>
Co-Authored-By: Chris Markiewicz <effigies@gmail.com>
@adelavega
Copy link
Collaborator Author

Okay, your suggestions made sense, and I applied them. Regarding the input spec, I'll leave it as we have it. I'll test this out on NS tomorrow.

variance_maps.append(_variances[0])
zscore_maps.append()
pvalue_maps.append()
stat_maps.append()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think these three lines will cause problems?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah, yes, I thought I reverted that commit.

adelavega and others added 2 commits October 1, 2019 15:24
Co-Authored-By: Chris Markiewicz <effigies@gmail.com>
Copy link
Collaborator

@effigies effigies left a comment

Choose a reason for hiding this comment

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

Cool. Assuming tests pass, I'm good with this. This has been tested for your use case?

@adelavega
Copy link
Collaborator Author

Hmm, there seems to be a minor problem with the outputting of plots now, let me investigate

@adelavega
Copy link
Collaborator Author

adelavega commented Oct 2, 2019

Okay, tested and working. My linter suggested I change == to is and that screwed things up.

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.

Handle variables which only occur in some runs
3 participants