Skip to content

Commit 182c9ec

Browse files
committed
Added caching features to get ~2 order of magnitude speedup.
1 parent 7080ef9 commit 182c9ec

File tree

1 file changed

+39
-14
lines changed

1 file changed

+39
-14
lines changed

nipype/algorithms/icc.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,41 @@ def _list_outputs(self):
8686
return outputs
8787

8888

89-
def ICC_rep_anova(Y):
89+
def ICC_rep_anova(Y, nocache=False):
9090
"""
9191
the data Y are entered as a 'table' ie subjects are in rows and repeated
9292
measures in columns
93-
9493
One Sample Repeated measure ANOVA
95-
9694
Y = XB + E with X = [FaTor / Subjects]
97-
"""
9895
99-
[nb_subjects, nb_conditions] = Y.shape
100-
dfc = nb_conditions - 1
101-
dfe = (nb_subjects - 1) * dfc
102-
dfr = nb_subjects - 1
96+
This is a hacked up (but fully compatible) version of ICC_rep_anova
97+
from nipype that caches some very expensive operations that depend
98+
only on the input array shape - if you're going to run the routine
99+
multiple times (like, on every voxel of an image), this gives you a
100+
HUGE speed boost for large input arrays. If you change the dimensions
101+
of Y, it will reinitialize automatially. Set nocache to True to get
102+
the original, much slower behavior. No, actually, don't do that.
103+
"""
104+
global icc_inited
105+
global current_Y_shape
106+
global dfc, dfe, dfr
107+
global nb_subjects, nb_conditions
108+
global x, x0, X
109+
global centerbit
110+
111+
try:
112+
current_Y_shape
113+
if nocache or (current_Y_shape != Y.shape):
114+
icc_inited = False
115+
except NameError:
116+
icc_inited = False
117+
118+
if not icc_inited:
119+
[nb_subjects, nb_conditions] = Y.shape
120+
current_Y_shape = Y.shape
121+
dfc = nb_conditions - 1
122+
dfe = (nb_subjects - 1) * dfc
123+
dfr = nb_subjects - 1
103124

104125
# Compute the repeated measure effect
105126
# ------------------------------------
@@ -109,20 +130,22 @@ def ICC_rep_anova(Y):
109130
SST = ((Y - mean_Y) ** 2).sum()
110131

111132
# create the design matrix for the different levels
112-
x = kron(eye(nb_conditions), ones((nb_subjects, 1))) # sessions
113-
x0 = tile(eye(nb_subjects), (nb_conditions, 1)) # subjects
114-
X = hstack([x, x0])
133+
if not icc_inited:
134+
x = kron(eye(nb_conditions), ones((nb_subjects, 1))) # sessions
135+
x0 = tile(eye(nb_subjects), (nb_conditions, 1)) # subjects
136+
X = hstack([x, x0])
137+
centerbit = dot(dot(X, pinv(dot(X.T, X))), X.T)
115138

116139
# Sum Square Error
117-
predicted_Y = dot(dot(dot(X, pinv(dot(X.T, X))), X.T), Y.flatten("F"))
140+
predicted_Y = dot(centerbit, Y.flatten("F"))
118141
residuals = Y.flatten("F") - predicted_Y
119142
SSE = (residuals ** 2).sum()
120143

121144
residuals.shape = Y.shape
122145

123146
MSE = SSE / dfe
124147

125-
# Sum square session effect - between colums/sessions
148+
# Sum square session effect - between columns/sessions
126149
SSC = ((mean(Y, 0) - mean_Y) ** 2).sum() * nb_subjects
127150
MSC = SSC / dfc / nb_subjects
128151

@@ -134,9 +157,11 @@ def ICC_rep_anova(Y):
134157

135158
# ICC(3,1) = (mean square subjeT - mean square error) /
136159
# (mean square subjeT + (k-1)*-mean square error)
137-
ICC = (MSR - MSE) / (MSR + dfc * MSE)
160+
ICC = nan_to_num((MSR - MSE) / (MSR + dfc * MSE))
138161

139162
e_var = MSE # variance of error
140163
r_var = (MSR - MSE) / nb_conditions # variance between subjects
141164

165+
icc_inited = True
166+
142167
return ICC, r_var, e_var, session_effect_F, dfc, dfe

0 commit comments

Comments
 (0)