-
Notifications
You must be signed in to change notification settings - Fork 535
WIP: Adding a python wrapper for ICA_AROMA #1993
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
Conversation
keeping up to date
+1 for calling AROMA instead of copying the code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this. I've marked up a few changes. There are some issues that need fixing, but others are aesthetic, so feel free to argue points.
A few of them are coding style changes. Consider using flake8 to get style suggestions. That should reduce the amount of review space that needs to be spent on some, and may help catch some bugs before committing.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
desc='If a feat directory exists and temporal filtering ' | ||
'has not been run yet, ICA_AROMA can use the files in ' | ||
'this directory.',mandatory=False,xor=['infile','mask','affmat','warp','mc']) | ||
infile = File(exists=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in_file
would be more consistent with other interfaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adapted this change into updated script
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
import os | ||
|
||
class ICA_AROMAInputSpec(CommandLineInputSpec): | ||
featDir = Directory(exists=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a mix of camelCase and snake_case in your code. I think the dominant choice in nipype is snake, so I'd consider changing featDir
to feat_dir
.
I believe this should have argstr='-feat %s'
.
Also, just for (my own sense of) readability, I'd put mandatory=False
on the same line as exists=True
. And I prefer to put desc
last, just because it often is multi-line. These suggestions are by no means a matter of consensus; there are several styles for input specs, so don't feel obligated to indulge me here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't heard of flake8, that looks really useful, thanks!
argstr: D'oh, I made a couple of errors where I didn't specify the string correctly
I'll move 'desc' to the end and move 'mandatory' next to 'exists'. Question: if mandatory=False is the default behavior, should I still be explicit or is that too wordy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's non-mandatory, don't include it. But since you're in the situation where you have to either have feat_dir
or in_file
, then both should be mandatory, but xor
one another. (The mandatory check is smart enough to figure out these required-but-mutually-exclusive cases.)
I think your motion correction params, affine transform and warp are all required (if not using feat_dir
), so they should be set to mandatory as well, but the mask is generated by the program if not supplied, so it doesn't need to be set to mandatory.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
'this directory.',mandatory=False,xor=['infile','mask','affmat','warp','mc']) | ||
infile = File(exists=True, | ||
desc='volume to be denoised', | ||
argstr='-i %s',mandatory=False,xor=['featDir']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Space after commas. (Putting this here to stand in for all lines with no spaces after commas.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done (I think).
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
desc='volume to be denoised', | ||
argstr='-i %s',mandatory=False,xor=['featDir']) | ||
outdir = Directory(desc='path to output directory', | ||
argstr='-o %s',mandatory=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest making this 'out'
by default, and not requiring users to input it. Typically in nipype you don't really care about specifying locations during processing. Each Node gets a working directory where all of the outputs can be placed. So this would be:
out_dir = Directory('out', argstr='-o %s',
desc='output directory')
I'm adding the underscore for consistency with some other interfaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your comment here helped me understand how nipype works a bit better, thanks!
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
argstr='-o %s',mandatory=True) | ||
mask = File(exists=True, | ||
desc='path/name volume mask', | ||
argstr='-m %s',mandatory=False,xor=['featDir']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mandatory=False
is implied, so you can remove it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
>>> AROMA_obj.inputs.mc=mc | ||
>>> AROMA_obj.inputs.mask=mask | ||
>>> AROMA_obj.inputs.denoise_type=denoise_type | ||
>>> AROMA_obj.inputs.outdir=outDir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't separate AROMA_obj.inputs.x = y
from y = 'somefile'
. It's extra lines without much value.
Also, these files need to exist. You could create a set of empty files in nipype/testing/data/feat_dir
or something like that, and then use:
>>> AROMA_obj.inputs.in_file = 'feat_dir/mcImg_brain.nii.gz'
And so on.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
-mc /path/to/mcImg.par | ||
-o /path/to/ICA_AROMA_testout | ||
-warp /path/to/T1toMNI_warp.nii.gz' | ||
>>> AROMA_obj.run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't actually run this during the doctests. Do
>>> AROMA_obj.run() # doctest: +SKIP
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
print "No denoising selected" | ||
else: | ||
raise RuntimeError('denoise_type must be specified as one of' | ||
' noaggr,aggr,both, or none') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This particular exception will be caught by the Enum
trait, above.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
elif denoising_strategy is "both": | ||
outputs['out_file'] = (os.path.join(outdir,'denoised_func_data_nonaggr.nii.gz'), os.path.join(outdir,'denoised_func_data_aggr.nii.gz')) | ||
elif denoising_strategy is "none": | ||
print "No denoising selected" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We support Python 3, so if you're going to print, use print()
.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
elif denoising_strategy is "aggr": | ||
outputs['out_file'] = os.path.join(outdir,'denoised_func_data_aggr.nii.gz') | ||
elif denoising_strategy is "both": | ||
outputs['out_file'] = (os.path.join(outdir,'denoised_func_data_nonaggr.nii.gz'), os.path.join(outdir,'denoised_func_data_aggr.nii.gz')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming you split the output like I suggest above, you could do the following:
out_dir = os.path.abspath(self.inputs.out_dir)
if self.inputs.denoise_type in ('aggr', 'both'):
outputs['aggr_denoised_file'] = os.path.join(out_dir, 'denoised_func_data_aggr.nii.gz')
if self.inputs.denoise_type in ('nonaggr', 'both'):
outputs['nonaggr_denoised_file'] = os.path.join(out_dir, 'denoised_func_data_nonaggr.nii.gz')
keeping up to date
Codecov Report
@@ Coverage Diff @@
## master #1993 +/- ##
===========================================
- Coverage 72.15% 52.48% -19.68%
===========================================
Files 1117 118 -999
Lines 56456 23565 -32891
Branches 8112 0 -8112
===========================================
- Hits 40736 12368 -28368
+ Misses 14443 11197 -3246
+ Partials 1277 0 -1277
Continue to review full report at Codecov.
|
@effigies : I believe I responded to all of your edits, thanks for the detailed feedback and suggestions, it made it that much easier to change the code. |
@jdkent Thanks, it looks much improved! I'll have a more detailed look after the tests run. (Possibly in the morning. There's a bit of a test backlog.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great! A couple more comments and I think it'll be ready to go.
You should also run make specs
from the repository root directory, and then add nipype/interfaces/fsl/tests/test_auto_ICA_AROMA.py
. (Don't add all of the changes; just the ones relevant to your PR.)
Once you've committed your changes, you can clean up by doing something like:
git add . && git stash && git stash drop
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
>>> AROMA_obj.inputs.denoise_type='both' | ||
>>> AROMA_obj.inputs.out_dir='ICA_testout' | ||
>>> AROMA_obj.cmdline | ||
u'ICA_AROMA.py -den both -warp /home/travis/build/nipy/nipype/nipype/testing/data/warpfield.nii -i /home/travis/build/nipy/nipype/nipype/testing/data/functional.nii -m /home/travis/build/nipy/nipype/nipype/testing/data/mask.nii.gz -affmat /home/travis/build/nipy/nipype/nipype/testing/data/func_to_struct.mat -mc /home/travis/build/nipy/nipype/nipype/testing/data/functional.par -o ICA_testout' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need example_data()
, which adds the full path, for doctests.
How about this:
>>> AROMA_obj.inputs.in_file = 'functional.nii'
>>> AROMA_obj.inputs.mat_file = 'func_to_struct.mat'
>>> AROMA_obj.inputs.fnirt_warp_file = 'warpfield.nii'
>>> AROMA_obj.inputs.motion_parameters = 'fsl_mcflirt_movpar.txt'
>>> AROMA_obj.inputs.mask = 'mask.nii.gz'
>>> AROMA_obj.inputs.denoise_type = 'both'
>>> AROMA_obj.inputs.out_dir='ICA_testout'
>>> AROMA_obj.cmdline # doctest: +ALLOW_UNICODE
'ICA_AROMA.py -den both -warp warpfield.nii -i functional.nii -m mask.nii.gz \
-affmat func_to_struct.mat -mc fsl_mcflirt_movpar.txt -o ICA_testout'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for this!
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
output_spec = ICA_AROMAOutputSpec | ||
|
||
def _list_outputs(self): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No blank line here.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
outputs['out_dir'] = out_dir | ||
#outputs = self.output_spec.get() | ||
#outdir = self.input_spec.outdir | ||
#denoising_strategy = input_spec.denoise_type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's drop these comments.
if self.inputs.denoise_type in ('nonaggr', 'both'): | ||
outputs['nonaggr_denoised_file'] = os.path.join(out_dir, 'denoised_func_data_nonaggr.nii.gz') | ||
|
||
return outputs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a newline at the end of file.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
|
||
class ICA_AROMA(CommandLine): | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should describe what this interface does and link to external documentation. Have a look at some other interfaces for examples. You can largely copy this from any existing ICA_AROMA documentation.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
|
||
class ICA_AROMAInputSpec(CommandLineInputSpec): | ||
feat_dir = Directory(exists=True, mandatory=True, | ||
argstr='-feat %s', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be shifted right by one space (aligned with the inside of the parentheses).
import os | ||
|
||
class ICA_AROMAInputSpec(CommandLineInputSpec): | ||
feat_dir = Directory(exists=True, mandatory=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think feat_dir is mandatory in ICA AROMA.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feat_dir
or in_file
and friends. xor
handles competing mandatory=True
traits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok - sorry!
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
desc='path/name of the mat-file describing the ' | ||
'affine registration (e.g. FSL FLIRT) of the ' | ||
'functional data to structural space (.mat file)') | ||
fnirt_warp_file = File(exists=True, mandatory=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also does not seem to be mandatory - ICA AROMA also supports inputs that are in the MNI space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be true. I honestly haven't dug into their documentation to be sure what all is actually mandatory.
Here is their argparse setup: https://github.com/rhr-pruim/ICA-AROMA/blob/master/ICA_AROMA.py#L27-L37
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch @chrisfilo, fnirt_warp_file and mat_file don't appear to be mandatory
ICA-AROMA checking inputs: https://github.com/rhr-pruim/ICA-AROMA/blob/master/ICA_AROMA.py#L109-L116
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would actually check inside this function, to see what it does: https://github.com/rhr-pruim/ICA-AROMA/blob/master/ICA_AROMA.py#L197
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
>>> AROMA_obj.inputs.mask = 'mask.nii.gz' | ||
>>> AROMA_obj.inputs.denoise_type = 'both' | ||
>>> AROMA_obj.inputs.out_dir = 'ICA_testout' | ||
>>> AROMA_obj.cmdline # doctest: +ALLOW_UNICODE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do I get rid of the u'string' output? (and I need to to change the motion parameter file)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The u''
part is handled by the +ALLOW_UNICODE
flag. The problem here is actually that there's a tab after the newline. You can either remove the spaces before -affmat
or just remove the newline entirely and just have the whole string on one line.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
aggr_denoised_file=File(exists=True, | ||
desc='if generated: aggressively denoised volume') | ||
nonaggr_denoised_file=File(exists=True, | ||
desc='if generated: non aggressively denoised volume' ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For both of these traits, add spaces around =
, and align your desc
with exists
.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
|
||
class ICA_AROMA(CommandLine): | ||
""" | ||
Interface for the ICA_AROMA.py script (v0.3 beta). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd remove the version number. Unless the interface changes, we don't really need to tag versions, since it'll cause confusion about whether the interface is still valid for future versions. (If it does, then we'll figure out how to make it work with multiple versions.)
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
See link for further documentation: https://github.com/rhr-pruim/ICA-AROMA | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Limit blank lines (here and above the See link...
line) to one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. One final question. (Sorry I just noticed this.)
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
'-none: only classification, no denoising ' | ||
'-nonaggr (default): non-aggresssive denoising, i.e. partial component regression ' | ||
'-aggr: aggressive denoising, i.e. full component regression ' | ||
'-both: both aggressive and non-aggressive denoising (two outputs)') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This desc
string is:
Type of denoising strategy: -none: only classification, no denoising -nonaggr (default): non-aggresssive denoising, i.e. partial component regression -aggr: aggressive denoising, i.e. full component regression -both: both aggressive and non-aggressive denoising (two outputs)
Is this correct, or do you want it to be:
Type of denoising strategy:
-none: only classification, no denoising
-nonaggr (default): non-aggresssive denoising, i.e. partial component regression
-aggr: aggressive denoising, i.e. full component regression
-both: both aggressive and non-aggressive denoising (two outputs)
If the latter, replace the space at the end of each line with \n
. If the former is what you intended, no change necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I want the latter, thanks! I wasn't sure if newlines were interpreted within single quotes or if I had to go to double quotes (because that makes a difference in bash, I didn't know in python).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Python, they're the same, you just use whichever looks nicer to you. The only difference is that with double-quotes you have to escape double-quotes and with single-quotes you have to escape single-quotes. (My preference is to use single quotes unless the string contains a single quote, but there is no enforced rule in nipype.)
LGTM. Any final comments, @chrisfilo? |
I'm afraid I may be in over my head interpreting what I need to change to make this code pass the ci/circleci test. Does this have to do with modifying the docker files? |
I think it's a random failure. I'll restart the job when I can. Might be in
the morning.
…On May 4, 2017 7:03 PM, "James Kent" ***@***.***> wrote:
I'm afraid I may be in over my head interpreting what I need to change to
make this code pass the ci/circleci test. Does this have to do with
modifying the docker files?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1993 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAFF8par0YUdcEmreCbGES2rTTxTms4Cks5r2oOMgaJpZM4NO97u>
.
|
@jdkent Can you merge from master? The tests should be fixed. |
up to date
This looks like it should have passed. @satra did something change with the doctests? Circle is complaining about a missing |
------- | ||
|
||
>>> from nipype.interfaces.fsl import ICA_AROMA | ||
>>> from nipype.testing import example_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove the example_data
import. Shouldn't have any effect on the tests, though.
nipype/interfaces/fsl/ICA_AROMA.py
Outdated
@@ -0,0 +1,108 @@ | |||
#ICA_AROMA pulled from: https://github.com/rhr-pruim/ICA-AROMA | |||
#This assumes ICA_AROMA.py is already installed and callable via $PATH |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Sorry, I'm new to all this.
The last line having trouble in the Travis CI build says this: Would this be anything related to how I have ICA_AROMA formatted? |
@jdkent that's a very specific and not always reproducible bug we've been encountering in our test builds, unrelated to ICA_AROMA. I've restarted those specific builds |
@mgxd I've restarted them multiple times. The bug may be getting more reliable... |
Not going to hold this one up for the segfault bug. Merging. |
This is my attempt at adding ICA_AROMA's functionality to nipype, although it would probably be better just to incorporate the code from ICA_AROMA.py, I didn't want to take the code and then have it be maintained in two places, so I wrote a wrapper treating the python file as a commandline tool. I don't know the best place for commandline tools, but ICA_AROMA.py uses fsl tools so I placed it there.
Thanks for looking!