Skip to content

Commit bba591b

Browse files
author
Shoshana Berleant
committed
add headers to outputs of compcor, framewise displacement + test fix
1 parent b3187f3 commit bba591b

File tree

3 files changed

+54
-21
lines changed

3 files changed

+54
-21
lines changed

nipype/algorithms/confounds.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def _run_interface(self, runtime):
255255
'out_file': op.abspath(self.inputs.out_file),
256256
'fd_average': float(fd_res.mean())
257257
}
258-
np.savetxt(self.inputs.out_file, fd_res)
258+
np.savetxt(self.inputs.out_file, fd_res, header='framewise_displacement')
259259

260260
if self.inputs.save_plot:
261261
tr = None
@@ -291,6 +291,8 @@ class CompCorInputSpec(BaseInterfaceInputSpec):
291291
'pre-component extraction')
292292
regress_poly_degree = traits.Range(low=1, default=1, usedefault=True,
293293
desc='the degree polynomial to use')
294+
header = traits.Str(desc='the desired header for the output tsv file (one column).'
295+
'If undefined, will default to "CompCor"')
294296

295297
class CompCorOutputSpec(TraitedSpec):
296298
components_file = File(exists=True,
@@ -359,7 +361,9 @@ def _run_interface(self, runtime):
359361
u, _, _ = linalg.svd(M, full_matrices=False)
360362
components = u[:, :self.inputs.num_components]
361363
components_file = os.path.join(os.getcwd(), self.inputs.components_file)
362-
np.savetxt(components_file, components, fmt=b"%.10f")
364+
365+
self._set_header()
366+
np.savetxt(components_file, components, fmt=b"%.10f", header=self._make_headers(components.shape[1]))
363367
return runtime
364368

365369
def _list_outputs(self):
@@ -374,6 +378,26 @@ def _compute_tSTD(self, M, x):
374378
stdM[np.isnan(stdM)] = x
375379
return stdM
376380

381+
def _set_header(self, header='CompCor'):
382+
self.inputs.header = self.inputs.header if isdefined(self.inputs.header) else header
383+
384+
def _make_headers(self, num_col):
385+
headers = []
386+
for i in range(num_col):
387+
headers.append(self.inputs.header + str(i))
388+
return '\t'.join(headers)
389+
390+
391+
class ACompCor(CompCor):
392+
''' Anatomical compcor; for input/output, see CompCor.
393+
If the mask provided is an anatomical mask, CompCor == ACompCor '''
394+
395+
def __init__(self, *args, **kwargs):
396+
''' exactly the same as compcor except the header '''
397+
super(ACompCor, self).__init__(*args, **kwargs)
398+
self._set_header('aCompCor')
399+
400+
377401
class TCompCorInputSpec(CompCorInputSpec):
378402
# and all the fields in CompCorInputSpec
379403
percentile_threshold = traits.Range(low=0., high=1., value=.02,
@@ -439,12 +463,11 @@ def _run_interface(self, runtime):
439463
IFLOG.debug('tCompcor computed and saved mask of shape {} to mask_file {}'
440464
.format(mask.shape, mask_file))
441465
self.inputs.mask_file = mask_file
466+
self._set_header('tCompCor')
442467

443468
super(TCompCor, self)._run_interface(runtime)
444469
return runtime
445470

446-
ACompCor = CompCor
447-
448471
class TSNRInputSpec(BaseInterfaceInputSpec):
449472
in_file = InputMultiPath(File(exists=True), mandatory=True,
450473
desc='realigned 4D file or a list of 3D files')

nipype/algorithms/tests/test_compcor.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,20 @@ def test_compcor(self):
3838
['0.4206466244', '-0.3361270124'],
3939
['-0.1246655485', '-0.1235705610']]
4040

41-
ccresult = self.run_cc(CompCor(realigned_file=self.realigned_file,
42-
mask_file=self.mask_file),
41+
self.run_cc(CompCor(realigned_file=self.realigned_file, mask_file=self.mask_file),
4342
expected_components)
4443

45-
accresult = self.run_cc(ACompCor(realigned_file=self.realigned_file,
46-
mask_file=self.mask_file,
47-
components_file='acc_components_file'),
48-
expected_components)
49-
50-
assert_equal(os.path.getsize(ccresult.outputs.components_file),
51-
os.path.getsize(accresult.outputs.components_file))
44+
self.run_cc(ACompCor(realigned_file=self.realigned_file, mask_file=self.mask_file,
45+
components_file='acc_components_file'),
46+
expected_components, 'aCompCor')
5247

5348
def test_tcompcor(self):
5449
ccinterface = TCompCor(realigned_file=self.realigned_file, percentile_threshold=0.75)
5550
self.run_cc(ccinterface, [['-0.1114536190', '-0.4632908609'],
5651
['0.4566907310', '0.6983205193'],
5752
['-0.7132557407', '0.1340170559'],
5853
['0.5022537643', '-0.5098322262'],
59-
['-0.1342351356', '0.1407855119']])
54+
['-0.1342351356', '0.1407855119']], 'tCompCor')
6055

6156
def test_tcompcor_no_percentile(self):
6257
ccinterface = TCompCor(realigned_file=self.realigned_file)
@@ -96,7 +91,7 @@ def test_tcompcor_bad_input_dim(self):
9691
interface = TCompCor(realigned_file=data_file)
9792
self.assertRaisesRegexp(ValueError, '4-D', interface.run)
9893

99-
def run_cc(self, ccinterface, expected_components):
94+
def run_cc(self, ccinterface, expected_components, expected_header='CompCor'):
10095
# run
10196
ccresult = ccinterface.run()
10297

@@ -108,12 +103,19 @@ def run_cc(self, ccinterface, expected_components):
108103
assert_equal(ccinterface.inputs.num_components, 6)
109104

110105
with open(ccresult.outputs.components_file, 'r') as components_file:
106+
expected_n_components = min(ccinterface.inputs.num_components, self.fake_data.shape[3])
107+
111108
components_data = [line.split() for line in components_file]
112-
num_got_components = len(components_data)
113-
assert_true(num_got_components == ccinterface.inputs.num_components
114-
or num_got_components == self.fake_data.shape[3])
115-
first_two = [row[:2] for row in components_data]
116-
assert_equal(first_two, expected_components)
109+
110+
header = components_data.pop(0)[1:] # the first item will be '#', we can throw it out
111+
assert_equal(header, [expected_header + str(i) for i in range(expected_n_components)])
112+
113+
num_got_timepoints = len(components_data)
114+
assert_equal(num_got_timepoints, self.fake_data.shape[3])
115+
for index, timepoint in enumerate(components_data):
116+
assert_true(len(timepoint) == ccinterface.inputs.num_components
117+
or len(timepoint) == self.fake_data.shape[3])
118+
assert_equal(timepoint[:2], expected_components[index])
117119
return ccresult
118120

119121
def tearDown(self):

nipype/algorithms/tests/test_confounds.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from tempfile import mkdtemp
55
from shutil import rmtree
66

7-
from nipype.testing import (assert_equal, example_data, skipif, assert_true)
7+
from nipype.testing import (assert_equal, example_data, skipif, assert_true, assert_in)
88
from nipype.algorithms.confounds import FramewiseDisplacement, ComputeDVARS
99
import numpy as np
1010

@@ -24,8 +24,14 @@ def test_fd():
2424
out_file=tempdir + '/fd.txt')
2525
res = fdisplacement.run()
2626

27+
with open(res.outputs.out_file) as all_lines:
28+
for line in all_lines:
29+
yield assert_in, 'framewise_displacement', line
30+
break
31+
2732
yield assert_true, np.allclose(ground_truth, np.loadtxt(res.outputs.out_file), atol=.16)
2833
yield assert_true, np.abs(ground_truth.mean() - res.outputs.fd_average) < 1e-2
34+
2935
rmtree(tempdir)
3036

3137
@skipif(nonitime)
@@ -40,3 +46,5 @@ def test_dvars():
4046

4147
dv1 = np.loadtxt(res.outputs.out_std)
4248
yield assert_equal, (np.abs(dv1 - ground_truth).sum()/ len(dv1)) < 0.05, True
49+
50+
rmtree(tempdir)

0 commit comments

Comments
 (0)