Skip to content

Commit 002955a

Browse files
bbfrederickeffigies
authored andcommitted
Added caching features to get ~2 order of magnitude speedup.
1 parent 3dc858e commit 002955a

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

nipype/algorithms/icc.py

Lines changed: 38 additions & 13 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 = X @ pinv(X.T @ X, hermitian=True) @ X.T
115138

116139
# Sum Square Error
117-
predicted_Y = X @ (pinv(X.T @ X, hermitian=True) @ (X.T @ Y.flatten("F")))
140+
predicted_Y = 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

@@ -139,4 +162,6 @@ def ICC_rep_anova(Y):
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)