vneumodpy 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vneumodpy/__init__.py +38 -0
- vneumodpy/glm/__init__.py +0 -0
- vneumodpy/glm/adjust_volume_dir.py +24 -0
- vneumodpy/glm/canonical_hrf.py +23 -0
- vneumodpy/glm/contrast_image.py +40 -0
- vneumodpy/glm/hrf_design_matrix.py +42 -0
- vneumodpy/glm/resampling_nifti_volume.py +73 -0
- vneumodpy/glm/roi_ts_from4dimage.py +39 -0
- vneumodpy/glm/roi_ts_to4dimage.py +31 -0
- vneumodpy/glm/tukey.py +97 -0
- vneumodpy/glm/tukey_mp.py +112 -0
- vneumodpy/glm/tukey_mth.py +112 -0
- vneumodpy/measures/__init__.py +0 -0
- vneumodpy/measures/ac.py +32 -0
- vneumodpy/measures/ccm.py +63 -0
- vneumodpy/measures/cm.py +31 -0
- vneumodpy/measures/cos_sim.py +22 -0
- vneumodpy/measures/dft.py +43 -0
- vneumodpy/measures/mskewkurt.py +30 -0
- vneumodpy/measures/mtess.py +309 -0
- vneumodpy/measures/pac.py +32 -0
- vneumodpy/measures/pccm.py +99 -0
- vneumodpy/measures/pccm_.py +81 -0
- vneumodpy/measures/pcm.py +56 -0
- vneumodpy/models/__init__.py +0 -0
- vneumodpy/models/group_range.py +30 -0
- vneumodpy/models/multivaliate_var_network.py +256 -0
- vneumodpy/models/mvar_init_with_cell_mp.py +69 -0
- vneumodpy/models/mvar_init_with_cell_mth.py +22 -0
- vneumodpy/models/regress.py +78 -0
- vneumodpy/nuisance/__init__.py +0 -0
- vneumodpy/nuisance/acompcor.py +61 -0
- vneumodpy/nuisance/mean_time_series.py +42 -0
- vneumodpy/nuisance/regression_out.py +21 -0
- vneumodpy/surrogate/__init__.py +0 -0
- vneumodpy/surrogate/dbs_multivariate_var.py +122 -0
- vneumodpy/surrogate/multivariate_var.py +91 -0
- vneumodpy/surrogate/vnm_addmul_signals.py +66 -0
- vneumodpy/surrogate/vnm_subject_perm.py +26 -0
- vneumodpy/surrogate/vnm_var_surrogate.py +53 -0
- vneumodpy-0.1.6.dist-info/METADATA +30 -0
- vneumodpy-0.1.6.dist-info/RECORD +45 -0
- vneumodpy-0.1.6.dist-info/WHEEL +5 -0
- vneumodpy-0.1.6.dist-info/licenses/LICENSE.md +23 -0
- vneumodpy-0.1.6.dist-info/top_level.txt +1 -0
vneumodpy/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
from .models.multivaliate_var_network import MultivariateVARNetwork
|
|
3
|
+
from .models.mvar_init_with_cell_mth import call_executor as mvar_init_with_cell_mth
|
|
4
|
+
from .models.regress import linear
|
|
5
|
+
from .models.regress import prepare
|
|
6
|
+
from .models.regress import inv_qr
|
|
7
|
+
from .models import group_range
|
|
8
|
+
|
|
9
|
+
from .glm.canonical_hrf import get as canonical_hrf
|
|
10
|
+
from .glm.hrf_design_matrix import get as hrf_design_matrix
|
|
11
|
+
from .glm.tukey import calc as tukey
|
|
12
|
+
from .glm.tukey_mp import calc as tukey_mp # multi processing version
|
|
13
|
+
from .glm.contrast_image import calc as contrast_image
|
|
14
|
+
from .glm.roi_ts_to4dimage import get as roi_ts_to4dimage
|
|
15
|
+
from .glm.roi_ts_from4dimage import get as roi_ts_from4dimage
|
|
16
|
+
from .glm.adjust_volume_dir import adjust_volume_dir
|
|
17
|
+
from .glm.resampling_nifti_volume import resampling_nifti_volume
|
|
18
|
+
|
|
19
|
+
from .surrogate.multivariate_var import calc as multivariate_var
|
|
20
|
+
from .surrogate.dbs_multivariate_var import calc as dbs_multivariate_var
|
|
21
|
+
from .surrogate.vnm_addmul_signals import get as vnm_addmul_signals
|
|
22
|
+
from .surrogate.vnm_var_surrogate import calc as vnm_var_surrogate
|
|
23
|
+
from .surrogate.vnm_subject_perm import get as vnm_subject_perm
|
|
24
|
+
|
|
25
|
+
from .measures import ac
|
|
26
|
+
from .measures import pac
|
|
27
|
+
from .measures import cm
|
|
28
|
+
from .measures import ccm
|
|
29
|
+
from .measures import pcm
|
|
30
|
+
from .measures import pccm
|
|
31
|
+
from .measures import pccm_
|
|
32
|
+
from .measures import mtess
|
|
33
|
+
from .measures import mskewkurt
|
|
34
|
+
from .measures.cos_sim import calc as cos_sim
|
|
35
|
+
|
|
36
|
+
from .nuisance.mean_time_series import get as nuisance_mean_time_series
|
|
37
|
+
from .nuisance.acompcor import get as nuisance_acompcor
|
|
38
|
+
from .nuisance.regression_out import get as nuisance_regression_out
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Adjust NIfTI volume direction based on NIfTI info Transpose matrix
|
|
4
|
+
# returns adjusted NIfTI volume
|
|
5
|
+
# input:
|
|
6
|
+
# V nifti 4D volume (X x Y x Z x frames)
|
|
7
|
+
|
|
8
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def adjust_volume_dir(V, info):
|
|
14
|
+
A = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])
|
|
15
|
+
if np.sum(np.abs(A * info.affine[:3, :3])) > 0:
|
|
16
|
+
print('nifti volume transformation is not supported.')
|
|
17
|
+
return V
|
|
18
|
+
if info.affine[0, 0] < 0: # check flip X axis
|
|
19
|
+
V = np.flipud(V).copy()
|
|
20
|
+
if info.affine[1, 1] < 0: # check flip Y axis
|
|
21
|
+
V = np.fliplr(V).copy()
|
|
22
|
+
if info.affine[2, 2] < 0: # check flip Z axis
|
|
23
|
+
V = np.flip(V, axis=2).copy()
|
|
24
|
+
return V
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# get GLM Canonical hemodynamic response function
|
|
4
|
+
# returns time range (t), HRF time-series (hrf)
|
|
5
|
+
# input:
|
|
6
|
+
# dt time resolution (sec) (default:0.045)
|
|
7
|
+
# responseDelay delay of response (gamma a)(sec) (default:6)
|
|
8
|
+
# underShootDelay delay of undershoot (gamma a)(sec) (default:16)
|
|
9
|
+
# kernelSec kernel time length (sec) (default: 32)
|
|
10
|
+
# underShootRatio ratio of underShoot (default: 0.167)
|
|
11
|
+
# hrfScale HRF scale (gamma b) (default: 0.9)
|
|
12
|
+
|
|
13
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from scipy.stats import gamma
|
|
17
|
+
|
|
18
|
+
def get(dt=0.045, response_delay=6, under_shoot_delay=16, kernel_sec=32, under_shoot_ratio=0.167, hrf_scale=0.9):
|
|
19
|
+
|
|
20
|
+
t = np.arange(0, kernel_sec+dt, dt)
|
|
21
|
+
hrf = gamma.pdf(t,response_delay,scale=hrf_scale) - gamma.pdf(t,under_shoot_delay,scale=hrf_scale) * under_shoot_ratio
|
|
22
|
+
hrf = hrf.T / np.sum(hrf)
|
|
23
|
+
return t, hrf
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Calculate GLM Contrast Image with prewhitening.
|
|
4
|
+
# based on K.J.Friston et al. (2000), M.W.Woolrich et al. (2001), K.J.Worsley (2001)
|
|
5
|
+
# returns cells of T-value matrix (Ts)
|
|
6
|
+
# input:
|
|
7
|
+
# Cs cells of contrast vectors (contrasts (predictor size) x 1)
|
|
8
|
+
# B predictor variables (node x predictor variables)
|
|
9
|
+
# RSS Residual Sum of Squares (node x 1)
|
|
10
|
+
# X2is Vector or single value of inv(X' * X) for contrast
|
|
11
|
+
# tRs Vector or single value of trace(R) for contrast
|
|
12
|
+
|
|
13
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from math import sqrt
|
|
17
|
+
|
|
18
|
+
def calc(Cs, B, RSS, X2is, tRs):
|
|
19
|
+
# GLM contrast image
|
|
20
|
+
# T = (c' * B) / sqrt(c' * X' * inv(V) * X * c * (RSS / trace(R)))
|
|
21
|
+
Ts = []
|
|
22
|
+
roiNum = RSS.shape[0]
|
|
23
|
+
T2 = np.zeros((roiNum,1), dtype=np.float32)
|
|
24
|
+
|
|
25
|
+
for c in Cs:
|
|
26
|
+
for i in range(roiNum):
|
|
27
|
+
if X2is.shape[0] == roiNum:
|
|
28
|
+
X2i = X2is[i, :, :]
|
|
29
|
+
d = sqrt(c.T @ X2i @ c)
|
|
30
|
+
else:
|
|
31
|
+
d = sqrt(c.T @ X2is @ c)
|
|
32
|
+
|
|
33
|
+
if tRs.shape[0] == roiNum:
|
|
34
|
+
se2 = sqrt(RSS[i].item() / tRs[i].item()) # to scalar
|
|
35
|
+
else:
|
|
36
|
+
se2 = sqrt(RSS[i].item() / tRs) # to scalar
|
|
37
|
+
|
|
38
|
+
T2[i] = (c.T @ B[i, :].T) / (d * se2)
|
|
39
|
+
Ts.append(T2.copy())
|
|
40
|
+
return Ts
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# get GLM HRF (hemodynamic response function) design matrix
|
|
3
|
+
# input:
|
|
4
|
+
# onsets cells of task set start time
|
|
5
|
+
# durations cells of task set duration
|
|
6
|
+
# frames fMRI time frames
|
|
7
|
+
# TR fMRI TR
|
|
8
|
+
# res sampling resolution of HRF
|
|
9
|
+
# sp sampling starting point (in resolution)
|
|
10
|
+
# hrf Canonical hemodynamic response function (optional)
|
|
11
|
+
|
|
12
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from ..glm.canonical_hrf import get as canonical_hrf # for command mode
|
|
17
|
+
|
|
18
|
+
def get(onsets, durations, frames, TR, res, sp, hrf=[]):
|
|
19
|
+
|
|
20
|
+
# get Canonical hemodynamic response function
|
|
21
|
+
if len(hrf) == 0:
|
|
22
|
+
dt = TR / res
|
|
23
|
+
t, hrf = canonical_hrf(dt)
|
|
24
|
+
|
|
25
|
+
tasknum = len(onsets)
|
|
26
|
+
X = np.zeros((frames * res, tasknum), float)
|
|
27
|
+
U = np.zeros((frames * res, tasknum), float)
|
|
28
|
+
for k in range(tasknum):
|
|
29
|
+
onset = onsets[k]
|
|
30
|
+
duration = durations[k]
|
|
31
|
+
for i in range(len(onset)):
|
|
32
|
+
t1 = int(np.ceil(onset[i] / TR * res))
|
|
33
|
+
t2 = int(np.ceil(t1 + duration[i] / TR * res))
|
|
34
|
+
U[t1-1:t2,k] = 1
|
|
35
|
+
# get design matrix
|
|
36
|
+
C = np.convolve(U[:,k], hrf)
|
|
37
|
+
X[:,k] = C[0:frames * res]
|
|
38
|
+
|
|
39
|
+
# final sampling
|
|
40
|
+
X = X[np.arange((sp-1),X.shape[0],res),:]
|
|
41
|
+
U = U[np.arange((sp-1),U.shape[0],res),:]
|
|
42
|
+
return X, U
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# resampling NIfTI volume.
|
|
3
|
+
# returns re-sampled volume (outV)
|
|
4
|
+
# input:
|
|
5
|
+
# V nifti 3D volume (X x Y x Z)
|
|
6
|
+
# stepXY XY axes resampling rate
|
|
7
|
+
# stepZ Z axes resampling rate
|
|
8
|
+
# operation operation for each plane ('mode'(default),'max','min','mean','median')
|
|
9
|
+
|
|
10
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy.stats import mode
|
|
14
|
+
|
|
15
|
+
def resampling_nifti_volume(V, stepXY, stepZ, operation='mode'):
|
|
16
|
+
xs = int(np.floor(V.shape[0] / stepXY))
|
|
17
|
+
ys = int(np.floor(V.shape[1] / stepXY))
|
|
18
|
+
zs = int(np.floor(V.shape[2] / stepZ))
|
|
19
|
+
outV = np.zeros((xs, ys, zs), dtype=np.float32)
|
|
20
|
+
|
|
21
|
+
if stepXY >= 1 and stepZ >= 1:
|
|
22
|
+
# scale down
|
|
23
|
+
for z in range(1,zs+1):
|
|
24
|
+
for y in range(1,ys+1):
|
|
25
|
+
for x in range(1,xs+1):
|
|
26
|
+
x_start = int(round(x * stepXY - (stepXY - 1))) - 1
|
|
27
|
+
x_end = int(round(x * stepXY))
|
|
28
|
+
y_start = int(round(y * stepXY - (stepXY - 1))) - 1
|
|
29
|
+
y_end = int(round(y * stepXY))
|
|
30
|
+
z_start = int(round(z * stepZ - (stepZ - 1))) - 1
|
|
31
|
+
z_end = int(round(z * stepZ))
|
|
32
|
+
A = V[x_start:x_end, y_start:y_end, z_start:z_end]
|
|
33
|
+
|
|
34
|
+
if operation == 'mode':
|
|
35
|
+
A_flat = A[~np.isnan(A)]
|
|
36
|
+
if A_flat.size == 0:
|
|
37
|
+
m = np.nan
|
|
38
|
+
else:
|
|
39
|
+
m = mode(A_flat, axis=None).mode[0]
|
|
40
|
+
elif operation == 'min':
|
|
41
|
+
m = np.nanmin(A)
|
|
42
|
+
elif operation == 'max':
|
|
43
|
+
m = np.nanmax(A)
|
|
44
|
+
elif operation == 'mean':
|
|
45
|
+
m = np.nanmean(A)
|
|
46
|
+
elif operation == 'median':
|
|
47
|
+
m = np.nanmedian(A)
|
|
48
|
+
else:
|
|
49
|
+
raise ValueError(f"Unsupported operation: {operation}")
|
|
50
|
+
|
|
51
|
+
outV[x-1, y-1, z-1] = m
|
|
52
|
+
else:
|
|
53
|
+
# scale up
|
|
54
|
+
out_shape = (
|
|
55
|
+
int(np.ceil(V.shape[0] / stepXY)),
|
|
56
|
+
int(np.ceil(V.shape[1] / stepXY)),
|
|
57
|
+
int(np.ceil(V.shape[2] / stepZ))
|
|
58
|
+
)
|
|
59
|
+
outV = np.zeros(out_shape, dtype=np.float32)
|
|
60
|
+
|
|
61
|
+
for z in range(1,V.shape[2]+1):
|
|
62
|
+
for y in range(1,V.shape[1]+1):
|
|
63
|
+
for x in range(1,V.shape[0]+1):
|
|
64
|
+
m = V[x-1, y-1, z-1]
|
|
65
|
+
xx_start = int(round((x-1) / stepXY))
|
|
66
|
+
xx_end = int(round(x / stepXY))
|
|
67
|
+
yy_start = int(round((y-1) / stepXY))
|
|
68
|
+
yy_end = int(round(y / stepXY))
|
|
69
|
+
zz_start = int(round((z-1) / stepZ))
|
|
70
|
+
zz_end = int(round(z / stepZ))
|
|
71
|
+
outV[xx_start:xx_end, yy_start:yy_end, zz_start:zz_end] = m
|
|
72
|
+
|
|
73
|
+
return outV
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# get ROI time-series (matrix) from NIfTI 4D volume.
|
|
4
|
+
# returns ROI time-series (X)
|
|
5
|
+
# input:
|
|
6
|
+
# V nifti 4D volume (X x Y x Z x frames)
|
|
7
|
+
# atlasV nifti 3D atlas (X x Y x Z)
|
|
8
|
+
# operation calc operation for each plane ('mode'(default),'max','min','mean','median','sum')
|
|
9
|
+
|
|
10
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy.stats import mode
|
|
14
|
+
|
|
15
|
+
def get(V, atlasV, operation='mode'):
|
|
16
|
+
roiIdx = np.unique(atlasV)
|
|
17
|
+
roiIdx = roiIdx[roiIdx != 0] # remove 0
|
|
18
|
+
|
|
19
|
+
X = np.zeros((len(roiIdx), V.shape[3]), dtype=np.float32)
|
|
20
|
+
A = V.reshape(-1, V.shape[3])
|
|
21
|
+
for i in range(len(roiIdx)):
|
|
22
|
+
j = roiIdx[i]
|
|
23
|
+
B = A[atlasV.flatten() == j, :]
|
|
24
|
+
if operation == 'mode':
|
|
25
|
+
m = mode(B, axis=0, nan_policy='omit').mode[0]
|
|
26
|
+
elif operation == 'min':
|
|
27
|
+
m = np.nanmin(B, axis=0)
|
|
28
|
+
elif operation == 'max':
|
|
29
|
+
m = np.nanmax(B, axis=0)
|
|
30
|
+
elif operation == 'mean':
|
|
31
|
+
m = np.nanmean(B, axis=0)
|
|
32
|
+
elif operation == 'median':
|
|
33
|
+
m = np.nanmedian(B, axis=0)
|
|
34
|
+
elif operation == 'sum':
|
|
35
|
+
m = np.nansum(B, axis=0)
|
|
36
|
+
else:
|
|
37
|
+
raise ValueError(f"Unsupported operation: {operation}")
|
|
38
|
+
X[i, :] = m
|
|
39
|
+
return X
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# get NIfTI 4D volume from ROI time-series (matrix).
|
|
4
|
+
# returns nifti 4D volume (V)(X x Y x Z x frames)
|
|
5
|
+
# input:
|
|
6
|
+
# X ROI time-series (ROIs x frames)
|
|
7
|
+
# atlasV nifti 3D atlas (X x Y x Z)
|
|
8
|
+
|
|
9
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
def get(X, atlasV):
|
|
14
|
+
roinum = X.shape[0]
|
|
15
|
+
|
|
16
|
+
xs, ys, zs = atlasV.shape
|
|
17
|
+
V = np.full((xs, ys, zs, X.shape[1]), np.nan, dtype=np.float32)
|
|
18
|
+
|
|
19
|
+
idxs = [None] * roinum
|
|
20
|
+
aVf = atlasV.flatten()
|
|
21
|
+
aVfidx = np.array(range(aVf.shape[0]))
|
|
22
|
+
for i in range(roinum):
|
|
23
|
+
idxs[i] = aVfidx[aVf == (i+1)]
|
|
24
|
+
|
|
25
|
+
for t in range(X.shape[1]):
|
|
26
|
+
A = np.full((xs, ys, zs), np.nan, dtype=np.float32)
|
|
27
|
+
for i in range(roinum):
|
|
28
|
+
A.flat[idxs[i]] = X[i, t]
|
|
29
|
+
V[:, :, :, t] = A
|
|
30
|
+
|
|
31
|
+
return V
|
vneumodpy/glm/tukey.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Calculate General Linear Model with prewhitening (Tukey-Taper, Auto-correlation estimation Version)
|
|
4
|
+
# based on M.W.Woolrich et al. (2001)
|
|
5
|
+
# Y = X * B + e
|
|
6
|
+
# e ~ N(0, s^2 * V)
|
|
7
|
+
# S * Y = S * X * B + r
|
|
8
|
+
# S = inv(K)
|
|
9
|
+
# V = K * K'
|
|
10
|
+
# V is auto-corerlation matrix estimated by Tukey-Taper
|
|
11
|
+
# r ~ N(0, s^2 * SVS') therefore N(0, s^2 * I)
|
|
12
|
+
# This function is used for single session.
|
|
13
|
+
# returns predictor variables (B), Residual Sum of Squares (RSS), degree of freedom (df),
|
|
14
|
+
# inv(X' * X) for contrast (X2is), trace(R) for contrast (tRs), full of Residuals (R)
|
|
15
|
+
# input:
|
|
16
|
+
# Y ROI or voxel time series (time series x node)
|
|
17
|
+
# X design matrix (time series x predictor variables)
|
|
18
|
+
# tuM Tukey-Taper window size (default: sqrt(time series length))
|
|
19
|
+
|
|
20
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import time
|
|
24
|
+
from scipy.linalg import toeplitz, cholesky, inv
|
|
25
|
+
from scipy.stats import linregress
|
|
26
|
+
|
|
27
|
+
def calc(Y, X, tuM=None, isOutX2is=False):
|
|
28
|
+
if tuM is None:
|
|
29
|
+
tuM = int(np.floor(np.sqrt(X.shape[0])))
|
|
30
|
+
|
|
31
|
+
print('process GLM with Tukey-Taper(' +str(tuM)+ ') estimation ...')
|
|
32
|
+
roiNum = Y.shape[1]
|
|
33
|
+
xsz = X.shape[1]
|
|
34
|
+
X2is = np.full((roiNum, xsz, xsz), np.nan, dtype=np.float32)
|
|
35
|
+
tRs = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
36
|
+
B = np.full((roiNum, xsz), np.nan, dtype=np.float32)
|
|
37
|
+
RSS = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
38
|
+
df = X.shape[0] - X.shape[1]
|
|
39
|
+
|
|
40
|
+
# make Tukey window
|
|
41
|
+
tuWin = np.zeros(tuM, dtype=np.float32)
|
|
42
|
+
for k in range(tuM):
|
|
43
|
+
tuWin[k] = 0.5 * (1 + np.cos(np.pi * (k+1) / tuM))
|
|
44
|
+
|
|
45
|
+
def regress(y, X):
|
|
46
|
+
# Equivalent to MATLAB regress: returns b, residuals
|
|
47
|
+
b, _, _, _ = np.linalg.lstsq(X, y, rcond=None)
|
|
48
|
+
r = y - X @ b
|
|
49
|
+
return b, r
|
|
50
|
+
|
|
51
|
+
start = time.time()
|
|
52
|
+
for i in range(roiNum):
|
|
53
|
+
Yi = Y[:, i]
|
|
54
|
+
# 1st step OLS regression
|
|
55
|
+
_, r = regress(Yi, X)
|
|
56
|
+
|
|
57
|
+
# if residuals are all zero, probably original signal is just zero. ignore this voxel
|
|
58
|
+
if np.sum(r == 0) == r.shape[0]:
|
|
59
|
+
B[i, :] = 0
|
|
60
|
+
RSS[i] = 0
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
# calc AR coefficients by Tukey-Taper of frequency domain
|
|
64
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
65
|
+
mid = len(C) // 2
|
|
66
|
+
Rxx = C[mid:mid + tuM]
|
|
67
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
68
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
69
|
+
V1 = toeplitz(Pxx)
|
|
70
|
+
K1 = np.linalg.cholesky(V1) # upper=False is not necessary (numpy v1)
|
|
71
|
+
|
|
72
|
+
# second time regression
|
|
73
|
+
# Ki1 = np.linalg.inv(K1)
|
|
74
|
+
# Ya = Ki1 @ Yi
|
|
75
|
+
# Xt = Ki1 @ X
|
|
76
|
+
Ya = np.linalg.solve(K1, Yi)
|
|
77
|
+
Xt = np.linalg.solve(K1, X)
|
|
78
|
+
b, r = regress(Ya, Xt)
|
|
79
|
+
|
|
80
|
+
B[i, :] = b
|
|
81
|
+
RSS[i] = r.T @ r
|
|
82
|
+
if not isOutX2is:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# used for contrast
|
|
86
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
87
|
+
Rxx = C[mid:mid + tuM]
|
|
88
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
89
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
90
|
+
|
|
91
|
+
V2 = toeplitz(Pxx)
|
|
92
|
+
X2is[i, :, :] = inv(X.T @ (inv(V2) @ X))
|
|
93
|
+
IR = np.eye(Xt.shape[0]) - Xt @ inv(Xt.T @ Xt) @ Xt.T
|
|
94
|
+
tRs[i] = np.trace(IR)
|
|
95
|
+
|
|
96
|
+
print('done t=' + str(time.time() - start) + ' sec')
|
|
97
|
+
return B, RSS, df, X2is, tRs
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Calculate General Linear Model with prewhitening (Tukey-Taper, Auto-correlation estimation Version)
|
|
4
|
+
# based on M.W.Woolrich et al. (2001)
|
|
5
|
+
# Y = X * B + e
|
|
6
|
+
# e ~ N(0, s^2 * V)
|
|
7
|
+
# S * Y = S * X * B + r
|
|
8
|
+
# S = inv(K)
|
|
9
|
+
# V = K * K'
|
|
10
|
+
# V is auto-corerlation matrix estimated by Tukey-Taper
|
|
11
|
+
# r ~ N(0, s^2 * SVS') therefore N(0, s^2 * I)
|
|
12
|
+
# This function is used for single session.
|
|
13
|
+
# returns predictor variables (B), Residual Sum of Squares (RSS), degree of freedom (df),
|
|
14
|
+
# inv(X' * X) for contrast (X2is), trace(R) for contrast (tRs), full of Residuals (R)
|
|
15
|
+
# input:
|
|
16
|
+
# Y ROI or voxel time series (time series x node)
|
|
17
|
+
# X design matrix (time series x predictor variables)
|
|
18
|
+
# tuM Tukey-Taper window size (default: sqrt(time series length))
|
|
19
|
+
|
|
20
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import time
|
|
24
|
+
import os
|
|
25
|
+
from scipy.linalg import toeplitz, cholesky, inv
|
|
26
|
+
from scipy.stats import linregress
|
|
27
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def regress(y, X):
|
|
31
|
+
# Equivalent to MATLAB regress: returns b, residuals
|
|
32
|
+
b, _, _, _ = np.linalg.lstsq(X, y, rcond=None)
|
|
33
|
+
r = y - X @ b
|
|
34
|
+
return b, r
|
|
35
|
+
|
|
36
|
+
def loop_fn(i, Yi, X, tuWin, tuM, isOutX2is):
|
|
37
|
+
# 1st step OLS regression
|
|
38
|
+
_, r = regress(Yi, X)
|
|
39
|
+
|
|
40
|
+
# if residuals are all zero, probably original signal is just zero. ignore this voxel
|
|
41
|
+
if np.sum(r == 0) == r.shape[0]:
|
|
42
|
+
return i, 0, 0
|
|
43
|
+
|
|
44
|
+
# calc AR coefficients by Tukey-Taper of frequency domain
|
|
45
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
46
|
+
mid = len(C) // 2
|
|
47
|
+
Rxx = C[mid:mid + tuM]
|
|
48
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
49
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
50
|
+
V1 = toeplitz(Pxx)
|
|
51
|
+
K1 = np.linalg.cholesky(V1) # upper=False is not necessary (numpy v1)
|
|
52
|
+
|
|
53
|
+
# second time regression
|
|
54
|
+
# Ki1 = np.linalg.inv(K1)
|
|
55
|
+
# Ya = Ki1 @ Yi
|
|
56
|
+
# Xt = Ki1 @ X
|
|
57
|
+
Ya = np.linalg.solve(K1, Yi)
|
|
58
|
+
Xt = np.linalg.solve(K1, X)
|
|
59
|
+
b, r = regress(Ya, Xt)
|
|
60
|
+
|
|
61
|
+
rss = r.T @ r
|
|
62
|
+
if not isOutX2is:
|
|
63
|
+
return i, b, rss
|
|
64
|
+
|
|
65
|
+
# used for contrast
|
|
66
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
67
|
+
Rxx = C[mid:mid + tuM]
|
|
68
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
69
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
70
|
+
|
|
71
|
+
V2 = toeplitz(Pxx)
|
|
72
|
+
x2is = inv(X.T @ (inv(V2) @ X))
|
|
73
|
+
IR = np.eye(Xt.shape[0]) - Xt @ inv(Xt.T @ Xt) @ Xt.T
|
|
74
|
+
trs = np.trace(IR)
|
|
75
|
+
return i, b, rss, x2is, trs
|
|
76
|
+
|
|
77
|
+
def calc(Y, X, tuM=None, isOutX2is=False, n_jobs=8):
|
|
78
|
+
if tuM is None:
|
|
79
|
+
tuM = int(np.floor(np.sqrt(X.shape[0])))
|
|
80
|
+
|
|
81
|
+
print('process GLM with Tukey-Taper(' +str(tuM)+ ') estimation ...')
|
|
82
|
+
roiNum = Y.shape[1]
|
|
83
|
+
xsz = X.shape[1]
|
|
84
|
+
X2is = np.full((roiNum, xsz, xsz), np.nan, dtype=np.float32)
|
|
85
|
+
tRs = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
86
|
+
B = np.full((roiNum, xsz), np.nan, dtype=np.float32)
|
|
87
|
+
RSS = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
88
|
+
df = X.shape[0] - X.shape[1]
|
|
89
|
+
|
|
90
|
+
# make Tukey window
|
|
91
|
+
tuWin = np.zeros(tuM, dtype=np.float32)
|
|
92
|
+
for k in range(tuM):
|
|
93
|
+
tuWin[k] = 0.5 * (1 + np.cos(np.pi * (k+1) / tuM))
|
|
94
|
+
|
|
95
|
+
start = time.time()
|
|
96
|
+
with ProcessPoolExecutor(max_workers=n_jobs) as executor: # -----(2)
|
|
97
|
+
futures = set()
|
|
98
|
+
for i in range(roiNum):
|
|
99
|
+
future = executor.submit(loop_fn, i, Y[:, i], X, tuWin, tuM, isOutX2is)
|
|
100
|
+
futures.add(future)
|
|
101
|
+
|
|
102
|
+
for f in futures:
|
|
103
|
+
r = f.result()
|
|
104
|
+
i = r[0]
|
|
105
|
+
B[i, :] = r[1]
|
|
106
|
+
RSS[i] = r[2]
|
|
107
|
+
if isOutX2is:
|
|
108
|
+
X2is[i, :, :] = r[3]
|
|
109
|
+
tRs[i] = r[4]
|
|
110
|
+
|
|
111
|
+
print('done t=' + str(time.time() - start) + ' sec')
|
|
112
|
+
return B, RSS, df, X2is, tRs
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Calculate General Linear Model with prewhitening (Tukey-Taper, Auto-correlation estimation Version)
|
|
4
|
+
# based on M.W.Woolrich et al. (2001)
|
|
5
|
+
# Y = X * B + e
|
|
6
|
+
# e ~ N(0, s^2 * V)
|
|
7
|
+
# S * Y = S * X * B + r
|
|
8
|
+
# S = inv(K)
|
|
9
|
+
# V = K * K'
|
|
10
|
+
# V is auto-corerlation matrix estimated by Tukey-Taper
|
|
11
|
+
# r ~ N(0, s^2 * SVS') therefore N(0, s^2 * I)
|
|
12
|
+
# This function is used for single session.
|
|
13
|
+
# returns predictor variables (B), Residual Sum of Squares (RSS), degree of freedom (df),
|
|
14
|
+
# inv(X' * X) for contrast (X2is), trace(R) for contrast (tRs), full of Residuals (R)
|
|
15
|
+
# input:
|
|
16
|
+
# Y ROI or voxel time series (time series x node)
|
|
17
|
+
# X design matrix (time series x predictor variables)
|
|
18
|
+
# tuM Tukey-Taper window size (default: sqrt(time series length))
|
|
19
|
+
|
|
20
|
+
from __future__ import print_function, division # for Python 2 compatible
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
import time
|
|
24
|
+
import os
|
|
25
|
+
from scipy.linalg import toeplitz, cholesky, inv
|
|
26
|
+
from scipy.stats import linregress
|
|
27
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def regress(y, X):
|
|
31
|
+
# Equivalent to MATLAB regress: returns b, residuals
|
|
32
|
+
b, _, _, _ = np.linalg.lstsq(X, y, rcond=None)
|
|
33
|
+
r = y - X @ b
|
|
34
|
+
return b, r
|
|
35
|
+
|
|
36
|
+
def loop_fn(i, Yi, X, tuWin, tuM, isOutX2is):
|
|
37
|
+
# 1st step OLS regression
|
|
38
|
+
_, r = regress(Yi, X)
|
|
39
|
+
|
|
40
|
+
# if residuals are all zero, probably original signal is just zero. ignore this voxel
|
|
41
|
+
if np.sum(r == 0) == r.shape[0]:
|
|
42
|
+
return i, 0, 0
|
|
43
|
+
|
|
44
|
+
# calc AR coefficients by Tukey-Taper of frequency domain
|
|
45
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
46
|
+
mid = len(C) // 2
|
|
47
|
+
Rxx = C[mid:mid + tuM]
|
|
48
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
49
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
50
|
+
V1 = toeplitz(Pxx)
|
|
51
|
+
K1 = np.linalg.cholesky(V1) # upper=False is not necessary (numpy v1)
|
|
52
|
+
|
|
53
|
+
# second time regression
|
|
54
|
+
# Ki1 = np.linalg.inv(K1)
|
|
55
|
+
# Ya = Ki1 @ Yi
|
|
56
|
+
# Xt = Ki1 @ X
|
|
57
|
+
Ya = np.linalg.solve(K1, Yi)
|
|
58
|
+
Xt = np.linalg.solve(K1, X)
|
|
59
|
+
b, r = regress(Ya, Xt)
|
|
60
|
+
|
|
61
|
+
rss = r.T @ r
|
|
62
|
+
if not isOutX2is:
|
|
63
|
+
return i, b, rss
|
|
64
|
+
|
|
65
|
+
# used for contrast
|
|
66
|
+
C = np.correlate(r, r, mode='full') / np.dot(r, r)
|
|
67
|
+
Rxx = C[mid:mid + tuM]
|
|
68
|
+
Pxx = np.zeros(r.shape[0], dtype=np.float32)
|
|
69
|
+
Pxx[:tuM] = Rxx[:tuM] * tuWin[:tuM]
|
|
70
|
+
|
|
71
|
+
V2 = toeplitz(Pxx)
|
|
72
|
+
x2is = inv(X.T @ (inv(V2) @ X))
|
|
73
|
+
IR = np.eye(Xt.shape[0]) - Xt @ inv(Xt.T @ Xt) @ Xt.T
|
|
74
|
+
trs = np.trace(IR)
|
|
75
|
+
return i, b, rss, x2is, trs
|
|
76
|
+
|
|
77
|
+
def calc(Y, X, tuM=None, isOutX2is=False):
|
|
78
|
+
if tuM is None:
|
|
79
|
+
tuM = int(np.floor(np.sqrt(X.shape[0])))
|
|
80
|
+
|
|
81
|
+
print('process GLM with Tukey-Taper(' +str(tuM)+ ') estimation ...')
|
|
82
|
+
roiNum = Y.shape[1]
|
|
83
|
+
xsz = X.shape[1]
|
|
84
|
+
X2is = np.full((roiNum, xsz, xsz), np.nan, dtype=np.float32)
|
|
85
|
+
tRs = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
86
|
+
B = np.full((roiNum, xsz), np.nan, dtype=np.float32)
|
|
87
|
+
RSS = np.full((roiNum,1), np.nan, dtype=np.float32)
|
|
88
|
+
df = X.shape[0] - X.shape[1]
|
|
89
|
+
|
|
90
|
+
# make Tukey window
|
|
91
|
+
tuWin = np.zeros(tuM, dtype=np.float32)
|
|
92
|
+
for k in range(tuM):
|
|
93
|
+
tuWin[k] = 0.5 * (1 + np.cos(np.pi * (k+1) / tuM))
|
|
94
|
+
|
|
95
|
+
start = time.time()
|
|
96
|
+
with ThreadPoolExecutor(max_workers=os.cpu_count() // 2) as executor: # -----(2)
|
|
97
|
+
futures = set()
|
|
98
|
+
for i in range(roiNum):
|
|
99
|
+
future = executor.submit(loop_fn, i, Y[:, i], X, tuWin, tuM, isOutX2is)
|
|
100
|
+
futures.add(future)
|
|
101
|
+
|
|
102
|
+
for f in futures:
|
|
103
|
+
r = f.result()
|
|
104
|
+
i = r[0]
|
|
105
|
+
B[i, :] = r[1]
|
|
106
|
+
RSS[i] = r[2]
|
|
107
|
+
if isOutX2is:
|
|
108
|
+
X2is[i, :, :] = r[3]
|
|
109
|
+
tRs[i] = r[4]
|
|
110
|
+
|
|
111
|
+
print('done t=' + str(time.time() - start) + ' sec')
|
|
112
|
+
return B, RSS, df, X2is, tRs
|
|
File without changes
|
vneumodpy/measures/ac.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
##
|
|
3
|
+
# Calculate auto-correlation (simple version)
|
|
4
|
+
# returns matrix (node x lags)
|
|
5
|
+
# input:
|
|
6
|
+
# X multivariate time series matrix (node x time series)
|
|
7
|
+
# max_lag time lags for Auto-Correlation function (default: 15)
|
|
8
|
+
|
|
9
|
+
from __future__ import print_function, division
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import statsmodels.api as sm
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def calc(x, max_lag=15):
|
|
17
|
+
node_num = x.shape[0]
|
|
18
|
+
xr = np.zeros((node_num, max_lag+1), dtype=x.dtype)
|
|
19
|
+
for i in range(node_num):
|
|
20
|
+
xr[i, :] = sm.tsa.stattools.acf(x[i, :], nlags=max_lag)
|
|
21
|
+
return xr
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def plot(x, max_lag=15):
|
|
25
|
+
xr = calc(x=x, max_lag=max_lag)
|
|
26
|
+
plt.matshow(xr, vmin=-1, vmax=1)
|
|
27
|
+
plt.colorbar()
|
|
28
|
+
plt.xlabel('Source Nodes')
|
|
29
|
+
plt.ylabel('Target Nodes')
|
|
30
|
+
plt.show(block=False)
|
|
31
|
+
plt.pause(1)
|
|
32
|
+
return xr
|