OSCR

Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs.

Code ↔ Paper

8 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 8 matches
  1. [1] § Materials and Methods › Predictive models ↔ LSVR_model.py, lines 37–114 · score 0.92 · temporal weight vector, Gram matrix, dual variables, Longitudinal Support Vector, temporal trends, insensitive
  2. [2] § Materials and Methods › Predictive models ↔ LSVR_model.py, lines 37–114 · score 0.81 · temporal trend vector, dual coefficients, dual variables, support vectors, QP, linear
  3. [3] § Materials and Methods › Predictive models ↔ LSVC_model.py, lines 95–176 · score 0.74 · temporal trend vector, dual coefficients, support vectors, QP, linear, iterative
  4. [4] § Materials and Methods › Predictive models ↔ LSVC_model.py, lines 27–93 · score 0.73 · Gram matrix, Longitudinal Support Vector, temporal trends, QP, dual, model
  5. [5] § Results › Prediction results › BCI performance prediction ↔ LSVR_model.py, lines 567–648 · score 0.70 · Square Error, LSVR model, continuous predicted, BCI score, RMSE, shuffled
  6. [6] § Materials and Methods › Predictive models ↔ LSVC_model.py, lines 27–93 · score 0.63 · Longitudinal Support Vector, temporal trend, LSVC, Classifier, model, matrix
  7. [7] § Materials and Methods › Predictive models ↔ LSVC_model.py, lines 95–176 · score 0.56 · dual coefficients, LSVC, linear, iterative, temporal, matrix
  8. [8] § Results › Prediction results ↔ LSVC_model.py, lines 528–573 · score 0.53 · Support Vector Classifier, classification model, BCI score, LSVC, longitudinal, predictive

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 · 754 lines · 34 KB · no license · 5 matches

  1. # %%
  2. """
  3. ==============================================================
  4. Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs
  5. Longitudinal Support Vector Classification (LSVC) model
  6. ================================================================
  7. """
  8. # Authors: Camilla Mannino <[email hidden]>
  9. # License: BSD (3-clause)
  10. from qpsolvers import solve_qp
  11. import pickle
  12. from sklearn.metrics import confusion_matrix
  13. import matplotlib.pyplot as plt
  14. from matplotlib.lines import Line2D
  15. from sklearn.model_selection import LeaveOneOut
  16. from sklearn.svm import SVC
  17. from sklearn.linear_model import LogisticRegression
  18. from sklearn.ensemble import RandomForestClassifier
  19. from sklearn.metrics import balanced_accuracy_score
  20. import numpy as np
  21. import random
  22. #%% Functions
  23. def longitudinal_svc(X_list, y_list, C=1.0):
  24. """
  25. Trains a Longitudinal Support Vector Classifier (LSVC) model.
  26. Parameters:
  27. X_list : list of np.ndarray
  28. List of T x p feature matrices per subject.
  29. y_list : list of np.ndarray
  30. List of T-dimensional binary target vectors (0 or 1) per subject.
  31. C : float
  32. Regularization parameter.
  33. Returns:
  34. alpha : np.ndarray
  35. Dual coefficients.
  36. beta : np.ndarray
  37. Temporal trend vector of shape (T,).
  38. """
  39. N = len(X_list)
  40. T = X_list[0].shape[0]
  41. beta = np.ones(T)
  42. # Compute Gram matrix G where G[i,j] = beta^T X_i X_j^T beta
  43. G = np.zeros((N, N))
  44. for i in range(N):
  45. Xi = X_list[i]
  46. for j in range(N):
  47. Xj = X_list[j]
  48. G[i, j] = beta @ Xi @ Xj.T @ beta
  49. # Convert targets to +1/-1
  50. y_bin = np.array([2 * int(np.mean(y_list[i])) - 1 for i in range(N)]) # mean in case T > 1
  51. # Create QP matrices
  52. P = np.outer(y_bin, y_bin) * G
  53. P = (P + P.T) / 2 + 1e-6 * np.eye(N)
  54. q = -np.ones(N)
  55. # Constraints: 0 <= alpha_i <= C
  56. G_qp = np.vstack([np.eye(N), -np.eye(N)])
  57. h_qp = np.hstack([C * np.ones(N), np.zeros(N)])
  58. # Equality constraint: sum alpha_i * y_i = 0
  59. A_qp = y_bin.reshape(1, -1)
  60. b_qp = np.array([0.0])
  61. # Solve QP
  62. alpha = solve_qp(P, q, G_qp, h_qp, A_qp, b_qp, solver='quadprog')
  63. if alpha is None:
  64. raise ValueError("QP solver failed to find a solution!")
  65. # Compute beta from duals
  66. numerator = np.zeros(T)
  67. denominator = np.zeros((T, T))
  68. for i in range(N):
  69. yi = y_bin[i]
  70. Xi = X_list[i]
  71. coeff_i = alpha[i] * yi
  72. numerator += coeff_i * np.mean(y_list[i]) # again averaging if T > 1
  73. for j in range(N):
  74. yj = y_bin[j]
  75. Xj = X_list[j]
  76. coeff_j = alpha[j] * yj
  77. denominator += coeff_i * coeff_j * (Xi @ Xj.T)
  78. beta = np.linalg.solve(denominator + 1e-6 * np.eye(T), numerator)
  79. return alpha, beta, y_bin
  80. def longitudinal_svc_iterative(X_list, y_list, C=1.0, max_iter=200, tol=1e-4):
  81. """
  82. Iterative Longitudinal Support Vector Classifier (LSVC).
  83. Parameters:
  84. X_list : list of np.ndarray
  85. Each entry is a T x p matrix for a subject.
  86. y_list : list of int
  87. Class labels (0 or 1) for each subject.
  88. C : float
  89. Regularization parameter.
  90. max_iter : int
  91. Maximum number of iterations.
  92. tol : float
  93. Convergence tolerance on beta.
  94. Returns:
  95. alpha : np.ndarray
  96. Dual coefficients.
  97. beta : np.ndarray
  98. Temporal trend vector of shape (T,).
  99. """
  100. N = len(X_list)
  101. T = X_list[0].shape[0]
  102. # Binary labels {-1, +1}
  103. # Ensure one label per subject
  104. # If your y_list is a list of arrays of shape (T,) per subject:
  105. if isinstance(y_list[0], (list, np.ndarray)) and len(y_list[0]) > 1:
  106. y_bin = np.array([2 * int(round(np.mean(y))) - 1 for y in y_list])
  107. else:
  108. y_bin = np.array([2 * int(y) - 1 for y in y_list]) # Already per-subject
  109. # Initialize beta
  110. beta = np.ones(T)
  111. beta /= np.linalg.norm(beta)
  112. for it in range(max_iter):
  113. # --- Step 1: Solve QP for alpha given beta ---
  114. G = np.zeros((N, N))
  115. for i in range(N):
  116. Xi = X_list[i]
  117. for j in range(N):
  118. Xj = X_list[j]
  119. G[i, j] = beta @ Xi @ Xj.T @ beta
  120. P = np.outer(y_bin, y_bin) * G
  121. P = (P + P.T) / 2 + 1e-6 * np.eye(N) # Ensure positive semi-definite
  122. q = -np.ones(N)
  123. # Constraints
  124. G_qp = np.vstack([np.eye(N), -np.eye(N)])
  125. h_qp = np.hstack([C * np.ones(N), np.zeros(N)])
  126. A_qp = y_bin.reshape(1, -1)
  127. b_qp = np.array([0.0])
  128. alpha = solve_qp(P, q, G_qp, h_qp, A_qp, b_qp, solver='quadprog')
  129. if alpha is None:
  130. raise ValueError("QP solver failed to converge.")
  131. # --- Step 2: Update beta given alpha ---
  132. numerator = np.zeros(T)
  133. denominator = np.zeros((T, T))
  134. for i in range(N):
  135. ai_yi = alpha[i] * y_bin[i]
  136. Xi = X_list[i]
  137. for j in range(N):
  138. aj_yj = alpha[j] * y_bin[j]
  139. Xj = X_list[j]
  140. denominator += ai_yi * aj_yj * (Xi @ Xj.T)
  141. numerator += ai_yi * (Xi @ np.ones(Xi.shape[1])) # Linear aggregation
  142. # Regularize to ensure invertibility
  143. beta_new = np.linalg.solve(denominator + 1e-6 * np.eye(T), numerator)
  144. beta_new /= np.linalg.norm(beta_new)
  145. # Check convergence
  146. if np.linalg.norm(beta_new - beta) < tol:
  147. break
  148. beta = beta_new
  149. return alpha, beta, y_bin
  150. def predict_lsvc(X_new_list, support_vectors, alpha, beta, y_bin):
  151. preds = []
  152. N = len(support_vectors)
  153. for X_new in X_new_list:
  154. score = 0.0
  155. for i in range(N):
  156. Xi = support_vectors[i]
  157. score += alpha[i] * y_bin[i] * (beta @ X_new @ Xi.T @ beta)
  158. preds.append(1 if score >= 0 else 0)
  159. return np.array(preds)
  160. def vertical_dot_plot(y_true, y_pred, title, save_path=None):
  161. n_subjects = len(y_true)
  162. fig, ax = plt.subplots(figsize=(10, 4))
  163. for i, (yt, yp) in enumerate(zip(y_true, y_pred), start=1):
  164. y_pos = 1 if yt == 1 else 0.5
  165. color = 'green' if yt == yp else 'red'
  166. # Draw the vertical line
  167. ax.plot([i, i], [0, y_pos], color='black', linewidth=1)
  168. # Draw the circle
  169. ax.scatter(i, y_pos, color=color, s=200, zorder=3, edgecolors='black')
  170. # Reference horizontal dashed line at 0.57
  171. ax.axhline(0.57, color='teal', linewidth=2, linestyle='--')
  172. # Axis settings
  173. ax.set_xticks(range(1, n_subjects + 1))
  174. ax.set_xticklabels(range(1, n_subjects + 1))
  175. ax.set_xlabel("Subjects", fontsize=12, fontweight='bold')
  176. ax.set_ylabel("Performance", fontsize=12, fontweight='bold')
  177. ax.set_xlim(0.5, n_subjects + 0.5)
  178. ax.set_ylim(0, 1.5)
  179. ax.set_yticks([])
  180. ax.spines['top'].set_visible(False)
  181. ax.spines['right'].set_visible(False)
  182. # Custom axis arrows
  183. ax.plot([0, 0], [0, 1.5], color='teal', linewidth=2) # Y-axis line
  184. ax.plot([0.5, n_subjects + 0.5], [0, 0], color='teal', linewidth=2) # X-axis line
  185. # Add legend
  186. legend_elements = [
  187. Line2D([0], [0], marker='o', color='w', label='Correctly Classified',
  188. markerfacecolor='green', markersize=10, markeredgecolor='black'),
  189. Line2D([0], [0], marker='o', color='w', label='Miss Classified',
  190. markerfacecolor='red', markersize=10, markeredgecolor='black'),
  191. Line2D([0], [0], color='teal', linestyle='--', linewidth=2, label='Chance Level: 57%')
  192. ]
  193. ax.legend(handles=legend_elements, loc='upper right')
  194. ax.set_title(title, fontsize=14, fontweight='bold')
  195. plt.tight_layout()
  196. if save_path:
  197. plt.savefig(save_path, format='jpg', dpi=300)
  198. plt.show()
  199. #%%
  200. # ==== Step 1: Input Data ====
  201. # Avalanches Length features
  202. with open("Mean_Avalanches_MI_task_feedback_broad_1st_session.pkl", "rb") as f:
  203. subject_avalanche_analysis_MI_concat_1st = pickle.load(f)
  204. with open("Mean_Avalanches_Rest_task_feedback_broad_1st_session.pkl", "rb") as f:
  205. subject_avalanche_analysis_Rest_concat_1st = pickle.load(f)
  206. with open("Mean_Avalanches_MI_task_feedback_broad_2nd_session.pkl", "rb") as f:
  207. subject_avalanche_analysis_MI_concat_2nd = pickle.load(f)
  208. with open("Mean_Avalanches_Rest_task_feedback_broad_2nd_session.pkl", "rb") as f:
  209. subject_avalanche_analysis_Rest_concat_2nd = pickle.load(f)
  210. with open("Mean_Avalanches_MI_task_feedback_broad_3th_session.pkl", "rb") as f:
  211. subject_avalanche_analysis_MI_concat_3th = pickle.load(f)
  212. with open("Mean_Avalanches_Rest_task_feedback_broad_3th_session.pkl", "rb") as f:
  213. subject_avalanche_analysis_Rest_concat_3th = pickle.load(f)
  214. with open("Mean_Avalanches_MI_task_feedback_broad_4th_session.pkl", "rb") as f:
  215. subject_avalanche_analysis_MI_concat_4th = pickle.load(f)
  216. with open("Mean_Avalanches_Rest_task_feedback_broad_4th_session.pkl", "rb") as f:
  217. subject_avalanche_analysis_Rest_concat_4th = pickle.load(f)
  218. #%% Activations Features
  219. mean_activation_1 = np.load('/Users/camilla.mannino/Library/CloudStorage/OneDrive-ICM/paper_preATM/activations_1st&2nd_tot_mean_broad_weighted.pkl', allow_pickle=True)
  220. mean_activation_2 = np.load('/Users/camilla.mannino/Library/CloudStorage/OneDrive-ICM/paper_preATM/activations_3rd&4th_tot_mean_broad_weighted.pkl', allow_pickle=True)
  221. mean_activation_MI_1st = mean_activation_1['activations_MI_1st_tot_mean_broad_weigh']
  222. mean_activation_Rest_1st = mean_activation_1['activations_Rest_1st_tot_mean_broad_weigh']
  223. mean_activation_MI_2nd = mean_activation_1['activations_MI_2nd_tot_mean_broad_weigh']
  224. mean_activation_Rest_2nd = mean_activation_1['activations_Rest_2nd_tot_mean_broad_weigh']
  225. mean_activation_MI_3rd = mean_activation_2['activations_MI_3rd_tot_mean_broad_weigh']
  226. mean_activation_Rest_3rd = mean_activation_2['activations_Rest_3rd_tot_mean_broad_weigh']
  227. mean_activation_MI_4th = mean_activation_2['activations_MI_4th_tot_mean_broad_weigh']
  228. mean_activation_Rest_4th = mean_activation_2['activations_Rest_4th_tot_mean_broad_weigh']
  229. #%% Load Data over a set of selected ROIs
  230. # Avalanches Length Features
  231. #with open("Mean_Avalanches_MI_task_feedback_broad_1st_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  232. # subject_avalanche_analysis_MI_concat_1st = pickle.load(f)
  233. #with open("Mean_Avalanches_Rest_task_feedback_broad_1st_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  234. # subject_avalanche_analysis_Rest_concat_1st = pickle.load(f)
  235. #with open("Mean_Avalanches_MI_task_feedback_broad_2nd_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  236. # subject_avalanche_analysis_MI_concat_2nd = pickle.load(f)
  237. #with open("Mean_Avalanches_Rest_task_feedback_broad_2nd_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  238. # subject_avalanche_analysis_Rest_concat_2nd = pickle.load(f)
  239. #with open("Mean_Avalanches_MI_task_feedback_broad_3rd_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  240. # subject_avalanche_analysis_MI_concat_3th = pickle.load(f)
  241. #with open("Mean_Avalanches_Rest_task_feedback_broad_3rd_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  242. # subject_avalanche_analysis_Rest_concat_3th = pickle.load(f)
  243. #with open("Mean_Avalanches_MI_task_feedback_broad_4th_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  244. # subject_avalanche_analysis_MI_concat_4th = pickle.load(f)
  245. #with open("Mean_Avalanches_Rest_task_feedback_broad_4th_session_selected_ROIs_preATM_normalized.pkl", "rb") as f:
  246. # subject_avalanche_analysis_Rest_concat_4th = pickle.load(f)
  247. # Activations Features
  248. #mean_activation = np.load('/Users/camilla.mannino/Library/CloudStorage/OneDrive-ICM/paper_preATM/activations_tot_mean_broad_selected_ROIs_preATM.pkl', allow_pickle=True)
  249. #mean_activation_MI_1st = mean_activation['activations_MI_1st_tot_mean_broad']
  250. #mean_activation_Rest_1st = mean_activation['activations_Rest_1st_tot_mean_broad']
  251. #mean_activation_MI_2nd = mean_activation['activations_MI_2nd_tot_mean_broad']
  252. #mean_activation_Rest_2nd = mean_activation['activations_Rest_2nd_tot_mean_broad']
  253. #mean_activation_MI_3rd = mean_activation['activations_MI_3rd_tot_mean_broad']
  254. #mean_activation_Rest_3rd = mean_activation['activations_Rest_3rd_tot_mean_broad']
  255. #mean_activation_MI_4th = mean_activation['activations_MI_4th_tot_mean_broad']
  256. #mean_activation_Rest_4th = mean_activation['activations_Rest_4th_tot_mean_broad']
  257. #%% Mean across trials for each subject inside each parameters' combination excluding trials without avalanches
  258. subjects = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
  259. # Helper function to compute mean considering only values > 0
  260. def mean_gt_zero(values):
  261. values_gt_zero = [v for v in values if v > 0]
  262. return np.mean(values_gt_zero) if values_gt_zero else 0
  263. # ---------- First Interval ----------
  264. max_cardinal_order_MI_1st = max(len(subject_avalanche_analysis_MI_concat_1st[subject]) for subject in subjects)
  265. mean_length_array_MI_1st = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_MI_1st)]
  266. for subject_index, subject in enumerate(subjects):
  267. subject_keys = list(subject_avalanche_analysis_MI_concat_1st[subject].keys())
  268. for cardinal_position, key in enumerate(subject_keys):
  269. mean_val = mean_gt_zero(
  270. subject_avalanche_analysis_MI_concat_1st[subject][key]["mean_lengths_per_trial"])
  271. mean_length_array_MI_1st[cardinal_position][subject_index] = mean_val
  272. max_cardinal_order_Rest_1st = max(len(subject_avalanche_analysis_Rest_concat_1st[subject]) for subject in subjects)
  273. mean_length_array_Rest_1st = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_Rest_1st)]
  274. for subject_index, subject in enumerate(subjects):
  275. subject_keys = list(subject_avalanche_analysis_Rest_concat_1st[subject].keys())
  276. for cardinal_position, key in enumerate(subject_keys):
  277. mean_val = mean_gt_zero(
  278. subject_avalanche_analysis_Rest_concat_1st[subject][key]["mean_lengths_per_trial"])
  279. mean_length_array_Rest_1st[cardinal_position][subject_index] = mean_val
  280. # ---------- Second Interval ----------
  281. max_cardinal_order_MI_2nd = max(len(subject_avalanche_analysis_MI_concat_2nd[subject]) for subject in subjects)
  282. mean_length_array_MI_2nd = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_MI_2nd)]
  283. for subject_index, subject in enumerate(subjects):
  284. subject_keys = list(subject_avalanche_analysis_MI_concat_2nd[subject].keys())
  285. for cardinal_position, key in enumerate(subject_keys):
  286. mean_val = mean_gt_zero(
  287. subject_avalanche_analysis_MI_concat_2nd[subject][key]["mean_lengths_per_trial"])
  288. mean_length_array_MI_2nd[cardinal_position][subject_index] = mean_val
  289. max_cardinal_order_Rest_2nd = max(len(subject_avalanche_analysis_Rest_concat_2nd[subject]) for subject in subjects)
  290. mean_length_array_Rest_2nd = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_Rest_2nd)]
  291. for subject_index, subject in enumerate(subjects):
  292. subject_keys = list(subject_avalanche_analysis_Rest_concat_2nd[subject].keys())
  293. for cardinal_position, key in enumerate(subject_keys):
  294. mean_val = mean_gt_zero(
  295. subject_avalanche_analysis_Rest_concat_2nd[subject][key]["mean_lengths_per_trial"])
  296. mean_length_array_Rest_2nd[cardinal_position][subject_index] = mean_val
  297. # ---------- Third Interval ----------
  298. max_cardinal_order_MI_3rd = max(len(subject_avalanche_analysis_MI_concat_3th[subject]) for subject in subjects)
  299. mean_length_array_MI_3rd = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_MI_3rd)]
  300. for subject_index, subject in enumerate(subjects):
  301. subject_keys = list(subject_avalanche_analysis_MI_concat_3th[subject].keys())
  302. for cardinal_position, key in enumerate(subject_keys):
  303. mean_val = mean_gt_zero(
  304. subject_avalanche_analysis_MI_concat_3th[subject][key]["mean_lengths_per_trial"])
  305. mean_length_array_MI_3rd[cardinal_position][subject_index] = mean_val
  306. max_cardinal_order_Rest_3rd = max(len(subject_avalanche_analysis_Rest_concat_3th[subject]) for subject in subjects)
  307. mean_length_array_Rest_3rd = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_Rest_3rd)]
  308. for subject_index, subject in enumerate(subjects):
  309. subject_keys = list(subject_avalanche_analysis_Rest_concat_3th[subject].keys())
  310. for cardinal_position, key in enumerate(subject_keys):
  311. mean_val = mean_gt_zero(
  312. subject_avalanche_analysis_Rest_concat_3th[subject][key]["mean_lengths_per_trial"])
  313. mean_length_array_Rest_3rd[cardinal_position][subject_index] = mean_val
  314. # ---------- Fourth Interval ----------
  315. max_cardinal_order_MI_4th = max(len(subject_avalanche_analysis_MI_concat_4th[subject]) for subject in subjects)
  316. mean_length_array_MI_4th = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_MI_4th)]
  317. for subject_index, subject in enumerate(subjects):
  318. subject_keys = list(subject_avalanche_analysis_MI_concat_4th[subject].keys())
  319. for cardinal_position, key in enumerate(subject_keys):
  320. mean_val = mean_gt_zero(
  321. subject_avalanche_analysis_MI_concat_4th[subject][key]["mean_lengths_per_trial"])
  322. mean_length_array_MI_4th[cardinal_position][subject_index] = mean_val
  323. max_cardinal_order_Rest_4th = max(len(subject_avalanche_analysis_Rest_concat_4th[subject]) for subject in subjects)
  324. mean_length_array_Rest_4th = [np.zeros(len(subjects)) for _ in range(max_cardinal_order_Rest_4th)]
  325. for subject_index, subject in enumerate(subjects):
  326. subject_keys = list(subject_avalanche_analysis_Rest_concat_4th[subject].keys())
  327. for cardinal_position, key in enumerate(subject_keys):
  328. mean_val = mean_gt_zero(
  329. subject_avalanche_analysis_Rest_concat_4th[subject][key]["mean_lengths_per_trial"])
  330. mean_length_array_Rest_4th[cardinal_position][subject_index] = mean_val
  331. #%% Difference avalanches' length computation for all sessions: Avalanches length
  332. mean_length_diff = {}
  333. for session_index, mean_length_array_Rest, mean_length_array_MI in zip(
  334. range(1, 5),
  335. [mean_length_array_Rest_1st, mean_length_array_Rest_2nd, mean_length_array_Rest_3rd, mean_length_array_Rest_4th],
  336. [mean_length_array_MI_1st, mean_length_array_MI_2nd, mean_length_array_MI_3rd, mean_length_array_MI_4th],
  337. ):
  338. mean_length_diff[session_index] = {
  339. cardinal_position: mean_length_array_Rest[cardinal_position] - mean_length_array_MI[cardinal_position]
  340. for cardinal_position in range(len(mean_length_array_Rest))
  341. }
  342. #%% Difference activations computation for all sessions: Activations
  343. mean_activation_diff = {}
  344. for session_index, mean_activation_array_Rest, mean_activation_array_MI in zip(range(1, 5),
  345. [mean_activation_Rest_1st, mean_activation_Rest_2nd, mean_activation_Rest_3rd, mean_activation_Rest_4th],
  346. [mean_activation_MI_1st, mean_activation_MI_2nd, mean_activation_MI_3rd, mean_activation_MI_4th],):
  347. mean_activation_diff[session_index] = {
  348. cardinal_position: {
  349. key: mean_activation_array_Rest[cardinal_position][key] - mean_activation_array_MI[cardinal_position][key]
  350. for key in mean_activation_array_Rest[cardinal_position]
  351. }
  352. for cardinal_position in range(len(mean_activation_array_Rest))
  353. }
  354. #%%
  355. n_cardinal_position = 10
  356. BCI_scores = {
  357. 1: np.array([73.8, 45, 66.1, 50, 51.7, 61.2, 54.4, 59.4, 52.8, 42.2, 53.9, 47.22, 53.3, 54.17, 60, 76.1, 48.33, 56, 53.89, 45]),
  358. 2: np.array([90.2, 35.6, 61.7, 50, 51.7, 50.6, 58.9, 66.11, 50, 45.6, 45, 52.22, 65.6, 50, 68.9, 70.6, 61.67, 63.89, 53.89, 57.22]),
  359. 3: np.array([86.6, 38.9, 72.8, 62.8, 61.7, 79.3, 51.7, 56.67, 68.9, 58.9, 55.6, 47.78, 57.8, 60, 76.1, 53.9, 66, 72, 52.78, 58.33]),
  360. 4: np.array([89.3, 38.1, 86.1, 73.1, 63.9, 88.9, 56.7, 70.0, 60.0, 66.1, 63.3, 53.9, 77.2, 69.2, 80.0, 61.1, 71.0, 73.0, 68.3, 56.7])
  361. }
  362. # 57.8 is the chance level - we set it as threshold
  363. binary_BCI_scores = {
  364. key: (values > 57).astype(int)
  365. for key, values in BCI_scores.items()
  366. }
  367. #%%
  368. # Parameters
  369. n_subjects = 20
  370. n_sessions = 4
  371. results = {}
  372. for cardinal_position in range(10):
  373. X_data = {session: [] for session in range(1, n_sessions + 1)}
  374. Y_data = binary_BCI_scores
  375. X_temporal_list = []
  376. for subj in range(n_subjects):
  377. features_per_subject = []
  378. for session in range(1, n_sessions + 1):
  379. length_feat = mean_length_diff[session][cardinal_position][subj]
  380. activations_feat = mean_activation_diff[session][cardinal_position][subj]
  381. features_matrix = np.array([length_feat, activations_feat])
  382. X_data[session].append(features_matrix)
  383. features_per_subject.append(features_matrix)
  384. y_true_session4 = Y_data[4]
  385. X_list, y_list = [], []
  386. for subj in range(n_subjects):
  387. X_subj = np.vstack([X_data[1][subj], X_data[2][subj], X_data[3][subj]])
  388. y_subj = np.array([Y_data[1][subj], Y_data[2][subj], Y_data[3][subj]])
  389. X_list.append(X_subj)
  390. y_list.append(y_subj)
  391. best_acc = 0
  392. best_acc_perc = 0
  393. best_acc_preds = []
  394. # Define grid of hyperparameters
  395. C_grid = [0.01, 0.1, 1, 10]
  396. tol_grid = [1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1]
  397. max_iter_grid = [50,100, 200]
  398. grid_results = {}
  399. # Store results for each value of C
  400. results_normal = {}
  401. for C in C_grid:
  402. y_preds_normal = []
  403. # Normal Model (SVC)
  404. normal_model = SVC(C=C, kernel='linear')
  405. # Perform Leave-One-Out Cross Validation (LOO)
  406. loo = LeaveOneOut()
  407. for train_idx, test_idx in loo.split(X_list):
  408. X_train = np.vstack([X_list[i] for i in train_idx])
  409. y_train = np.concatenate([y_list[i] for i in train_idx])
  410. X_test = np.vstack([X_list[i] for i in test_idx])
  411. # Train and predict with the normal model
  412. normal_model.fit(X_train, y_train)
  413. y_pred_normal = normal_model.predict(X_test)
  414. y_preds_normal.append(y_pred_normal[0])
  415. # Calculate accuracy for the normal model
  416. acc_normal = balanced_accuracy_score(y_true_session4, y_preds_normal)
  417. # Confusion matrix for the normal model
  418. cm_normal = confusion_matrix(y_true_session4, y_preds_normal)
  419. # Plot for normal SVC
  420. # vertical_dot_plot(y_true_session4, y_preds_normal, "SVC, Accuracy: 41%", save_path="normal_svc_results_new_2.jpg")
  421. # Store results for the current value of C
  422. results_normal[C] = {'Accuracy Normal': acc_normal, 'Confusion Matrix': cm_normal}
  423. # Print the results for the current value of C
  424. print(f"Results for Normal Model with C={C}:, Cardinal_position:{cardinal_position}")
  425. print(f"Accuracy Normal, Cardinal_position: {cardinal_position}: {acc_normal:.2f}")
  426. print(f"Confusion Matrix Normal, Cardinal_position {cardinal_position}:\n{cm_normal}")
  427. # Optional: Print the best model (highest accuracy) after the loop
  428. best_C = max(results_normal, key=lambda k: results_normal[k]['Accuracy Normal'])
  429. best_result = results_normal[best_C]
  430. print(f"Best C, Cardinal_position {cardinal_position}: {best_C}")
  431. print(f"Best Accuracy Normal, Cardinal_position {cardinal_position}: {best_result['Accuracy Normal']:.2f}")
  432. print(f"Best Confusion Matrix, Cardinal_position {cardinal_position}:\n{best_result['Confusion Matrix']}")
  433. #%% Logitudinal Support Vector Classifier Model
  434. # Hyperparameter grid search
  435. for C in C_grid:
  436. for tol in tol_grid:
  437. for max_iter in max_iter_grid:
  438. y_preds_lsvc = []
  439. # Perform Leave-One-Out Cross Validation (LOO)
  440. loo = LeaveOneOut()
  441. for train_idx, test_idx in loo.split(X_list):
  442. X_train = [X_list[i] for i in train_idx]
  443. y_train = [y_list[i] for i in train_idx]
  444. X_test = [X_list[i] for i in test_idx]
  445. try:
  446. alpha, beta, y_bin_train = longitudinal_svc_iterative(X_train, y_train, C=C, tol=tol,
  447. max_iter=max_iter)
  448. y_pred_lsvc = predict_lsvc(X_test, X_train, alpha, beta, y_bin_train)[0]
  449. except Exception as e:
  450. print(f"Skipped config C={C}, tol={tol}, max_iter={max_iter} due to error: {e}")
  451. y_pred_lsvc = 0 # Fallback value
  452. y_preds_lsvc.append(y_pred_lsvc)
  453. # Calculate accuracy and store results
  454. acc_l = balanced_accuracy_score(y_true_session4, y_preds_lsvc)
  455. grid_results[(C, tol, max_iter)] = {'Accuracy Long': acc_l, 'Predictions Long': y_preds_lsvc}
  456. # Find best hyperparameter configuration
  457. best_config = max(grid_results, key=lambda k: grid_results[k]['Accuracy Long'])
  458. best_result = grid_results[best_config]
  459. # Get predicted BCI scores
  460. y_true = np.array(Y_data[4]) # Actual BCI scores for session 4
  461. y_pred = np.array(best_result['Predictions Long']) # Predicted BCI scores for session 4
  462. # Compute confusion matrix
  463. cm = confusion_matrix(y_true, y_pred)
  464. #vertical_dot_plot(y_true_session4, y_pred, "LSVC, Accuracy: 88%", save_path="lsvc_results_new_2.jpg")
  465. print(f"Confusion Matrix for Cardinal Position {cardinal_position}:")
  466. print(cm)
  467. # Print best hyperparameter configuration and results
  468. print(f"Best configuration, Cardinal_position:{cardinal_position}: C={best_config[0]}, tol={best_config[1]}, max_iter={best_config[2]}")
  469. print(f"Best Accuracy, Cardinal_position:{cardinal_position}: {best_result['Accuracy Long']:.2f}")
  470. print(f"Predictions, Cardinal_position:{cardinal_position}: {best_result['Predictions Long']}")
  471. #%% Longitudinal Support Vector Classifier random session
  472. # Data structure to hold the features per session
  473. for cardinal_position in range(10):
  474. X_data = {session: [] for session in range(1, n_sessions + 1)} # Initialize X_data
  475. # Step 1: Extract features for each subject and session
  476. for subj in range(n_subjects):
  477. for session in range(1, n_sessions + 1):
  478. # Placeholder for actual feature extraction (replace with your real data extraction)
  479. length_feat = mean_length_diff[session][cardinal_position][subj]
  480. activations_feat = mean_activation_diff[session][cardinal_position][subj]
  481. features_matrix = np.array([length_feat, activations_feat])
  482. X_data[session].append(features_matrix)
  483. # Step 2: Shuffle the sessions for each subject independently (shuffling only X_data, not Y_data)
  484. shuffled_X_data = {session: [] for session in range(1, n_sessions + 1)}
  485. for subj in range(n_subjects):
  486. sessions_order = np.random.permutation(n_sessions) # Shuffle the sessions for each subject
  487. for i, session_idx in enumerate(sessions_order):
  488. session = session_idx + 1 # Session number starts from 1
  489. shuffled_X_data[session].append(X_data[session][subj])
  490. # Step 3: Create the final X_data and y_data
  491. X_list, y_list = [], []
  492. for subj in range(n_subjects):
  493. # Stack the shuffled session data for each subject
  494. X_subj = np.vstack([shuffled_X_data[session][subj] for session in range(1, n_sessions + 1)])
  495. # Keep the original labels (y_subj) for each subject intact (no shuffling of Y_data)
  496. y_subj = np.array([Y_data[1][subj], Y_data[2][subj], Y_data[3][subj]])
  497. X_list.append(X_subj)
  498. y_list.append(y_subj)
  499. # Convert to numpy arrays for easy handling during model training
  500. X_data_final = np.vstack(X_list)
  501. y_data_final = np.array(y_list)
  502. print("Shuffling Session complete. X_data_final and y_data_final are ready for further analysis.")
  503. # Step 4: Hyperparameter grid search initialization
  504. grid_results = {}
  505. # Step 5: Perform Leave-One-Out Cross Validation (LOO) and grid search for hyperparameters
  506. y_true_session4 = Y_data[4] # Labels for session 4
  507. best_acc = 0
  508. best_acc_perc = 0
  509. best_acc_preds = []
  510. for C in C_grid:
  511. for tol in tol_grid:
  512. for max_iter in max_iter_grid:
  513. y_preds = []
  514. # Leave-One-Out Cross Validation
  515. loo = LeaveOneOut()
  516. for train_idx, test_idx in loo.split(X_list):
  517. X_train = [X_list[i] for i in train_idx]
  518. y_train = [y_list[i] for i in train_idx]
  519. X_test = [X_list[i] for i in test_idx]
  520. try:
  521. # Replace this with your actual model training and prediction functions
  522. alpha, beta, y_bin_train = longitudinal_svc_iterative(X_train, y_train, C=C, tol=tol, max_iter=max_iter)
  523. y_pred_lsvc = predict_lsvc(X_test, X_train, alpha, beta, y_bin_train)[0]
  524. except Exception as e:
  525. print(f"Skipped config C={C}, tol={tol}, max_iter={max_iter} due to error: {e}")
  526. y_pred_lsvc = 0 # Fallback value
  527. y_preds.append(y_pred_lsvc)
  528. # Evaluate and store results
  529. acc = balanced_accuracy_score(y_true_session4, y_preds)
  530. grid_results[(C, tol, max_iter)] = {'Accuracy': acc, 'Predictions': y_preds}
  531. # Step 6: Find best hyperparameter configuration
  532. best_config = max(grid_results, key=lambda k: grid_results[k]['Accuracy'])
  533. best_result = grid_results[best_config]
  534. # Step 7: Get predicted and actual BCI scores
  535. y_true = np.array(Y_data[4]) # Actual BCI scores for session 4
  536. y_pred = np.array(best_result['Predictions']) # Predicted BCI scores for session 4
  537. # Step 8: Compute confusion matrix
  538. cm = confusion_matrix(y_true, y_pred)
  539. print(f"Confusion Matrix:")
  540. print(cm)
  541. # vertical_dot_plot(y_true_session4, y_pred, "LSVC, Random Sessions, Accuracy: 47%", save_path="lsvc_results_random_session_new2.jpg")
  542. # Step 9: Print best hyperparameter configuration and results
  543. print(f"Best configuration, Cardinal_position:{cardinal_position}: C={best_config[0]}, tol={best_config[1]}, max_iter={best_config[2]}")
  544. print(f"Best Accuracy, Cardinal_position:{cardinal_position}: {best_result['Accuracy']:.2f}")
  545. print(f"Predictions, Cardinal_position:{cardinal_position}: {best_result['Predictions']}")
  546. #%% Longitudinal Support Vector Classifier with Random Session and Random Subjects
  547. # Set seeds for reproducibility
  548. #seed_value = 24
  549. #np.random.seed(seed_value)
  550. #random.seed(seed_value)
  551. #for cardinal_position in range(10):
  552. # Extract features per subject and session
  553. # for subj in range(n_subjects):
  554. # for session in range(1, n_sessions + 1):
  555. # length_feat = mean_length_diff[session][cardinal_position][subj]
  556. # activations_feat = mean_activation_diff[session][cardinal_position][subj]
  557. # features_matrix = np.array([length_feat, activations_feat])
  558. # X_data[session].append(features_matrix)
  559. # Shuffle the sessions for each subject independently
  560. # shuffled_X_data = {session: [] for session in range(1, n_sessions + 1)}
  561. # for subj in range(n_subjects):
  562. # sessions_order = np.random.permutation(n_sessions)
  563. # for i, session_idx in enumerate(sessions_order):
  564. # session = session_idx + 1
  565. # shuffled_X_data[session].append(X_data[session][subj])
  566. # Shuffle subjects themselves
  567. # subject_order = np.random.permutation(n_subjects)
  568. # X_list, y_list = [], []
  569. # for subj_idx in subject_order:
  570. # X_subj = np.vstack([shuffled_X_data[session][subj_idx] for session in range(1, n_sessions + 1)])
  571. # y_subj = np.array([Y_data[session][subj_idx] for session in range(1, n_sessions + 1)])
  572. # X_list.append(X_subj)
  573. # y_list.append(y_subj)
  574. # X_data_final = np.vstack(X_list)
  575. # y_data_final = np.hstack(y_list)
  576. # print("Shuffling Subj + Sessions complete. X_data_final and y_data_final are ready for further analysis.")
  577. # Hyperparameter grid search initialization
  578. # grid_results = {}
  579. # y_true_session4 = Y_data[4]
  580. # best_acc = 0
  581. # best_acc_perc = 0
  582. # best_acc_preds = []
  583. # for C in C_grid:
  584. # for tol in tol_grid:
  585. # for max_iter in max_iter_grid:
  586. # y_preds = []
  587. # loo = LeaveOneOut()
  588. # for train_idx, test_idx in loo.split(X_list):
  589. # X_train = [X_list[i] for i in train_idx]
  590. # y_train = [y_list[i] for i in train_idx]
  591. # X_test = [X_list[i] for i in test_idx]
  592. # try:
  593. # alpha, beta, y_bin_train = longitudinal_svc_iterative(X_train, y_train, C=C, tol=tol,
  594. # max_iter=max_iter)
  595. # y_pred_lsvc = predict_lsvc(X_test, X_train, alpha, beta, y_bin_train)[0]
  596. # except Exception as e:
  597. # print(f"Skipped config C={C}, tol={tol}, max_iter={max_iter} due to error: {e}")
  598. # y_pred_lsvc = 0
  599. # y_preds.append(y_pred_lsvc)
  600. # acc = balanced_accuracy_score(y_true_session4, y_preds)
  601. # grid_results[(C, tol, max_iter)] = {'Accuracy': acc, 'Predictions': y_preds}
  602. # best_config = max(grid_results, key=lambda k: grid_results[k]['Accuracy'])
  603. # best_result = grid_results[best_config]
  604. # y_true = np.array(Y_data[4])
  605. # y_pred = np.array(best_result['Predictions'])
  606. # cm = confusion_matrix(y_true, y_pred)
  607. # print(f"Confusion Matrix for Cardinal Position {cardinal_position}:")
  608. # print(cm)
  609. # vertical_dot_plot(y_true_session4, y_pred, "LSVC, Random Sessions & Random Subjects, Accuracy: 62%",
  610. # save_path="lsvc_results_random_subject&sessions_new2.jpg")
  611. # print(
  612. # f"Best configuration, Cardinal_position:{cardinal_position}: C={best_config[0]}, tol={best_config[1]}, max_iter={best_config[2]}")
  613. # print(f"Best Accuracy, Cardinal_position:{cardinal_position}: {best_result['Accuracy']:.2f}")
  614. # print(f"Predictions, Cardinal_position:{cardinal_position}: {best_result['Predictions']}")

LSVC_model.py at commit 45a88f8, no license · at the source

Overview

  1. Sorbonne Université, Paris Brain Institute-ICM, CNRS, Inria, Inserm, AP-HP, Hôpital de la Pitié Salpêtrière, Paris, France
  2. Institut de Neurosciences des Systèmes, Aix-Marseille Université, Marseille, France
  3. Department of Biomedical Sciences, University of Sassari, Sassari, Italy
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1259
Dates: received 12 August 2025; accepted 4 May 2026; published online 29 May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1259 · PMID 42232076 · PMCID PMC13224313 · OpenAlex W4411036309
Open access: diamond, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), clinical / translational (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, fMRI & imaging, Single-unit activity, calcium imaging
Keywords: neuronal avalanches, Brain-Computer Interface (BCI), motor imagery, Electroencephalography (EEG), task-condition effect, learning effect, BCI-score repeated correlation, longitudinal predictive model, personalized training protocol
Topic: EEG and Brain-Computer Interfaces (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: not cited yet (Europe PMC); 50 references in the paper

Abstract

Motor imagery-based Brain-Computer Interfaces (BCIs) restore control in persons with motor impairments, but up to 30% of users struggle, a phenomenon known as “BCI inefficiency”. This study tackles a key limitation of current protocol: the use of fixed-length sessions training paradigms that ignore individual learning variability. We propose a novel approach based on neuronal avalanches, spatiotemporal cascades of brain activities, as biomarkers to characterize and predict user-specific learning. From electroencephalography data across four sessions in 20 subjects, we characterized avalanches by their length and their spatiotemporal size. These features showed significant training and task effects and were found to correlate to BCI performance across sessions. We further assessed their ability to predict BCI success through longitudinal models, achieving up to 91% accuracy, improved by spatial filtering on selected brain regions. These findings demonstrate the utility of neuronal avalanche dynamics as robust biomarkers for BCI training, supporting the development of personalized protocols aimed at mitigating BCI illiteracy.

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

CamiMannino/Neuronal-avalanches-as-a-predictive-biomarker-for-guiding-tailored-BCI-training-programs

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 45a88f8b7911f116d0359b5433c72acd2bd05636, 11 August 2025
Languages: Python (9)
Size: 10 files, 9 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (9 files), NumPy (9 files), MNE-Python (7 files), SciPy (7 files), statsmodels (7 files), pandas (6 files), scikit-learn (6 files), seaborn (6 files), Pingouin (4 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
10 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;
  • 9 scripts, each with its path and the digest of its content;
  • 8 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 study dataset has been fully collected and curated. A dedicated data-descriptor paper is currently in preparation; upon its acceptance, the de-identified dataset and full documentation will be deposited in an open repository with a citable DOI and made publicly available. Until release, controlled access may be granted on reasonable request to the corresponding author.

Code used to import data, analyse data, and generate manuscript figures are available on GitHub: https://github.com/CamiMannino/Neuronal-avalanches-as-a-predictive-biomarker-for-guiding-tailored-BCI-training-programs.

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

Recorded: type, language, journal, volume, pages, dates, 4 authors, 9 keywords, 49 references.

Cite

This paper

Mannino, C., Sorrentino, P., Chavez, M., & Corsi, M.-C. (2026). Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1259. https://doi.org/10.1162/imag.a.1259

BibTeX

@article{mannino2026neuronal,
author = {Mannino, Camilla and Sorrentino, Pierpaolo and Chavez, Mario and Corsi, Marie-Constance},
title = {{Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = may,
volume = {4},
pages = {IMAG.a.1259},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/imag.a.1259},
url = {https://doi.org/10.1162/imag.a.1259},
pmid = {42232076},
pmcid = {PMC13224313}
}

RIS

TY - JOUR
AU - Mannino, Camilla
AU - Sorrentino, Pierpaolo
AU - Chavez, Mario
AU - Corsi, Marie-Constance
TI - Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/05/29
VL - 4
SP - IMAG.a.1259
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1259
UR - https://doi.org/10.1162/imag.a.1259
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1259",
"type": "article-journal",
"title": "Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Mannino",
"given": "Camilla"
},
{
"family": "Sorrentino",
"given": "Pierpaolo"
},
{
"family": "Chavez",
"given": "Mario"
},
{
"family": "Corsi",
"given": "Marie-Constance"
}
],
"container-title-short": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1259",
"DOI": "10.1162/imag.a.1259",
"PMID": "42232076",
"PMCID": "PMC13224313",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1259",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
29
]
]
}
}

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.1162/imag.a.1348 [code]
The role of high-amplitude bursts of high-gamma activity in naturalistic speech and music listening.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: MNE-Python, seaborn, pandas, 3 other tools, 5 references
[2] doi:10.1162/imag.a.1193
Linking local and large-scale salient events with oscillatory and broadband arrhythmic activities in the resting human brain.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: 5 references, author Pierpaolo Sorrentino
[3] doi:10.7554/elife.100605 [code]
Age-related changes in ‘cortical’ 1/f dynamics are linked to cardiac activity
Journal: n/a
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, 1 reference
[4] doi:10.7554/elife.108673 [code]
Adaptive behavior is guided by integrated representations of controlled and non-controlled information.
Journal: eLife
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, EEG
[5] doi:10.1162/imag.a.1269 [code]
From early to contemporary normative modeling: Mapping individual differences in neurophysiological signals.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, EEG
[6] doi:10.1093/cercor/bhag077 [code]
The longitudinal development of intrinsic timescales in infancy and their relation to alpha brain rhythm.
Journal: Cerebral cortex (New York, N.Y. : 1991)
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, EEG
[7] doi:10.1038/s41597-026-07350-9 [code]
An open multi-center MEG-EEG dataset for studying conscious visual perception.
Journal: Scientific data
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, EEG
[8] doi:10.1038/s41531-026-01380-1 [code]
Identifying maximal beta power from directional subthalamic local field potentials in Parkinson's disease.
Journal: NPJ Parkinson's disease
In common: Pingouin, MNE-Python, statsmodels, 6 other tools, clinical / translational
[9] doi:10.1162/imag.a.1245 [code]
Towards precision EEG connectomics: Evaluating the benefits of dense sampling.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Pingouin, MNE-Python, statsmodels, 5 other tools, EEG, 1 reference
[10] doi:10.1038/s41467-026-75662-w [code]
Distinct Roles of Deep and Superficial Cortical Layers in Tone Prediction, Comparison, and Adaptation in Human Auditory Cortices.
Journal: Nature communications
In common: Pingouin, MNE-Python, statsmodels, 6 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.