OSCR

Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex.

Code ↔ Paper

11 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 11 matches
  1. [1] § Methods › Statistical analysis › Control analysis: QSM- versus ECM-based PLSR ↔ PLSR_QSM.py, lines 130–196 · score 0.80 · X_train, y_train, latent scores, squared error, QSM, fold
  2. [2] § Methods › Statistical analysis › Control analysis: QSM- versus ECM-based PLSR ↔ PLSR_ECM.py, lines 157–209 · score 0.71 · X_train, y_train, squared error, fold, ECM, MSE
  3. [3] § Methods › Statistical analysis › Generating functional localizer masks within the sensorimotor cortex ↔ connected_cluster_masks_tasks.py, lines 59–85 · score 0.68 · connected cluster, localizer masks, combined mask, largest, body part, tongue
  4. [4] § Methods › Statistical analysis › Robust shared response modelling for amyotrophic lateral sclerosis versus control classification ↔ ALS_vs_control_rSRM.py, lines 230–323 · score 0.58 · rSRM, cross validation, shared space, trained, ALS
  5. [5] § Methods › Statistical analysis › Control analysis: QSM- versus ECM-based PLSR ↔ PLSR_QSM.py, lines 634–711 · score 0.58 · LV2 weight, PLS models, QSM, ECM, LV1, tongue
  6. [6] § Methods › Statistical analysis › Robust shared response modelling for amyotrophic lateral sclerosis versus control classification ↔ ALS_vs_control_rSRM.py, lines 230–323 · score 0.57 · cross validation, shared spaces, transforming, concatenated, training, predictions
  7. [7] § Methods › Statistical analysis › Partial least squares regression analysis ↔ PLSR_ECM.py, lines 157–209 · score 0.54 · cross validation, latent variable, LOO, MSE, split, model
  8. [8] § Methods › Statistical analysis › Partial least squares regression analysis ↔ PLSR_BOLD.py, lines 189–243 · score 0.53 · cross validation, latent variable, LOO, MSE, model, weights
  9. [9] § Methods › Statistical analysis › Per cent signal change analyses over time ↔ filtered_connectivity.py, lines 32–93 · score 0.53 · band pass filtering, signal, 0.01 Hz, voxel
  10. [10] § Methods › Pre-processing of fMRI data ↔ NL_AA_mni.py, lines 15–52 · score 0.52 · ANTs, affine, smoothing, anatomical, template, MNI
  11. [11] § Methods › Statistical analysis › Functional activation ↔ filtered_connectivity.py, lines 32–93 · score 0.51 · band pass filtering, fMRI, 0.01 Hz, maps, body

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

Python · 711 lines · 30 KB · no license · 2 matches

  1. import os
  2. import warnings
  3. import sys
  4. import numpy as np
  5. import pandas as pd
  6. import nibabel as nib
  7. import matplotlib.pyplot as plt
  8. from scipy import stats
  9. from scipy.stats import spearmanr, pearsonr, ttest_rel # Added pearsonr and ttest_rel here
  10. from sklearn.cross_decomposition import PLSRegression
  11. from sklearn.model_selection import LeaveOneOut
  12. from sklearn.metrics import mean_squared_error
  13. from nilearn import masking
  14. from nilearn import plotting # Added for brain plotting
  15. # --- Configuration and Path Definitions ---
  16. BASE_DIR = '/home/akalyani/mounts/zdv/Users/akalyani/ALS_project'
  17. RESULTS_DIR = os.path.join(BASE_DIR, 'results')
  18. FIGURES_DIR = os.path.join(BASE_DIR, 'figures')
  19. # Create results and figures directories if they don't exist
  20. os.makedirs(RESULTS_DIR, exist_ok=True)
  21. os.makedirs(FIGURES_DIR, exist_ok=True)
  22. # Define file paths
  23. CONTROL_BEHAV_PATH = os.path.join(BASE_DIR, 'control_behavioral.csv')
  24. ALS_BEHAV_PATH_OLD = os.path.join(BASE_DIR, 'ALS_behavioral.csv')
  25. ALS_BEHAV_PATH_NEW = os.path.join(BASE_DIR, 'New_ALS_behave.csv')
  26. QSM_BASE_DIR = os.path.join(BASE_DIR, 'QSM_MNI_outputs_re_registered')
  27. QSM_ALS_DIR = os.path.join(QSM_BASE_DIR, 'ALS_QSM_MNI2')
  28. MASK_PATH = os.path.join(BASE_DIR, 'ALS_functional_localizer_masks', 'wb_functional_HFC.nii.gz')
  29. REF_IMAGE_PATH = os.path.join(BASE_DIR, 'ALS_functional_localizer_masks', 'wb_functional_HFC.nii.gz')
  30. # ROI Masks for analysis
  31. HAND_MASK_PATH = os.path.join(BASE_DIR, "ALS_functional_localizer_masks", "roi_vbm", "largest_cluster_mask_hand_merged.nii")
  32. FOOT_MASK_PATH = os.path.join(BASE_DIR, "ALS_functional_localizer_masks", "roi_vbm", "largest_cluster_mask_foot_merged.nii")
  33. TONGUE_MASK_PATH = os.path.join(BASE_DIR, "ALS_functional_localizer_masks", "roi_vbm", "largest_cluster_mask_tongue_merged.nii")
  34. # ALS subjects with QSM data (from your original script)
  35. ALS_QSM_SUBJECTS = ["dl35", "na78", 'pn47', 'rs25', 'vj48', 'bo91', 'nv39', 'bo58']
  36. # --- Helper Functions ---
  37. def load_behavioral_data(old_path, new_path, qsm_subjects):
  38. """
  39. Loads and combines ALS behavioral data, then filters for subjects
  40. present in the QSM dataset.
  41. """
  42. df_old = pd.read_csv(old_path)
  43. df_new = pd.read_csv(new_path)
  44. # Combine and remove duplicates, if any
  45. df_combined = pd.concat([df_old, df_new], ignore_index=True).drop_duplicates(subset=['Subject'])
  46. # Filter for subjects that have QSM data
  47. df_filtered = df_combined[df_combined['Subject'].isin(qsm_subjects)].reset_index(drop=True)
  48. # Define columns to include for ALSFRS-R scores
  49. alsfrs_columns_to_include = [
  50. 'ALSFRS 3 Swallow', 'ALSFRS 2 Saliva', 'ALSFRS 1 Language',
  51. 'ALSFRS 4 Handwriting', 'ALSFRS 5a cutting food', 'ALSFRS 8 walking',
  52. 'ALSFRS 9 climbing stairs', 'ALSFRS 10 shortness of breath',
  53. 'ALSFRS 11 orthopnea', 'ALSFRS 12 respiratory failure'
  54. ]
  55. # Select and return the relevant ALSFRS-R scores
  56. return df_filtered[['Subject'] + alsfrs_columns_to_include], df_filtered['King Stage'] # Return King Stage as well
  57. def load_qsm_data(qsm_dir, mask_path, subjects):
  58. """
  59. Loads QSM data for a list of subjects, applies a mask, and returns
  60. a list of masked QSM arrays.
  61. """
  62. mask_img = nib.load(mask_path)
  63. all_sub_qsm_data = []
  64. for sub in subjects:
  65. qsm_filepath = os.path.join(qsm_dir, f'{sub}/QSM_re_registered_to_MNI.nii.gz')
  66. if os.path.exists(qsm_filepath):
  67. qsm_val = masking.apply_mask(qsm_filepath, mask_img, dtype='f', ensure_finite=True)
  68. all_sub_qsm_data.append(qsm_val)
  69. else:
  70. print(f"Warning: QSM file not found for {sub} at {qsm_filepath}. Skipping.")
  71. return np.array(all_sub_qsm_data)
  72. def r2_score_custom(y_true, y_pred):
  73. """Calculates R-squared (R2) score."""
  74. y_mean = np.mean(y_true)
  75. total_sum_squares = np.sum((y_true - y_mean) ** 2)
  76. residual_sum_squares = np.sum((y_true - y_pred) ** 2)
  77. if total_sum_squares == 0:
  78. return np.nan # Avoid division by zero if y_true is constant
  79. return 1 - (residual_sum_squares / total_sum_squares)
  80. def save_voxel_weights_as_nifti(weights_mean, ref_image_path, output_filepath):
  81. """
  82. Unmasks mean voxel weights and saves them as a NIfTI image.
  83. """
  84. ref_image = nib.load(ref_image_path)
  85. nifti_image = masking.unmask(weights_mean, ref_image)
  86. nib.save(nifti_image, output_filepath)
  87. print(f"Saved PLS weights to {output_filepath}")
  88. def extract_mean_weight_from_roi(stat_img_path, roi_mask_path, ref_image_path):
  89. """
  90. Extracts the mean weight within a specified ROI, ensuring the ROI mask
  91. is aligned with the reference image.
  92. """
  93. try:
  94. stat_img_data = nib.load(stat_img_path).get_fdata()
  95. roi_mask_data = nib.load(roi_mask_path).get_fdata()
  96. ref_img_data = nib.load(ref_image_path).get_fdata()
  97. # Ensure masks are boolean and same shape as stat_img
  98. combined_mask = np.logical_and(roi_mask_data > 0, ref_img_data > 0)
  99. # Apply the combined mask
  100. weights = stat_img_data[combined_mask]
  101. return np.mean(weights) if weights.size > 0 else 0.0
  102. except Exception as e:
  103. print(f"Error extracting mean weight for {stat_img_path} with {roi_mask_path}: {e}")
  104. return 0.0 # Return 0.0 or NaN in case of error
  105. # --- Main Analysis Functions ---
  106. def perform_pls_analysis(X_data, Y_data, labels, n_components, n_voxels, qsm_subjects, ref_image_path):
  107. """
  108. Performs Leave-One-Out PLS regression and saves results.
  109. """
  110. pls = PLSRegression(n_components=n_components)
  111. loo = LeaveOneOut()
  112. mse_values = []
  113. r2_values = []
  114. x_latent_scores_all = [] # Training set X latent scores
  115. y_latent_scores_all = [] # Training set Y latent scores
  116. x_latent_test_all = [] # Test set X latent scores
  117. y_latent_test_all = [] # Test set Y latent scores
  118. test_labels_all = [] # Actual labels of test subjects
  119. print("\n--- Starting PLS Leave-One-Out Cross-Validation ---")
  120. for i, (train_index, test_index) in enumerate(loo.split(X_data)):
  121. X_train, X_test = X_data[train_index], X_data[test_index]
  122. y_train, y_test = Y_data[train_index], Y_data[test_index]
  123. labels_train, labels_test = labels[train_index], labels[test_index]
  124. print(f"Processing LOO fold {i+1}/{len(qsm_subjects)}")
  125. pls.fit(X_train, y_train)
  126. y_pred = pls.predict(X_test)
  127. mse = mean_squared_error(y_test, y_pred)
  128. mse_values.append(mse)
  129. # Save weights as NIfTI for each fold (as per original code)
  130. weights_mean = pls.x_weights_[:, 0].reshape(n_voxels, -1).mean(axis=1) # LV1 weights
  131. output_filepath = os.path.join(RESULTS_DIR, f'weights_QSM_KS_{qsm_subjects[test_index[0]]}_LV1.nii.gz')
  132. save_voxel_weights_as_nifti(weights_mean, ref_image_path, output_filepath)
  133. # Get latent variables for training data (to plot scatter)
  134. x_latent_train, y_latent_train = pls.transform(X_train, y_train)
  135. x_latent_scores_all.append(x_latent_train[:, 0])
  136. y_latent_scores_all.append(y_latent_train[:, 0])
  137. # Get latent variables for test data
  138. x_latent_test, y_latent_test = pls.transform(X_test, y_test)
  139. x_latent_test_all.append(x_latent_test[0, :]) # Take all components for test subject
  140. y_latent_test_all.append(y_latent_test[0, :])
  141. test_labels_all.append(labels_test[0])
  142. # Calculate R-squared
  143. if np.var(y_test) != 0:
  144. r2 = r2_score_custom(y_test, y_pred)
  145. r2_values.append(r2)
  146. # print(f" R2 for test subject {qsm_subjects[test_index[0]]}: {r2:.4f}")
  147. else:
  148. print(f" Variance of y_test is zero for subject {qsm_subjects[test_index[0]]}, skipping R2 calculation.")
  149. print(f"\nMean MSE: {np.mean(mse_values):.4f} ? {stats.sem(mse_values):.4f}")
  150. print(f"Mean R2: {np.mean(r2_values):.4f} ? {stats.sem(r2_values):.4f}")
  151. return {
  152. 'pls_model_final': pls, # Keep the last fitted model for full data loadings
  153. 'mse_values': mse_values,
  154. 'r2_values': r2_values,
  155. 'x_latent_scores_all': np.array(x_latent_scores_all),
  156. 'y_latent_scores_all': np.array(y_latent_scores_all),
  157. 'x_latent_test_all': np.array(x_latent_test_all),
  158. 'y_latent_test_all': np.array(y_latent_test_all),
  159. 'test_labels_all': np.array(test_labels_all)
  160. }
  161. def plot_pls_scores_and_mse(results, qsm_subjects, output_path):
  162. """
  163. Plots the PLS latent variable scores for training and test data,
  164. including MSE for each fold.
  165. """
  166. num_folds = len(results['mse_values'])
  167. fig, axs = plt.subplots(int(np.ceil(num_folds / 2)), 2, figsize=(10, num_folds * 2.5))
  168. axs = axs.flat
  169. for i in range(num_folds):
  170. ax = axs[i]
  171. # Plot training data
  172. scatter = ax.scatter(results['x_latent_scores_all'][i], results['y_latent_scores_all'][i],
  173. c=results['test_labels_all'][i], cmap='viridis', alpha=0.9, vmin=1, vmax=3) # Use test labels as a proxy for color
  174. # Plot test data
  175. ax.scatter(results['x_latent_test_all'][i, 0], results['y_latent_test_all'][i, 0],
  176. edgecolor='red', facecolor='none', marker='o', s=100, linewidth=2)
  177. ax.scatter(results['x_latent_test_all'][i, 0], results['y_latent_test_all'][i, 0],
  178. c=[results['test_labels_all'][i]], cmap='viridis', vmin=1, vmax=3, s=40)
  179. ax.set_xlabel('X Score (LV 1)')
  180. ax.set_ylabel('Y Score (LV 1)')
  181. ax.set_title(f'Test Subject: {qsm_subjects[i]}, Stage: {int(results["test_labels_all"][i])}, MSE: {results["mse_values"][i]:.2f}')
  182. plt.tight_layout()
  183. plt.savefig(output_path, dpi=600)
  184. plt.close()
  185. print(f"PLS scores plot saved to {output_path}")
  186. def plot_behavioral_loadings(pls_model, behav_columns, output_path):
  187. """
  188. Plots the PLS loadings on behavioral features for LV1 and LV2.
  189. """
  190. lv1_weights = pls_model.y_weights_[:, 0]
  191. lv2_weights = pls_model.y_weights_[:, 1]
  192. loading_df = pd.DataFrame({
  193. 'Behavior': behav_columns,
  194. 'LV1': lv1_weights,
  195. 'LV2': lv2_weights
  196. })
  197. fig, ax = plt.subplots(figsize=(10, 6))
  198. bar_width = 0.35
  199. index = np.arange(len(behav_columns))
  200. ax.bar(index, loading_df['LV1'], bar_width, label='LV1', color='skyblue')
  201. ax.bar(index + bar_width, loading_df['LV2'], bar_width, label='LV2', color='orange')
  202. ax.set_xlabel('Behavioral Features')
  203. ax.set_ylabel('Loading Weight')
  204. ax.set_title('PLSR Loadings on Behavioral Features')
  205. ax.set_xticks(index + bar_width / 2)
  206. ax.set_xticklabels(behav_columns, rotation=45, ha='right')
  207. ax.legend()
  208. plt.tight_layout()
  209. plt.savefig(output_path, dpi=300)
  210. plt.close()
  211. print(f"Behavioral loadings plot saved to {output_path}")
  212. def plot_subject_lv_projection(results, qsm_subjects, output_path, kings_stage_labels):
  213. """
  214. Plots each subject's projection in LV space (LV1 vs LV2 for fMRI data).
  215. """
  216. fig, ax = plt.subplots(figsize=(8, 6))
  217. # Create a mapping for King Stage to colors
  218. unique_stages = sorted(list(np.unique(kings_stage_labels)))
  219. colors = plt.cm.viridis(np.linspace(0, 1, len(unique_stages)))
  220. stage_to_color = {stage: colors[i] for i, stage in enumerate(unique_stages)}
  221. for i in range(len(results['x_latent_test_all'])):
  222. subj_name = qsm_subjects[i]
  223. lv1_fmri = results['x_latent_test_all'][i, 0]
  224. lv2_fmri = results['x_latent_test_all'][i, 1]
  225. king_stage = int(kings_stage_labels[i]) # Ensure king stage is integer for indexing
  226. # Use the stage_to_color map
  227. color = stage_to_color[king_stage]
  228. ax.scatter(lv1_fmri, lv2_fmri, c=color, label=f'Stage {king_stage}' if subj_name == qsm_subjects[0] else '', alpha=0.8) # Label once
  229. ax.text(lv1_fmri, lv2_fmri, subj_name, fontsize=8, ha='right', va='bottom')
  230. # Create a single legend from the stage_to_color map
  231. handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=stage_to_color[stage], markersize=10, label=f'Stage {int(stage)}')
  232. for stage in unique_stages]
  233. ax.legend(handles=handles, title="King Stage")
  234. ax.axhline(0, color='gray', linestyle='--', linewidth=0.5)
  235. ax.axvline(0, color='gray', linestyle='--', linewidth=0.5)
  236. ax.set_xlabel('fMRI Score (LV1)')
  237. ax.set_ylabel('fMRI Score (LV2)')
  238. ax.set_title('Subject Projection in LV Space (QSM)')
  239. plt.tight_layout()
  240. plt.savefig(output_path, dpi=300)
  241. plt.close()
  242. print(f"Subject LV projection plot saved to {output_path}")
  243. def project_lv_to_brain(pls_model, qsm_data, mask_img, subjects, results_dir):
  244. """
  245. Projects LV weights onto individual subject QSM data and saves
  246. the resulting brain images.
  247. """
  248. lv1_weights = pls_model.x_weights_[:, 0]
  249. lv2_weights = pls_model.x_weights_[:, 1]
  250. for i, sub in enumerate(subjects):
  251. subject_data = qsm_data[i] # Already masked data
  252. # Projection score = element-wise product of QSM data and LV weights
  253. # Original logic: LV_weights * (dot product of subject_data and LV_weights)
  254. # This will scale the LV_weights by a scalar projection value.
  255. proj_lv1_map = lv1_weights * np.dot(subject_data, lv1_weights)
  256. proj_lv2_map = lv2_weights * np.dot(subject_data, lv2_weights)
  257. img_lv1 = masking.unmask(proj_lv1_map, mask_img)
  258. img_lv2 = masking.unmask(proj_lv2_map, mask_img)
  259. nib.save(img_lv1, os.path.join(results_dir, f'LV1_brain_projection_{sub}.nii.gz'))
  260. nib.save(img_lv2, os.path.join(results_dir, f'LV2_brain_projection_{sub}.nii.gz'))
  261. print(f"Saved LV1 & LV2 projections to brain space for {sub}")
  262. def plot_brain_projections(subjects, results_dir, mask_path, fig_dir):
  263. """
  264. Plots LV1 and LV2 brain projections on a surface mesh.
  265. """
  266. mask_img = nib.load(mask_path)
  267. ref_image_for_plotting = nib.load(mask_path) # Use mask as reference for plotting affine
  268. surf_mesh = 'fsaverage5'
  269. # Adjusted threshold as it was very low in original, possibly indicating it needed to be unmasked more appropriately
  270. # or the projections are very small. Setting a reasonable default.
  271. threshold = 0.0001 # This might need tuning based on your actual projected values
  272. for sub in subjects:
  273. lv1_path = os.path.join(results_dir, f'LV1_brain_projection_{sub}.nii.gz')
  274. lv2_path = os.path.join(results_dir, f'LV2_brain_projection_{sub}.nii.gz')
  275. if not os.path.exists(lv1_path) or not os.path.exists(lv2_path):
  276. print(f"Skipping plotting for {sub}: projection NIfTI files not found.")
  277. continue
  278. img_lv1 = nib.load(lv1_path)
  279. img_lv2 = nib.load(lv2_path)
  280. # Plot LV1
  281. plotting.plot_img_on_surf(
  282. img_lv1,
  283. surf_mesh=surf_mesh,
  284. views=['lateral', 'medial'],
  285. title=f'LV1 Brain Projection: {sub}',
  286. colorbar=True,
  287. threshold=threshold
  288. )
  289. plt.savefig(os.path.join(fig_dir, f'LV1_brain_projection_surf_{sub}.png'))
  290. plt.close()
  291. # Plot LV2
  292. plotting.plot_img_on_surf(
  293. img_lv2,
  294. surf_mesh=surf_mesh,
  295. views=['lateral', 'medial'],
  296. title=f'LV2 Brain Projection: {sub}',
  297. colorbar=True,
  298. threshold=threshold
  299. )
  300. plt.savefig(os.path.join(fig_dir, f'LV2_brain_projection_surf_{sub}.png'))
  301. plt.close()
  302. print(f"Generated surface plots for {sub}")
  303. def get_regional_lv_means(subjects, lv_paths_list, hand_mask, foot_mask, tongue_mask, ref_img_path):
  304. """
  305. Calculates and returns mean LV weights for Hand, Foot, and Tongue regions for each subject.
  306. """
  307. hand_means, foot_means, tongue_means = [], [], []
  308. for path in lv_paths_list:
  309. hand_means.append(extract_mean_weight_from_roi(path, hand_mask, ref_img_path))
  310. foot_means.append(extract_mean_weight_from_roi(path, foot_mask, ref_img_path))
  311. tongue_means.append(extract_mean_weight_from_roi(path, tongue_mask, ref_img_path))
  312. df_regional_means = pd.DataFrame({
  313. "Subject": subjects,
  314. "Hand": hand_means,
  315. "Foot": foot_means,
  316. "Tongue": tongue_means
  317. })
  318. return df_regional_means
  319. def plot_per_subject_region_weights(lv1_df, lv2_df, output_path):
  320. """
  321. Plots per-subject region weights for LV1 and LV2.
  322. """
  323. n_subjects = len(lv1_df)
  324. subjects_labels = [f"Sub-{i+1}" for i in range(n_subjects)]
  325. x = np.arange(n_subjects)
  326. bar_width = 0.2
  327. fig, axs = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
  328. # LV1 plot
  329. axs[0].bar(x - bar_width, lv1_df['Hand'], width=bar_width, label="Hand", color='red', alpha=0.7)
  330. axs[0].bar(x, lv1_df['Foot'], width=bar_width, label="Foot", color='green', alpha=0.7)
  331. axs[0].bar(x + bar_width, lv1_df['Tongue'], width=bar_width, label="Tongue", color='blue', alpha=0.7)
  332. axs[0].set_title("LV1 Region Weights")
  333. axs[0].set_xticks(x)
  334. axs[0].set_xticklabels(subjects_labels, rotation=45, ha='right')
  335. axs[0].set_ylabel("Mean Weight")
  336. axs[0].legend()
  337. # LV2 plot
  338. axs[1].bar(x - bar_width, lv2_df['Hand'], width=bar_width, label="Hand", color='red', alpha=0.7)
  339. axs[1].bar(x, lv2_df['Foot'], width=bar_width, label="Foot", color='green', alpha=0.7)
  340. axs[1].bar(x + bar_width, lv2_df['Tongue'], width=bar_width, label="Tongue", color='blue', alpha=0.7)
  341. axs[1].set_title("LV2 Region Weights")
  342. axs[1].set_xticks(x)
  343. axs[1].set_xticklabels(subjects_labels, rotation=45, ha='right')
  344. axs[1].legend()
  345. fig.suptitle("Per-Subject Region Weights for LV1 and LV2 (X as QSM)", fontsize=16)
  346. plt.tight_layout(rect=[0, 0, 1, 0.95])
  347. plt.savefig(output_path, dpi=300)
  348. plt.close()
  349. print(f"Per-subject region weights plot saved to {output_path}")
  350. def plot_average_region_weights(lv1_df, lv2_df, output_path):
  351. """
  352. Plots average region weights for LV1 and LV2 across all subjects.
  353. """
  354. labels = ["Hand", "Foot", "Tongue"]
  355. lv1_means = [lv1_df['Hand'].mean(), lv1_df['Foot'].mean(), lv1_df['Tongue'].mean()]
  356. lv2_means = [lv2_df['Hand'].mean(), lv2_df['Foot'].mean(), lv2_df['Tongue'].mean()]
  357. x = np.arange(len(labels))
  358. bar_width = 0.35
  359. fig, ax = plt.subplots(figsize=(7, 6))
  360. ax.bar(x - bar_width/2, lv1_means, width=bar_width, label='LV1', color='skyblue')
  361. ax.bar(x + bar_width/2, lv2_means, width=bar_width, label='LV2', color='orange')
  362. ax.set_xticks(x)
  363. ax.set_xticklabels(labels, fontsize=14, fontweight='bold')
  364. ax.set_ylabel("Mean Weight", fontsize=14, fontweight='bold')
  365. ax.set_xlabel("Region", fontsize=14, fontweight='bold')
  366. ax.set_title("Average Region Weights Across Subjects (X as QSM values)", fontsize=16, fontweight='bold')
  367. ax.legend(fontsize=12)
  368. plt.tight_layout()
  369. plt.savefig(output_path, dpi=300)
  370. plt.close()
  371. print(f"Average region weights plot saved to {output_path}")
  372. def analyze_correlations_with_behavior(df_behav, lv_weights_df, als_qsm_subjects, results_dir):
  373. """
  374. Performs Spearman correlation analysis between LV region weights and behavioral scores.
  375. """
  376. features_mapping = {
  377. 'Hand': ['ALSFRS 4 Handwriting', 'ALSFRS 5a cutting food'],
  378. 'Foot': ['ALSFRS 8 walking', 'ALSFRS 9 climbing stairs'],
  379. 'Tongue': ['ALSFRS 3 Swallow', 'ALSFRS 1 Language']
  380. }
  381. results = []
  382. for lv_label in ['LV1', 'LV2']:
  383. for region, behav_features in features_mapping.items():
  384. # Get the correct column name from the df_regional_means (e.g., Hand, Foot, Tongue)
  385. region_weights = lv_weights_df[f"{region}_{lv_label}"]
  386. for feat in behav_features:
  387. if feat in df_behav.columns:
  388. # Align behavioral scores with the subjects present in QSM data
  389. # Ensure behavioral scores match the length of region_weights (which should be len(ALS_QSM_SUBJECTS))
  390. # df_behav is already filtered for ALS_QSM_SUBJECTS
  391. behav_scores = df_behav[feat].values[:len(region_weights)] # Slice to match subjects if needed
  392. rho, pval = spearmanr(region_weights, behav_scores)
  393. results.append({
  394. 'LV': lv_label,
  395. 'Region': region,
  396. 'Feature': feat,
  397. 'Spearman ?': rho,
  398. 'p-value': pval
  399. })
  400. df_stats = pd.DataFrame(results)
  401. df_stats = df_stats.sort_values('p-value').round(4)
  402. print("\n--- Spearman Correlation Results (QSM LV Weights vs. Behavioral Scores) ---")
  403. print(df_stats)
  404. df_stats.to_csv(os.path.join(results_dir, "QSM_LV_behavioral_correlations.csv"), index=False)
  405. print(f"Correlation results saved to {os.path.join(results_dir, 'QSM_LV_behavioral_correlations.csv')}")
  406. return df_stats
  407. def compare_qsm_fmri_weights(df_qsm, df_fmri, results_dir):
  408. """
  409. Compares Z-scored QSM and fMRI regional LV weights using correlation and t-tests.
  410. Plots per-subject comparison.
  411. """
  412. # Ensure dataframes are sorted by subject for correct pairing
  413. df_fmri = df_fmri.sort_values(by="Subject").reset_index(drop=True)
  414. df_qsm = df_qsm.sort_values(by="Subject").reset_index(drop=True)
  415. # Z-score all numerical columns (excluding 'Subject')
  416. for df in [df_fmri, df_qsm]:
  417. for col in df.columns[1:]:
  418. df[col] = stats.zscore(df[col])
  419. corr_results = []
  420. ttest_results = []
  421. regions = ['Hand', 'Foot', 'Tongue']
  422. lvs = ['LV1', 'LV2']
  423. for lv in lvs:
  424. for region in regions:
  425. col_name = f"{region}_{lv}"
  426. if col_name in df_qsm.columns and col_name in df_fmri.columns:
  427. qsm_vals = df_qsm[col_name].values
  428. fmri_vals = df_fmri[col_name].values
  429. # Pearson and Spearman correlations
  430. r, p_r = pearsonr(qsm_vals, fmri_vals)
  431. rho, p_rho = spearmanr(qsm_vals, fmri_vals)
  432. corr_results.append({
  433. "Region": region, "LV": lv,
  434. "Pearson r": r, "Pearson p": p_r,
  435. "Spearman ?": rho, "Spearman p": p_rho
  436. })
  437. # Paired t-test
  438. t_stat, p_val = ttest_rel(qsm_vals, fmri_vals)
  439. ttest_results.append({
  440. 'Region': region, 'LV': lv,
  441. 't-statistic': t_stat, 'p-value': p_val
  442. })
  443. else:
  444. print(f"Warning: Column '{col_name}' not found in both QSM and fMRI dataframes for comparison.")
  445. df_corr = pd.DataFrame(corr_results).round(4)
  446. df_ttest = pd.DataFrame(ttest_results).round(4)
  447. print("\n--- QSM vs fMRI LV Regional Weight Correlations ---")
  448. print(df_corr)
  449. df_corr.to_csv(os.path.join(results_dir, "QSM_fMRI_LV_regional_correlations.csv"), index=False)
  450. print("\n--- QSM vs fMRI LV Regional Weight Paired t-tests ---")
  451. print(df_ttest)
  452. df_ttest.to_csv(os.path.join(results_dir, "QSM_fMRI_LV_regional_ttests.csv"), index=False)
  453. # Plotting comparison
  454. fig, axs = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
  455. for i, lv in enumerate(lvs):
  456. ax = axs[i]
  457. # Collect handles for unique legend
  458. qsm_handle = None
  459. fmri_handle = None
  460. for region in regions:
  461. col_name = f"{region}_{lv}"
  462. if col_name in df_qsm.columns and col_name in df_fmri.columns:
  463. qsm_vals = df_qsm[col_name].values
  464. fmri_vals = df_fmri[col_name].values
  465. x_vals = np.arange(len(df_qsm)) # one x per subject
  466. for j in range(len(x_vals)):
  467. ax.plot([x_vals[j] - 0.1, x_vals[j] + 0.1], [qsm_vals[j], fmri_vals[j]], color='gray', alpha=0.5, zorder=1)
  468. p1 = ax.scatter(x_vals[j] - 0.1, qsm_vals[j], color='steelblue', zorder=2)
  469. p2 = ax.scatter(x_vals[j] + 0.1, fmri_vals[j], color='darkorange', zorder=2)
  470. if j == 0 and region == regions[0]: # Assign handle once for the legend
  471. qsm_handle = p1
  472. fmri_handle = p2
  473. ax.set_title(f"{lv} Region Weights Comparison")
  474. ax.set_xticks(x_vals)
  475. ax.set_xticklabels(df_qsm["Subject"], rotation=45, ha='right')
  476. ax.set_ylabel("Z-scored Mean Weight")
  477. if qsm_handle and fmri_handle:
  478. ax.legend([qsm_handle, fmri_handle], ['QSM', 'fMRI'], title="Modality")
  479. fig.suptitle("Per-Subject QSM vs fMRI Region Weights (LV1 and LV2)", fontsize=16)
  480. plt.tight_layout(rect=[0, 0, 1, 0.95])
  481. plt.savefig(os.path.join(FIGURES_DIR, "QSM_vs_fMRI_per_subject_comparison_LV1_LV2.png"), dpi=300)
  482. plt.close()
  483. print(f"QSM vs fMRI comparison plot saved to {os.path.join(FIGURES_DIR, 'QSM_vs_fMRI_per_subject_comparison_LV1_LV2.png')}")
  484. # --- Main Execution ---
  485. if __name__ == "__main__":
  486. print("--- Starting PLS Regression Analysis ---")
  487. # 1. Load and prepare data
  488. df_als_behav_filtered, king_stage_labels = load_behavioral_data(
  489. ALS_BEHAV_PATH_OLD, ALS_BEHAV_PATH_NEW, ALS_QSM_SUBJECTS
  490. )
  491. # Filter king_stage_labels to match ALS_QSM_SUBJECTS
  492. # To ensure correct pairing, we should get labels directly from filtered behavioral df.
  493. king_stage_labels = df_als_behav_filtered['King Stage'].values
  494. # Select only the ALSFRS columns for Y data
  495. alsfrs_columns = [col for col in df_als_behav_filtered.columns if 'ALSFRS' in col]
  496. Y_data_raw = df_als_behav_filtered[alsfrs_columns].values
  497. Y_data = np.nan_to_num(stats.zscore(Y_data_raw, axis=0)) # Z-score behavioral data
  498. QSM_data = load_qsm_data(QSM_ALS_DIR, MASK_PATH, ALS_QSM_SUBJECTS)
  499. X_data = QSM_data.reshape(len(ALS_QSM_SUBJECTS), -1) # Flatten QSM data for PLS
  500. n_voxels = QSM_data.shape[1] # Number of voxels after masking
  501. print(f"Loaded {len(ALS_QSM_SUBJECTS)} ALS subjects with QSM data of shape {QSM_data.shape}.")
  502. print(f"Loaded behavioral data for {len(Y_data)} subjects.")
  503. # 2. Perform PLS Analysis with LOO
  504. pls_results = perform_pls_analysis(
  505. X_data, Y_data, king_stage_labels,
  506. n_components=2, n_voxels=n_voxels,
  507. qsm_subjects=ALS_QSM_SUBJECTS, ref_image_path=REF_IMAGE_PATH
  508. )
  509. # 3. Plot PLS Scores and MSE
  510. plot_pls_scores_and_mse(
  511. pls_results, ALS_QSM_SUBJECTS,
  512. os.path.join(FIGURES_DIR, 'scores_KingStage_QSM.png')
  513. )
  514. # 4. Plot Behavioral Loadings
  515. plot_behavioral_loadings(
  516. pls_results['pls_model_final'], alsfrs_columns, # Pass the behavioral columns used
  517. os.path.join(FIGURES_DIR, 'PLS_R_Behavioral_Loadings_LV1_LV2.png')
  518. )
  519. # 5. Save and Plot Subject LV Projections
  520. lv_df = pd.DataFrame({
  521. 'Subject': ALS_QSM_SUBJECTS,
  522. 'LV1_fMRI': [x[0] for x in pls_results['x_latent_test_all']], # Extract LV1
  523. 'LV2_fMRI': [x[1] for x in pls_results['x_latent_test_all']], # Extract LV2
  524. 'LV1_Behavior': [y[0] for y in pls_results['y_latent_test_all']],
  525. 'LV2_Behavior': [y[1] for y in pls_results['y_latent_test_all']],
  526. 'KingStage': pls_results['test_labels_all']
  527. })
  528. lv_df.to_csv(os.path.join(FIGURES_DIR, "subject_LV1_LV2_scores.csv"), index=False)
  529. print(f"Subject LV scores saved to {os.path.join(FIGURES_DIR, 'subject_LV1_LV2_scores.csv')}")
  530. plot_subject_lv_projection(
  531. pls_results, ALS_QSM_SUBJECTS,
  532. os.path.join(FIGURES_DIR, 'LV1_LV2_subject_projection_QSM.png'),
  533. king_stage_labels # Pass the actual King Stage labels for coloring
  534. )
  535. # 6. Project LV weights to brain space and save NIfTI files
  536. # The original code here used `pls` which would be the last fitted model from LOO,
  537. # but `pls_results['pls_model_final']` is more explicit for the full-data model.
  538. project_lv_to_brain(
  539. pls_results['pls_model_final'], QSM_data, nib.load(MASK_PATH),
  540. ALS_QSM_SUBJECTS, RESULTS_DIR
  541. )
  542. # 7. Plot Brain Projections on Surface (requires nilearn.plotting)
  543. # Ensure nilearn is installed and working with its plotting backend
  544. plot_brain_projections(
  545. ALS_QSM_SUBJECTS, RESULTS_DIR, MASK_PATH, FIGURES_DIR
  546. )
  547. # 8. Calculate and Plot Region-wise Mean Weights
  548. lv1_proj_paths = [os.path.join(RESULTS_DIR, f'LV1_brain_projection_{sub}.nii.gz') for sub in ALS_QSM_SUBJECTS]
  549. lv2_proj_paths = [os.path.join(RESULTS_DIR, f'LV2_brain_projection_{sub}.nii.gz') for sub in ALS_QSM_SUBJECTS]
  550. lv1_regional_means_df = get_regional_lv_means(ALS_QSM_SUBJECTS, lv1_proj_paths, HAND_MASK_PATH, FOOT_MASK_PATH, TONGUE_MASK_PATH, REF_IMAGE_PATH)
  551. lv2_regional_means_df = get_regional_lv_means(ALS_QSM_SUBJECTS, lv2_proj_paths, HAND_MASK_PATH, FOOT_MASK_PATH, TONGUE_MASK_PATH, REF_IMAGE_PATH)
  552. # Combine regional means into one DataFrame for easier access in correlations
  553. # Naming convention: Hand_LV1, Foot_LV1, etc.
  554. combined_regional_means_df = pd.DataFrame({'Subject': ALS_QSM_SUBJECTS})
  555. for col in lv1_regional_means_df.columns:
  556. if col != 'Subject':
  557. combined_regional_means_df[f"{col}_LV1"] = lv1_regional_means_df[col]
  558. combined_regional_means_df[f"{col}_LV2"] = lv2_regional_means_df[col]
  559. combined_regional_means_df.to_csv(os.path.join(RESULTS_DIR, "per_subject_LV_weights.csv"), index=False)
  560. print(f"Per-subject LV regional weights saved to {os.path.join(RESULTS_DIR, 'per_subject_LV_weights.csv')}")
  561. plot_per_subject_region_weights(
  562. lv1_regional_means_df, lv2_regional_means_df,
  563. os.path.join(FIGURES_DIR, 'per_subject_LV1_LV2_weights_KS_QSM.png')
  564. )
  565. plot_average_region_weights(
  566. lv1_regional_means_df, lv2_regional_means_df,
  567. os.path.join(FIGURES_DIR, 'mean_LV1_LV2_region_weights_QSM.png')
  568. )
  569. # 9. Analyze Correlations with Behavioral Data
  570. analyze_correlations_with_behavior(
  571. df_als_behav_filtered, combined_regional_means_df, ALS_QSM_SUBJECTS, RESULTS_DIR
  572. )
  573. # 10. Compare QSM vs fMRI Weights (if fMRI data is available)
  574. # This part requires df_fmri to be present.
  575. # The original script assumes 'per_subject_LV_weights_ECM.csv' exists.
  576. # If this file is generated by another script, ensure it's run first.
  577. fmri_weights_path = os.path.join(BASE_DIR, "results", "per_subject_LV_weights_ECM.csv")
  578. if os.path.exists(fmri_weights_path):
  579. df_fmri_weights = pd.read_csv(fmri_weights_path)
  580. compare_qsm_fmri_weights(
  581. combined_regional_means_df, df_fmri_weights, RESULTS_DIR
  582. )
  583. else:
  584. print(f"\nSkipping QSM vs fMRI comparison: fMRI weights file not found at {fmri_weights_path}")
  585. print("\n--- PLS Regression Analysis Complete ---")

PLSR_QSM.py at commit 4543945, no license · at the source

Overview

Authors: Avinash Kalyani1,2,3,4, Alicia Northall1,5, Stefanie Schreiber2,6, Jascha Brüggemann7, Stefan Vielhaber6, Marwa Al Dubai6, Abrar Bin Ramadan6, Hendrik Mattern2,7,8, Oliver Speck2,7,8,9,10, Christoph Reichert9, Esther Kuehn1,3,4,8
  1. Institute for Cognitive Neurology and Dementia Research (IKND), Otto-von-Guericke University Magdeburg, Magdeburg 39120, Germany
  2. German Center for Neurodegenerative Diseases (DZNE), Magdeburg 39120, Germany
  3. Hertie Institute for Clinical Brain Research (HIH), Tübingen 72076, Germany
  4. German Center for Neurodegenerative Diseases (DZNE), Tübingen 72076, Germany
  5. Nuffield Department of Clinical Neurosciences, University of Oxford, Oxford OX3 9DU, United Kingdom
  6. Clinic for Neurology, Otto-von-Guericke University Magdeburg, Magdeburg 39120, Germany
  7. Department Biomedical Magnetic Resonance (BMMR), Otto-von-Guericke University Magdeburg, Magdeburg 39120, Germany
  8. Center for Behavioral Brain Sciences (CBBS) Magdeburg, Magdeburg 39120, Germany
  9. Leibniz Institute for Neurobiology (LIN), Otto-von-Guericke University Magdeburg, Magdeburg 39120, Germany
  10. Research Campus STIMULATE, Otto von Guericke University Magdeburg, Magdeburg 39106, Germany
Journal: Brain communications, volume 8, issue 2, article fcag127
Dates: received 15 January 2025; accepted 8 April 2026; published online 9 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1093/braincomms/fcag127 · PMID 42058282 · PMCID PMC13122844 · OpenAlex W7153843825
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism), other condition (population), systems (subfield)
Methods: Spectral & time-frequency, Statistics, Machine learning, Preprocessing, fMRI & imaging, Smoothing, state filtering, decompositions
Keywords: amyotrophic lateral sclerosis, PLSR, sensorimotor cortex, 7T-fMRI, disease progression
Topic: Amyotrophic Lateral Sclerosis Research (Neurology, Medicine), according to OpenAlex
Funding: Deutsche Forschungsgemeinschaft (MA 9235/3-1/SCHR 1418/5-1, 501214112, SFB 1436 Z02, 425899996); Else Kröner Fresenius Stiftung (2019-A03)
Citations: not cited yet (Europe PMC); 47 references in the paper

Abstract

Amyotrophic lateral sclerosis (ALS) is a progressive neurodegenerative disease characterized by the loss of motor neurons in primary motor cortex, leading to muscle weakness, atrophy and death within a median of 3 years. Even though ALS is characterized by different disease subtypes affecting different body parts, individualized phenotyping of functional ALS pathology has so far not been achieved. We recorded 7 Tesla functional MRI data while ALS patients and matched controls moved affected and non-affected body parts in the MR scanner. We applied robust Shared Response Modelling for capturing ALS-specific shared responses for group classification, and Partial Least Squares regression for relating the latent variables to clinical subtypes and the degree of disease progression. We show that disease onset and severity can be best modelled by functional connectivity rather than local activation changes. We also show that functional disease-defining information in primary motor cortex is not the strongest in the area that is behaviourally first-affected, deviating from the behavioural phenotype of the patients. When computing the model’s weight distribution of the King stage classification and projecting them back into voxel space, the highest mean weights are present in the foot and tongue/face regions. Our data highlight the importance of 7 Tesla functional MRI task-based functional connectivity measures for classifying ALS patients in addition to structural readouts and provides evidence that a 7 Tesla functional MRI can be used for identifying a disease signature of each individual ALS patient.

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 11 matches between paragraphs and lines of code.

avinashkalyani/ALS_PLSr

License: none: the authors keep all their rights
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 4543945228adb31aaf0d69ec57b493dffaca05c7, 24 July 2025
Languages: Python (11), MATLAB (2), Shell (1)
Size: 15 files, 14 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (8 files), SciPy (8 files), Matplotlib (7 files), NiBabel (7 files), pandas (6 files), Nilearn (5 files), scikit-learn (5 files), FSL (3 files), seaborn (3 files), ANTs (2 files), BrainIAK (2 files), SPM (2 files), AFNI (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
15 files

Code availability

All code used for PLSR-based functional data analysis is available at: https://github.com/avinashkalyani/ALS_PLSr

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

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;
  • 14 scripts, each with its path and the digest of its content;
  • 11 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

Datasets cited

Data availability

fMRI/MRI data will be made available by the first author upon request. The extracted structural data from patients and controls is available at https://github.com/alicianorthall/In-vivo-Pathology-ALS/blob/main/ALS_Data.xlsx

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 1, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 11 authors, 5 keywords, 2 funders, 41 references.

Cite

This paper

Kalyani, A., Northall, A., Schreiber, S., Brüggemann, J., Vielhaber, S., Al Dubai, M., Bin Ramadan, A., Mattern, H., Speck, O., Reichert, C., & Kuehn, E. (2026). Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex. Brain communications, 8(2), fcag127. https://doi.org/10.1093/braincomms/fcag127

BibTeX

@article{kalyani2026individualized,
author = {Kalyani, Avinash and Northall, Alicia and Schreiber, Stefanie and Brüggemann, Jascha and Vielhaber, Stefan and Al Dubai, Marwa and Bin Ramadan, Abrar and Mattern, Hendrik and Speck, Oliver and Reichert, Christoph and Kuehn, Esther},
title = {{Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex}},
journal = {Brain communications},
year = {2026},
month = apr,
volume = {8},
number = {2},
pages = {fcag127},
publisher = {Oxford University Press},
issn = {2632-1297},
doi = {10.1093/braincomms/fcag127},
url = {https://doi.org/10.1093/braincomms/fcag127},
pmid = {42058282},
pmcid = {PMC13122844}
}

RIS

TY - JOUR
AU - Kalyani, Avinash
AU - Northall, Alicia
AU - Schreiber, Stefanie
AU - Brüggemann, Jascha
AU - Vielhaber, Stefan
AU - Al Dubai, Marwa
AU - Bin Ramadan, Abrar
AU - Mattern, Hendrik
AU - Speck, Oliver
AU - Reichert, Christoph
AU - Kuehn, Esther
TI - Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex
T2 - Brain communications
J2 - Brain Commun
PY - 2026
DA - 2026/04/09
VL - 8
IS - 2
SP - fcag127
SN - 2632-1297
PB - Oxford University Press
DO - 10.1093/braincomms/fcag127
UR - https://doi.org/10.1093/braincomms/fcag127
LA - en
ER -

CSL-JSON

{
"id": "10.1093/braincomms/fcag127",
"type": "article-journal",
"title": "Individualized phenotyping of functional amyotrophic lateral sclerosis pathology in sensorimotor cortex",
"container-title": "Brain communications",
"author": [
{
"family": "Kalyani",
"given": "Avinash"
},
{
"family": "Northall",
"given": "Alicia"
},
{
"family": "Schreiber",
"given": "Stefanie"
},
{
"family": "Brüggemann",
"given": "Jascha"
},
{
"family": "Vielhaber",
"given": "Stefan"
},
{
"family": "Al Dubai",
"given": "Marwa"
},
{
"family": "Bin Ramadan",
"given": "Abrar"
},
{
"family": "Mattern",
"given": "Hendrik"
},
{
"family": "Speck",
"given": "Oliver"
},
{
"family": "Reichert",
"given": "Christoph"
},
{
"family": "Kuehn",
"given": "Esther"
}
],
"container-title-short": "Brain Commun",
"volume": "8",
"issue": "2",
"page": "fcag127",
"DOI": "10.1093/braincomms/fcag127",
"PMID": "42058282",
"PMCID": "PMC13122844",
"ISSN": "2632-1297",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/braincomms/fcag127",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
9
]
]
}
}

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.70542 [code]
The In Vivo Microstructural Profile of Human Hippocampal Subfield CA1 and Its Relation to Memory Performance.
Journal: Human brain mapping
In common: NiBabel, pandas, SciPy, 2 other tools, 4 references, 2 authors
[2] doi:10.1371/journal.pbio.3003856 [code]
Aging and metabolism contribute separately to brain-body health.
Journal: PLoS biology
In common: AFNI, ANTs, FSL, 9 other tools, 2 references
[3] doi:10.1038/s41467-026-73668-y [code]
Convergent and divergent brain-cognition development in early adolescence.
Journal: Nature communications
In common: AFNI, ANTs, FSL, 9 other tools, fMRI
[4] doi:10.1002/epi.70323 [code]
Individual-specific resting-state networks predict language dominance in drug-resistant epilepsy.
Journal: Epilepsia
In common: AFNI, ANTs, FSL, 7 other tools, fMRI, 1 reference
[5] doi:10.1162/imag.a.1222 [code]
Network-based near-scalp personalized brain stimulation targets.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: AFNI, ANTs, FSL, 7 other tools, systems, 1 reference
[6] doi:10.21203/rs.3.rs-9326213/v1 [code]
Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brain
Journal: Research Square (preprint)
In common: ANTs, FSL, Nilearn, 8 other tools, fMRI, 1 reference
[7] doi:10.1038/s41467-026-76452-0 [code]
Music evokes shared neural representations of imagined narratives across sensory modalities.
Journal: Nature communications
In common: AFNI, ANTs, FSL, 9 other tools
[8] doi:10.1016/j.xcrm.2026.102943 [code]
Parent-of-origin effects in Alzheimer's liability dissociate neurocognitive and cardiovascular traits in at-risk individuals.
Journal: Cell reports. Medicine
In common: AFNI, ANTs, FSL, 9 other tools
[9] doi:10.1038/s41398-026-04025-2 [code]
Brain energetic landscapes shape state dysregulation in major depressive disorder: a morphological network controllability perspective.
Journal: Translational psychiatry
In common: AFNI, ANTs, FSL, 9 other tools
[10] 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: AFNI, ANTs, FSL, 9 other tools

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.