Skip to content

Commit d75713d

Browse files
committed
Merge pull request #615 from chrisfilo/enh/FLAMEO
Enh/flameo
2 parents c2ec358 + 5473235 commit d75713d

File tree

2 files changed

+93
-62
lines changed

2 files changed

+93
-62
lines changed

nipype/interfaces/fsl/model.py

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from nipype.utils.filemanip import (
2727
list_to_filename, filename_to_list, fname_presuffix)
2828
from nibabel import load
29+
from nipype.utils.misc import human_order_sorted
2930

3031
warn = warnings.warn
3132
warnings.filterwarnings('always', category=UserWarning)
@@ -678,6 +679,9 @@ class FLAMEOOutputSpec(TraitedSpec):
678679
exists=True, desc="Variance estimates for each contrast")
679680
zstats = OutputMultiPath(exists=True, desc="z-stat file for each contrast")
680681
tstats = OutputMultiPath(exists=True, desc="t-stat file for each contrast")
682+
zfstats = OutputMultiPath(
683+
exists=True, desc="z stat file for each f contrast")
684+
fstats = OutputMultiPath(exists=True, desc="f-stat file for each contrast")
681685
mrefvars = OutputMultiPath(
682686
exists=True, desc="mean random effect variances for each contrast")
683687
tdof = OutputMultiPath(
@@ -732,41 +736,57 @@ def _list_outputs(self):
732736
outputs = self._outputs().get()
733737
pth = os.path.join(os.getcwd(), self.inputs.log_dir)
734738

735-
pes = glob(os.path.join(pth, 'pe[0-9]*.*'))
739+
pes = human_order_sorted(glob(os.path.join(pth, 'pe[0-9]*.*')))
736740
assert len(pes) >= 1, 'No pe volumes generated by FSL Estimate'
737741
outputs['pes'] = pes
738742

739-
res4d = glob(os.path.join(pth, 'res4d.*'))
743+
res4d = human_order_sorted(glob(os.path.join(pth, 'res4d.*')))
740744
assert len(res4d) == 1, 'No residual volume generated by FSL Estimate'
741745
outputs['res4d'] = res4d[0]
742746

743-
copes = glob(os.path.join(pth, 'cope[0-9]*.*'))
747+
copes = human_order_sorted(glob(os.path.join(pth, 'cope[0-9]*.*')))
744748
assert len(copes) >= 1, 'No cope volumes generated by FSL CEstimate'
745749
outputs['copes'] = copes
746750

747-
var_copes = glob(os.path.join(pth, 'varcope[0-9]*.*'))
751+
var_copes = human_order_sorted(
752+
glob(os.path.join(pth, 'varcope[0-9]*.*')))
748753
assert len(
749754
var_copes) >= 1, 'No varcope volumes generated by FSL CEstimate'
750755
outputs['var_copes'] = var_copes
751756

752-
zstats = glob(os.path.join(pth, 'zstat[0-9]*.*'))
757+
zstats = human_order_sorted(glob(os.path.join(pth, 'zstat[0-9]*.*')))
753758
assert len(zstats) >= 1, 'No zstat volumes generated by FSL CEstimate'
754759
outputs['zstats'] = zstats
755760

756-
tstats = glob(os.path.join(pth, 'tstat[0-9]*.*'))
761+
if isdefined(self.inputs.f_con_file):
762+
zfstats = human_order_sorted(
763+
glob(os.path.join(pth, 'zfstat[0-9]*.*')))
764+
assert len(
765+
zfstats) >= 1, 'No zfstat volumes generated by FSL CEstimate'
766+
outputs['zfstats'] = zfstats
767+
768+
fstats = human_order_sorted(
769+
glob(os.path.join(pth, 'fstat[0-9]*.*')))
770+
assert len(
771+
fstats) >= 1, 'No fstat volumes generated by FSL CEstimate'
772+
outputs['fstats'] = fstats
773+
774+
tstats = human_order_sorted(glob(os.path.join(pth, 'tstat[0-9]*.*')))
757775
assert len(tstats) >= 1, 'No tstat volumes generated by FSL CEstimate'
758776
outputs['tstats'] = tstats
759777

760-
mrefs = glob(os.path.join(pth, 'mean_random_effects_var[0-9]*.*'))
778+
mrefs = human_order_sorted(
779+
glob(os.path.join(pth, 'mean_random_effects_var[0-9]*.*')))
761780
assert len(
762781
mrefs) >= 1, 'No mean random effects volumes generated by FLAMEO'
763782
outputs['mrefvars'] = mrefs
764783

765-
tdof = glob(os.path.join(pth, 'tdof_t[0-9]*.*'))
784+
tdof = human_order_sorted(glob(os.path.join(pth, 'tdof_t[0-9]*.*')))
766785
assert len(tdof) >= 1, 'No T dof volumes generated by FLAMEO'
767786
outputs['tdof'] = tdof
768787

769-
weights = glob(os.path.join(pth, 'weights[0-9]*.*'))
788+
weights = human_order_sorted(
789+
glob(os.path.join(pth, 'weights[0-9]*.*')))
770790
assert len(weights) >= 1, 'No weight volumes generated by FLAMEO'
771791
outputs['weights'] = weights
772792

@@ -784,7 +804,7 @@ class ContrastMgrInputSpec(FSLCommandInputSpec):
784804
param_estimates = InputMultiPath(File(exists=True),
785805
argstr='', copyfile=False,
786806
mandatory=True,
787-
desc='Parameter estimates for each column of the design matrix')
807+
desc='Parameter estimates for each column of the design matrix')
788808
corrections = File(exists=True, copyfile=False, mandatory=True,
789809
desc='statistical corrections used within FILM modelling')
790810
dof_file = File(exists=True, argstr='', copyfile=False, mandatory=True,
@@ -793,26 +813,26 @@ class ContrastMgrInputSpec(FSLCommandInputSpec):
793813
copyfile=False, mandatory=True,
794814
desc='summary of residuals, See Woolrich, et. al., 2001')
795815
contrast_num = traits.Int(min=1, argstr='-cope',
796-
desc='contrast number to start labeling copes from')
816+
desc='contrast number to start labeling copes from')
797817
suffix = traits.Str(argstr='-suffix %s',
798818
desc='suffix to put on the end of the cope filename before the contrast number, default is nothing')
799819

800820

801821
class ContrastMgrOutputSpec(TraitedSpec):
802822
copes = OutputMultiPath(File(exists=True),
803-
desc='Contrast estimates for each contrast')
823+
desc='Contrast estimates for each contrast')
804824
varcopes = OutputMultiPath(File(exists=True),
805-
desc='Variance estimates for each contrast')
825+
desc='Variance estimates for each contrast')
806826
zstats = OutputMultiPath(File(exists=True),
807-
desc='z-stat file for each contrast')
827+
desc='z-stat file for each contrast')
808828
tstats = OutputMultiPath(File(exists=True),
809-
desc='t-stat file for each contrast')
829+
desc='t-stat file for each contrast')
810830
fstats = OutputMultiPath(File(exists=True),
811-
desc='f-stat file for each contrast')
831+
desc='f-stat file for each contrast')
812832
zfstats = OutputMultiPath(File(exists=True),
813833
desc='z-stat file for each F contrast')
814834
neffs = OutputMultiPath(File(exists=True),
815-
desc='neff file ?? for each contrast')
835+
desc='neff file ?? for each contrast')
816836

817837

818838
class ContrastMgr(FSLCommand):
@@ -881,16 +901,16 @@ def _list_outputs(self):
881901
neffs = []
882902
for i in range(numtcons):
883903
copes.append(self._gen_fname('cope%d.nii' % (base_contrast + i),
884-
cwd=pth))
904+
cwd=pth))
885905
varcopes.append(
886906
self._gen_fname('varcope%d.nii' % (base_contrast + i),
887-
cwd=pth))
907+
cwd=pth))
888908
zstats.append(self._gen_fname('zstat%d.nii' % (base_contrast + i),
889-
cwd=pth))
909+
cwd=pth))
890910
tstats.append(self._gen_fname('tstat%d.nii' % (base_contrast + i),
891-
cwd=pth))
911+
cwd=pth))
892912
neffs.append(self._gen_fname('neff%d.nii' % (base_contrast + i),
893-
cwd=pth))
913+
cwd=pth))
894914
if copes:
895915
outputs['copes'] = copes
896916
outputs['varcopes'] = varcopes
@@ -901,10 +921,10 @@ def _list_outputs(self):
901921
zfstats = []
902922
for i in range(numfcons):
903923
fstats.append(self._gen_fname('fstat%d.nii' % (base_contrast + i),
904-
cwd=pth))
924+
cwd=pth))
905925
zfstats.append(
906926
self._gen_fname('zfstat%d.nii' % (base_contrast + i),
907-
cwd=pth))
927+
cwd=pth))
908928
if fstats:
909929
outputs['fstats'] = fstats
910930
outputs['zfstats'] = zfstats
@@ -913,7 +933,7 @@ def _list_outputs(self):
913933

914934
class L2ModelInputSpec(BaseInterfaceInputSpec):
915935
num_copes = traits.Int(min=1, mandatory=True,
916-
desc='number of copes to be combined')
936+
desc='number of copes to be combined')
917937

918938

919939
class L2ModelOutputSpec(TraitedSpec):
@@ -1011,7 +1031,7 @@ class MultipleRegressDesignInputSpec(BaseInterfaceInputSpec):
10111031
mandatory=True,
10121032
desc='dictionary containing named lists of regressors')
10131033
groups = traits.List(traits.Int,
1014-
desc='list of group identifiers (defaults to single group)')
1034+
desc='list of group identifiers (defaults to single group)')
10151035

10161036

10171037
class MultipleRegressDesignOutputSpec(TraitedSpec):
@@ -1091,7 +1111,7 @@ def _run_interface(self, runtime):
10911111
convals = np.zeros((nwaves, 1))
10921112
for regidx, reg in enumerate(self.inputs.contrasts[idx][2]):
10931113
convals[regs.index(reg)
1094-
] = self.inputs.contrasts[idx][3][regidx]
1114+
] = self.inputs.contrasts[idx][3][regidx]
10951115
con_txt.append(' '.join(['%e' % val for val in convals]))
10961116
con_txt = '\n'.join(con_txt)
10971117
# write f-con file
@@ -1150,11 +1170,11 @@ def _list_outputs(self):
11501170
class SMMInputSpec(FSLCommandInputSpec):
11511171
spatial_data_file = File(
11521172
exists=True, position=0, argstr='--sdf="%s"', mandatory=True,
1153-
desc="statistics spatial map", copyfile=False)
1173+
desc="statistics spatial map", copyfile=False)
11541174
mask = File(exist=True, position=1, argstr='--mask="%s"', mandatory=True,
11551175
desc="mask file", copyfile=False)
11561176
no_deactivation_class = traits.Bool(position=2, argstr="--zfstatmode",
1157-
desc="enforces no deactivation class")
1177+
desc="enforces no deactivation class")
11581178

11591179

11601180
class SMMOutputSpec(TraitedSpec):
@@ -1189,7 +1209,7 @@ def _list_outputs(self):
11891209
class MELODICInputSpec(FSLCommandInputSpec):
11901210
in_files = InputMultiPath(
11911211
File(exists=True), argstr="-i %s", mandatory=True, position=0,
1192-
desc="input file names (either single file name or a list)")
1212+
desc="input file names (either single file name or a list)")
11931213
out_dir = Directory(
11941214
argstr="-o %s", desc="output directory name", genfile=True)
11951215
mask = File(exists=True, argstr="-m %s",
@@ -1474,7 +1494,7 @@ def _list_outputs(self):
14741494
suffix='_' + suffix,
14751495
change_ext=change_ext)
14761496
else:
1477-
outputs[outkey] = os.pardir.abspath(inval)
1497+
outputs[outkey] = os.path.abspath(inval)
14781498
return outputs
14791499

14801500
def _format_arg(self, name, spec, value):
@@ -1492,7 +1512,7 @@ class RandomiseInputSpec(FSLCommandInputSpec):
14921512
position=0, mandatory=True)
14931513
base_name = traits.Str(
14941514
'tbss_', desc='the rootname that all generated files will have',
1495-
argstr='-o "%s"', position=1, usedefault=True)
1515+
argstr='-o "%s"', position=1, usedefault=True)
14961516
design_mat = File(
14971517
exists=True, desc='design matrix file', argstr='-d %s', position=2)
14981518
tcon = File(
@@ -1505,27 +1525,27 @@ class RandomiseInputSpec(FSLCommandInputSpec):
15051525
desc='demean data temporally before model fitting', argstr='-D')
15061526
one_sample_group_mean = traits.Bool(
15071527
desc='perform 1-sample group-mean test instead of generic permutation test',
1508-
argstr='-1')
1528+
argstr='-1')
15091529
show_total_perms = traits.Bool(
15101530
desc='print out how many unique permutations would be generated and exit',
1511-
argstr='-q')
1531+
argstr='-q')
15121532
show_info_parallel_mode = traits.Bool(
15131533
desc='print out information required for parallel mode and exit',
1514-
argstr='-Q')
1534+
argstr='-Q')
15151535
vox_p_values = traits.Bool(
15161536
desc='output voxelwise (corrected and uncorrected) p-value images',
1517-
argstr='-x')
1537+
argstr='-x')
15181538
tfce = traits.Bool(
15191539
desc='carry out Threshold-Free Cluster Enhancement', argstr='-T')
15201540
tfce2D = traits.Bool(
15211541
desc='carry out Threshold-Free Cluster Enhancement with 2D optimisation',
1522-
argstr='--T2')
1542+
argstr='--T2')
15231543
f_only = traits.Bool(desc='calculate f-statistics only', argstr='--f_only')
15241544
raw_stats_imgs = traits.Bool(
15251545
desc='output raw ( unpermuted ) statistic images', argstr='-R')
15261546
p_vec_n_dist_files = traits.Bool(
15271547
desc='output permutation vector and null distribution text files',
1528-
argstr='-P')
1548+
argstr='-P')
15291549
num_perm = traits.Int(
15301550
argstr='-n %d', desc='number of permutations (default 5000, set to 0 for exhaustive)')
15311551
seed = traits.Int(
@@ -1596,9 +1616,9 @@ class Randomise(FSLCommand):
15961616
def _list_outputs(self):
15971617
outputs = self.output_spec().get()
15981618
outputs['tstat_files'] = glob(self._gen_fname(
1599-
'%s_tstat*.nii' % self.inputs.base_name))
1619+
'%s_tstat*.nii' % self.inputs.base_name))
16001620
outputs['fstat_files'] = glob(self._gen_fname(
1601-
'%s_fstat*.nii' % self.inputs.base_name))
1621+
'%s_fstat*.nii' % self.inputs.base_name))
16021622
prefix = False
16031623
if self.inputs.tfce or self.inputs.tfce2D:
16041624
prefix = 'tfce'
@@ -1610,7 +1630,7 @@ def _list_outputs(self):
16101630
prefix = 'clusterm'
16111631
if prefix:
16121632
outputs['t_p_files'] = glob(self._gen_fname(
1613-
'%s_%s_p_tstat*' % (self.inputs.base_name, prefix)))
1633+
'%s_%s_p_tstat*' % (self.inputs.base_name, prefix)))
16141634
outputs['t_corrected_p_files'] = glob(self._gen_fname(
16151635
'%s_%s_corrp_tstat*.nii' % (self.inputs.base_name, prefix)))
16161636

@@ -1625,22 +1645,22 @@ class GLMInputSpec(FSLCommandInputSpec):
16251645
in_file = File(exists=True, argstr='-i %s', mandatory=True, position=1,
16261646
desc='input file name (text matrix or 3D/4D image file)')
16271647
out_file = File(argstr='-o %s', genfile=True, position=3,
1628-
desc=('filename for GLM parameter estimates'
1629-
+ ' (GLM betas)'))
1648+
desc=('filename for GLM parameter estimates'
1649+
+ ' (GLM betas)'))
16301650
design = File(exists=True, argstr='-d %s', mandatory=True, position=2,
1631-
desc=('file name of the GLM design matrix (text time'
1632-
+ ' courses for temporal regression or an image'
1633-
+ ' file for spatial regression)'))
1651+
desc=('file name of the GLM design matrix (text time'
1652+
+ ' courses for temporal regression or an image'
1653+
+ ' file for spatial regression)'))
16341654
contrasts = File(exists=True, argstr='-c %s', desc=('matrix of t-statics'
16351655
+ ' contrasts'))
16361656
mask = File(exists=True, argstr='-m %s', desc=('mask image file name if'
16371657
+ ' input is image'))
16381658
dof = traits.Int(argstr='--dof %d', desc=('set degrees of freedom'
16391659
+ ' explicitly'))
16401660
des_norm = traits.Bool(argstr='--des_norm', desc=('switch on normalization'
1641-
+ ' of the design matrix'
1642-
+ ' columns to unit std'
1643-
+ ' deviation'))
1661+
+ ' of the design matrix'
1662+
+ ' columns to unit std'
1663+
+ ' deviation'))
16441664
dat_norm = traits.Bool(argstr='--dat_norm', desc=('switch on normalization'
16451665
+ ' of the data time'
16461666
+ ' series to unit std'
@@ -1652,30 +1672,30 @@ class GLMInputSpec(FSLCommandInputSpec):
16521672
out_cope = File(argstr='--out_cope=%s',
16531673
desc='output file name for COPE (either as txt or image')
16541674
out_z_name = File(argstr='--out_z=%s',
1655-
desc='output file name for Z-stats (either as txt or image')
1675+
desc='output file name for Z-stats (either as txt or image')
16561676
out_t_name = File(argstr='--out_t=%s',
1657-
desc='output file name for t-stats (either as txt or image')
1677+
desc='output file name for t-stats (either as txt or image')
16581678

16591679
out_p_name = File(argstr='--out_p=%s',
1660-
desc=('output file name for p-values of Z-stats (either as'
1661-
+ ' text file or image)'))
1680+
desc=('output file name for p-values of Z-stats (either as'
1681+
+ ' text file or image)'))
16621682
out_f_name = File(argstr='--out_f=%s',
1663-
desc='output file name for F-value of full model fit')
1683+
desc='output file name for F-value of full model fit')
16641684
out_pf_name = File(argstr='--out_pf=%s',
1665-
desc='output file name for p-value for full model fit')
1685+
desc='output file name for p-value for full model fit')
16661686
out_res_name = File(argstr='--out_res=%s',
1667-
desc='output file name for residuals')
1687+
desc='output file name for residuals')
16681688
out_varcb_name = File(argstr='--out_varcb=%s',
1669-
desc='output file name for variance of COPEs')
1689+
desc='output file name for variance of COPEs')
16701690

16711691
out_sigsq_name = File(argstr='--out_sigsq=%s',
1672-
desc=('output file name for residual noise variance'
1673-
+ ' sigma-square'))
1692+
desc=('output file name for residual noise variance'
1693+
+ ' sigma-square'))
16741694
out_data_name = File(argstr='--out_data=%s',
1675-
desc='output file name for pre-processed data')
1695+
desc='output file name for pre-processed data')
16761696
out_vnscales_name = File(argstr='--out_vnscales=%s',
1677-
desc=('output file name for scaling factors for variance'
1678-
+ ' normalisation'))
1697+
desc=('output file name for scaling factors for variance'
1698+
+ ' normalisation'))
16791699

16801700

16811701
class GLMOutputSpec(TraitedSpec):

nipype/utils/misc.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@
99
import numpy as np
1010
from textwrap import dedent
1111
import sys
12+
import re
13+
14+
def human_order_sorted(l):
15+
"""Sorts string in human order (i.e. 'stat10' will go after 'stat2')"""
16+
def atoi(text):
17+
return int(text) if text.isdigit() else text
18+
19+
def natural_keys(text):
20+
return [ atoi(c) for c in re.split('(\d+)', text) ]
21+
22+
return sorted(l, key=natural_keys)
1223

1324
def trim(docstring, marker=None):
1425
if not docstring:

0 commit comments

Comments
 (0)