Skip to content

[ENH] Add afni.LocalStat and afni.ReHo, update afni.ROIStats inputs #2740

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

Merged
merged 14 commits into from
Oct 30, 2018
6 changes: 3 additions & 3 deletions nipype/interfaces/afni/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .utils import (
ABoverlap, AFNItoNIFTI, Autobox, Axialize, BrickStat, Bucket, Calc, Cat,
CatMatvec, CenterMass, ConvertDset, Copy, Dot, Edge3, Eval, FWHMx,
LocalBistat, MaskTool, Merge, Notes, NwarpApply, NwarpAdjust, NwarpCat,
OneDToolPy, Refit, Resample, TCat, TCatSubBrick, TStat, To3D, Unifize,
Undump, ZCutUp, GCOR, Zcat, Zeropad)
LocalBistat, Localstat, MaskTool, Merge, Notes, NwarpApply, NwarpAdjust,
NwarpCat, OneDToolPy, Refit, ReHo, Resample, TCat, TCatSubBrick, TStat,
To3D, Unifize, Undump, ZCutUp, GCOR, Zcat, Zeropad)
from .model import (Deconvolve, Remlfit, Synthesize)
128 changes: 110 additions & 18 deletions nipype/interfaces/afni/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1802,28 +1802,107 @@ class QualityIndex(CommandLine):

class ROIStatsInputSpec(CommandLineInputSpec):
in_file = File(
desc='input file to 3dROIstats',
desc='input dataset',
argstr='%s',
position=-1,
position=-2,
mandatory=True,
exists=True)
mask = File(desc='input mask', argstr='-mask %s', position=3, exists=True)
mask_file = File(desc='input mask', argstr='-mask %s', exists=True)
Copy link
Member

Choose a reason for hiding this comment

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

When renaming a field, you should use deprecated and new_name (see the developer guide) fields (in addition to the new trait).

mask = File(desc='input mask', argstr='-mask %s', position=3, exists=True,
            deprecated='1.1.4', new_name='mask_file')
mask_file = File(desc='input mask', argstr='-mask %s', exists=True)

mask_f2short = traits.Bool(
desc='Tells the program to convert a float mask to short integers, '
'by simple rounding.',
argstr='-mask_f2short',
position=2)
quiet = traits.Bool(desc='execute quietly', argstr='-quiet', position=1)
argstr='-mask_f2short')
num_roi = traits.Int(
desc='Forces the assumption that the mask dataset\'s ROIs are '
'denoted by 1 to n inclusive. Normally, the program '
'figures out the ROIs on its own. This option is '
'useful if a) you are certain that the mask dataset '
'has no values outside the range [0 n], b) there may '
'be some ROIs missing between [1 n] in the mask data-'
'set and c) you want those columns in the output any-'
'way so the output lines up with the output from other '
'invocations of 3dROIstats.',
argstr='-numroi %s')
zerofill = traits.Str(
requires=['num_roi'],
desc='For ROI labels not found, use the provided string instead of '
'a \'0\' in the output file. Only active if `num_roi` is '
'enabled.',
argstr='-zerofill %s')
roisel = traits.File(
exists=True,
desc='Only considers ROIs denoted by values found in the specified '
'file. Note that the order of the ROIs as specified in the file '
'is not preserved. So an SEL.1D of \'2 8 20\' produces the same '
'output as \'8 20 2\'',
argstr='-roisel %s')
debug = traits.Bool(
desc='print debug information',
argstr='-debug')
quiet = traits.Bool(
desc='execute quietly',
argstr='-quiet')
nomeanout = traits.Bool(
desc='Do not include the (zero-inclusive) mean among computed stats',
argstr='-nomeanout')
nobriklab = traits.Bool(
desc='Do not print the sub-brick label next to its index',
argstr='-nobriklab')
format1D = traits.Bool(
xor=['format1DR'],
desc='Output results in a 1D format that includes commented labels',
argstr='-1Dformat')
format1DR = traits.Bool(
xor=['format1D'],
desc='Output results in a 1D format that includes uncommented '
'labels. May not work optimally with typical 1D functions, '
'but is useful for R functions.',
argstr='-1DRformat')
_stat_names = ['mean', 'sum', 'voxels', 'minmax', 'sigma', 'median',
'mode', 'summary', 'zerominmax', 'zerosigma', 'zeromedian',
'zeromode']
stat = InputMultiObject(
traits.Enum(_stat_names),
desc='statistics to compute. Options include: '
' * mean = Compute the mean using only non_zero voxels.'
' Implies the opposite for the mean computed '
' by default.\n'
' * median = Compute the median of nonzero voxels\n'
' * mode = Compute the mode of nonzero voxels.'
' (integral valued sets only)\n'
' * minmax = Compute the min/max of nonzero voxels\n'
' * sum = Compute the sum using only nonzero voxels.\n'
' * voxels = Compute the number of nonzero voxels\n'
' * sigma = Compute the standard deviation of nonzero'
' voxels\n'
'Statistics that include zero-valued voxels:\n'
' * zerominmax = Compute the min/max of all voxels.\n'
' * zerosigma = Compute the standard deviation of all'
' voxels.\n'
' * zeromedian = Compute the median of all voxels.\n'
' * zeromode = Compute the mode of all voxels.\n'
' * summary = Only output a summary line with the grand '
' mean across all briks in the input dataset.'
' This option cannot be used with nomeanout.\n'
'More that one option can be specified.',
argstr='%s...')
out_file = File(
name_template='%s_roistat.1D',
desc='output file',
keep_extension=False,
argstr='> %s',
name_source='in_file',
position=-1)


class ROIStatsOutputSpec(TraitedSpec):
stats = File(desc='output tab separated values file', exists=True)
out_file = File(desc='output tab-separated values file', exists=True)


class ROIStats(AFNICommandBase):
"""Display statistics over masked regions

For complete details, see the `3dROIstats Documentation.
For complete details, see the `3dROIstats Documentation
<https://afni.nimh.nih.gov/pub/dist/doc/program_help/3dROIstats.html>`_

Examples
Expand All @@ -1832,10 +1911,11 @@ class ROIStats(AFNICommandBase):
>>> from nipype.interfaces import afni
>>> roistats = afni.ROIStats()
>>> roistats.inputs.in_file = 'functional.nii'
>>> roistats.inputs.mask = 'skeleton_mask.nii.gz'
>>> roistats.inputs.quiet = True
>>> roistats.inputs.mask_file = 'skeleton_mask.nii.gz'
>>> roistats.inputs.stat = ['mean', 'median', 'voxels']
>>> roistats.inputs.nomeanout = True
>>> roistats.cmdline
'3dROIstats -quiet -mask skeleton_mask.nii.gz functional.nii'
'3dROIstats -mask skeleton_mask.nii.gz -nomeanout -nzmean -nzmedian -nzvoxels functional.nii > functional_roistat.1D'
>>> res = roistats.run() # doctest: +SKIP

"""
Expand All @@ -1844,14 +1924,26 @@ class ROIStats(AFNICommandBase):
input_spec = ROIStatsInputSpec
output_spec = ROIStatsOutputSpec

def aggregate_outputs(self, runtime=None, needed_outputs=None):
outputs = self._outputs()
output_filename = 'roi_stats.csv'
with open(output_filename, 'w') as f:
f.write(runtime.stdout)
def _format_arg(self, name, spec, value):

outputs.stats = os.path.abspath(output_filename)
return outputs
_stat_dict = {
'mean': '-nzmean',
'median': '-nzmedian',
'mode': '-nzmode',
'minmax': '-nzminmax',
'sigma': '-nzsigma',
'voxels': '-nzvoxels',
'sum': '-nzsum',
'summary': '-summary',
'zerominmax': '-minmax',
'zeromedian': '-median',
'zerosigma': '-sigma',
'zeromode': '-mode'
}
Copy link
Member

Choose a reason for hiding this comment

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

We (try to) conform to PEP8. Can you restyle this as follows (4 space indentation, no double spaces, closing brace matching entries)?

_stat_dict = {
    'mean': '-nzmean',
    ...
    }

if name == 'stat':
value = [_stat_dict[v] for v in value]

return super(ROIStats, self)._format_arg(name, spec, value)


class RetroicorInputSpec(AFNICommandInputSpec):
Expand Down
71 changes: 71 additions & 0 deletions nipype/interfaces/afni/tests/test_auto_Localstat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from __future__ import unicode_literals
from ..utils import Localstat


def test_Localstat_inputs():
input_map = dict(
args=dict(argstr='%s', ),
automask=dict(argstr='-automask', ),
environ=dict(
nohash=True,
usedefault=True,
),
grid_rmode=dict(
argstr='-grid_rmode %s',
requires=['reduce_restore_grid'],
),
in_file=dict(
argstr='%s',
mandatory=True,
position=-1,
),
mask_file=dict(argstr='-mask %s', ),
neighborhood=dict(
argstr="-nbhd '%s(%s)'",
mandatory=True,
),
nonmask=dict(argstr='-use_nonmask', ),
num_threads=dict(
nohash=True,
usedefault=True,
),
out_file=dict(
argstr='-prefix %s',
keep_extension=True,
name_source='in_file',
name_template='%s_localstat',
position=0,
),
outputtype=dict(),
overwrite=dict(argstr='-overwrite', ),
quiet=dict(argstr='-quiet', ),
reduce_grid=dict(
argstr='-reduce_grid %s',
xor=['reduce_restore_grid', 'reduce_max_vox'],
),
reduce_max_vox=dict(
argstr='-reduce_max_vox %s',
xor=['reduce_restore_grid', 'reduce_grid'],
),
reduce_restore_grid=dict(
argstr='-reduce_restore_grid %s',
xor=['reduce_max_vox', 'reduce_grid'],
),
stat=dict(
argstr='-stat %s...',
mandatory=True,
),
)
inputs = Localstat.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value
def test_Localstat_outputs():
output_map = dict(out_file=dict(), )
outputs = Localstat.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
40 changes: 28 additions & 12 deletions nipype/interfaces/afni/tests/test_auto_ROIStats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,42 @@
def test_ROIStats_inputs():
input_map = dict(
args=dict(argstr='%s', ),
debug=dict(argstr='-debug', ),
environ=dict(
nohash=True,
usedefault=True,
),
format1D=dict(
argstr='-1Dformat',
xor=['format1DR'],
),
format1DR=dict(
argstr='-1DRformat',
xor=['format1D'],
),
in_file=dict(
argstr='%s',
mandatory=True,
position=-1,
position=-2,
),
mask=dict(
argstr='-mask %s',
position=3,
),
mask_f2short=dict(
argstr='-mask_f2short',
position=2,
mask_f2short=dict(argstr='-mask_f2short', ),
mask_file=dict(argstr='-mask %s', ),
nobriklab=dict(argstr='-nobriklab', ),
nomeanout=dict(argstr='-nomeanout', ),
num_roi=dict(argstr='-numroi %s', ),
out_file=dict(
argstr='> %s',
keep_extension=False,
name_source='in_file',
name_template='%s_roistat.1D',
position=-1,
),
quiet=dict(
argstr='-quiet',
position=1,
quiet=dict(argstr='-quiet', ),
roisel=dict(argstr='-roisel %s', ),
stat=dict(argstr='%s...', ),
zerofill=dict(
argstr='-zerofill %s',
requires=['num_roi'],
),
)
inputs = ROIStats.input_spec()
Expand All @@ -34,7 +50,7 @@ def test_ROIStats_inputs():
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value
def test_ROIStats_outputs():
output_map = dict(stats=dict(), )
output_map = dict(out_file=dict(), )
outputs = ROIStats.output_spec()

for key, metadata in list(output_map.items()):
Expand Down
56 changes: 56 additions & 0 deletions nipype/interfaces/afni/tests/test_auto_ReHo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from __future__ import unicode_literals
from ..utils import ReHo


def test_ReHo_inputs():
input_map = dict(
args=dict(argstr='%s', ),
chi_sq=dict(argstr='-chi_sq', ),
ellipsoid=dict(
argstr='-neigh_X %s -neigh_Y %s -neigh_Z %s',
xor=['sphere', 'neighborhood'],
),
environ=dict(
nohash=True,
usedefault=True,
),
in_file=dict(
argstr='-inset %s',
mandatory=True,
position=1,
),
label_set=dict(argstr='-in_rois %s', ),
mask=dict(argstr='-mask %s', ),
neighborhood=dict(
argstr='-nneigh %s',
xor=['sphere', 'ellipsoid'],
),
out_file=dict(
argstr='-prefix %s',
keep_extension=True,
name_source='in_file',
name_template='%s_reho',
position=0,
),
overwrite=dict(argstr='-overwrite', ),
sphere=dict(
argstr='-neigh_RAD %s',
xor=['neighborhood', 'ellipsoid'],
),
)
inputs = ReHo.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value
def test_ReHo_outputs():
output_map = dict(
out_file=dict(),
out_vals=dict(),
)
outputs = ReHo.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
Loading