Skip to content

Commit cb0a195

Browse files
committed
Add tools for fMRI analysis
1 parent 341805f commit cb0a195

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

Diff for: experiments/fmri/fmri.py

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from __future__ import print_function
2+
from __future__ import division
3+
4+
import nibabel as nib
5+
import numpy as np
6+
7+
8+
def compute_variance_of_cluster(clusters, cluster_index, coords):
9+
filtered = coords[clusters == cluster_index]
10+
return ((filtered - filtered.mean(axis=0)) ** 2).sum(axis=1).mean(axis=0)
11+
12+
13+
def plot_least_varying(plt, clusters, coords, left, right):
14+
n_clusters = np.max(clusters) + 1
15+
variances = [compute_variance_of_cluster(clusters, k, coords) for k in range(n_clusters)]
16+
order = np.argsort(variances)
17+
fig = plt.figure(figsize=(6, 6))
18+
ax = fig.gca(projection='3d')
19+
for k in range(left, right):
20+
print(variances[order[k]])
21+
index = (clusters == order[k])
22+
filtered = coords[index]
23+
ax.scatter(filtered[:, 0], filtered[:, 1], filtered[:, 2], s=5, alpha=0.1)
24+
25+
26+
def plot_most_important(plt, clusters, importance, coords, left, right, mode='absolute'):
27+
a = np.array(importance).copy()
28+
if mode == 'relative':
29+
a = np.array(importance).copy()
30+
n_clusters = np.max(clusters)
31+
for j in range(n_clusters):
32+
cnt = np.sum(clusters == j)
33+
a[j] /= (cnt + 1e-4)
34+
35+
order = np.argsort(-a)
36+
fig = plt.figure(figsize=(6, 6))
37+
ax = fig.gca(projection='3d')
38+
for k in range(left, right):
39+
print(a[order[k]])
40+
index = (clusters == order[k])
41+
filtered = coords[index]
42+
ax.scatter(filtered[:, 0], filtered[:, 1], filtered[:, 2], s=5, alpha=0.1)
43+
44+
45+
def plot_biggest(plt, clusters, coords, left, right):
46+
n_clusters = np.max(clusters) + 1
47+
cnt = [0] * n_clusters
48+
for j in range(n_clusters):
49+
cnt[j] = np.sum(clusters == j)
50+
order = np.argsort(-np.array(cnt))
51+
fig = plt.figure(figsize=(6, 6))
52+
ax = fig.gca(projection='3d')
53+
for k in range(left, right):
54+
print(cnt[order[k]])
55+
index = (clusters == order[k])
56+
filtered = coords[index]
57+
ax.scatter(filtered[:, 0], filtered[:, 1], filtered[:, 2], s=5, alpha=0.1)
58+
59+
60+
def plot_clusters_probabilistic(plotting, prob_clusters, coords, source_img):
61+
""" Plot probabilistic atlas.
62+
:param plotting: nilearn.plotting
63+
:param prob_clusters: (n_clusters, n_voxels)
64+
:param coords: (n_voxels, 3)
65+
:return:
66+
"""
67+
X, Y, Z, T = source_img.shape
68+
a = np.zeros((X, Y, Z))
69+
for j in range(prob_clusters.shape[0]):
70+
for i in range(prob_clusters.shape[1]):
71+
x = int(coords[i, 0])
72+
y = int(coords[i, 1])
73+
z = int(coords[i, 2])
74+
a[x, y, z, j] = prob_clusters[j, i]
75+
atlas = nib.Nifti1Image(a, affine=source_img.affine)
76+
plotting.plot_prob_atlas(atlas, bg_img=False)
77+
78+
79+
def plot_clusters(plotting, clusters, coords, source_img, output_file=None, figure=None):
80+
""" Plot probabilistic atlas.
81+
:param plotting: nilearn.plotting
82+
:param clusters: (n_voxels,)
83+
:param coords: (n_voxels, 3)
84+
:param output_file: if given the plot is saved here
85+
:param figure: figure param to be passed to plotting.plot_roi function
86+
:return:
87+
"""
88+
X, Y, Z, T = source_img.shape
89+
a = np.zeros((X, Y, Z))
90+
for i in range(clusters.shape[0]):
91+
x = int(coords[i, 0])
92+
y = int(coords[i, 1])
93+
z = int(coords[i, 2])
94+
a[x, y, z] = clusters[i]
95+
img = nib.Nifti1Image(a, affine=source_img.affine)
96+
return plotting.plot_roi(img, output_file=output_file, figure=figure)

Diff for: experiments/utils.py

+113
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,116 @@ def plot_for_next_timestep(plt, data, covs, title="Negative log-likelihood of es
8585
plt.show()
8686
print("NLL for next time step = {}".format(np.mean(nll)))
8787
return np.mean(nll)
88+
89+
90+
""" Helper functions for working with large low-rank plus diagonal matrices
91+
"""
92+
93+
94+
def diag_from_left(A, d):
95+
""" diag(d) A """
96+
m, n = A.shape
97+
X = np.zeros_like(A)
98+
for i in range(m):
99+
X[i, :] = A[i, :] * d[i]
100+
return X
101+
102+
103+
def diag_from_right(A, d):
104+
""" A diag(d) """
105+
m, n = A.shape
106+
X = np.zeros_like(A)
107+
for i in range(n):
108+
X[:, i] = A[:, i] * d[i]
109+
return X
110+
111+
112+
def inverse(A, d):
113+
""" Compute inverse of A^T A + diag(d) faster than n^2.
114+
A - (m, n)
115+
d - (n,)
116+
Return: V, d_inv such that inverse = d_inv - V^T V
117+
"""
118+
m, n = A.shape
119+
d_inv = 1 / d
120+
M = np.eye(m) + np.dot(diag_from_right(A, d_inv), A.T) # m^2 * n
121+
M_inv = np.linalg.inv(M) # m^3
122+
R = np.linalg.cholesky(M_inv) # m^3
123+
V = diag_from_left(np.dot(A.T, R), d_inv).T # m^2 * n
124+
return V, d_inv
125+
126+
127+
def compute_inverses(A):
128+
""" Given the low-rank parts of correlation matrices, compute low-rank + diagonal representation
129+
of inverse correlation matrices.
130+
Returns: B, d_inv such that Sigma^{-1}_t = d_inv_t - B_t^T B_t
131+
"""
132+
nt = len(A)
133+
d = [None] * nt
134+
d_inv = [None] * nt
135+
B = [None] * nt
136+
for t in range(nt):
137+
d[t] = 1 - (A[t] ** 2).sum(axis=0)
138+
d_inv[t] = 1.0 / d[t]
139+
140+
for t in range(nt):
141+
B[t], d_inv[t] = inverse(A[t], d[t])
142+
143+
return B, d_inv
144+
145+
146+
def estimate_diff_norm(A_1, d_1, A_2, d_2, n_iters=300):
147+
""" Estimates ||(d_1 - A_1^T A_1) - (d_2 - A_2^T A_2)|| using power method.
148+
"""
149+
ret = 0
150+
for iteration in range(n_iters):
151+
if iteration == 0:
152+
x = np.random.normal(size=(A_1.shape[1], 1))
153+
else:
154+
x = -np.dot(A_1.T, np.dot(A_1, x)) + np.dot(A_2.T, np.dot(A_2, x)) + (d_1 - d_2).reshape((-1, 1)) * x
155+
s = np.linalg.norm(x, ord=2)
156+
x = x / s
157+
ret = s
158+
# print("\tIteration: {}, ret: {}".format(iteration, ret), end='\r')
159+
return ret
160+
161+
162+
def compute_diff_norms(A):
163+
""" Given the low-rank parts of correlation matrices, compute spectral norm of differences of inverse
164+
correlation matrices of neighboring time steps.
165+
"""
166+
B, d_inv = compute_inverses(A)
167+
nt = len(A)
168+
diffs = [None] * (nt-1)
169+
for t in range(nt - 1):
170+
print("Estimating norm of difference at time step: {}".format(t))
171+
diffs[t] = estimate_diff_norm(B[t], d_inv[t], B[t + 1], d_inv[t + 1])
172+
return diffs
173+
174+
175+
def compute_diff_norm_fro(A, d_1, B, d_2):
176+
""" Estimates ||(d_1 - A^T A) - (d_2 - B^T B)||_F analytically.
177+
"""
178+
# First compute || B^T B - A^T A ||^2_F
179+
_, sigma_a, _ = np.linalg.svd(A, full_matrices=False)
180+
_, sigma_b, _ = np.linalg.svd(B, full_matrices=False)
181+
low_rank_norm = np.sum(sigma_a ** 4) - 2 * np.trace(np.dot(np.dot(B, A.T), np.dot(A, B.T))) + np.sum(sigma_b ** 4)
182+
183+
# Let X = (B^T B - A^T A), D = diag(d_1 - d_2). Compute ||X + D||^2_F
184+
d = d_1 - d_2
185+
ret = low_rank_norm + (np.linalg.norm(d) ** 2) + 2 * np.inner(np.sum(B ** 2 - A ** 2, axis=0), d)
186+
return np.sqrt(ret)
187+
188+
189+
def compute_diff_norms_fro(A):
190+
""" Given the low-rank parts of correlation matrices, compute Frobenius norm of differences of inverse
191+
correlation matrices of neighboring time steps.
192+
"""
193+
B, d_inv = compute_inverses(A)
194+
nt = len(A)
195+
diffs = [None] * (nt-1)
196+
for t in range(nt - 1):
197+
print("Calculating Frobenius norm of difference at time step: {}".format(t))
198+
diffs[t] = compute_diff_norm_fro(B[t], d_inv[t], B[t + 1], d_inv[t + 1])
199+
return diffs
200+

0 commit comments

Comments
 (0)