OSCR

Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function.

Code ↔ Paper

21 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 21 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Results › Brain-age gap is associated with lifestyle factors and cognitive functions ↔ figures/IMAG2026/plot_sig_voxel.ipynb, lines 43–55 · score 0.83 · fluid intelligence, sleep duration, spent watching, physical exercise, correctly identify matches, principal component
  2. [2] § Methods › Brain-phenotype prediction ↔ figures/IMAG2026/compare_mmiva_msiva_sz.ipynb, lines 75–119 · score 0.77 · sex classification, diagnosis classification, Age regression, stratified, SVM, ridge
  3. [3] § Methods › Brain-phenotype prediction ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 269–311 · score 0.77 · sex classification, diagnosis classification, Age regression, stratified, SVM, ridge
  4. [4] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @utils/mymvlap.m, the whole file · a weak match · score 0.76 · dispersion matrix, positive definite, Laplace distribution, correlation matrix, covariance, dimensionality
  5. [5] § Results › Brain-age gap is associated with lifestyle factors and cognitive functions ↔ figures/IMAG2026/plot_sig_voxel.ipynb, lines 43–55 · score 0.72 · fluid intelligence, sleep duration, spent watching, physical exercise, voxels
  6. [6] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @utils/mymvk.m, the whole file · a weak match · score 0.71 · dispersion matrix, positive definite, correlation matrix, gamma, Laplace, covariance
  7. [7] § Methods › Datasets › Neuroimaging data ↔ figures/IMAG2026/plot_img_ukb.ipynb, lines 297–347 · score 0.68 · standard deviation, age median, sMRI, fMRI
  8. [8] § Methods › Experiments › Neuroimaging data experiment ↔ figures/IMAG2026/utils.py, lines 102–125 · score 0.62 · randomized dependence coefficient, correlation coefficient, RDC, nonlinear
  9. [9] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/compare_mmiva_msiva_sz.ipynb, lines 75–119 · score 0.61 · age regression MAE, sex classification, diagnosis classification, accuracy, SZ, MSIVA
  10. [10] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 269–311 · score 0.61 · age regression MAE, sex classification, diagnosis classification, accuracy, SZ, subspace
  11. [11] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_ukb_rdc.ipynb, lines 121–180 · score 0.60 · modal RDC, sMRI, unimodal initialization, CMCCs, multimodal initialization, CMDs
  12. [12] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_sz_rdc.ipynb, lines 121–180 · score 0.58 · modal RDC, sMRI, unimodal initialization, CMCCs, multimodal initialization, CMDs
  13. [13] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 133–192 · score 0.58 · cross modal Pearson, sMRI, fMRI, unimodal initialization, CMCCs, multimodal initialization
  14. [14] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_ukb.ipynb, lines 133–192 · score 0.58 · cross modal Pearson, sMRI, fMRI, unimodal initialization, CMCCs, multimodal initialization
  15. [15] § Methods › Quantitative evaluation metrics › Mean correlation coefficient and minimum distance ↔ figures/IMAG2026/plot_loss.ipynb, lines 120–264 · score 0.58 · min max, MMCC, aggregated, CMCC, CMD, MMD
  16. [16] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ other_methods/jbd.m, lines 1–147 · score 0.58 · Kullback Leibler, divergence, algorithm, latent, joint
  17. [17] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @MISAK/MISAK.m, lines 29–122 · score 0.57 · objective function, combinatorial optimization, greedy, Kotz, gradient, joint
  18. [18] § Results › MSIVA identifies ground-truth subspace structures in synthetic data ↔ figures/IMAG2026/plot_loss.ipynb, lines 11–118 · score 0.57 · numerical optimization, initialization workflow, MMCC, lowest, CMCC, CMD
  19. [19] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 539–602 · score 0.56 · older patients, cross modal correlations, 1–3, SZ, age, subspace
  20. [20] § Methods › Datasets › Neuroimaging data ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 313–362 · score 0.54 · age median, sMRI, fMRI
  21. [21] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 539–602 · score 0.52 · older patient, Younger control, diagnosis, median, age, subspace

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 622 lines · 26 KB · Apache-2.0 · 6 matches

  1. # %%
  2. import os
  3. import numpy as np
  4. import scipy.io as sio
  5. import seaborn as sns
  6. import matplotlib as mpl
  7. import matplotlib.pyplot as plt
  8. from matplotlib.colors import Normalize
  9. import ancillary as ac
  10. from sklearn.svm import LinearSVC
  11. from sklearn.linear_model import Ridge
  12. from sklearn.model_selection import GridSearchCV
  13. from sklearn.model_selection import train_test_split
  14. import hdmedians as hd
  15. from utils import correlation, calculate_mcc, age_regression, sex_classification, sz_classification, plot_sq, convert_pvalue, pvalue_to_r
  16. from scipy import stats
  17. from scipy.stats import pearsonr
  18. # %%
  19. res_dir = "/data/users4/xli/MSIVA/MSIVA/results"
  20. sz_smri_data_path = os.path.join(res_dir, "mat", "mancovaOuts_allHCSZ_combinedRelatives_wX_preregSite_C30_SMRI_GICAinit.mat")
  21. sz_smri_data = sio.loadmat(sz_smri_data_path)['NMODELHCSZ0ns']
  22. sz_smri_data_array = sz_smri_data[0][0][0]
  23. age = sz_smri_data_array[:,0]
  24. sex = sz_smri_data_array[:,1]
  25. diagnosis = sz_smri_data_array[:,2]
  26. id = sio.loadmat(os.path.join(res_dir, "mat", "SZID.mat"))['ID'][0] - 1
  27. # %%
  28. img_dir = os.path.join(res_dir, "img")
  29. subspace_struct_list = ["s1", "s2", "s3", "s4", "s5"]
  30. num_subspace_struct = len(subspace_struct_list)
  31. Y = np.zeros((num_subspace_struct,3,2,12,999)) # S1-4, UA/MSIVA/GICA, M1-2, voxel, source
  32. W = np.zeros((num_subspace_struct,3,2,12,44318)) # S1-4, UA/MSIVA/GICA, M1-2, voxel, source
  33. num_iter = 21
  34. corr = np.zeros((num_subspace_struct,9,12,12))
  35. for i,ss in enumerate(subspace_struct_list):
  36. data = sio.loadmat(os.path.join(img_dir, ss, "um_neuroimaging_sz_Y.mat"))
  37. Y1 = np.squeeze(data['Y1'])
  38. data = sio.loadmat(os.path.join(img_dir, ss, "ummm_neuroimaging_sz_Y.mat"))
  39. Y2 = np.squeeze(data['Y2'])
  40. data = sio.loadmat(os.path.join(img_dir, ss, "mm_neuroimaging_sz_Y.mat"))
  41. Y3 = np.squeeze(data['Y3'])
  42. Y[i,0,0] = Y1[0][:,id]
  43. Y[i,0,1] = Y1[1][:,id]
  44. Y[i,1,0] = Y2[0][:,id]
  45. Y[i,1,1] = Y2[1][:,id]
  46. Y[i,2,0] = Y3[0][:,id]
  47. Y[i,2,1] = Y3[1][:,id]
  48. # for j in range(2):
  49. # for k in range(2):
  50. # for l in range(12):
  51. # sgn = np.sign(correlation(Y[i,j,k,l],age))
  52. # Y[i,j,k,l] = -sgn * Y[i,j,k,l]
  53. data = sio.loadmat(os.path.join(img_dir, ss, "um_neuroimaging_sz_W.mat"))
  54. W1 = np.squeeze(data['W1'])
  55. data = sio.loadmat(os.path.join(img_dir, ss, "ummm_neuroimaging_sz_W.mat"))
  56. W2 = np.squeeze(data['W2'])
  57. data = sio.loadmat(os.path.join(img_dir, ss, "mm_neuroimaging_sz_W.mat"))
  58. W3 = np.squeeze(data['W3'])
  59. W[i,0,0] = W1[0]
  60. W[i,0,1] = W1[1]
  61. W[i,1,0] = W2[0]
  62. W[i,1,1] = W2[1]
  63. W[i,2,0] = W3[0]
  64. W[i,2,1] = W3[1]
  65. corr[i,0] = np.corrcoef(Y1[0],Y1[0])[:12,:12]
  66. corr[i,1] = np.corrcoef(Y1[1],Y1[1])[:12,:12]
  67. corr[i,2] = np.corrcoef(Y1[0],Y1[1])[12:,:12]
  68. corr[i,3] = np.corrcoef(Y2[0],Y2[0])[:12,:12]
  69. corr[i,4] = np.corrcoef(Y2[1],Y2[1])[:12,:12]
  70. corr[i,5] = np.corrcoef(Y2[0],Y2[1])[12:,:12]
  71. corr[i,6] = np.corrcoef(Y3[0],Y3[0])[:12,:12]
  72. corr[i,7] = np.corrcoef(Y3[1],Y3[1])[:12,:12]
  73. corr[i,8] = np.corrcoef(Y3[0],Y3[1])[12:,:12]
  74. # %%
  75. val = 1
  76. num_source = 12
  77. # S1
  78. num_unique_source = 3
  79. s1 = np.zeros((num_source, num_source))
  80. s1[:2,:2] = val
  81. s1[2:5,2:5] = val*2
  82. s1[5:9,5:9] = val*3
  83. # S2
  84. num_unique_source = 2
  85. s2 = np.zeros((num_source, num_source))
  86. s2[:2,:2] = val
  87. s2[2:4,2:4] = val*2
  88. s2[4:6,4:6] = val*3
  89. s2[6:8,6:8] = val*4
  90. s2[8:10,8:10] = val*5
  91. # S3
  92. num_unique_source = 3
  93. s3 = np.zeros((num_source, num_source))
  94. s3[:3,:3] = val
  95. s3[3:6,3:6] = val*2
  96. s3[6:9,6:9] = val*3
  97. # S4
  98. num_unique_source = 4
  99. s4 = np.zeros((num_source, num_source))
  100. s4[:4,:4] = val
  101. s4[4:8,4:8] = val*2
  102. # S5
  103. num_unique_source = 4
  104. s5 = np.zeros((num_source, num_source))
  105. for i in range(12):
  106. s5[i,i] = val*(i+1)
  107. s_list = [s1, s2, s3, s4, s5]
  108. # %%
  109. analysis_list = ["I. Unimodal initialization sMRI Pearson correlations",
  110. "II. Unimodal initialization fMRI Pearson correlations",
  111. "III. Unimodal initialization cross-modal Pearson correlations",
  112. "IV. Default initialization sMRI Pearson correlations",
  113. "V. Default initialization fMRI Pearson correlations",
  114. "VI. Default initialization cross-modal Pearson correlations",
  115. "VII. Multimodal initialization sMRI Pearson correlations",
  116. "VIII. Multimodal initialization fMRI Pearson correlations",
  117. "IX. Multimodal initialization cross-modal Pearson correlations"]
  118. modality_list = ["sMRI", "fMRI"]
  119. subspace_dict = {"S1": [2, 3, 4], "S2": [2, 2, 2, 2, 2], "S3": [3, 3, 3], "S4": [4, 4], "S5": [1]*12}
  120. n_row = 9
  121. fig, axes = plt.subplots(n_row, num_subspace_struct + 1, figsize = (2.4 * num_subspace_struct, 3 * n_row), gridspec_kw = {'width_ratios': [1, 1, 1, 1, 1, 0.05]})
  122. for i in range(num_subspace_struct):
  123. ss = subspace_dict[f"S{i+1}"]
  124. for j in range(n_row):
  125. ax = axes[j,i]
  126. abscorr = np.abs(corr[i,j])
  127. if j in [2, 5, 8]:
  128. mcc, md, aggcorr, _ = calculate_mcc(abscorr, ss, sort=False)
  129. sns.heatmap(abscorr, cmap="magma", vmin=0, vmax=1, ax=ax, cbar=False)
  130. ax.text(9.9, 1.6, f"$S_{i+1}^{{Test}}$", fontsize=18, color="white", ha="center", va="center")
  131. ax.set_title(f"CMCC:{mcc:.3f} CMD:{md:.3f}", fontsize=12)
  132. if i == 2:
  133. ax.set_xlabel("sMRI", fontsize=18)
  134. if i == 0:
  135. ax.set_ylabel("fMRI", fontsize=18, rotation=0, labelpad=20)
  136. plot_sq(ax, i, crossmodal=True)
  137. ax.set_xlim(-0.1, 12.1)
  138. ax.set_ylim(12.1, -0.1)
  139. else:
  140. mcc = np.mean(np.diag(abscorr))
  141. sns.heatmap(abscorr, cmap="magma", vmin=0, vmax=1, ax=ax, cbar=False)
  142. ax.text(9.9, 1.6, f"$S_{i+1}^{{Test}}$", fontsize=18, color="white", ha="center", va="center")
  143. if i == 2:
  144. ax.set_xlabel(f"{modality_list[j%3]}", fontsize=18)
  145. if i == 0:
  146. ax.set_ylabel(f"{modality_list[j%3]}", fontsize=18, rotation=0, labelpad=20)
  147. plot_sq(ax, i)
  148. ax.set_xlim(-0.1, 12.1)
  149. ax.set_ylim(12.1, -0.1)
  150. ax.set_xticks([])
  151. ax.set_yticks([])
  152. norm = mpl.colors.Normalize(vmin=0, vmax=1)
  153. sm = mpl.cm.ScalarMappable(cmap="magma", norm=norm)
  154. for i in range(n_row):
  155. ax = fig.add_subplot(n_row, 1, i+1)
  156. ax.set_title(analysis_list[i], fontsize=20, fontweight='bold', pad=28)
  157. ax.axis('off')
  158. cbar = fig.colorbar(sm, cax=axes[i, 5])
  159. cbar.ax.tick_params(labelsize=11)
  160. plt.tight_layout(pad=1, h_pad=0, w_pad=1)
  161. plt.savefig("figures/neuroimaging_sz.pdf")
  162. # %%
  163. A = sio.loadmat(os.path.join(res_dir, "mat", "A_sz.mat"))["A"]
  164. WAY_list = []
  165. cca_corr_list = []
  166. for i in np.arange(0,10,2):
  167. # S1-4, UA/MSIVA, M1-2, voxel, source
  168. A1 = A[1,1,0,:,i:i+2] # structure 2, MSIVA, M1
  169. A2 = A[1,1,1,:,i:i+2] # structure 2, MSIVA, M2
  170. Y1 = Y[1,1,0,i:i+2]
  171. Y2 = Y[1,1,1,i:i+2]
  172. AY1 = A1@Y1
  173. AY2 = A2@Y2
  174. # PCA AY1, AY2
  175. AY1_p, AY1_p_projM, AY1_p_projM_std = ac.base_PCA(AY1, num_PC=None, axis=-2, whitening=True)
  176. AY2_p, AY2_p_projM, AY2_p_projM_std = ac.base_PCA(AY2, num_PC=None, axis=-2, whitening=True)
  177. # Post-PCA eigenvalue problem for CCA
  178. S12 = AY1_p @ AY2_p.T
  179. Z1 = np.zeros((AY1_p.shape[0],AY1_p.shape[0]), dtype=AY1_p.dtype)
  180. Z2 = np.zeros((AY2_p.shape[0],AY2_p.shape[0]), dtype=AY2_p.dtype)
  181. J = np.block([[Z1, S12],[S12.T, Z2]])
  182. U, S = ac.do_cov_EVD(J, k=2) # here, k = smallest subspace size in each modality
  183. # Final transformations: these multiply AY
  184. W1 = U[:2,].T @ AY1_p_projM
  185. W2 = U[2:,].T @ AY2_p_projM
  186. WAY1 = W1 @ AY1
  187. WAY2 = W2 @ AY2
  188. WAY_list.append( [WAY1, WAY2] )
  189. cca_corr = np.corrcoef(WAY1, WAY2)[2:,0:2]
  190. cca_corr_list.append(cca_corr)
  191. # %%
  192. num_voxel = A.shape[3]
  193. num_crossmodal_subspace = 5
  194. voxelwise_cca_corr = np.zeros((num_crossmodal_subspace, num_voxel))
  195. for j, i in enumerate(np.arange(0,10,2)):
  196. A1 = A[1,1,0,:,i:i+2] # structure 2, MSIVA, M1
  197. A2 = A[1,1,1,:,i:i+2] # structure 2, MSIVA, M2
  198. Y1 = Y[1,1,0,i:i+2]
  199. Y2 = Y[1,1,1,i:i+2]
  200. AY1 = A1@Y1
  201. AY2 = A2@Y2
  202. for k in range(num_voxel):
  203. AY1_voxel = np.expand_dims(AY1[k, :], axis=0)
  204. AY2_voxel = np.expand_dims(AY2[k, :], axis=0)
  205. # PCA AY1, AY2
  206. AY1_p, AY1_p_projM, AY1_p_projM_std = ac.base_PCA(AY1_voxel, num_PC=None, axis=-2, whitening=True)
  207. AY2_p, AY2_p_projM, AY2_p_projM_std = ac.base_PCA(AY2_voxel, num_PC=None, axis=-2, whitening=True)
  208. # Post-PCA eigenvalue problem for CCA
  209. S12 = AY1_p @ AY2_p.T
  210. Z1 = np.zeros((AY1_p.shape[0],AY1_p.shape[0]), dtype=AY1_p.dtype)
  211. Z2 = np.zeros((AY2_p.shape[0],AY2_p.shape[0]), dtype=AY2_p.dtype)
  212. J = np.block([[Z1, S12], [S12.T, Z2]])
  213. U, S = ac.do_cov_EVD(J, k=2) # here, k = smallest subspace size in each modality
  214. # Final transformations: these multiply AY
  215. W1 = U[:1,].T @ AY1_p_projM
  216. W2 = U[1:,].T @ AY2_p_projM
  217. WAY1 = W1 @ AY1_voxel
  218. WAY2 = W2 @ AY2_voxel
  219. voxelwise_cca_corr[j, k] = np.corrcoef(WAY1, WAY2)[2:,0:2][0,0]
  220. # sio.savemat("voxelwise_cca_corr_sz.mat", {"corr": voxelwise_cca_corr})
  221. # %%
  222. regularizer_range = np.linspace(0.1, 1, 10)
  223. param_grid_rr = [{'alpha': regularizer_range}]
  224. param_grid_svm = [{'C': regularizer_range}]
  225. age_mae = np.zeros(5)
  226. age_coef = np.zeros((5, 4))
  227. diagnosis_acc = np.zeros(5)
  228. diagnosis_coef = np.zeros((5, 4))
  229. sex_acc = np.zeros(5)
  230. sex_coef = np.zeros((5, 4))
  231. for i in range(5):
  232. X12 = (np.concatenate((WAY_list[i][0], WAY_list[i][1]), axis=0)).T
  233. age_subset = age[age>15] # there is only one subject with age 15 and it can't be stratified
  234. X12_subset = X12[age>15, :]
  235. X_train, X_test, y_train, y_test = train_test_split(X12_subset, age_subset, test_size=0.3, random_state=42, stratify=age_subset)
  236. X_train = np.concatenate([X_train, X12[age==15, :]], axis=0)
  237. y_train = np.concatenate([y_train, np.array([15])], axis=0)
  238. base_estimator = Ridge()
  239. rr = GridSearchCV(base_estimator, param_grid_rr, cv=10).fit(X_train, y_train)
  240. mae, coef = age_regression(X_train, X_test, y_train, y_test, a=rr.best_params_['alpha'])
  241. age_mae[i] = mae
  242. age_coef[i] = coef
  243. X12_subset = X12[diagnosis<2, :]
  244. diagnosis_subset = diagnosis[diagnosis < 2]
  245. X_train, X_test, y_train, y_test = train_test_split(X12_subset, diagnosis_subset, test_size=0.3, random_state=42, stratify=diagnosis_subset)
  246. base_estimator = LinearSVC(dual=False)
  247. svc = GridSearchCV(base_estimator, param_grid_svm, cv=10).fit(X_train, y_train)
  248. acc, coef = sz_classification(X_train, X_test, y_train, y_test, c=svc.best_params_['C'])
  249. diagnosis_acc[i] = acc*100
  250. diagnosis_coef[i] = coef[0]
  251. X_train, X_test, y_train, y_test = train_test_split(X12, sex, test_size=0.3, random_state=42, stratify=sex)
  252. base_estimator = LinearSVC(dual=False)
  253. svc = GridSearchCV(base_estimator, param_grid_svm, cv=10).fit(X_train, y_train)
  254. acc, coef = sex_classification(X_train, X_test, y_train, y_test, c=svc.best_params_['C'])
  255. sex_acc[i] = acc*100
  256. sex_coef[i] = coef[0]
  257. print(f"Subspace {i+1}: age regression MAE {age_mae[i]:.3f}, diagnosis classification accuracy {diagnosis_acc[i]:.3f}, sex classification accuracy {sex_acc[i]:.3f}")
  258. # %%
  259. num_subject = len(age)
  260. age_median = np.median(age)
  261. cmap = plt.cm.jet
  262. norm = Normalize(vmin=age.min(), vmax=age.max())
  263. lim = 3.6
  264. fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
  265. for k in range(5):
  266. for i in range(2):
  267. WAY1 = WAY_list[k][0]
  268. WAY2 = WAY_list[k][1]
  269. sign1 = np.sign(correlation(WAY1,age))
  270. sign2 = np.sign(correlation(WAY2,age))
  271. for j in range(2):
  272. WAY1[j,:] = -sign1[j]*WAY1[j,:]
  273. WAY2[j,:] = -sign2[j]*WAY2[j,:]
  274. axes[i,k].set_aspect('equal', 'box')
  275. if k == 2:
  276. axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
  277. if k == 0:
  278. axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
  279. axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
  280. axes[i,k].set_xlim([-lim,lim])
  281. axes[i,k].set_ylim([-lim-0.4,lim-0.4])
  282. r = format(round(cca_corr_list[k][i,i],3), '.3f')
  283. stat1, p1 = stats.ttest_ind(WAY1[i][age<age_median], WAY1[i][age>=age_median])
  284. stat2, p2 = stats.ttest_ind(WAY2[i][age<age_median], WAY2[i][age>=age_median])
  285. p1_str = convert_pvalue(p1*20)
  286. p2_str = convert_pvalue(p2*20)
  287. if k == 4 and i == 1:
  288. axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
  289. else:
  290. axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
  291. axes[i,k].annotate(f'MAE={age_mae[k]:.3f}yr', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
  292. if k == 4 and i == 1:
  293. axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  294. else:
  295. axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  296. age_subplot = axes[i,k].scatter(WAY1[i], WAY2[i], c=age, cmap=cmap, norm=norm, marker='.', alpha=1)
  297. cbar_ax = fig.add_axes([1, 0.13, 0.012, 0.8])
  298. cbar = plt.colorbar(age_subplot, cax=cbar_ax)
  299. cbar.set_label('Age (yr)', fontsize=14)
  300. cbar.ax.tick_params(labelsize=12)
  301. plt.tight_layout()
  302. plt.savefig("figures/cca_age_sz.png", bbox_inches='tight', dpi=2000)
  303. # %%
  304. lim = 3.6
  305. fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
  306. for k in range(5):
  307. for i in range(2):
  308. WAY1 = WAY_list[k][0]
  309. WAY2 = WAY_list[k][1]
  310. sign1 = np.sign(correlation(WAY1,age))
  311. sign2 = np.sign(correlation(WAY2,age))
  312. for j in range(2):
  313. WAY1[j,:] = -sign1[j]*WAY1[j,:]
  314. WAY2[j,:] = -sign2[j]*WAY2[j,:]
  315. axes[i,k].set_aspect('equal', 'box')
  316. if k == 2:
  317. axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
  318. if k == 0:
  319. axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
  320. axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
  321. axes[i,k].set_xlim([-lim,lim])
  322. axes[i,k].set_ylim([-lim-0.4,lim-0.4])
  323. r = format(round(cca_corr_list[k][i,i],3), '.3f')
  324. stat1, p1 = stats.ttest_ind(WAY1[i][diagnosis==0], WAY1[i][diagnosis==1])
  325. stat2, p2 = stats.ttest_ind(WAY2[i][diagnosis==0], WAY2[i][diagnosis==1])
  326. p1_str = convert_pvalue(p1*20) # correct for number of subjects
  327. p2_str = convert_pvalue(p2*20)
  328. if k == 4 and i == 1:
  329. axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
  330. else:
  331. axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
  332. axes[i,k].annotate(f'Acc.={diagnosis_acc[k]:.3f}%', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
  333. if k == 4 and i == 1:
  334. axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  335. else:
  336. axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  337. if k==0 and i==0:
  338. axes[i,k].scatter(WAY1[i,diagnosis==0], WAY2[i,diagnosis==0],color=sns.color_palette("tab10")[0],marker='.',alpha=0.5,label="HC")
  339. axes[i,k].scatter(WAY1[i,diagnosis==1], WAY2[i,diagnosis==1],color=sns.color_palette("tab10")[1],marker='.',alpha=0.5,label="SZ")
  340. else:
  341. axes[i,k].scatter(WAY1[i,diagnosis==0], WAY2[i,diagnosis==0],color=sns.color_palette("tab10")[0],marker='.',alpha=0.5)
  342. axes[i,k].scatter(WAY1[i,diagnosis==1], WAY2[i,diagnosis==1],color=sns.color_palette("tab10")[1],marker='.',alpha=0.5)
  343. fig.legend(bbox_to_anchor=(1.07, 0.27), fontsize=14)
  344. plt.tight_layout()
  345. plt.savefig("figures/cca_diag_sz.png", bbox_inches='tight', dpi=2000)
  346. # %%
  347. lim = 3.6
  348. fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
  349. num_subject = len(age)
  350. for k in range(5):
  351. for i in range(2):
  352. WAY1 = WAY_list[k][0]
  353. WAY2 = WAY_list[k][1]
  354. sign1 = np.sign(correlation(WAY1,age))
  355. sign2 = np.sign(correlation(WAY2,age))
  356. for j in range(2):
  357. WAY1[j,:] = -sign1[j]*WAY1[j,:]
  358. WAY2[j,:] = -sign2[j]*WAY2[j,:]
  359. axes[i,k].set_aspect('equal', 'box')
  360. if k == 2:
  361. axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
  362. if k == 0:
  363. axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
  364. axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
  365. axes[i,k].set_xlim([-lim,lim])
  366. axes[i,k].set_ylim([-lim-0.4,lim-0.4])
  367. r = format(round(cca_corr_list[k][i,i],3), '.3f')
  368. stat1, p1 = stats.ttest_ind(WAY1[i][sex==0], WAY1[i][sex==1])
  369. stat2, p2 = stats.ttest_ind(WAY2[i][sex==0], WAY2[i][sex==1])
  370. p1_str = convert_pvalue(p1*20) # correct for number of subjects
  371. p2_str = convert_pvalue(p2*20)
  372. if k == 4 and i == 1:
  373. axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
  374. else:
  375. axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
  376. axes[i,k].annotate(f'Acc.={sex_acc[k]:.3f}%', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
  377. if k == 4 and i == 1:
  378. axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  379. else:
  380. axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
  381. if k==0 and i==0:
  382. axes[i,k].plot(WAY1[i][sex==0], WAY2[i][sex==0],'b.',alpha=0.3,label='M')
  383. axes[i,k].plot(WAY1[i][sex==1], WAY2[i][sex==1],'r.',alpha=0.3,label='F')
  384. else:
  385. axes[i,k].plot(WAY1[i][sex==0], WAY2[i][sex==0],'b.',alpha=0.3)
  386. axes[i,k].plot(WAY1[i][sex==1], WAY2[i][sex==1],'r.',alpha=0.3)
  387. fig.legend(bbox_to_anchor=(1.06, 0.27), fontsize=14)
  388. plt.tight_layout()
  389. plt.savefig("figures/cca_sex_sz.png", bbox_inches='tight', dpi=2000)
  390. # %%
  391. # M1-2, S1-5, median/young control/old control/young patient/old patient, voxel
  392. age_median = np.median(age)
  393. AY_median = np.zeros((2, 5, 5, 44318))
  394. for s, i in enumerate(np.arange(0,10,2)):
  395. for m in range(2):
  396. Am = A[1,1,m,:,i:i+2]
  397. Ym = Y[1,1,m,i:i+2]
  398. AYm = Am@Ym
  399. AYm_young_hc = AYm[:,(age<age_median)&(diagnosis==0)]
  400. AYm_old_hc = AYm[:,(age>=age_median)&(diagnosis==0)]
  401. AYm_young_sz = AYm[:,(age<age_median)&(diagnosis==1)]
  402. AYm_old_sz = AYm[:,(age>=age_median)&(diagnosis==1)]
  403. AYm_median = hd.geomedian(AYm,axis=1)
  404. AYm_young_hc_median = hd.geomedian(AYm_young_hc,axis=1)
  405. AYm_old_hc_median = hd.geomedian(AYm_old_hc,axis=1)
  406. AYm_young_sz_median = hd.geomedian(AYm_young_sz,axis=1)
  407. AYm_old_sz_median = hd.geomedian(AYm_old_sz,axis=1)
  408. AY_median[m,s,0] = AYm_median
  409. AY_median[m,s,1] = AYm_young_hc_median
  410. AY_median[m,s,2] = AYm_old_hc_median
  411. AY_median[m,s,3] = AYm_young_sz_median
  412. AY_median[m,s,4] = AYm_old_sz_median
  413. # sio.savemat(os.path.join(res_dir, "mat", "AY_median_sz_interaction.mat"), {"AY_median": AY_median})
  414. # %%
  415. # M1-2, S1-5, median/young control/old control/young patient/old patient, voxel
  416. n_voxel = 44318
  417. age_median = np.median(age)
  418. AY_median = np.zeros((2, 5, 5, n_voxel))
  419. for s, i in enumerate(np.arange(0,10,2)):
  420. A1 = A[1,1,0,:,i:i+2]
  421. Y1 = Y[1,1,0,i:i+2]
  422. A2 = A[1,1,1,:,i:i+2]
  423. Y2 = Y[1,1,1,i:i+2]
  424. AY1 = A1@Y1
  425. AY2 = A2@Y2
  426. AY = np.concatenate((AY1, AY2), axis=0)
  427. AYm_young_hc = AY[:,(age<age_median)&(diagnosis==0)]
  428. AYm_old_hc = AY[:,(age>=age_median)&(diagnosis==0)]
  429. AYm_young_sz = AY[:,(age<age_median)&(diagnosis==1)]
  430. AYm_old_sz = AY[:,(age>=age_median)&(diagnosis==1)]
  431. AYm_median = hd.geomedian(AY,axis=1)
  432. AYm_young_hc_median = hd.geomedian(AYm_young_hc,axis=1)
  433. AYm_old_hc_median = hd.geomedian(AYm_old_hc,axis=1)
  434. AYm_young_sz_median = hd.geomedian(AYm_young_sz,axis=1)
  435. AYm_old_sz_median = hd.geomedian(AYm_old_sz,axis=1)
  436. AY_median[0,s,0] = AYm_median[:n_voxel]
  437. AY_median[0,s,1] = AYm_young_hc_median[:n_voxel]
  438. AY_median[0,s,2] = AYm_old_hc_median[:n_voxel]
  439. AY_median[0,s,3] = AYm_young_sz_median[:n_voxel]
  440. AY_median[0,s,4] = AYm_old_sz_median[:n_voxel]
  441. AY_median[1,s,0] = AYm_median[n_voxel:]
  442. AY_median[1,s,1] = AYm_young_hc_median[n_voxel:]
  443. AY_median[1,s,2] = AYm_old_hc_median[n_voxel:]
  444. AY_median[1,s,3] = AYm_young_sz_median[n_voxel:]
  445. AY_median[1,s,4] = AYm_old_sz_median[n_voxel:]
  446. # sio.savemat(os.path.join(res_dir, "mat", "stacked_AY_median_sz_interaction.mat"), {"AY_median": AY_median})
  447. # %%
  448. age_median = np.median(age)
  449. AY_corr = np.zeros((5, 5, 44318))
  450. p_value = np.zeros((5, 5, 44318))
  451. for s, i in enumerate(np.arange(0,10,2)):
  452. A1 = A[1,1,0,:,i:i+2]
  453. Y1 = Y[1,1,0,i:i+2]
  454. A2 = A[1,1,1,:,i:i+2]
  455. Y2 = Y[1,1,1,i:i+2]
  456. AY1 = A1@Y1
  457. AY2 = A2@Y2
  458. for j in range(44318):
  459. AY_corr[s,0,j], p_value[s,0,j] = pearsonr(AY1[j], AY2[j])
  460. AY_corr[s,1,j], p_value[s,1,j] = pearsonr(AY1[j,(age<age_median)&(diagnosis==0)], AY2[j,(age<age_median)&(diagnosis==0)])
  461. AY_corr[s,2,j], p_value[s,2,j] = pearsonr(AY1[j,(age>=age_median)&(diagnosis==0)], AY2[j,(age>=age_median)&(diagnosis==0)])
  462. AY_corr[s,3,j], p_value[s,3,j] = pearsonr(AY1[j,(age<age_median)&(diagnosis==1)], AY2[j,(age<age_median)&(diagnosis==1)])
  463. AY_corr[s,4,j], p_value[s,4,j] = pearsonr(AY1[j,(age>=age_median)&(diagnosis==1)], AY2[j,(age>=age_median)&(diagnosis==1)])
  464. # sio.savemat(os.path.join(res_dir, "mat", "AY_group_corr_sz_interaction.mat"), {"AY_corr": AY_corr})
  465. # sio.savemat(os.path.join(res_dir, "mat", "AY_group_pvalue_sz_interaction.mat"), {"p_value": p_value})
  466. # %%
  467. # AY_corr = sio.loadmat("mat/AY_group_corr_sz_interaction.mat")["AY_corr"]
  468. age_median = np.median(age)
  469. n_young_control = np.sum((age<age_median)&(diagnosis==0))
  470. n_old_control = np.sum((age>=age_median)&(diagnosis==0))
  471. n_young_patient = np.sum((age<age_median)&(diagnosis==1))
  472. n_old_patient = np.sum((age>=age_median)&(diagnosis==1))
  473. n_list = [n_young_control, n_old_control, n_young_patient, n_old_patient]
  474. cmap = plt.get_cmap('jet')
  475. percentiles = [30, 70]
  476. norm_percentiles = [p / 100.0 for p in percentiles]
  477. colors = [cmap(norm) for norm in norm_percentiles]
  478. title_list = ["All", "Younger control", "Older control", "Younger patient", "Older patient"]
  479. group_list = ["control", "patient"]
  480. color_list = [colors, [sns.color_palette("tab10")[0], sns.color_palette("tab10")[1]]]
  481. for s in range(5):
  482. for i, j in enumerate([1,3]):
  483. fig, ax = plt.subplots(1, 1, figsize=(3, 3))
  484. pct_neg, pct_pos, h_pct_neg, h_pct_pos = [], [], [], []
  485. for k in range(2):
  486. fd = 2*(np.percentile(AY_corr[s,j+k],75) - np.percentile(AY_corr[s,j+k],25))*len(AY_corr[s,j+k])**(-1/3)
  487. corr_range = np.max(AY_corr[s,j+k]) - np.min(AY_corr[s,j+k])
  488. num_bins = int(corr_range/(fd/2))
  489. counts, bins = np.histogram(AY_corr[s,j+k], bins=num_bins)
  490. hist_plot = sns.histplot(data=AY_corr[s,j+k], bins=num_bins, kde=True, color=color_list[i][k], edgecolor=color_list[i][k], label=title_list[j+k],ax=ax)
  491. pct = np.percentile(AY_corr[s,j+k], [15, 85])
  492. pct_neg.append(pct[0])
  493. pct_pos.append(pct[1])
  494. for q, p in enumerate(pct):
  495. ind = np.argmin(np.abs(bins - p))
  496. ax.vlines(p, 0, counts[ind], colors=color_list[i][k], linestyles='dashed', linewidth=2)
  497. if q == 0:
  498. h_pct_neg.append(counts[ind])
  499. else:
  500. h_pct_pos.append(counts[ind])
  501. if k == 0:
  502. thr_corr = pvalue_to_r(0.01/44318, n_list[j+k-1])
  503. ind = np.argmin(np.abs(bins - thr_corr))
  504. ax.vlines(thr_corr, 0, counts[ind], colors='k', linestyles='dotted', linewidth=2)
  505. ax.text(thr_corr-0.2, counts[ind]+500, "$p=\\frac{0.01}{44318}$", fontsize=16)
  506. ax.annotate("", xy=(thr_corr, counts[ind]), xytext=(thr_corr, counts[ind]+400), arrowprops=dict(arrowstyle="->"))
  507. ind = np.argmin(np.abs(bins + thr_corr))
  508. ax.vlines(-thr_corr, 0, counts[ind], colors='k', linestyles='dotted', linewidth=2)
  509. ax.text(-thr_corr-0.62, counts[ind]+500, "$p=\\frac{0.01}{44318}$", fontsize=16)
  510. ax.annotate("", xy=(-thr_corr, counts[ind]), xytext=(-thr_corr, counts[ind]+400), arrowprops=dict(arrowstyle="->"))
  511. ax.text(min(pct_neg)-0.32, 50, "15%", fontsize=13)
  512. ax.text(max(pct_pos)-0.04, 50, "15%", fontsize=13)
  513. # ax.set_title(f"Subspace {s+1}", fontsize=16)
  514. ax.set_xlabel("Cross-modal correlation", fontsize=15)
  515. ax.set_ylabel("Count", fontsize=15)
  516. ax.set_xlim([-1, 1])
  517. ax.set_ylim([0, 2500])
  518. ax.legend(loc="upper left", fontsize=13) # bbox_to_anchor=(0.92, 1.35)
  519. plt.tick_params(axis='both', labelsize=12)
  520. plt.savefig(f"figures/AY_histogram/sz/subspace{s+1}_{group_list[i]}.png", bbox_inches="tight", dpi=500)
  521. # %%
  522. Xpath = os.path.join(res_dir, "mat", "sMRI-fMRI", "X_sz.mat")
  523. X = sio.loadmat(Xpath)['X']
  524. X = np.squeeze(X)
  525. X[0].shape
  526. # %%
  527. AY_corr = np.zeros((5, 44318))
  528. for m in range(2):
  529. Xm = X[m][:,id]
  530. Xm_demean = Xm - np.mean(Xm)
  531. sstot = np.sum(Xm_demean**2)
  532. for s, i in enumerate(np.arange(0,10,2)):
  533. Am = A[1,1,m,:,i:i+2]
  534. Ym = Y[1,1,m,i:i+2]
  535. AYm = Am@Ym
  536. ssres = np.sum((Xm-AYm)**2)
  537. r2 = (1 - ssres/sstot)*100
  538. print(f"modality {m+1} subspace {s+1} variance explained: {r2:.3f}%")

plot_img_sz.ipynb at commit 4126a6f, under Apache-2.0 · at the source

Overview

Authors: Xinhui Li1,2, Peter Kochunov3, Tulay Adali4, Rogers F Silva1, Vince D Calhoun1,2
  1. Tri-institutional Center for Translational Research in Neuroimaging and Data Science, Georgia State University, Georgia Institute of Technology, Emory University, Atlanta, GA, United States
  2. School of Electrical and Computer Engineering, Georgia Institute of Technology, Atlanta, GA, United States
  3. Department of Psychiatry and Behavioral Sciences, McGovern Medical School, University of Texas Health Science Center at Houston, Houston, TX, United States
  4. Department of Computer Science and Electrical Engineering, University of Maryland Baltimore County, Baltimore, MD, United States
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1266
Dates: received 22 October 2024; accepted 20 April 2026; published online 18 June 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1266 · PMID 42326564 · PMCID PMC13281777 · OpenAlex W7161737125
Open access: diamond, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), schizophrenia / psychosis (population), clinical / translational (subfield)
Methods: Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing
Keywords: multimodal fusion, sMRI, fMRI, biomarker, age, sex, schizophrenia
Topic: Functional Brain Connectivity Studies (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: National Science Foundation (2316420, R01MH123610, 2112455); Emory University; National Institutes of Health (5r01mh123610-04, T32EB025816, R01 MH123610); National Institute of Biomedical Imaging and Bioengineering (T32 EB025816)
Citations: not cited yet (Europe PMC); 82 references in the paper

Abstract

A key challenge in neuroscience is inferring relationships between brain structure and function from high-dimensional, multimodal neuroimaging data. While conventional multivariate approaches often simplify statistical assumptions and estimate one-dimensional independent sources shared across modalities, the true relationships between latent sources are likely more complex—statistical dependence may exist both within and between modalities and span more than one dimension per modality. Here, we introduce Multimodal Subspace Independent Vector Analysis (MSIVA), a method for capturing both joint and unique vector sources from multiple data modalities by defining cross-modal and unimodal subspaces with variable dimensions. MSIVA enables flexible estimation of varying-size independent subspaces within modalities and their one-to-one linkage to corresponding subspaces across modalities. Crucially, it captures subject-level variability at the voxel level within independent subspaces, in contrast to traditional methods that share identical independent components across subjects. We evaluated three initialization workflows with five candidate subspace structures in multiple synthetic datasets and two large multimodal neuroimaging datasets, including structural MRI (sMRI) and functional MRI (fMRI). After confirming that MSIVA successfully recovered ground-truth subspace structures in synthetic data, we applied MSIVA to identify latent subspace structures in neuroimaging data. Subsequent subspace-specific canonical correlation analysis, brain-phenotype prediction, and voxelwise brain-age delta analysis revealed that MSIVA sources were strongly associated with multiple phenotype variables, including age, sex, schizophrenia, lifestyle factors, and cognitive functions. Further, we identified modality- and group-specific brain regions related to age (for example, cerebellum, precentral gyrus, and cingulate gyrus in sMRI; occipital lobe and superior frontal gyrus in fMRI), sex (for example, cerebellum in sMRI, frontal lobe in fMRI, and precuneus in both sMRI and fMRI), and schizophrenia (for example, cerebellar, frontal, and insular cortices in sMRI; occipital pole, lingual gyrus, and precuneus in fMRI), shedding light on linked phenotypic and neuropsychiatric biomarkers of brain structure and function.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 21 matches between paragraphs and lines of code.

trendscenter/MSIVA

License: Apache-2.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 4126a6f9db37b123b703c463cb017bd5f3288820, 20 August 2026
Languages: MATLAB (122), Jupyter (17), Python (5)
Size: 167 files, 144 scripts
Software Heritage: archived
Found in: “Data and Code Availability”
Holds: README, license file, 17 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (22 files), SciPy (20 files), Matplotlib (18 files), Optimization Toolbox (14 files), seaborn (13 files), GIFT (10 files), SPM (10 files), Statistics and Machine Learning Toolbox (6 files), scikit-learn (6 files), export_fig (4 files), statsmodels (4 files), pandas (3 files), Image Processing Toolbox (1 file), NiBabel (1 file), Nilearn (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
146 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 144 scripts, each with its path and the digest of its content;
  • 21 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data and Code Availability

The UK Biobank dataset can be accessed at https://www.ukbiobank.ac.uk/. The BSNIP and MPRC datasets are available through the NIMH Data Archive (NDA) https://nda.nih.gov/. The COBRE dataset is available from the Collaborative Informatics and Neuroimaging Suite (COINS) https://coins.trendscenter.org/. The FBIRN phase III dataset cannot be shared directly due to the Institutional Review Board (IRB) restrictions. Individuals interested in requesting access can contact Vince D. Calhoun ().

All code used in this study is publicly available at https://github.com/trendscenter/MSIVA. Code for brain-age delta analysis was adapted from https://www.fmrib.ox.ac.uk/datasets/BrainAgeDelta/. Code for dual-coded images was adapted from https://trendscenter.org/x/datavis/.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 2, 28 September 2026

  • Funding: added National Science Foundation: 2316420, R01MH123610, 2112455; Emory University; National Institutes of Health: 5r01mh123610-04, T32EB025816, R01 MH123610; National Institute of Biomedical Imaging and Bioengineering: T32 EB025816

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 5 authors, 7 keywords, 79 references.

Cite

This paper

Li, X., Kochunov, P., Adali, T., Silva, R. F., & Calhoun, V. D. (2026). Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1266. https://doi.org/10.1162/imag.a.1266

BibTeX

@article{li2026multimodal,
author = {Li, Xinhui and Kochunov, Peter and Adali, Tulay and Silva, Rogers F and Calhoun, Vince D},
title = {{Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1266},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/imag.a.1266},
url = {https://doi.org/10.1162/imag.a.1266},
pmid = {42326564},
pmcid = {PMC13281777}
}

RIS

TY - JOUR
AU - Li, Xinhui
AU - Kochunov, Peter
AU - Adali, Tulay
AU - Silva, Rogers F
AU - Calhoun, Vince D
TI - Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/06/18
VL - 4
SP - IMAG.a.1266
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1266
UR - https://doi.org/10.1162/imag.a.1266
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1266",
"type": "article-journal",
"title": "Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Li",
"given": "Xinhui"
},
{
"family": "Kochunov",
"given": "Peter"
},
{
"family": "Adali",
"given": "Tulay"
},
{
"family": "Silva",
"given": "Rogers F"
},
{
"family": "Calhoun",
"given": "Vince D"
}
],
"container-title-short": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1266",
"DOI": "10.1162/imag.a.1266",
"PMID": "42326564",
"PMCID": "PMC13281777",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1266",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
18
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1002/hbm.70599 [code]
Group Joint ICA (gjICA): A Method for Multimodal Fusion of Concurrent EEG and fMRI Data.
Journal: Human brain mapping
In common: GIFT, export_fig, Optimization Toolbox, 4 other tools, fMRI, 6 references, author Vince Calhoun
[2] doi:10.1162/netn.a.575 [code]
Parallel multilink group joint ICA (pmg-jICA): Fusion of 3D structural and 4D functional data across multiple resting fMRI networks.
Journal: Network neuroscience (Cambridge, Mass.)
In common: GIFT, SPM, Statistics and Machine Learning Toolbox, 3 other tools, fMRI, 5 references, author Tülay Adali
[3] doi:10.1162/imag.a.1220 [code]
Brain functional network connectivity interpolation characterizes the neuropsychiatric continuum and heterogeneity.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: seaborn, scikit-learn, pandas, 3 other tools, schizophrenia / psychosis, 2 references, 2 authors
[4] doi:10.1038/s41467-026-71151-2 [code]
Common and distinct neural correlates of social interaction processing and theory of mind in narratives.
Journal: Nature communications
In common: GIFT, Optimization Toolbox, Nilearn, 11 other tools
[5] doi:10.1038/s41586-026-10631-3 [code]
A prognostic human brain network for diffuse midline glioma.
Journal: Nature
In common: export_fig, Optimization Toolbox, Nilearn, 10 other tools, clinical / translational
[6] doi:10.1371/journal.pbio.3003856 [code]
Aging and metabolism contribute separately to brain-body health.
Journal: PLoS biology
In common: Nilearn, SPM, Image Processing Toolbox, 9 other tools, clinical / translational, 2 references
[7] doi:10.1038/s41398-026-04278-x [code]
Aberrant recovery of timescale-aligned amplitude balance links to symptoms and cognition in schizophrenia.
Journal: Translational psychiatry
In common: GIFT, Statistics and Machine Learning Toolbox, schizophrenia / psychosis, 3 references, author Vince Calhoun
[8] doi:10.1038/s42003-026-10276-y [code]
The cellular correlates and adolescent reorganisation of cortical myelination networks in the common marmoset.
Journal: Communications biology
In common: Optimization Toolbox, SPM, Image Processing Toolbox, 9 other tools, 1 reference
[9] doi:10.64898/2026.08.13.26360304 [code]
Lifespan brain structural variation reveals shared organization across mental health conditions
Journal: medRxiv (preprint)
In common: Nilearn, NiBabel, statsmodels, 6 other tools, clinical / translational, author Vince Calhoun
[10] doi:10.1002/hbm.70602 [code]
Neuroimaging Correlates of Post-Stroke Pain After Ischemic Stroke: Secondary Analysis of the INSPiRE-TMS Trial.
Journal: Human brain mapping
In common: export_fig, Optimization Toolbox, SPM, 8 other tools, clinical / translational

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.