Skip to content

Commit dedf5ca

Browse files
authored
Merge pull request #2845 from nipy/model
ENH: Support for BIDS event files
2 parents 4468b3a + e88a825 commit dedf5ca

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

nipype/algorithms/modelgen.py

+80-1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,61 @@ def scale_timings(timelist, input_units, output_units, time_repetition):
144144
timelist = [np.max([0., _scalefactor * t]) for t in timelist]
145145
return timelist
146146

147+
def bids_gen_info(bids_event_files,
148+
condition_column='trial_type',
149+
amplitude_column=None,
150+
time_repetition=False,
151+
):
152+
"""Generate subject_info structure from a list of BIDS .tsv event files.
153+
154+
Parameters
155+
----------
156+
157+
bids_event_files : list of str
158+
Filenames of BIDS .tsv event files containing columns including:
159+
'onset', 'duration', and 'trial_type' or the `condition_column` value.
160+
condition_column : str
161+
Column of files in `bids_event_files` based on the values of which
162+
events will be sorted into different regressors
163+
amplitude_column : str
164+
Column of files in `bids_event_files` based on the values of which
165+
to apply amplitudes to events. If unspecified, all events will be
166+
represented with an amplitude of 1.
167+
168+
Returns
169+
-------
170+
171+
list of Bunch
172+
"""
173+
info = []
174+
for bids_event_file in bids_event_files:
175+
with open(bids_event_file) as f:
176+
f_events = csv.DictReader(f, skipinitialspace=True, delimiter='\t')
177+
events = [{k: v for k, v in row.items()} for row in f_events]
178+
conditions = list(set([i[condition_column] for i in events]))
179+
runinfo = Bunch(conditions=[], onsets=[], durations=[], amplitudes=[])
180+
for condition in conditions:
181+
selected_events = [i for i in events if i[condition_column]==condition]
182+
onsets = [float(i['onset']) for i in selected_events]
183+
durations = [float(i['duration']) for i in selected_events]
184+
if time_repetition:
185+
decimals = math.ceil(-math.log10(time_repetition))
186+
onsets = [np.round(i, decimals) for i in onsets]
187+
durations = [np.round(i ,decimals) for i in durations]
188+
if condition:
189+
runinfo.conditions.append(condition)
190+
else:
191+
runinfo.conditions.append('e0')
192+
runinfo.onsets.append(onsets)
193+
runinfo.durations.append(durations)
194+
try:
195+
amplitudes = [float(i[amplitude_column]) for i in selected_events]
196+
runinfo.amplitudes.append(amplitudes)
197+
except KeyError:
198+
runinfo.amplitudes.append([1] * len(onsets))
199+
info.append(runinfo)
200+
return info
201+
147202

148203
def gen_info(run_event_files):
149204
"""Generate subject_info structure from a list of event files
@@ -190,6 +245,23 @@ class SpecifyModelInputSpec(BaseInterfaceInputSpec):
190245
desc='List of event description files 1, 2 or 3 '
191246
'column format corresponding to onsets, '
192247
'durations and amplitudes')
248+
bids_event_file = InputMultiPath(
249+
File(exists=True),
250+
mandatory=True,
251+
xor=['subject_info', 'event_files', 'bids_event_file'],
252+
desc='TSV event file containing common BIDS fields: `onset`,'
253+
'`duration`, and categorization and amplitude columns')
254+
bids_condition_column = traits.Str(exists=True,
255+
mandatory=False,
256+
default_value='trial_type',
257+
usedefault=True,
258+
desc='Column of the file passed to `bids_event_file` to the '
259+
'unique values of which events will be assigned'
260+
'to regressors')
261+
bids_amplitude_column = traits.Str(exists=True,
262+
mandatory=False,
263+
desc='Column of the file passed to `bids_event_file` '
264+
'according to which to assign amplitudes to events')
193265
realignment_parameters = InputMultiPath(
194266
File(exists=True),
195267
desc='Realignment parameters returned '
@@ -432,8 +504,15 @@ def _generate_design(self, infolist=None):
432504
if infolist is None:
433505
if isdefined(self.inputs.subject_info):
434506
infolist = self.inputs.subject_info
435-
else:
507+
elif isdefined(self.inputs.event_files):
436508
infolist = gen_info(self.inputs.event_files)
509+
elif isdefined(self.inputs.bids_event_file):
510+
infolist = bids_gen_info(
511+
self.inputs.bids_event_file,
512+
self.inputs.bids_condition_column,
513+
self.inputs.bids_amplitude_column,
514+
self.inputs.time_repetition,
515+
)
437516
self._sessinfo = self._generate_standard_design(
438517
infolist,
439518
functional_runs=self.inputs.functional_runs,

nipype/algorithms/tests/test_auto_SpecifyModel.py

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55

66
def test_SpecifyModel_inputs():
77
input_map = dict(
8+
bids_amplitude_column=dict(
9+
exists=True,
10+
mandatory=False,
11+
),
12+
bids_condition_column=dict(
13+
exists=True,
14+
mandatory=False,
15+
usedefault=True,
16+
),
17+
bids_event_file=dict(
18+
mandatory=True,
19+
xor=['subject_info', 'event_files', 'bids_event_file'],
20+
),
821
event_files=dict(
922
mandatory=True,
1023
xor=['subject_info', 'event_files'],

nipype/algorithms/tests/test_auto_SpecifySPMModel.py

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55

66
def test_SpecifySPMModel_inputs():
77
input_map = dict(
8+
bids_amplitude_column=dict(
9+
exists=True,
10+
mandatory=False,
11+
),
12+
bids_condition_column=dict(
13+
exists=True,
14+
mandatory=False,
15+
usedefault=True,
16+
),
17+
bids_event_file=dict(
18+
mandatory=True,
19+
xor=['subject_info', 'event_files', 'bids_event_file'],
20+
),
821
concatenate_runs=dict(usedefault=True, ),
922
event_files=dict(
1023
mandatory=True,

nipype/algorithms/tests/test_auto_SpecifySparseModel.py

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55

66
def test_SpecifySparseModel_inputs():
77
input_map = dict(
8+
bids_amplitude_column=dict(
9+
exists=True,
10+
mandatory=False,
11+
),
12+
bids_condition_column=dict(
13+
exists=True,
14+
mandatory=False,
15+
usedefault=True,
16+
),
17+
bids_event_file=dict(
18+
mandatory=True,
19+
xor=['subject_info', 'event_files', 'bids_event_file'],
20+
),
821
event_files=dict(
922
mandatory=True,
1023
xor=['subject_info', 'event_files'],

0 commit comments

Comments
 (0)