Skip to content

Commit 52cec30

Browse files
authored
Merge pull request #181 from CoBrALab/updates
Updates
2 parents f478d3f + 2b52291 commit 52cec30

21 files changed

+1426
-927
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ RUN mkdir -p /opt/ANTs/build && git clone https://github.com/ANTsX/ANTs.git /opt
5050
&& cd /opt/ANTs/src \
5151
&& git checkout 1759e5e23772e114a490cfa33a5764b400307b9d \
5252
&& cd /opt/ANTs/build \
53-
&& cmake -GNinja -DITK_BUILD_MINC_SUPPORT=ON ../src \
53+
&& cmake -GNinja -DITK_BUILD_MINC_SUPPORT=ON -DBUILD_TESTING=OFF ../src \
5454
&& cmake --build . \
5555
&& cd ANTS-build \
5656
&& cmake --install .

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -588,16 +588,16 @@ rabies -p MultiProc preprocess test_dataset/ preprocess_outputs/ --TR 1.0s --no_
588588
First, this will run the minimal preprocessing step on the test dataset and store outputs into preprocess_outputs/ folder. The option -p MultiProc specifies to run the pipeline in parallel according to available local threads.
589589
<br/>
590590

591-
**confound_regression**
591+
**confound_correction**
592592
```sh
593-
rabies -p MultiProc confound_regression preprocess_outputs/ confound_regression_outputs/ --TR 1.0s --commonspace_bold --smoothing_filter 0.3 --conf_list WM_signal CSF_signal vascular_signal mot_6
593+
rabies -p MultiProc confound_correction preprocess_outputs/ confound_correction_outputs/ --TR 1.0s --commonspace_bold --smoothing_filter 0.3 --conf_list WM_signal CSF_signal vascular_signal mot_6
594594
```
595-
Next, to conduct the modeling and regression of confounding sources, the confound_regression step can be run with custom options for denoising. In this case, we apply a highpass filtering at 0.01Hz, together with the voxelwise regression of the 6 rigid realignment parameters and the mean WM,CSF and vascular signal which are derived from masks provided along with the anatomical template. Finally, a smoothing filter 0.3mm is applied. We are running this on the commonspace outputs from preprocess (--commonspace_bold), since we will run analysis in commonspace in the next step.
595+
Next, to conduct the modeling and regression of confounding sources, the confound_correction step can be run with custom options for denoising. In this case, we apply a highpass filtering at 0.01Hz, together with the voxelwise regression of the 6 rigid realignment parameters and the mean WM,CSF and vascular signal which are derived from masks provided along with the anatomical template. Finally, a smoothing filter 0.3mm is applied. We are running this on the commonspace outputs from preprocess (--commonspace_bold), since we will run analysis in commonspace in the next step.
596596
<br/>
597597

598598
**analysis**
599599
```sh
600-
rabies -p MultiProc analysis confound_regression_outputs analysis_outputs/ --TR 1.0s --group_ICA --DR_ICA
600+
rabies -p MultiProc analysis confound_correction_outputs analysis_outputs/ --TR 1.0s --group_ICA --DR_ICA
601601
```
602602
Finally, RABIES has a few standard analysis options provided, which are specified in the Analysis documentation. In this example, we are going to run group independent component analysis (--group_ICA), using FSL's MELODIC function, followed by a dual regression (--DR_ICA) to back propagate the group components onto individual subjects.
603603

@@ -627,12 +627,12 @@ singularity run -B $PWD/test_dataset:/test_dataset:ro \
627627
```
628628
<br/>
629629

630-
**confound_regression**
630+
**confound_correction**
631631
```sh
632632
singularity run -B $PWD/test_dataset:/test_dataset:ro \
633633
-B $PWD/preprocess_outputs:/preprocess_outputs/ \
634-
-B $PWD/confound_regression_outputs:/confound_regression_outputs/ \
635-
/path_to_singularity_image/rabies.sif -p MultiProc confound_regression /preprocess_outputs/ /confound_regression_outputs/ --TR 1.0s --highpass 0.01 --commonspace_bold --smoothing_filter 0.3 --conf_list WM_signal CSF_signal vascular_signal mot_6
634+
-B $PWD/confound_correction_outputs:/confound_correction_outputs/ \
635+
/path_to_singularity_image/rabies.sif -p MultiProc confound_correction /preprocess_outputs/ /confound_correction_outputs/ --TR 1.0s --highpass 0.01 --commonspace_bold --smoothing_filter 0.3 --conf_list WM_signal CSF_signal vascular_signal mot_6
636636
```
637637
Note here that the path to the dataset is still linked to the container with -B, even though it is not explicitely part of the inputs in the confound regression call. This is necessary since the paths used in the preprocess steps are still accessed in the background, and there will be an error if the paths are not kept consistent across processing steps.
638638
<br/>
@@ -641,9 +641,9 @@ Note here that the path to the dataset is still linked to the container with -B,
641641
```sh
642642
singularity run -B $PWD/test_dataset:/test_dataset:ro \
643643
-B $PWD/preprocess_outputs:/preprocess_outputs/ \
644-
-B $PWD/confound_regression_outputs:/confound_regression_outputs/ \
644+
-B $PWD/confound_correction_outputs:/confound_correction_outputs/ \
645645
-B $PWD/analysis_outputs:/analysis_outputs/ \
646-
/path_to_singularity_image/rabies.sif -p MultiProc analysis /confound_regression_outputs /analysis_outputs/ --TR 1.0s --group_ICA --DR_ICA
646+
/path_to_singularity_image/rabies.sif -p MultiProc analysis /confound_correction_outputs /analysis_outputs/ --TR 1.0s --group_ICA --DR_ICA
647647
```
648648
<br/>
649649

@@ -733,8 +733,8 @@ The following image presents an example of the overlap for the EPI2Anat registra
733733
<details><summary><b>Click to expand</b></summary>
734734
<p>
735735

736-
Important outputs from confound regression will be found in the confound_regression_datasink present in the provided output folder:
737-
- **confound_regression_datasink**: Includes outputs specific to the anatomical preprocessing workflow
736+
Important outputs from confound regression will be found in the confound_correction_datasink present in the provided output folder:
737+
- **confound_correction_datasink**: Includes outputs specific to the anatomical preprocessing workflow
738738
- cleaned_timeseries: Resulting timeseries after the application of confound regression
739739
- VE_file: .pkl file which contains a dictionary vectors, where each vector corresponds to the voxelwise the variance explained (VE) from each regressor in the regression model
740740
- aroma_out: if --run_aroma is selected, the outputs from running ICA-AROMA will be saved, which includes the MELODIC ICA outputs and the component classification results

rabies/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
# 88YbdP88 8P 88""" dP__Yb Yb 88"Yb dP__Yb Yb "88 88""
44
# 88 YY 88 dP 88 dP""""Yb YboodP 88 Yb dP""""Yb YboodP 888888
55

6-
VERSION = (0, 3, 2)
6+
VERSION = (0, 3, 3)
77

88
__version__ = '.'.join(map(str, VERSION))

rabies/analysis_pkg/analysis_functions.py

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np
22
import nibabel as nb
3-
3+
from .analysis_math import vcorrcoef,closed_form
44

55
def seed_based_FC(bold_file, brain_mask, seed_dict, seed_name):
66
import os
@@ -57,15 +57,6 @@ def seed_corr(bold_file, brain_mask, seed):
5757
return corrs
5858

5959

60-
def vcorrcoef(X, y): # return a correlation between each row of X with y
61-
Xm = np.reshape(np.mean(X, axis=1), (X.shape[0], 1))
62-
ym = np.mean(y)
63-
r_num = np.sum((X-Xm)*(y-ym), axis=1)
64-
r_den = np.sqrt(np.sum((X-Xm)**2, axis=1)*np.sum((y-ym)**2))
65-
r = r_num/r_den
66-
return r
67-
68-
6960
def get_CAPs(data, volumes, n_clusters):
7061
from sklearn.cluster import KMeans
7162
kmeans = KMeans(n_clusters=n_clusters, n_init=10, max_iter=300)
@@ -209,7 +200,7 @@ def plot_matrix(filename, corr_matrix):
209200
'''
210201

211202

212-
def run_group_ICA(bold_file_list, mask_file, dim):
203+
def run_group_ICA(bold_file_list, mask_file, dim, random_seed):
213204
import os
214205
import pandas as pd
215206

@@ -222,7 +213,7 @@ def run_group_ICA(bold_file_list, mask_file, dim):
222213

223214
from rabies.preprocess_pkg.utils import run_command
224215
out_dir = os.path.abspath('group_melodic.ica')
225-
command = f'melodic -i {file_path} -m {mask_file} -o {out_dir} -d {dim} --report --seed=1'
216+
command = f'melodic -i {file_path} -m {mask_file} -o {out_dir} -d {dim} --report --seed={str(random_seed)}'
226217
rc = run_command(command)
227218
IC_file = out_dir+'/melodic_IC.nii.gz'
228219
return out_dir, IC_file
@@ -307,21 +298,6 @@ def run_DR_ICA(bold_file, mask_file, IC_file):
307298
return DR_maps_filename, dual_regression_timecourse_csv
308299

309300

310-
'''
311-
LINEAR REGRESSION --- CLOSED-FORM SOLUTION
312-
'''
313-
314-
315-
def closed_form(X, Y, intercept=False): # functions that computes the Least Squares Estimates
316-
if intercept:
317-
X = np.concatenate((X, np.ones([X.shape[0], 1])), axis=1)
318-
return np.linalg.inv(X.transpose().dot(X)).dot(X.transpose()).dot(Y)
319-
320-
321-
def mse(X, Y, w): # function that computes the Mean Square Error (MSE)
322-
return np.mean((Y-np.matmul(X, w))**2)
323-
324-
325301
def dual_regression(all_IC_vectors, timeseries):
326302
### compute dual regression
327303
### Here, we adopt an approach where the algorithm should explain the data
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import numpy as np
2+
3+
def vcorrcoef(X, y): # return a correlation between each row of X with y
4+
Xm = np.reshape(np.mean(X, axis=1), (X.shape[0], 1))
5+
ym = np.mean(y)
6+
r_num = np.sum((X-Xm)*(y-ym), axis=1)
7+
r_den = np.sqrt(np.sum((X-Xm)**2, axis=1)*np.sum((y-ym)**2))
8+
r = r_num/r_den
9+
return r
10+
11+
12+
def elementwise_corrcoef(X, Y):
13+
# X and Y are each of shape num_observations X num_element
14+
# computes the correlation between each element of X and Y
15+
Xm = X.mean(axis=0)
16+
Ym = Y.mean(axis=0)
17+
r_num = np.sum((X-Xm)*(Y-Ym), axis=0)
18+
19+
r_den = np.sqrt(np.sum((X-Xm)**2, axis=0)*np.sum((Y-Ym)**2, axis=0))
20+
r = r_num/r_den
21+
return r
22+
23+
24+
def elementwise_spearman(X,Y):
25+
order = X.argsort(axis=0)
26+
X_ranks = order.argsort(axis=0)
27+
order = Y.argsort(axis=0)
28+
Y_ranks = order.argsort(axis=0)
29+
return elementwise_corrcoef(X_ranks, Y_ranks)
30+
31+
32+
def dice_coefficient(mask1,mask2):
33+
dice = np.sum(mask1*mask2)*2.0 / (np.sum(mask1) + np.sum(mask2))
34+
return dice
35+
36+
37+
'''
38+
LINEAR REGRESSION --- CLOSED-FORM SOLUTION
39+
'''
40+
41+
42+
def closed_form(X, Y, intercept=False): # functions that computes the Least Squares Estimates
43+
if intercept:
44+
X = np.concatenate((X, np.ones([X.shape[0], 1])), axis=1)
45+
return np.linalg.inv(X.transpose().dot(X)).dot(X.transpose()).dot(Y)
46+
47+
48+
def mse(X, Y, w): # function that computes the Mean Square Error (MSE)
49+
return np.mean((Y-np.matmul(X, w))**2)
50+
51+
52+
53+
'''
54+
55+
def closed_form_3d(X,Y):
56+
return np.matmul(np.matmul(np.linalg.inv(np.matmul(X.transpose(0,2,1),X)),X.transpose(0,2,1)),Y)
57+
58+
def lme_stats_3d(X,Y):
59+
#add an intercept
60+
X=np.concatenate((X,np.ones((X.shape[0],X.shape[1],1))),axis=2)
61+
[num_comparisons,num_observations,num_predictors] = X.shape
62+
[num_comparisons,num_observations,num_features] = Y.shape
63+
64+
w=closed_form_3d(X,Y)
65+
66+
residuals = Y-np.matmul(X, w)
67+
MSE = (((residuals)**2).sum(axis=1)/(num_observations-num_predictors))
68+
69+
70+
var_b = np.expand_dims(MSE, axis=1)*np.expand_dims(np.linalg.inv(np.matmul(X.transpose(0,2,1),X)).diagonal(axis1=1,axis2=2), axis=2)
71+
sd_b = np.sqrt(var_b) # standard error on the Betas
72+
ts_b = w/sd_b # calculate t-values for the Betas
73+
p_values =[2*(1-stats.t.cdf(np.abs(ts_b[:,i,:]),(num_observations-num_predictors))) for i in range(ts_b.shape[1])] # calculate a p-value map for each predictor
74+
75+
return ts_b,p_values,w,residuals
76+
77+
non_nan_idx = (np.isnan(voxelwise_array).sum(axis=(0,1))==0)
78+
79+
# take out the voxels which have null values
80+
X=voxelwise_array[:,:6,non_nan_idx].transpose(2,0,1)
81+
Y=voxelwise_array[:,6:,non_nan_idx].transpose(2,0,1)
82+
83+
84+
ts_b,p_values,w,residuals = lme_stats_3d(X,Y)
85+
86+
x_name=['Somatomotor','Dorsal Comp','DMN', 'Prior Modeling 1', 'Prior Modeling 2', 'Prior Modeling 3']
87+
y_name=['group','temporal_std','VE_spatial','GS_corr','DVARS_corr','FD_corr']
88+
89+
fig,axes = plt.subplots(nrows=len(x_name), ncols=len(y_name),figsize=(12*len(y_name),3*len(x_name)))
90+
91+
92+
for i,x_label in zip(range(len(x_name)),x_name):
93+
for j,y_label in zip(range(len(y_name)),y_name):
94+
ax=axes[i,j]
95+
96+
stat_map=np.zeros(voxelwise_array.shape[2])
97+
stat_map[non_nan_idx]=ts_b[:,j,i]
98+
99+
ax.set_title('T-value of {} on {}'.format(y_label,x_label), fontsize=15)
100+
plot_stat_map(analysis_functions.recover_3D(mask_file,stat_map),bg_img='DSURQE.nii.gz', axes=ax, cut_coords=(0,1,2,3,4,5), display_mode='y')
101+
'''

rabies/analysis_pkg/analysis_wf.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def init_analysis_wf(opts, commonspace_cr=False, seed_list=[], name="analysis_wf
6161

6262
include_group_ICA = opts.group_ICA
6363

64-
if opts.DR_ICA and not opts.data_diagnosis:
64+
if opts.DR_ICA or opts.data_diagnosis:
6565
if not commonspace_cr:
6666
raise ValueError(
6767
'Outputs from confound regression must be in commonspace to run dual regression. Try running confound regression again with --commonspace_analysis.')
@@ -91,11 +91,12 @@ def init_analysis_wf(opts, commonspace_cr=False, seed_list=[], name="analysis_wf
9191
if not commonspace_cr:
9292
raise ValueError(
9393
'Outputs from confound regression must be in commonspace to run group-ICA. Try running confound regression again with --nativespace_analysis.')
94-
group_ICA = pe.Node(Function(input_names=['bold_file_list', 'mask_file', 'dim'],
94+
group_ICA = pe.Node(Function(input_names=['bold_file_list', 'mask_file', 'dim', 'random_seed'],
9595
output_names=['out_dir', 'IC_file'],
9696
function=run_group_ICA),
9797
name='group_ICA', mem_gb=1)
9898
group_ICA.inputs.dim = opts.dim
99+
group_ICA.inputs.random_seed = opts.melodic_random_seed
99100

100101
workflow.connect([
101102
(group_inputnode, group_ICA, [
@@ -108,16 +109,11 @@ def init_analysis_wf(opts, commonspace_cr=False, seed_list=[], name="analysis_wf
108109
]),
109110
])
110111

111-
if opts.dual_ICA>0 and not opts.data_diagnosis:
112+
if opts.dual_ICA>0:
112113
if not commonspace_cr:
113114
raise ValueError(
114115
'Outputs from confound regression must be in commonspace to run group-ICA. Try running confound regression again with --nativespace_analysis.')
115116
from .analysis_functions import dual_ICA_wrapper
116-
from .data_diagnosis import resample_IC_file
117-
resample_IC = pe.Node(Function(input_names=['in_file', 'ref_file'],
118-
output_names=['out_file'],
119-
function=resample_IC_file),
120-
name='resample_IC', mem_gb=1)
121117

122118
dual_ICA_node = pe.Node(dual_ICA_wrapper(),
123119
name='dual_ICA_node', mem_gb=1)

rabies/analysis_pkg/diagnosis_pkg/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)