Skip to content

Commit 0d20d5b

Browse files
mariomario
authored andcommitted
improved commenting & code layout
1 parent f06f5a9 commit 0d20d5b

File tree

3 files changed

+189
-190
lines changed

3 files changed

+189
-190
lines changed

code/python/cni_toolbox/IRM.py

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'''
22
-----------------------------------------------------------------------------
3-
LICENSE
3+
LICENSE
44
55
Copyright 2020 Mario Senden
66
@@ -27,41 +27,41 @@
2727
class IRM:
2828
'''
2929
Input-referred model (IRM) mapping tool.
30-
30+
3131
irm = IRM(parameters) creates an instance of the IRM class.
3232
parameters is a dictionary with 5 required keys
3333
- f_sampling: sampling frequency (1/TR)
3434
- n_samples : number of samples (volumes)
3535
- n_rows : number of rows (in-plane resolution)
3636
- n_cols : number of columns (in-plance resolution)
3737
- n_slices : number of slices
38-
38+
3939
optional inputs are
4040
- hrf : either a column vector containing a single hemodynamic
4141
response used for every voxel;
4242
or a matrix with a unique hemodynamic response along
4343
its columns for each voxel.
4444
By default the canonical two-gamma hemodynamic response
4545
function is generated internally based on the scan parameters.
46-
46+
4747
This class has the following functions
48-
48+
4949
- hrf = IRM.get_hrf()
5050
- stimulus = IRM.get_stimulus()
5151
- tc = IRM.get_timecourses()
5252
- IRM.set_hrf(hrf)
5353
- IRM.set_stimulus(stimulus)
5454
- IRM.create_timecourses()
5555
- results = IRM.mapping(data)
56-
56+
5757
5858
Typical workflow:
5959
1. irm = IRM(params)
6060
2. irm.set_stimulus()
6161
3. irm.create_timecourse(FUN,xdata)
6262
4. results = irm.mapping(data)
6363
'''
64-
64+
6565
def __init__(self, parameters, hrf = None):
6666
self.f_sampling = parameters['f_sampling']
6767
self.p_sampling = 1 / self.f_sampling
@@ -70,7 +70,7 @@ def __init__(self, parameters, hrf = None):
7070
self.n_cols = parameters['n_cols']
7171
self.n_slices = parameters['n_slices']
7272
self.n_total = self.n_rows * self.n_cols * self.n_slices
73-
73+
7474
if hrf != None:
7575
self.l_hrf = hrf.shape[0]
7676
if hrf.ndim>2:
@@ -80,23 +80,23 @@ def __init__(self, parameters, hrf = None):
8080
self.n_total)))),
8181
axis = 0)
8282
else:
83-
self.hrf_fft = fft(np.append(hrf,
83+
self.hrf_fft = fft(np.append(hrf,
8484
np.zeros(self.n_samples)),
8585
axis = 0 )
8686
else:
8787
self.l_hrf = int(32 * self.f_sampling)
88-
timepoints = np.arange(0,
88+
timepoints = np.arange(0,
8989
self.p_sampling * (self.n_samples +
90-
self.l_hrf) - 1,
90+
self.l_hrf) - 1,
9191
self.p_sampling)
9292
self.hrf_fft = fft(two_gamma(timepoints), axis = 0)
93-
94-
93+
94+
9595
def get_hrf(self):
9696
'''
9797
Returns
9898
-------
99-
hrf : floating point array
99+
hrf: floating point array
100100
hemodynamic response function(s) used by the class
101101
'''
102102
if self.hrf_fft.ndim>1:
@@ -109,34 +109,34 @@ def get_hrf(self):
109109
axis = 0)[0:self.l_hrf, :]
110110
else:
111111
hrf = ifft(self.hrf_fft, axis = 0)[0:self.l_hrf]
112-
112+
113113
return np.abs(hrf)
114-
114+
115115
def get_stimulus(self):
116116
'''
117117
Returns
118118
-------
119119
floating point array (1D)
120120
stimulus used by the class
121121
'''
122-
122+
123123
return self.stimulus
124-
124+
125125
def get_timecourses(self):
126126
'''
127127
Returns
128128
-------
129129
floating point array (time-by-gridsize)
130130
predicted timecourses
131131
'''
132-
132+
133133
return np.abs(ifft(self.tc_fft, axis = 0)[0:self.n_samples, :])
134-
134+
135135
def set_hrf(self, hrf):
136136
'''
137137
Parameters
138138
----------
139-
hrf : floating point array
139+
hrf: floating point array
140140
hemodynamic response function
141141
'''
142142
self.l_hrf = hrf.shape[0]
@@ -147,100 +147,100 @@ def set_hrf(self, hrf):
147147
self.n_total)))),
148148
axis = 0)
149149
else:
150-
self.hrf_fft = fft(np.append(hrf,
150+
self.hrf_fft = fft(np.append(hrf,
151151
np.zeros(self.n_samples)),
152152
axis = 0)
153-
153+
154154
def set_stimulus(self, stimulus):
155155
'''
156-
157156
Parameters
158157
----------
159-
stimulus : floating point array (1D)
158+
stimulus: floating point array (1D)
160159
stimulus used by the class
161-
162160
'''
163161
self.stimulus = stimulus
164-
162+
165163
def create_timecourses(self, FUN, xdata):
166-
'''
164+
'''
167165
creates predicted timecourses based on the stimulus protocol
168166
and a range of parameters for an input referred model.
169167
170168
Required inputs are
171-
- FUN : function handle
172-
defining the input referred model
169+
- FUN: function handle
170+
defining the input referred model
173171
- xdata: dictionary with p elements (p = number of parameters).
174-
Each cell element needs to contain a column vector
175-
of variable length with parameter values to be explored.
176-
'''
177-
172+
Each cell element needs to contain a column vector
173+
of variable length with parameter values to be explored.
174+
'''
175+
print('\ncreating timecourses')
176+
178177
self.xdata = xdata
179178
self.n_predictors = len(xdata)
179+
180180
n_observations = np.zeros(self.n_predictors)
181181
for idx, key in enumerate(self.xdata):
182182
n_observations[idx] = np.size(self.xdata[key])
183-
184183
self.n_points = np.prod(n_observations).astype(int)
185-
184+
186185
idx_all = np.arange(self.n_points)
187186
self.idx = np.zeros((self.n_points, self.n_predictors))
188-
189187
for p in range(self.n_predictors):
190188
self.idx[:, p] = (idx_all // (np.prod(n_observations[p+1::]))) \
191189
% n_observations[p]
192-
self.idx = self.idx.astype(int)
190+
self.idx = self.idx.astype(int)
191+
193192
tc = np.zeros((self.n_samples + self.l_hrf,
194193
self.n_points))
194+
195195
x = np.zeros(self.n_predictors)
196-
print('\ncreating timecourses')
196+
197197
for j in range(self.n_points):
198198
for p, key in enumerate(self.xdata):
199199
x[p] = self.xdata[key][self.idx[j, p]]
200200
tc[0:self.n_samples, j] = FUN(self.stimulus, x)
201201
i = int(j / self.n_points * 21)
202202
sys.stdout.write('\r')
203-
sys.stdout.write("[%-20s] %d%%"
203+
sys.stdout.write("[%-20s] %d%%"
204204
% ('='*i, 5*i))
205205

206206
self.tc_fft = fft(tc, axis = 0)
207-
207+
208208
def mapping(self, data, threshold = 100, mask = []):
209209
'''
210210
identifies the best fitting timecourse for each voxel and
211-
returns a dictionary with keyes corresponding to the
211+
returns a dictionary with keys corresponding to the
212212
parameters specified in xdata plus a key 'corr_fit'
213213
storing the fitness of the solution.
214214
215215
Required inputs are
216-
- data : floating point array
216+
- data: floating point array
217217
empirically observed BOLD timecourses
218218
whose rows correspond to time (volumes).
219219
220-
221220
Optional inputs are
222221
- threshold: float
223222
minimum voxel intensity (default = 100.0)
224-
- mask: boolean array
223+
- mask: boolean array
225224
binary mask for selecting voxels (default = None)
226225
'''
227-
data = np.reshape(data.astype(float),
226+
print('\nmapping model parameters')
227+
228+
data = np.reshape(data.astype(float),
228229
(self.n_samples,
229230
self.n_total))
230-
231+
231232
mean_signal = np.mean(data, axis = 0)
232233
data = zscore(data, axis = 0)
233-
234+
234235
if np.size(mask)==0:
235236
mask = mean_signal >= threshold
236237

237238
mask = np.reshape(mask,self.n_total)
238239
voxel_index = np.where(mask)[0]
239240
n_voxels = voxel_index.size
240-
241+
241242
mag_d = np.sqrt(np.sum(data[:, mask]**2, axis = 0))
242-
243-
243+
244244
results = {'corr_fit': np.zeros(self.n_total)}
245245
for key in self.xdata:
246246
results[key] = np.zeros(self.n_total)
@@ -254,60 +254,60 @@ def mapping(self, data, threshold = 100, mask = []):
254254
axis = 1), axis = 0)), axis = 0))
255255
tc = tc[:, 0:self.n_samples]
256256
mag_tc = np.sqrt(np.sum(tc**2, axis = 1))
257-
print('\nmapping model parameters')
257+
258258
for m in range(n_voxels):
259259
v = voxel_index[m]
260-
260+
261261
CS = np.matmul(tc, data[:, v]) / (mag_tc * mag_d[m])
262262
idx_remove = (CS == np.Inf)| (CS == np.NaN);
263263
CS[idx_remove] = 0
264-
264+
265265
results['corr_fit'][v] = np.max(CS)
266266
idx_best = np.argmax(CS)
267-
267+
268268
for pos, key in enumerate(self.xdata):
269269
results[key][v] = self.xdata[key][self.idx[idx_best, pos]]
270270

271271
i = int(m / n_voxels * 21)
272272
sys.stdout.write('\r')
273-
sys.stdout.write("[%-20s] %d%%"
273+
sys.stdout.write("[%-20s] %d%%"
274274
% ('='*i, 5*i))
275-
275+
276276
else:
277277
for m in range(n_voxels):
278278
v = voxel_index[m]
279-
279+
280280
tc = np.transpose(
281281
zscore(
282282
np.abs(
283283
ifft(self.tc_fft *
284284
np.expand_dims(self.hrf_fft[:, v],
285285
axis = 1), axis = 0)), axis = 0))
286-
286+
287287
tc = tc[:, 0:self.n_samples]
288288
mag_tc = np.sqrt(np.sum(tc**2, axis = 1))
289-
289+
290290
CS = np.matmul(tc, data[:, v]) / (mag_tc * mag_d[m])
291291
idx_remove = (CS == np.Inf)| (CS == np.NaN);
292292
CS[idx_remove] = 0
293-
293+
294294
results['corr_fit'][v] = np.max(CS)
295295
idx_best = np.argmax(CS)
296-
296+
297297
for pos, key in enumerate(self.xdata):
298298
results[key][v] = self.xdata[key][self.idx[idx_best, pos]]
299299

300300
i = int(m / n_voxels * 21)
301301
sys.stdout.write('\r')
302-
sys.stdout.write("[%-20s] %d%%"
302+
sys.stdout.write("[%-20s] %d%%"
303303
% ('='*i, 5*i))
304-
305-
304+
305+
306306
for key in results:
307307
results[key] = np.squeeze(
308308
np.reshape(results[key],
309309
(self.n_rows,
310310
self.n_cols,
311311
self.n_slices)))
312-
312+
313313
return results

0 commit comments

Comments
 (0)