OSCR

Divergent disruption of brain networks following total and chronic sleep loss: a longitudinal fMRI study.

Code ↔ Paper

10 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 10 matches
  1. [1] § Materials and Methods › Time series extraction ↔ Scripts/quality_check_FD_code.ipynb, lines 228–338 · score 0.68 · framewise displacement, Quality check, BOLD signal, Power, ROI, brain
  2. [2] § Materials and Methods › Subjective data analysis ↔ Scripts/subjective_behavior_state_traits.ipynb, lines 1–142 · score 0.67 · subjective sleepiness, global graph metrics, global efficiency, graph distance, modularity, mixed
  3. [3] § Materials and Methods › Subjective data analysis ↔ Scripts/subjective_behavior_state_traits.ipynb, lines 1–142 · score 0.63 · subjective behavioral, subjective sleepiness, objective graph, trait, baseline
  4. [4] § Materials and Methods › Statistical analyses › Global and nodal metrics comparisons ↔ Scripts/Graphs_nodal_global_metrics_HDI_CCML.ipynb, lines 714–810 · score 0.62 · unpaired permutation, global metrics, robustness, shuffled, LMMs, FDR
  5. [5] § Materials and Methods › Covariate-constraint manifold learning ↔ Scripts/Graphs_nodal_global_metrics_HDI_CCML.ipynb, lines 2931–3051 · score 0.62 · classical ISOMAP, global metrics, embeddings, manifold, CCML, covariates
  6. [6] § Results › Nodal graph metrics ↔ Scripts/Graphs_nodal_global_metrics_HDI_CCML.ipynb, lines 3399–3440 · score 0.60 · III VI, Thalamus, Vermis, VII, FPN, limbic
  7. [7] § Materials and Methods › Subjective data analysis ↔ Scripts/Graphs_nodal_global_metrics_HDI_CCML.ipynb, lines 527–586 · score 0.58 · linear mixed, global efficiency, graph distance, modularity, model, clustering
  8. [8] § Materials and Methods › Subjective data analysis ↔ Scripts/subjective_behavior_state_traits.ipynb, lines 145–255 · score 0.58 · global graph metric, HC3, scored, OLS, trait, PSQI
  9. [9] § Results › Nodal graph metrics ↔ Scripts/Graphs_nodal_global_metrics_HDI_CCML.ipynb, lines 3399–3440 · score 0.57 · III VI, Heschl, Precuneus, parietal, Angular, DMN
  10. [10] § Materials and Methods › Time series extraction ↔ Scripts/quality_check_FD_code.ipynb, lines 228–338 · score 0.56 · temporal derivatives, motion parameters, outlier, signal, zero, regression

Paper

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

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 3,559 lines · 126 KB · no license · 5 matches

  1. # %%
  2. import networkx as nx
  3. import matplotlib.pyplot as plt
  4. import string
  5. import numpy as np
  6. from networkx.algorithms import community
  7. import random
  8. import copy
  9. import scipy as sp
  10. from scipy.stats import ttest_ind
  11. from scipy.stats import mannwhitneyu
  12. import statsmodels.formula.api as smf
  13. from scipy.stats import ttest_rel
  14. from scipy.stats import ttest_1samp
  15. import statsmodels.api as sm
  16. from statsmodels.formula.api import mixedlm
  17. from statsmodels.stats.anova import AnovaRM
  18. from statsmodels.stats.multitest import multipletests
  19. from itertools import combinations
  20. import statsmodels.stats.multitest as smm
  21. from statsmodels.stats.multitest import fdrcorrection
  22. import scipy.optimize as opt
  23. import sklearn as sk
  24. from sklearn import manifold, datasets
  25. from sklearn.utils import resample
  26. from sklearn.linear_model import LinearRegression
  27. from scipy.interpolate import griddata
  28. from scipy.spatial.distance import cdist
  29. from matplotlib import ticker
  30. import pandas as pd
  31. import seaborn as sns
  32. from matplotlib.lines import Line2D
  33. import matplotlib.cm as cm
  34. from netplotbrain import plot
  35. from multipy.fwer import bonferroni
  36. from multipy.fdr import lsu
  37. import os
  38. import glob
  39. import re
  40. import warnings
  41. # %% [markdown]
  42. # ## In this code, we will use the adjacency matrix and calculate nodal graph metrics (e.g., closeness, degree, betweenness).
  43. # %%
  44. #load coordinates once - the coordinates are always the same, because they are obtained directly from the atlas
  45. coord_path = "/path/to/coordinates/from/AAL/atlas.txt"
  46. coord = pd.read_csv(coord_path, sep="\s+", header=None).to_numpy()
  47. pos = {k: (coord[k, 1], coord[k, 2]) for k in range(89)}
  48. #loop through all sessions and subjects
  49. base_path = "/path/to/mai/folder/with/data"
  50. sessions = ["ses-1", "ses-2", "ses-3"]
  51. for ses in sessions:
  52. ses_path = os.path.join(base_path, ses)
  53. subjects = glob.glob(os.path.join(ses_path, "sub-*"))
  54. for sub_path in subjects:
  55. sub = os.path.basename(sub_path) # e.g., "sub-01"
  56. graph_path = os.path.join(sub_path, "Graphs/wAALours")
  57. #only match files ending with "_400.txt" — this ensures we use adjacency matrices containing 400 edges.
  58. #you can modify the number (e.g., "_200.txt", "_600.txt") to explore matrices with a different number of edges.
  59. adj_files = glob.glob(os.path.join(graph_path, "*_400.txt"))
  60. for adj_file in adj_files:
  61. try:
  62. #load adjacency matrix
  63. df = pd.read_csv(adj_file, sep="\s+", header=None)
  64. X = df.to_numpy()
  65. G = nx.from_numpy_array(X)
  66. #plot
  67. plt.figure(figsize=(8, 8))
  68. nx.draw(G, pos, node_size=50, with_labels=False)
  69. #output directory
  70. metrics_dir = os.path.join(graph_path, "graph_metrics")
  71. os.makedirs(metrics_dir, exist_ok=True)
  72. #file names follow the pattern "ses-1_sub-01_Adj_mat_..._400.txt".
  73. #including "ses" (session) and "sub" (subject) helps verify that each file corresponds to the correct participant and session.
  74. file_id = f"{ses}_{sub}_{os.path.splitext(os.path.basename(adj_file))[0]}"
  75. #paths
  76. image_path = os.path.join(metrics_dir, f"{file_id}.png")
  77. metrics_path = os.path.join(metrics_dir, f"{file_id}_metrics.csv")
  78. plt.savefig(image_path)
  79. plt.close()
  80. #compute metrics using NetworkX
  81. closeness = nx.closeness_centrality(G)
  82. betweenness = nx.betweenness_centrality(G)
  83. clustering = nx.clustering(G)
  84. degree = nx.degree_centrality(G)
  85. #save to csv
  86. metrics_df = pd.DataFrame({
  87. "Node": list(closeness.keys()),
  88. "Closeness": list(closeness.values()),
  89. "Betweenness": list(betweenness.values()),
  90. "Clustering": list(clustering.values()),
  91. "Degree": list(degree.values())
  92. })
  93. metrics_df.to_csv(metrics_path, index=False)
  94. print(f"Processed: {file_id}")
  95. except Exception as e:
  96. print(f"Error processing {adj_file}: {e}")
  97. # %% [markdown]
  98. # ## In this code, we will use the adjacency matrix and calculate global graph metrics (e.g., global efficiency).
  99. # %% [markdown]
  100. # GLOBAL EFFICIENCY
  101. # %%
  102. #sessions
  103. session_labels = {
  104. "ses-1": "baseline",
  105. "ses-2": "acute",
  106. "ses-3": "chronic"
  107. }
  108. base_path = "/path/to/mai/folder/with/data"
  109. #result list
  110. efficiency_results = []
  111. #loop through each session and subject
  112. for ses_id in session_labels.keys():
  113. ses_path = os.path.join(base_path, ses_id)
  114. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  115. for sub_path in subject_paths:
  116. sub_id = os.path.basename(sub_path)
  117. adj_path = os.path.join(sub_path, "Graphs/wAALours", "*cost_400.txt")
  118. adj_files = glob.glob(adj_path)
  119. for adj_file in adj_files:
  120. try:
  121. df = pd.read_csv(adj_file, sep="\s+", header=None)
  122. X = df.to_numpy()
  123. G = nx.from_numpy_array(X)
  124. efficiency = nx.global_efficiency(G)
  125. efficiency_results.append({
  126. "subject_id": sub_id,
  127. "session_id": ses_id,
  128. "global_efficiency": efficiency
  129. })
  130. print(f"{sub_id} | {ses_id} | {efficiency:.4f}")
  131. except Exception as e:
  132. print(f"Error processing {adj_file}: {e}")
  133. #convert to df
  134. df_out = pd.DataFrame(efficiency_results)
  135. #pivot the dataframe so that each session becomes a column, subject_id forms the rows,
  136. #session_id serves as the column names, and global_efficiency values fill the table.
  137. glob_eff = df_out.pivot(index="subject_id", columns="session_id", values="global_efficiency").reset_index()
  138. glob_eff.columns.name = None # remove column index name, e.g. "session_id""
  139. #save to CSV
  140. output_file = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/global_efficiency_all_subjects.csv"
  141. os.makedirs(os.path.dirname(output_file), exist_ok=True)
  142. glob_eff.to_csv(output_file, index=False)
  143. print(f"Final results saved to: {output_file}")
  144. # %% [markdown]
  145. # AVERAGE CLUSTERING
  146. # %%
  147. #sessions
  148. session_labels = {
  149. "ses-1": "baseline",
  150. "ses-2": "acute",
  151. "ses-3": "chronic"
  152. }
  153. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  154. clustering_results = []
  155. #loop through each session and subject
  156. for ses_id in session_labels.keys():
  157. ses_path = os.path.join(base_path, ses_id)
  158. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  159. for sub_path in subject_paths:
  160. sub_id = os.path.basename(sub_path)
  161. adj_path = os.path.join(sub_path, "Graphs/wAALours", "*cost_400.txt")
  162. adj_files = glob.glob(adj_path)
  163. for adj_file in adj_files:
  164. try:
  165. df = pd.read_csv(adj_file, sep="\s+", header=None)
  166. X = df.to_numpy()
  167. G = nx.from_numpy_array(X)
  168. avg_clustering = nx.average_clustering(G)
  169. clustering_results.append({
  170. "subject_id": sub_id,
  171. "session_id": ses_id,
  172. "average_clustering": avg_clustering
  173. })
  174. print(f"{sub_id} | {ses_id}: {avg_clustering:.4f}")
  175. except Exception as e:
  176. print(f"Error processing {adj_file}: {e}")
  177. #convert to df
  178. df_out = pd.DataFrame(clustering_results)
  179. #pivot the dataframe so that each session becomes a column, subject_id forms the rows,
  180. #session_id serves as the column names, and global_efficiency values fill the table.
  181. clustering_df = df_out.pivot(index="subject_id", columns="session_id", values="average_clustering").reset_index()
  182. clustering_df.columns.name = None # remove column index name
  183. #save to CSV
  184. output_file = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/average_clustering_all_subjects.csv"
  185. os.makedirs(os.path.dirname(output_file), exist_ok=True)
  186. clustering_df.to_csv(output_file, index=False)
  187. print(f"Final results saved to: {output_file}")
  188. # %% [markdown]
  189. # PATH LENGTH
  190. # %%
  191. #sessions
  192. session_labels = {
  193. "ses-1": "baseline",
  194. "ses-2": "acute",
  195. "ses-3": "chronic"
  196. }
  197. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  198. path_length_results = []
  199. #loop through each session and subject
  200. for ses_id in session_labels.keys():
  201. ses_path = os.path.join(base_path, ses_id)
  202. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  203. for sub_path in subject_paths:
  204. sub_id = os.path.basename(sub_path)
  205. adj_path = os.path.join(sub_path, "Graphs/wAALours", "*cost_400.txt")
  206. adj_files = glob.glob(adj_path)
  207. for adj_file in adj_files:
  208. try:
  209. df = pd.read_csv(adj_file, sep="\s+", header=None)
  210. X = df.to_numpy()
  211. G = nx.from_numpy_array(X)
  212. if nx.is_connected(G):
  213. avg_path_length = nx.average_shortest_path_length(G)
  214. else:
  215. largest_cc = max(nx.connected_components(G), key=len)
  216. G_largest_cc = G.subgraph(largest_cc)
  217. avg_path_length = nx.average_shortest_path_length(G_largest_cc)
  218. path_length_results.append({
  219. "subject_id": sub_id,
  220. "session_id": ses_id,
  221. "average_path_length": avg_path_length
  222. })
  223. print(f"{sub_id} | {ses_id}: {avg_path_length:.4f}")
  224. except Exception as e:
  225. print(f"Error processing {adj_file}: {e}")
  226. #convert to df
  227. df_out = pd.DataFrame(path_length_results)
  228. #pivot the dataframe so that each session becomes a column, subject_id forms the rows,
  229. #session_id serves as the column names, and global_efficiency values fill the table.
  230. path_length_df = df_out.pivot(index="subject_id", columns="session_id", values="average_path_length").reset_index()
  231. path_length_df.columns.name = None # remove column index name
  232. #save to CSV
  233. output_file = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/average_path_length_all_subjects.csv"
  234. os.makedirs(os.path.dirname(output_file), exist_ok=True)
  235. path_length_df.to_csv(output_file, index=False)
  236. print(f"Final results saved to: {output_file}")
  237. # %% [markdown]
  238. # MODULARITY
  239. # %%
  240. #sessions
  241. session_labels = {
  242. "ses-1": "baseline",
  243. "ses-2": "acute",
  244. "ses-3": "chronic"
  245. }
  246. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  247. modularity_results = []
  248. #loop through each session and subject
  249. for ses_id in session_labels.keys():
  250. ses_path = os.path.join(base_path, ses_id)
  251. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  252. for sub_path in subject_paths:
  253. sub_id = os.path.basename(sub_path)
  254. adj_path = os.path.join(sub_path, "Graphs/wAALours", "*cost_400.txt")
  255. adj_files = glob.glob(adj_path)
  256. for adj_file in adj_files:
  257. try:
  258. df = pd.read_csv(adj_file, sep="\s+", header=None)
  259. X = df.to_numpy()
  260. G = nx.from_numpy_array(X)
  261. communities = community.greedy_modularity_communities(G)
  262. modularity = community.modularity(G, communities)
  263. modularity_results.append({
  264. "subject_id": sub_id,
  265. "session_id": ses_id,
  266. "modularity": modularity
  267. })
  268. print(f"{sub_id} | {ses_id}: {modularity:.4f}")
  269. except Exception as e:
  270. print(f"Error processing {adj_file}: {e}")
  271. #convert to df
  272. df_out = pd.DataFrame(modularity_results)
  273. #pivot the dataframe so that each session becomes a column, subject_id forms the rows,
  274. #session_id serves as the column names, and global_efficiency values fill the table.
  275. modularity_df = df_out.pivot(index="subject_id", columns="session_id", values="modularity").reset_index()
  276. modularity_df.columns.name = None # remove column index name
  277. #save to CSV
  278. output_file = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/modularity_all_subjects.csv"
  279. os.makedirs(os.path.dirname(output_file), exist_ok=True)
  280. modularity_df.to_csv(output_file, index=False)
  281. print(f"Final results saved to: {output_file}")
  282. # %% [markdown]
  283. # MODULARITY, COMMUNITY DISTANCES
  284. # %%
  285. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  286. coord_path = os.path.join(base_path, "ses-1", "coord_AALours.txt")
  287. coord = pd.read_csv(coord_path, sep="\s+", header=None).to_numpy()
  288. session_labels = {
  289. "ses-1": "baseline",
  290. "ses-2": "acute",
  291. "ses-3": "chronic"
  292. }
  293. community_summary = []
  294. # --- DISTANCE FUNCTIONS ---
  295. def compute_spatial_distances(communities, coord):
  296. comm_centroids = []
  297. for comm in communities:
  298. comm_coords = coord[list(comm)]
  299. centroid = comm_coords.mean(axis=0)
  300. comm_centroids.append(centroid)
  301. return cdist(comm_centroids, comm_centroids, metric='euclidean')
  302. def compute_graph_distances(G, communities):
  303. n = len(communities)
  304. dist_matrix = np.zeros((n, n))
  305. for i in range(n):
  306. for j in range(i + 1, n):
  307. paths = []
  308. for node_i in communities[i]:
  309. for node_j in communities[j]:
  310. try:
  311. length = nx.shortest_path_length(G, source=node_i, target=node_j)
  312. paths.append(length)
  313. except nx.NetworkXNoPath:
  314. continue
  315. avg_dist = np.mean(paths) if paths else np.nan
  316. dist_matrix[i, j] = dist_matrix[j, i] = avg_dist
  317. return dist_matrix
  318. for ses_id in session_labels.keys():
  319. ses_path = os.path.join(base_path, ses_id)
  320. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  321. for sub_path in subject_paths:
  322. sub_id = os.path.basename(sub_path)
  323. adj_path_pattern = os.path.join(sub_path, "Graphs/wAALours", "*cost_400.txt")
  324. adj_files = glob.glob(adj_path_pattern)
  325. for adj_file in adj_files:
  326. try:
  327. df = pd.read_csv(adj_file, sep="\s+", header=None)
  328. adj_matrix = df.to_numpy()
  329. G = nx.from_numpy_array(adj_matrix)
  330. communities = community.greedy_modularity_communities(G)
  331. modularity_value = community.modularity(G, communities)
  332. num_communities = len(communities)
  333. node_community_map = {node: idx for idx, comm in enumerate(communities) for node in comm}
  334. cmap = cm.get_cmap('tab20', num_communities)
  335. node_colors = [cmap(node_community_map[n]) for n in G.nodes()]
  336. #views
  337. views = {
  338. 'sagittal': {k: (coord[k, 0], coord[k, 2]) for k in range(len(G.nodes))},
  339. 'axial': {k: (coord[k, 0], coord[k, 1]) for k in range(len(G.nodes))},
  340. 'coronal': {k: (coord[k, 1], coord[k, 2]) for k in range(len(G.nodes))}
  341. }
  342. #plot
  343. fig, axs = plt.subplots(1, 3, figsize=(18, 6))
  344. for i, (view_name, pos) in enumerate(views.items()):
  345. nx.draw(G, pos, node_color=node_colors, with_labels=False, node_size=100, ax=axs[i])
  346. axs[i].set_title(f'{view_name.capitalize()} view')
  347. adj_basename = os.path.basename(adj_file).replace(".txt", "")
  348. fig.suptitle(f"{sub_id} | {ses_id} | Community Structure\n{adj_basename}", fontsize=16)
  349. plt.tight_layout()
  350. output_dir = os.path.join(sub_path, "Graphs/wAALours", "graph_metrics")
  351. os.makedirs(output_dir, exist_ok=True)
  352. plot_file = os.path.join(output_dir, f"{adj_basename}_communities.png")
  353. plt.savefig(plot_file)
  354. plt.close()
  355. #distances
  356. spatial_distances = compute_spatial_distances(communities, coord)
  357. graph_distances = compute_graph_distances(G, communities)
  358. #save matrices
  359. spatial_file = os.path.join(output_dir, f"{sub_id}_{ses_id}_communities_spatial_distances.csv")
  360. graph_file = os.path.join(output_dir, f"{sub_id}_{ses_id}_communities_graph_distances.csv")
  361. pd.DataFrame(spatial_distances).to_csv(spatial_file, index=False)
  362. pd.DataFrame(graph_distances).to_csv(graph_file, index=False)
  363. #add to global summary
  364. avg_spatial_dist = np.nanmean(spatial_distances[np.triu_indices(num_communities, k=1)])
  365. avg_graph_dist = np.nanmean(graph_distances[np.triu_indices(num_communities, k=1)])
  366. community_summary.append({
  367. "subject_id": sub_id,
  368. "session_id": ses_id,
  369. "modularity": modularity_value,
  370. "num_communities": num_communities,
  371. "avg_spatial_distance": avg_spatial_dist,
  372. "avg_graph_distance": avg_graph_dist
  373. })
  374. print(f"{sub_id} {ses_id}: saved plot and metrics to {output_dir}")
  375. except Exception as e:
  376. print(f"Error processing {adj_file}: {e}")
  377. output_base_dir = os.path.join(base_path, "graph_metrics")
  378. os.makedirs(output_base_dir, exist_ok=True)
  379. #convert summary to df
  380. summary_df = pd.DataFrame(community_summary)
  381. #create and save pivoted dfs
  382. modularity_df = summary_df.pivot(index="subject_id", columns="session_id", values="modularity").reset_index()
  383. num_communities_df = summary_df.pivot(index="subject_id", columns="session_id", values="num_communities").reset_index()
  384. avg_spatial_distance_df = summary_df.pivot(index="subject_id", columns="session_id", values="avg_spatial_distance").reset_index()
  385. avg_graph_distance_df = summary_df.pivot(index="subject_id", columns="session_id", values="avg_graph_distance").reset_index()
  386. for df in [modularity_df, num_communities_df, avg_spatial_distance_df, avg_graph_distance_df]:
  387. df.columns.name = None
  388. #save to CSV
  389. modularity_df.to_csv(os.path.join(output_base_dir, "modularity_all_subjects.csv"), index=False)
  390. num_communities_df.to_csv(os.path.join(output_base_dir, "num_communities_all_subjects.csv"), index=False)
  391. avg_spatial_distance_df.to_csv(os.path.join(output_base_dir, "avg_spatial_distance_all_subjects.csv"), index=False)
  392. avg_graph_distance_df.to_csv(os.path.join(output_base_dir, "avg_graph_distance_all_subjects.csv"), index=False)
  393. print(f"All community metrics saved to: {output_base_dir}")
  394. # %% [markdown]
  395. # ## Statistical comparisons of global metrics
  396. # %%
  397. glob_eff = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/global_efficiency_all_subjects.csv")
  398. clustering_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/average_clustering_all_subjects.csv")
  399. path_length_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/average_path_length_all_subjects.csv")
  400. modularity_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/modularity_all_subjects.csv")
  401. num_communities_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/num_communities_all_subjects.csv")
  402. avg_spatial_distance_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/avg_spatial_distance_all_subjects.csv")
  403. avg_graph_distance_df = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/avg_graph_distance_all_subjects.csv")
  404. # %%
  405. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics"
  406. metrics = {
  407. "Global efficiency": glob_eff,
  408. "Average clustering": clustering_df,
  409. "Average path length": path_length_df,
  410. "Modularity": modularity_df,
  411. "Average graph distance": avg_graph_distance_df #between communities
  412. }
  413. lmm_results = []
  414. #linear mixed model
  415. def run_lmm_with_ref(df_long, metric_name, ref_session):
  416. #session have to be "categories", otherwise they would be treated as continous variables
  417. df_long["session"] = pd.Categorical(df_long["session"], categories=["ses-1", "ses-2", "ses-3"])
  418. #by default LMM compares everything to ses-1 (alphabetic order), but we want to have the ses-2 vs ses-3 comparison as well, so here we will
  419. # build a new category order with the reference session first. Reference session is defined lower in the loop
  420. df_long["session"] = df_long["session"].cat.reorder_categories([ref_session,
  421. *(s for s in ["ses-1", "ses-2", "ses-3"] if s != ref_session)])
  422. try:
  423. model = smf.mixedlm("value ~ session", data=df_long, groups=df_long["subject_id"])
  424. result = model.fit()
  425. conf_int = result.conf_int()
  426. print(f"\n{metric_name} (reference: {ref_session}):\n", result.summary())
  427. for param in result.fe_params.index:
  428. lmm_results.append({
  429. "Metric": metric_name,
  430. "Reference Session": ref_session,
  431. "Effect": param,
  432. "Estimate": result.fe_params[param],
  433. "CI Lower Bound": conf_int.loc[param][0],
  434. "CI Upper Bound": conf_int.loc[param][1],
  435. "p-value": result.pvalues[param]
  436. })
  437. except Exception as e:
  438. print(f"Error fitting model for {metric_name} with ref {ref_session}: {e}")
  439. for metric_name, df_metric in metrics.items():
  440. df_long = df_metric.melt(id_vars="subject_id",
  441. var_name="session",
  442. value_name="value")
  443. #run lmm with ses-1 as reference (default)
  444. run_lmm_with_ref(df_long.copy(), metric_name, ref_session="ses-1")
  445. #run lmm with ses-2 as reference to get ses-3 vs ses-2 - this "ref_session" is used in our function "run_lmm_with_ref"
  446. run_lmm_with_ref(df_long.copy(), metric_name, ref_session="ses-2")
  447. #save
  448. lmm_df = pd.DataFrame(lmm_results)
  449. output_file = os.path.join(base_path, "lmm_results_all_metrics_with_ref.csv")
  450. lmm_df.to_csv(output_file, index=False)
  451. print(f"\nLinear mixed model results saved to: {output_file}")
  452. # %%
  453. import pandas as pd
  454. import matplotlib.pyplot as plt
  455. from statsmodels.stats.multitest import multipletests
  456. # LMM results with CIs
  457. file_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/lmm_results_all_metrics_with_ref.csv"
  458. lmm_df = pd.read_csv(file_path)
  459. selected_metrics = [
  460. "Global efficiency",
  461. "Average clustering",
  462. "Average path length",
  463. "Modularity",
  464. "Average graph distance"
  465. ]
  466. custom_titles = [
  467. "TSD vs RW", # ses-2 vs ses-1 (control)
  468. "CSR vs RW", # ses-3 vs ses-1 (control)
  469. "CSR vs TSD" # ses-3 vs ses-2 (control)
  470. ]
  471. #remove intercepts and filter metrics
  472. lmm_df = lmm_df[~lmm_df["Effect"].str.contains("Intercept")]
  473. lmm_df = lmm_df[lmm_df["Metric"].isin(selected_metrics)].copy()
  474. #extract comparison info
  475. lmm_df["Compared Session"] = lmm_df["Effect"].str.extract(r'session\[T\.(.*?)\]')
  476. lmm_df["Comparison"] = lmm_df["Compared Session"] + " vs " + lmm_df["Reference Session"]
  477. #add new columns for FDR correction
  478. lmm_df["p-value (FDR)"] = None
  479. lmm_df["Significant (FDR)"] = None
  480. #FDR correction
  481. for (comp_ses, ref_ses) in [
  482. ("ses-2", "ses-1"),
  483. ("ses-3", "ses-1"),
  484. ("ses-3", "ses-2")
  485. ]:
  486. mask = (lmm_df["Compared Session"] == comp_ses) & (lmm_df["Reference Session"] == ref_ses)
  487. pvals = lmm_df.loc[mask, "p-value"].values
  488. if len(pvals) > 0:
  489. rejected, pvals_corrected, _, _ = multipletests(pvals, method='fdr_bh', alpha=0.05)
  490. #corrected p-values and rejection flags - decisions
  491. lmm_df.loc[mask, "p-value (FDR)"] = pvals_corrected
  492. lmm_df.loc[mask, "Significant (FDR)"] = rejected
  493. #convert to correct types - numeric for p value and boolean for decision
  494. lmm_df["p-value (FDR)"] = pd.to_numeric(lmm_df["p-value (FDR)"])
  495. lmm_df["Significant (FDR)"] = lmm_df["Significant (FDR)"].astype(bool)
  496. #significance markers and colors
  497. def get_sig_marker_fdr(p):
  498. if p <= 0.001: return '***'
  499. elif p <= 0.01: return '**'
  500. elif p <= 0.05: return '*'
  501. elif p <= 0.1: return '#'
  502. else: return ''
  503. def get_color(p):
  504. if p < 0.05: return 'darkturquoise'
  505. elif p < 0.1: return 'black'
  506. else: return 'gray'
  507. lmm_df["Significance (FDR)"] = lmm_df["p-value (FDR)"].apply(get_sig_marker_fdr)
  508. lmm_df["Color"] = lmm_df["p-value (FDR)"].apply(get_color)
  509. #plot
  510. comparison_order = [
  511. ("ses-2", "ses-1"),
  512. ("ses-3", "ses-1"),
  513. ("ses-3", "ses-2")
  514. ]
  515. fig, axes = plt.subplots(1, 3, figsize=(18, 6), sharey=True)
  516. for i, (comp_ses, ref_ses) in enumerate(comparison_order):
  517. df_comp = lmm_df[(lmm_df["Compared Session"] == comp_ses) & (lmm_df["Reference Session"] == ref_ses)].copy()
  518. metric_order = selected_metrics[::-1]
  519. df_comp["Metric"] = pd.Categorical(df_comp["Metric"], categories=metric_order, ordered=True)
  520. df_comp = df_comp.sort_values("Metric")
  521. ax = axes[i]
  522. ax.axvline(x=0, color='gray', linestyle='--')
  523. ax.set_xlim(-0.6, 0.6)
  524. for _, row in df_comp.iterrows():
  525. y = row["Metric"]
  526. x = row["Estimate"]
  527. x_low = row["CI Lower Bound"]
  528. x_high = row["CI Upper Bound"]
  529. color = row["Color"]
  530. sig = row["Significance (FDR)"]
  531. label = f'{x:.4f}{sig}'
  532. ax.plot([x_low, x_high], [y, y], color=color, linewidth=4)
  533. ax.plot(x, y, 'o', color=color, markersize=10)
  534. ax.text(min(x_high + 0.03, 0.48), y, label, va='center', fontsize=13)
  535. ax.set_title(custom_titles[i], fontsize=18)
  536. ax.set_xlabel("Estimate", fontsize=16)
  537. if i == 0:
  538. ax.set_ylabel("Graph metric", fontsize=16)
  539. else:
  540. ax.set_ylabel("")
  541. ax.tick_params(axis='y', labelsize=14)
  542. # plt.suptitle("LMM coefficients and CI (FDR corrected)", fontsize=15)
  543. plt.tight_layout(rect=[0, 0.03, 1, 0.95])
  544. plt.show()
  545. #save to CSV
  546. output_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/lmm_results_with_FDR.csv"
  547. lmm_df.to_csv(output_path, index=False)
  548. # %% [markdown]
  549. # ## Unpaired permutations (broken pairs)
  550. # %%
  551. ############### permutations for checking the robustness of LMM for global metrics - broken pairs ##############
  552. #metrics
  553. metrics = {
  554. "Global efficiency": glob_eff,
  555. "Average clustering": clustering_df,
  556. "Average path length": path_length_df,
  557. "Modularity": modularity_df,
  558. "Average graph distance": avg_graph_distance_df
  559. }
  560. n_iterations = 10000
  561. n_subjects = 28
  562. session_names = ['ses-1', 'ses-2', 'ses-3']
  563. session_pairs = [
  564. ('ses-1', 'ses-2'),
  565. ('ses-1', 'ses-3'),
  566. ('ses-2', 'ses-3')
  567. ]
  568. comparison_labels = [
  569. "Acute vs Control",
  570. "Chronic vs Control",
  571. "Chronic vs Acute"
  572. ]
  573. # Loop over each metric
  574. for metric_name, df_metric in metrics.items():
  575. print(f"\n=== {metric_name} ===")
  576. # stack all session data together in one big list
  577. list_all = np.hstack([df_metric['ses-1'], df_metric['ses-2'], df_metric['ses-3']])
  578. stats = np.zeros((n_iterations, 3)) #place for storing t-stats for 3 comparisons
  579. for i in range(n_iterations):
  580. #now we want to randomly shuffle the values, with replacement, using resample
  581. new_sample = resample(list_all)
  582. #here we define the "new" groups - numbers are defined automatically based on the number of participants in the original groups
  583. new_ses_1 = new_sample[0:n_subjects]
  584. new_ses_2 = new_sample[n_subjects:2*n_subjects]
  585. new_ses_3 = new_sample[2*n_subjects:3*n_subjects]
  586. #pairwise t-tests
  587. stat12, _ = ttest_rel(new_ses_1, new_ses_2)
  588. stat13, _ = ttest_rel(new_ses_1, new_ses_3)
  589. stat23, _ = ttest_rel(new_ses_2, new_ses_3)
  590. stats[i, 0] = stat12
  591. stats[i, 1] = stat13
  592. stats[i, 2] = stat23
  593. #real t-tests (non-permuted, without resampling)
  594. stat12_real, p12_real = ttest_rel(df_metric['ses-1'], df_metric['ses-2'])
  595. stat13_real, p13_real = ttest_rel(df_metric['ses-1'], df_metric['ses-3'])
  596. stat23_real, p23_real = ttest_rel(df_metric['ses-2'], df_metric['ses-3'])
  597. real_stats = [stat12_real, stat13_real, stat23_real]
  598. real_pvals = [p12_real, p13_real, p23_real]
  599. #FDR correction
  600. rejected, real_pvals_fdr, _, _ = multipletests(real_pvals, alpha=0.05, method='fdr_bh')
  601. #plot
  602. fig, axes = plt.subplots(1, 3, figsize=(18, 5))
  603. for j, (ax, (ses_a, ses_b)) in enumerate(zip(axes, session_pairs)):
  604. ax.hist(stats[:, j], bins=30, alpha=0.7)
  605. ax.axvline(real_stats[j], color='red', linestyle='dashed', linewidth=2, label=f'Real t-stat\nt = {real_stats[j]:.2f}\np (FDR)= {real_pvals_fdr[j]:.4f}')
  606. ax.set_title(f'{metric_name}\n{comparison_labels[j]}\n(Unpaired permutation)', fontsize=16)
  607. ax.set_xlabel('t-statistic', fontsize=14)
  608. ax.set_ylabel('Frequency', fontsize=14)
  609. # ax.text(
  610. # 0.85, 0.85,
  611. # f"t = {real_stats[j]:.2f}\n"
  612. # f"p = {real_pvals[j]:.4f}\n"
  613. # f"FDR p = {real_pvals_fdr[j]:.4f}",
  614. # ha='right', va='top',
  615. # transform=ax.transAxes,
  616. # fontsize=10,
  617. # bbox=dict(boxstyle="round,pad=0.3", edgecolor="black", facecolor="white")
  618. # )
  619. ax.legend(fontsize=12)
  620. plt.tight_layout()
  621. plt.show()
  622. #print real and FDR corrected values
  623. print('Real t-statistics, raw p-values, and FDR-corrected p-values:')
  624. for label, t_val, p_val, p_fdr, sig in zip(comparison_labels, real_stats, real_pvals, real_pvals_fdr, rejected):
  625. significance = "SIGNIFICANT" if sig else "NON-SIGNIFICANT"
  626. print(f"{label}: t = {t_val:.3f}, p = {p_val:.4f}, FDR p = {p_fdr:.4f} [{significance}]")
  627. # %% [markdown]
  628. # ## paired permutations (pairs kept, sleep deprivation labels randomly changed)
  629. # %%
  630. ############### permutations for checking the robustness of LMM for global metrics - paired permutations ##############
  631. metrics = {
  632. "Global efficiency": glob_eff,
  633. "Average clustering": clustering_df,
  634. "Average path length": path_length_df,
  635. "Modularity": modularity_df,
  636. "Average graph distance": avg_graph_distance_df
  637. }
  638. n_iterations = 10000
  639. session_pairs = [
  640. ('ses-1', 'ses-2'),
  641. ('ses-1', 'ses-3'),
  642. ('ses-2', 'ses-3')
  643. ]
  644. comparison_labels = [
  645. "Acute vs Control",
  646. "Chronic vs Control",
  647. "Chronic vs Acute"
  648. ]
  649. for metric_name, df_metric in metrics.items():
  650. print(f"\n=== {metric_name} ===")
  651. n_subjects = len(df_metric)
  652. #extract values for each session
  653. ses1 = df_metric['ses-1'].values
  654. ses2 = df_metric['ses-2'].values
  655. ses3 = df_metric['ses-3'].values
  656. #store session values in a dictionary
  657. session_data = {
  658. 'ses-1': ses1,
  659. 'ses-2': ses2,
  660. 'ses-3': ses3
  661. }
  662. #prepare array for storing permutation t-stats and real stats
  663. perm_stats = np.zeros((n_iterations, 3))
  664. real_stats = []
  665. real_pvals = []
  666. #loop over comparisons, pairwise
  667. for idx, (ses_a, ses_b) in enumerate(session_pairs):
  668. x = session_data[ses_a]
  669. y = session_data[ses_b]
  670. #real t-statistic from "session_pairs"
  671. t_real, p_real = ttest_rel(x, y)
  672. real_stats.append(t_real)
  673. real_pvals.append(p_real)
  674. #permutation testing: subject-wise label flipping - random fliping of ses_a and ses_b, but within pair, 10000 times we will calculate
  675. #the differences (ttest_dep) based on mixed groups (randomly flipped labels)
  676. for i in range(n_iterations):
  677. flip = np.random.choice([True, False], size=n_subjects) #this will generate list of "decisions" about flipping, lenght of this list
  678. #will be equal to the number of participants (in my case 28), so each participant will be or will not be swapped
  679. x_perm = np.where(flip, x, y)
  680. y_perm = np.where(flip, y, x)
  681. #tstat after permutations
  682. t_perm, _ = ttest_rel(x_perm, y_perm)
  683. perm_stats[i, idx] = t_perm
  684. # FDR Correction
  685. rejected, real_pvals_fdr, _, _ = multipletests(real_pvals, alpha=0.05, method='fdr_bh')
  686. #plot
  687. fig, axes = plt.subplots(1, 3, figsize=(18, 5))
  688. for j, ax in enumerate(axes):
  689. ax.hist(perm_stats[:, j], bins=30, alpha=0.7)
  690. ax.axvline(real_stats[j], color='red', linestyle='dashed', linewidth=2, label=f'Real t-stat\nt = {real_stats[j]:.2f}\np (FDR)= {real_pvals_fdr[j]:.4f}')
  691. ax.set_title(f'{metric_name}\n{comparison_labels[j]}\n(Paired permutation)', fontsize=16)
  692. ax.set_xlabel('t-statistic', fontsize=14)
  693. ax.set_ylabel('Frequency', fontsize=14)
  694. # ax.text(
  695. # 0.85, 0.85,
  696. # f"t = {real_stats[j]:.2f}\n"
  697. # f"p = {real_pvals[j]:.4f}\n"
  698. # f"FDR p = {real_pvals_fdr[j]:.4f}",
  699. # ha='right', va='top',
  700. # transform=ax.transAxes,
  701. # fontsize=10,
  702. # bbox=dict(boxstyle="round,pad=0.3", edgecolor="black", facecolor="white")
  703. # )
  704. ax.legend(fontsize=12)
  705. plt.tight_layout()
  706. plt.show()
  707. #print real and FDR corrected values
  708. print('Real t-statistics, raw p-values, and FDR-corrected p-values:')
  709. for label, t_val, p_val, p_fdr, sig in zip(comparison_labels, real_stats, real_pvals, real_pvals_fdr, rejected):
  710. significance = "SIGNIFICANT" if sig else "NON-SIGNIFICANT"
  711. print(f"{label}: t = {t_val:.3f}, p = {p_val:.4f}, FDR p = {p_fdr:.4f} [{significance}]")
  712. # %% [markdown]
  713. # # nodal metrics
  714. # %%
  715. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  716. session_labels = {
  717. "ses-1": "baseline",
  718. "ses-2": "acute",
  719. "ses-3": "chronic"
  720. }
  721. metrics_list = ["Closeness", "Betweenness", "Clustering", "Degree"]
  722. n_regions = 89
  723. output_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/nodal_metrics_LMM"
  724. os.makedirs(output_dir, exist_ok=True)
  725. warnings.filterwarnings("ignore")
  726. lmm_results = []
  727. def run_lmm_with_ref(df_long, metric_name, ref_session, region_index):
  728. df_long["session"] = pd.Categorical(df_long["session"], categories=["ses-1", "ses-2", "ses-3"])
  729. df_long["session"] = df_long["session"].cat.reorder_categories([ref_session,
  730. *(s for s in ["ses-1", "ses-2", "ses-3"] if s != ref_session)])
  731. try:
  732. model = mixedlm("value ~ session", data=df_long, groups=df_long["subject_id"])
  733. result = model.fit()
  734. for param in result.fe_params.index:
  735. if param == "Intercept":
  736. continue
  737. pval = result.pvalues[param]
  738. estimate = result.fe_params[param]
  739. lmm_results.append({
  740. "Region": region_index,
  741. "Metric": metric_name,
  742. "Reference Session": ref_session,
  743. "Effect": param,
  744. "Estimate": estimate,
  745. "p-value": pval
  746. })
  747. except Exception as e:
  748. print(f"Error fitting model for {metric_name} with ref {ref_session}, region {region_index}: {e}")
  749. #loop over metrics
  750. for metric in metrics_list:
  751. print(f"\nProcessing metric: {metric}")
  752. pvals_per_region = np.full((n_regions, 3), np.nan)
  753. estimates_per_region = np.full((n_regions, 3), np.nan)
  754. for region in range(n_regions):
  755. data_long = []
  756. for ses_id in session_labels.keys():
  757. ses_path = os.path.join(base_path, ses_id)
  758. subject_paths = glob.glob(os.path.join(ses_path, "sub-*"))
  759. for sub_path in subject_paths:
  760. sub_id = os.path.basename(sub_path)
  761. graph_path = os.path.join(sub_path, "Graphs/wAALours", "graph_metrics")
  762. metric_files = glob.glob(os.path.join(graph_path, "*_metrics.csv"))
  763. for metrics_file in metric_files:
  764. try:
  765. df_metrics = pd.read_csv(metrics_file)
  766. value = df_metrics.loc[df_metrics['Node'] == region, metric].values[0]
  767. data_long.append({
  768. 'subject_id': sub_id,
  769. 'session': ses_id,
  770. 'value': value
  771. })
  772. except Exception as e:
  773. print(f"Error processing {metrics_file}: {e}")
  774. continue
  775. df_long = pd.DataFrame(data_long)
  776. if df_long['session'].nunique() < 2 or df_long['subject_id'].nunique() < 2:
  777. continue
  778. #reference sessions 1 and 2!!!
  779. run_lmm_with_ref(df_long.copy(), metric, ref_session="ses-1", region_index=region)
  780. run_lmm_with_ref(df_long.copy(), metric, ref_session="ses-2", region_index=region)
  781. df_lmm = pd.DataFrame(lmm_results)
  782. for i in range(n_regions):
  783. # p-values and estimates
  784. for j, (ref, contrast, col) in enumerate([
  785. ("ses-1", "session[T.ses-2]", 0),
  786. ("ses-1", "session[T.ses-3]", 1),
  787. ("ses-2", "session[T.ses-3]", 2)
  788. ]):
  789. match = df_lmm[(df_lmm["Metric"] == metric) & (df_lmm["Region"] == i) &
  790. (df_lmm["Reference Session"] == ref) &
  791. (df_lmm["Effect"] == contrast)]
  792. if not match.empty:
  793. pvals_per_region[i, col] = match["p-value"].values[0]
  794. estimates_per_region[i, col] = match["Estimate"].values[0]
  795. #save both results
  796. df_pvals = pd.DataFrame(pvals_per_region, columns=["ses1_vs_ses2", "ses1_vs_ses3", "ses2_vs_ses3"])
  797. df_ests = pd.DataFrame(estimates_per_region, columns=["ses1_vs_ses2", "ses1_vs_ses3", "ses2_vs_ses3"])
  798. df_pvals.to_csv(os.path.join(output_dir, f"{metric.lower()}_pvals_per_region_LMM.csv"), index=False)
  799. df_ests.to_csv(os.path.join(output_dir, f"{metric.lower()}_estimates_per_region_LMM.csv"), index=False)
  800. print(f"Saved: {metric.lower()}_pvals_per_region_LMM.csv")
  801. print(f"Saved: {metric.lower()}_estimates_per_region_LMM.csv")
  802. # %%
  803. #folder where the per-region p-value CSVs are saved
  804. output_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics"
  805. #list of metrics
  806. metrics_list = ["closeness", "betweenness", "clustering", "degree"]
  807. #load all p-values into a dictionary
  808. pvals_all = {}
  809. for metric in metrics_list:
  810. file_path = os.path.join(output_dir, f"{metric}_pvals_per_region_LMM.csv")
  811. if os.path.exists(file_path):
  812. df = pd.read_csv(file_path)
  813. pvals_all[metric.capitalize()] = df.values
  814. else:
  815. print(f"File not found: {file_path}")
  816. # %%
  817. #plot the histograms
  818. session_pairs = ['Ses-1 (control) vs Ses-2 (acute)', 'Ses-1 (control) vs Ses-3 (chronic)', 'Ses-2 (acute) vs Ses-3 (chronic)']
  819. #loop over metrics, pvals_all is the dictionary from the previous code
  820. for metric_name, pvals in pvals_all.items():
  821. print(f"\nPlotting for {metric_name}")
  822. fig, axes = plt.subplots(1, 3, figsize=(15, 4))
  823. for i, ax in enumerate(axes):
  824. ax.hist(pvals[:, i], bins=20)
  825. ax.set_title(f'{metric_name} - {session_pairs[i]}')
  826. ax.set_xlabel('p-value')
  827. ax.set_ylabel('Number of regions')
  828. plt.tight_layout()
  829. plt.show()
  830. # %% [markdown]
  831. # ## Multiple comparisons correction (nodal level)
  832. # %%
  833. correction_results = {}
  834. session_comparisons = ['ses1_vs_ses2', 'ses1_vs_ses3', 'ses2_vs_ses3']
  835. for metric_name, pvals in pvals_all.items():
  836. print(f"\n=== Multiple comparisons correction for {metric_name} ===")
  837. correction_results[metric_name] = {}
  838. for i_session, session_comparison in enumerate(session_comparisons):
  839. print(f"\n--- {session_comparison } ---")
  840. pvals_session = pvals[:, i_session] # p-values for this sessions comparison
  841. #bonferroni correction
  842. rej_bonf = bonferroni(pvals_session, alpha=0.05)
  843. print('Bonferroni reject decisions:')
  844. print(rej_bonf)
  845. # FDR correction (Benjamini-Hochberg LSU)
  846. rej_fdr = lsu(pvals_session, q=0.1)
  847. print('FDR (LSU) reject decisions:')
  848. print(rej_fdr)
  849. #save into dictionary
  850. correction_results[metric_name][session_comparison] = {
  851. "bonferroni": rej_bonf,
  852. "fdr": rej_fdr
  853. }
  854. # %%
  855. ######## plot significant ROIs of the graph on the brain #####
  856. os.makedirs("/Users/patrycjascislewska/Analizy_neuro/Graphs/significant_nodes", exist_ok=True)
  857. #ROI names
  858. roi_table_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/AAL_ours_89_regions_list.csv"
  859. roi_df = pd.read_csv(roi_table_path, sep=";")
  860. roi_df.columns = roi_df.columns.str.strip()
  861. roi_lookup = dict(zip(roi_df["Node_number"], roi_df["Region"]))
  862. #coordinates and adjacency matrix
  863. coord_path = "/Users/patrycjascislewska/Analizy_neuro/AAL3/AALours_coords_MNI.txt"
  864. coord = pd.read_csv(coord_path, sep="\s+", header=None).to_numpy()
  865. adj_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/ses-1/sub-14/Graphs/wAALours/Adj_mat_n.levels_3_n.regions_89proc_length384.cost_400.txt"
  866. adj_matrix = pd.read_csv(adj_path, sep="\s+", header=None).to_numpy()
  867. G = nx.from_numpy_array(adj_matrix)
  868. #metrics and session comparisons
  869. metrics_list = ["Closeness", "Betweenness", "Clustering", "Degree"]
  870. session_comparisons = ['ses1_vs_ses2', 'ses1_vs_ses3', 'ses2_vs_ses3']
  871. #comparison labels
  872. comparison_labels_map = {
  873. 'ses1_vs_ses2': 'Acute vs Control',
  874. 'ses1_vs_ses3': 'Chronic vs Control',
  875. 'ses2_vs_ses3': 'Chronic vs Acute'
  876. }
  877. #file mapping for LMM coefficient paths
  878. coef_file_map = {
  879. "Degree": "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/nodal_metrics_LMM/degree_estimates_per_region_LMM.csv",
  880. "Clustering": "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/nodal_metrics_LMM/clustering_estimates_per_region_LMM.csv",
  881. "Closeness": "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/nodal_metrics_LMM/closeness_estimates_per_region_LMM.csv",
  882. "Betweenness": "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/nodal_metrics_LMM/betweenness_estimates_per_region_LMM.csv"
  883. }
  884. #main plotting loop
  885. for metric_name in metrics_list:
  886. #load coefficients
  887. coef_path = coef_file_map[metric_name]
  888. coef_df = pd.read_csv(coef_path, sep=",")
  889. coef_df.columns = coef_df.columns.str.strip()
  890. for session_comparison in session_comparisons:
  891. print(f"\n=== {metric_name} - {session_comparison} ===")
  892. fdr_significant = correction_results[metric_name][session_comparison]["fdr"].astype(bool)
  893. coef_session = coef_df[session_comparison].to_numpy()
  894. #encode colors: -1 = ↓, 1 = ↑, 0 = n.s.
  895. node_colors = np.zeros_like(coef_session)
  896. node_colors[fdr_significant & (coef_session > 0)] = 1
  897. node_colors[fdr_significant & (coef_session < 0)] = -1
  898. #significant nodes with region names and coordinates
  899. print("Significant nodes (FDR-corrected):")
  900. significant_nodes_data = []
  901. for idx, (is_sig, coef) in enumerate(zip(fdr_significant, coef_session)):
  902. if is_sig:
  903. direction = "↑" if coef > 0 else "↓"
  904. roi_name = roi_lookup.get(idx, f"ROI_{idx}")
  905. coord_str = f"({coord[idx,0]:.1f}, {coord[idx,1]:.1f}, {coord[idx,2]:.1f})"
  906. print(f"Node {idx:02d} ({roi_name}): {direction} coef = {coef:.4f} | Coord: {coord_str}")
  907. significant_nodes_data.append({
  908. "Node": idx,
  909. "Region": roi_name,
  910. "X": round(coord[idx, 0], 1),
  911. "Y": round(coord[idx, 1], 1),
  912. "Z": round(coord[idx, 2], 1),
  913. "Coefficient": round(coef, 4),
  914. "Direction": "increase" if coef > 0 else "decrease"
  915. })
  916. #save significant nodes to CSV
  917. if significant_nodes_data:
  918. sig_df = pd.DataFrame(significant_nodes_data)
  919. out_path = f"/Users/patrycjascislewska/Analizy_neuro/Graphs/significant_nodes/{metric_name}_{session_comparison}_sig_nodes.csv"
  920. sig_df.to_csv(out_path, index=False)
  921. print(f"\nSaved significant node data to:\n{out_path}")
  922. #plotting setup
  923. color_map = { -1: '#0080FF', 0: '#A0A0A0', 1: '#FF8000' }
  924. node_color_list = [color_map[val] for val in node_colors]
  925. edge_color = '0.8'
  926. fig, axs = plt.subplots(1, 3, figsize=(18, 6))
  927. # Sagittal view (X-Z)
  928. pos_sagittal = {k: (coord[k, 1], coord[k, 2]) for k in range(89)}
  929. nx.draw(G, pos_sagittal, node_color=node_color_list, edge_color=edge_color,
  930. with_labels=False, node_size=100, ax=axs[0])
  931. axs[0].set_title('Sagittal view (Y-Z)')
  932. # Axial view (X-Y)
  933. pos_axial = {k: (coord[k, 1], coord[k, 0]) for k in range(89)}
  934. nx.draw(G, pos_axial, node_color=node_color_list, edge_color=edge_color,
  935. with_labels=False, node_size=100, ax=axs[1])
  936. axs[1].set_title('Axial view (X-Y)')
  937. # Coronal view (Y-Z)
  938. pos_coronal = {k: (coord[k, 0], coord[k, 2]) for k in range(89)}
  939. nx.draw(G, pos_coronal, node_color=node_color_list, edge_color=edge_color,
  940. with_labels=False, node_size=100, ax=axs[2])
  941. axs[2].set_title('Coronal view (X-Z)')
  942. # Legend
  943. legend_elements = [
  944. Line2D([0], [0], marker='o', color='w', label='Significant increase',
  945. markerfacecolor='#FF8000', markersize=10),
  946. Line2D([0], [0], marker='o', color='w', label='Significant decrease',
  947. markerfacecolor='#0080FF', markersize=10),
  948. Line2D([0], [0], marker='o', color='w', label='Not significant',
  949. markerfacecolor='#A0A0A0', markersize=10)
  950. ]
  951. axs[2].legend(handles=legend_elements, loc='upper right', fontsize=10)
  952. fig.suptitle(
  953. f"{metric_name} - {comparison_labels_map.get(session_comparison, session_comparison)}",
  954. fontsize=16
  955. )
  956. plt.tight_layout()
  957. plt.show()
  958. # %% [markdown]
  959. # ## Nodal results on the glass brain template
  960. # %%
  961. ##### plotting significant nodes (only) on the glass brain using netplotbrain package
  962. from netplotbrain import plot
  963. sig_nodes_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/significant_nodes"
  964. output_dir = os.path.join(sig_nodes_dir, "brain_plots")
  965. os.makedirs(output_dir, exist_ok=True)
  966. # all significant node CSVs
  967. sig_files = glob.glob(os.path.join(sig_nodes_dir, "*_sig_nodes.csv"))
  968. # color mapping for directions
  969. direction_color_map = {
  970. "increase": "#FF8000", # orange
  971. "decrease": "#0080FF" # blue
  972. }
  973. #loop through all "siginificant_nodes" files and and plot
  974. for file in sig_files:
  975. df = pd.read_csv(file)
  976. if df.empty:
  977. continue
  978. #extract metric and comparison from filename
  979. basename = os.path.basename(file)
  980. parts = basename.replace("_sig_nodes.csv", "").split("_")
  981. metric = parts[0]
  982. comparison = "_".join(parts[1:])
  983. #prepare plotting df to fit the netplotbrain requirements
  984. df_plot = df.copy()
  985. df_plot["x"] = df_plot["X"]
  986. df_plot["y"] = df_plot["Y"]
  987. df_plot["z"] = df_plot["Z"]
  988. df_plot["label"] = df_plot["Region"]
  989. df_plot["color"] = df_plot["Direction"].map(direction_color_map)
  990. fig, ax = plot(
  991. nodes=df_plot,
  992. node_color="color",
  993. node_size=20,
  994. node_labels=False,
  995. view="APLRISs", # here we can change views
  996. template='MNI152NLin6Asym',
  997. template_style='glass', # there is also e.g. surface
  998. title=f"{metric} - {comparison}"
  999. )
  1000. #save plot
  1001. output_path = os.path.join(output_dir, f"{metric}_{comparison}_brain.png")
  1002. fig.savefig(output_path, dpi=300)
  1003. plt.close(fig)
  1004. # %% [markdown]
  1005. # ## Within-Subject Hub Disruption Index
  1006. # %% [markdown]
  1007. # degree centrality
  1008. # %%
  1009. #here I use the degree from previously saved files with nodal metrics
  1010. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1011. ses_paths = {
  1012. "control": os.path.join(base_path, "ses-1"),
  1013. "acute": os.path.join(base_path, "ses-2"),
  1014. "chronic": os.path.join(base_path, "ses-3")
  1015. }
  1016. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1017. #subject IDs in all 3 sessions
  1018. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1019. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1020. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1021. common_ids = sorted(list(ids_ses1 & ids_ses2 & ids_ses3))
  1022. print(f"Subjects present in all 3 sessions: {len(common_ids)}")
  1023. #load degree vectors from metrics CSV (previously saved)
  1024. def load_degree_vector(subject_id, session):
  1025. folder = os.path.join(ses_paths[session], subject_id)
  1026. files = glob.glob(os.path.join(folder, metrics_pattern))
  1027. if not files:
  1028. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1029. df = pd.read_csv(files[0])
  1030. return df["Degree_centrality"].values
  1031. #plot multiple histograms - one per subject per comparison
  1032. fig, axes = plt.subplots(4, len(common_ids), figsize=(4 * len(common_ids), 12), sharey=True)
  1033. #kappas storage
  1034. kappa_acute_vs_control = []
  1035. kappa_chronic_vs_control = []
  1036. kappa_chronic_vs_acute = []
  1037. kappa_acute_vs_chronic = []
  1038. #main loop
  1039. for i, subject_id in enumerate(common_ids):
  1040. deg_control = load_degree_vector(subject_id, "control")
  1041. deg_acute = load_degree_vector(subject_id, "acute")
  1042. deg_chronic = load_degree_vector(subject_id, "chronic")
  1043. model = LinearRegression()
  1044. # Acute vs control
  1045. x = deg_control.reshape((-1, 1))
  1046. y = deg_acute - deg_control
  1047. model.fit(x, y)
  1048. kappa_acute_vs_control.append(model.coef_[0])
  1049. axes[0, i].scatter(deg_control, y, alpha=0.7)
  1050. axes[0, i].plot(deg_control, model.predict(x), color='red')
  1051. axes[0, i].set_title(f"{subject_id}\nκ1={model.coef_[0]:.3f}")
  1052. if i == 0:
  1053. axes[0, i].set_ylabel("Δ Degree (Acute - Control)")
  1054. # Chronic vs control
  1055. y = deg_chronic - deg_control
  1056. model.fit(x, y)
  1057. kappa_chronic_vs_control.append(model.coef_[0])
  1058. axes[1, i].scatter(deg_control, y, alpha=0.7)
  1059. axes[1, i].plot(deg_control, model.predict(x), color='red')
  1060. axes[1, i].set_title(f"κ2={model.coef_[0]:.3f}")
  1061. if i == 0:
  1062. axes[1, i].set_ylabel("Δ Degree (Chronic - Control)")
  1063. # Chronic vs Acute
  1064. x = deg_acute.reshape((-1, 1))
  1065. y = deg_chronic - deg_acute
  1066. model.fit(x, y)
  1067. kappa_chronic_vs_acute.append(model.coef_[0])
  1068. axes[2, i].scatter(deg_acute, y, alpha=0.7)
  1069. axes[2, i].plot(deg_acute, model.predict(x), color='red')
  1070. axes[2, i].set_title(f"κ3={model.coef_[0]:.3f}")
  1071. if i == 0:
  1072. axes[2, i].set_ylabel("Δ Degree (Chronic - Acute)")
  1073. # Acute vs Chronic
  1074. x = deg_chronic.reshape((-1, 1))
  1075. y = deg_acute - deg_chronic
  1076. model.fit(x, y)
  1077. kappa_acute_vs_chronic.append(model.coef_[0])
  1078. axes[3, i].scatter(deg_chronic, y, alpha=0.7)
  1079. axes[3, i].plot(deg_chronic, model.predict(x), color='red')
  1080. axes[3, i].set_title(f"κ4={model.coef_[0]:.3f}")
  1081. if i == 0:
  1082. axes[3, i].set_ylabel("Δ Degree (Acute - Chronic)")
  1083. #plot
  1084. plt.suptitle("Within subject 'HDI' scatterplots using degree", fontsize=16)
  1085. plt.tight_layout(rect=[0, 0, 1, 0.95])
  1086. plt.show()
  1087. # %%
  1088. # output_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject"
  1089. # os.makedirs(output_dir, exist_ok=True)
  1090. # output_path = os.path.join(output_dir, "kappas_degree.csv")
  1091. # df_kappa.to_csv(output_path, index=False)
  1092. # %% [markdown]
  1093. # closenness centrality
  1094. # %%
  1095. #here I use the closeness values from previously saved files with nodal metrics
  1096. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1097. ses_paths = {
  1098. "control": os.path.join(base_path, "ses-1"),
  1099. "acute": os.path.join(base_path, "ses-2"),
  1100. "chronic": os.path.join(base_path, "ses-3")
  1101. }
  1102. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1103. #subject IDs in all 3 sessions
  1104. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1105. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1106. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1107. common_ids = sorted(list(ids_ses1 & ids_ses2 & ids_ses3))
  1108. print(f"Subjects present in all 3 sessions: {len(common_ids)}")
  1109. #load closeness vectors from metrics CSV (previously saved)
  1110. def load_Closeness_vector(subject_id, session):
  1111. folder = os.path.join(ses_paths[session], subject_id)
  1112. files = glob.glob(os.path.join(folder, metrics_pattern))
  1113. if not files:
  1114. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1115. df = pd.read_csv(files[0])
  1116. return df["Closeness"].values
  1117. #plot multiple histograms - one per subject per comparison
  1118. fig, axes = plt.subplots(4, len(common_ids), figsize=(4 * len(common_ids), 12), sharey=True)
  1119. #kappas storage
  1120. kappa_acute_vs_control = []
  1121. kappa_chronic_vs_control = []
  1122. kappa_chronic_vs_acute = []
  1123. kappa_acute_vs_chronic = []
  1124. #main loop
  1125. for i, subject_id in enumerate(common_ids):
  1126. Closeness_control = load_Closeness_vector(subject_id, "control")
  1127. Closeness_acute = load_Closeness_vector(subject_id, "acute")
  1128. Closeness_chronic = load_Closeness_vector(subject_id, "chronic")
  1129. model = LinearRegression()
  1130. # Acute vs control
  1131. x = Closeness_control.reshape((-1, 1))
  1132. y = Closeness_acute - Closeness_control
  1133. kappa_acute_vs_control.append(model.coef_[0])
  1134. axes[0, i].scatter(Closeness_control, y, alpha=0.7)
  1135. axes[0, i].plot(Closeness_control, model.predict(x), color='red')
  1136. axes[0, i].set_title(f"{subject_id}\nκ1={model.coef_[0]:.3f}")
  1137. if i == 0:
  1138. axes[0, i].set_ylabel("Δ Closeness (Acute - Control)")
  1139. # Chronic vs control
  1140. y = Closeness_chronic - Closeness_control
  1141. model.fit(x, y)
  1142. kappa_chronic_vs_control.append(model.coef_[0])
  1143. axes[1, i].scatter(Closeness_control, y, alpha=0.7)
  1144. axes[1, i].plot(Closeness_control, model.predict(x), color='red')
  1145. axes[1, i].set_title(f"κ2={model.coef_[0]:.3f}")
  1146. if i == 0:
  1147. axes[1, i].set_ylabel("Δ Closeness (Chronic - Control)")
  1148. # Chronic vs Acute
  1149. x = Closeness_acute.reshape((-1, 1))
  1150. y = Closeness_chronic - Closeness_acute
  1151. model.fit(x, y)
  1152. kappa_chronic_vs_acute.append(model.coef_[0])
  1153. axes[2, i].scatter(Closeness_acute, y, alpha=0.7)
  1154. axes[2, i].plot(Closeness_acute, model.predict(x), color='red')
  1155. axes[2, i].set_title(f"κ3={model.coef_[0]:.3f}")
  1156. if i == 0:
  1157. axes[2, i].set_ylabel("Δ Closeness (Chronic - Acute)")
  1158. # Acute vs Chronic
  1159. x = Closeness_chronic.reshape((-1, 1))
  1160. y = Closeness_acute - Closeness_chronic
  1161. model.fit(x, y)
  1162. kappa_acute_vs_chronic.append(model.coef_[0])
  1163. axes[3, i].scatter(Closeness_chronic, y, alpha=0.7)
  1164. axes[3, i].plot(Closeness_chronic, model.predict(x), color='red')
  1165. axes[3, i].set_title(f"κ4={model.coef_[0]:.3f}")
  1166. if i == 0:
  1167. axes[3, i].set_ylabel("Δ Closeness (Acute - Chronic)")
  1168. #plot
  1169. plt.suptitle("Within subject 'HDI' scatterplots using Closeness", fontsize=16)
  1170. plt.tight_layout(rect=[0, 0, 1, 0.95])
  1171. plt.show()
  1172. # %%
  1173. # output_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject"
  1174. # os.makedirs(output_dir, exist_ok=True)
  1175. # output_path = os.path.join(output_dir, "kappas_closeness.csv")
  1176. # df_kappa.to_csv(output_path, index=False)
  1177. # %% [markdown]
  1178. # Clustering coefficient
  1179. # %%
  1180. #here I use the clustering coefficients from previously saved files with nodal metrics
  1181. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1182. ses_paths = {
  1183. "control": os.path.join(base_path, "ses-1"),
  1184. "acute": os.path.join(base_path, "ses-2"),
  1185. "chronic": os.path.join(base_path, "ses-3")
  1186. }
  1187. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1188. #subject IDs in all 3 sessions
  1189. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1190. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1191. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1192. common_ids = sorted(list(ids_ses1 & ids_ses2 & ids_ses3))
  1193. print(f"Subjects present in all 3 sessions: {len(common_ids)}")
  1194. #load clustering vectors from metrics CSV (previously saved)
  1195. def load_Clustering_vector(subject_id, session):
  1196. folder = os.path.join(ses_paths[session], subject_id)
  1197. files = glob.glob(os.path.join(folder, metrics_pattern))
  1198. if not files:
  1199. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1200. df = pd.read_csv(files[0])
  1201. return df["Clustering"].values
  1202. #plot multiple histograms - one per subject per comparison
  1203. fig, axes = plt.subplots(4, len(common_ids), figsize=(4 * len(common_ids), 12), sharey=True)
  1204. #kappas storage
  1205. kappa_acute_vs_control = []
  1206. kappa_chronic_vs_control = []
  1207. kappa_chronic_vs_acute = []
  1208. kappa_acute_vs_chronic = []
  1209. #main loop
  1210. for i, subject_id in enumerate(common_ids):
  1211. Clustering_control = load_Clustering_vector(subject_id, "control")
  1212. Clustering_acute = load_Clustering_vector(subject_id, "acute")
  1213. Clustering_chronic = load_Clustering_vector(subject_id, "chronic")
  1214. model = LinearRegression()
  1215. # Acute vs control
  1216. x = Clustering_control.reshape((-1, 1))
  1217. y = Clustering_acute - Clustering_control
  1218. model.fit(x, y)
  1219. kappa_acute_vs_control.append(model.coef_[0])
  1220. axes[0, i].scatter(Clustering_control, y, alpha=0.7)
  1221. axes[0, i].plot(Clustering_control, model.predict(x), color='red')
  1222. axes[0, i].set_title(f"{subject_id}\nκ1={model.coef_[0]:.3f}")
  1223. if i == 0:
  1224. axes[0, i].set_ylabel("Δ Clustering (Acute - Control)")
  1225. # Chronic vs control
  1226. y = Clustering_chronic - Clustering_control
  1227. model.fit(x, y)
  1228. kappa_chronic_vs_control.append(model.coef_[0])
  1229. axes[1, i].scatter(Clustering_control, y, alpha=0.7)
  1230. axes[1, i].plot(Clustering_control, model.predict(x), color='red')
  1231. axes[1, i].set_title(f"κ2={model.coef_[0]:.3f}")
  1232. if i == 0:
  1233. axes[1, i].set_ylabel("Δ Clustering (Chronic - Control)")
  1234. # Chronic vs Acute
  1235. x = Clustering_acute.reshape((-1, 1))
  1236. y = Clustering_chronic - Clustering_acute
  1237. model.fit(x, y)
  1238. kappa_chronic_vs_acute.append(model.coef_[0])
  1239. axes[2, i].scatter(Clustering_acute, y, alpha=0.7)
  1240. axes[2, i].plot(Clustering_acute, model.predict(x), color='red')
  1241. axes[2, i].set_title(f"κ3={model.coef_[0]:.3f}")
  1242. if i == 0:
  1243. axes[2, i].set_ylabel("Δ Clustering (Chronic - Acute)")
  1244. # Acute vs Chronic
  1245. x = Clustering_chronic.reshape((-1, 1))
  1246. y = Clustering_acute - Clustering_chronic
  1247. model.fit(x, y)
  1248. kappa_acute_vs_chronic.append(model.coef_[0])
  1249. axes[3, i].scatter(Clustering_chronic, y, alpha=0.7)
  1250. axes[3, i].plot(Clustering_chronic, model.predict(x), color='red')
  1251. axes[3, i].set_title(f"κ4={model.coef_[0]:.3f}")
  1252. if i == 0:
  1253. axes[3, i].set_ylabel("Δ Clustering (Acute - Chronic)")
  1254. #plot
  1255. plt.suptitle("Within subject 'HDI' scatterplots using Clustering", fontsize=16)
  1256. plt.tight_layout(rect=[0, 0, 1, 0.95])
  1257. plt.show()
  1258. # %%
  1259. # output_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject"
  1260. # os.makedirs(output_dir, exist_ok=True)
  1261. # output_path = os.path.join(output_dir, "kappas_clustering.csv")
  1262. # df_kappa.to_csv(output_path, index=False)
  1263. # %% [markdown]
  1264. # ## Validation of within-subject Hub Disruption Index (permutations)
  1265. # %%
  1266. ## kappa within-subject - kappa group ###
  1267. #real degree vectors from our csv files
  1268. def load_degree_vector(subject_id, session):
  1269. folder = os.path.join(ses_paths[session], subject_id)
  1270. files = glob.glob(os.path.join(folder, metrics_pattern))
  1271. if not files:
  1272. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1273. df = pd.read_csv(files[0])
  1274. return df["Degree_centrality"].values
  1275. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1276. ses_paths = {
  1277. "control": os.path.join(base_path, "ses-1"),
  1278. "acute": os.path.join(base_path, "ses-2")
  1279. }
  1280. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1281. #subject IDs present in both sessions
  1282. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1283. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1284. common_ids = sorted(list(ids_ses1 & ids_ses2))
  1285. print(f"Subjects in both control and acute: {len(common_ids)}")
  1286. #load degree vectors
  1287. deg_control_all = []
  1288. deg_acute_all = []
  1289. for subject_id in common_ids:
  1290. deg_control = load_degree_vector(subject_id, "control")
  1291. deg_acute = load_degree_vector(subject_id, "acute")
  1292. deg_control_all.append(deg_control)
  1293. deg_acute_all.append(deg_acute)
  1294. deg_control_all = np.array(deg_control_all)
  1295. deg_acute_all = np.array(deg_acute_all)
  1296. #compute group-averaged control degree vector
  1297. group_control_mean = np.mean(deg_control_all, axis=0)
  1298. #compute real kappa differences (within kappa - group average kappa)
  1299. real_diffs = []
  1300. model = LinearRegression()
  1301. for i in range(len(common_ids)):
  1302. subj_control = deg_control_all[i]
  1303. subj_acute = deg_acute_all[i]
  1304. # 1. Within-subject
  1305. delta_within = subj_acute - subj_control
  1306. model.fit(subj_control.reshape(-1, 1), delta_within)
  1307. kappa_within = model.coef_[0]
  1308. # 2. Group-based
  1309. delta_groupmean = subj_acute - group_control_mean
  1310. model.fit(group_control_mean.reshape(-1, 1), delta_groupmean)
  1311. kappa_groupmean = model.coef_[0]
  1312. real_diffs.append(kappa_within - kappa_groupmean)
  1313. real_diffs = np.array(real_diffs)
  1314. real_mean_diff = np.mean(real_diffs)
  1315. print(f"Real mean (κ_within - κ_groupmean): {real_mean_diff:.4f}")
  1316. # PERMUTATIONS - Shuffle node order in chronic session
  1317. n_iterations = 10000
  1318. null_diffs = np.zeros(n_iterations)
  1319. for it in range(n_iterations):
  1320. diffs = []
  1321. for i in range(len(common_ids)):
  1322. subj_control = deg_control_all[i]
  1323. subj_acute = np.random.permutation(deg_acute_all[i]) # shuffle node order
  1324. # 1. Within-subject
  1325. delta_within = subj_acute - subj_control
  1326. model.fit(subj_control.reshape(-1, 1), delta_within)
  1327. kappa_within = model.coef_[0]
  1328. # 2. Group-based
  1329. delta_groupmean = subj_acute - group_control_mean
  1330. model.fit(group_control_mean.reshape(-1, 1), delta_groupmean)
  1331. kappa_groupmean = model.coef_[0]
  1332. diffs.append(kappa_within - kappa_groupmean)
  1333. null_diffs[it] = np.mean(diffs)
  1334. #plot
  1335. plt.figure(figsize=(10, 5))
  1336. plt.hist(null_diffs, bins=30, alpha=0.7)
  1337. plt.axvline(real_mean_diff, color='red', linestyle='dashed', linewidth=2,
  1338. label=f"Real mean diff = {real_mean_diff:.3f}")
  1339. plt.xlabel("Mean κ_within − κ_groupmean (permutation distribution)")
  1340. plt.ylabel("Frequency")
  1341. plt.title("Permutations distribution of 'kappa within - kappa group' after node shuffling \n(acute nodes shuffled, control-acute pairs kept in order)")
  1342. plt.legend()
  1343. plt.tight_layout()
  1344. plt.show()
  1345. # P-value
  1346. p_value = np.mean(null_diffs <= real_mean_diff)
  1347. print(f"P-value: {p_value:.4f}")
  1348. # %%
  1349. ## the "mixed WITHIN PAIRS" aproach ##
  1350. #real degree vectors from our csv files
  1351. def load_degree_vector(subject_id, session):
  1352. folder = os.path.join(ses_paths[session], subject_id)
  1353. files = glob.glob(os.path.join(folder, metrics_pattern))
  1354. if not files:
  1355. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1356. df = pd.read_csv(files[0])
  1357. return df["Degree_centrality"].values
  1358. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1359. ses_paths = {
  1360. "control": os.path.join(base_path, "ses-1"),
  1361. "chronic": os.path.join(base_path, "ses-3")
  1362. }
  1363. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1364. #subject IDs present in both sessions
  1365. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1366. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1367. common_ids = sorted(list(ids_ses1 & ids_ses3))
  1368. print(f"Subjects in both control and chronic: {len(common_ids)}")
  1369. #load degree vectors
  1370. deg_control_all = []
  1371. deg_chronic_all = []
  1372. for subject_id in common_ids:
  1373. deg_control = load_degree_vector(subject_id, "control")
  1374. deg_chronic = load_degree_vector(subject_id, "chronic")
  1375. deg_control_all.append(deg_control)
  1376. deg_chronic_all.append(deg_chronic)
  1377. deg_control_all = np.array(deg_control_all)
  1378. deg_chronic_all = np.array(deg_chronic_all)
  1379. #COMPUTE REAL KAPPAS
  1380. real_kappas = []
  1381. model = LinearRegression()
  1382. for i in range(len(common_ids)):
  1383. x = deg_control_all[i].reshape(-1, 1)
  1384. y = deg_chronic_all[i] - deg_control_all[i]
  1385. model.fit(x, y)
  1386. real_kappas.append(model.coef_[0])
  1387. real_kappas = np.array(real_kappas)
  1388. real_kappa_mean = np.mean(real_kappas)
  1389. print(f"Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  1390. n_iterations = 10000
  1391. null_kappa_means = np.zeros(n_iterations)
  1392. for it in range(n_iterations):
  1393. perm_kappas = []
  1394. for i in range(len(common_ids)):
  1395. x = deg_control_all[i].reshape(-1, 1)
  1396. delta = deg_chronic_all[i] - deg_control_all[i]
  1397. if np.random.rand() < 0.5:
  1398. delta = -delta
  1399. model.fit(x, delta)
  1400. perm_kappas.append(model.coef_[0])
  1401. null_kappa_means[it] = np.mean(perm_kappas)
  1402. #plot
  1403. plt.figure(figsize=(10, 5))
  1404. plt.hist(null_kappa_means, bins=30, alpha=0.7)
  1405. plt.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2,
  1406. label=f"Real κ = {real_kappa_mean:.3f}")
  1407. plt.xlabel("Mean kappa (permutation distribution)")
  1408. plt.ylabel("Frequency")
  1409. plt.title("Permutations - distribution of mean kappa (chronic vs control) \n (flip signs of Δy ONLY and keep x axis the same)\n mixed WITHIN pairs")
  1410. plt.legend()
  1411. plt.tight_layout()
  1412. plt.show()
  1413. # %%
  1414. ## the "mixed WITHIN PAIRS" aproach ##
  1415. #real vectors from our csv files
  1416. def load_metric_vector(subject_id, session, metric_name):
  1417. folder = os.path.join(ses_paths[session], subject_id)
  1418. files = glob.glob(os.path.join(folder, metrics_pattern))
  1419. if not files:
  1420. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1421. df = pd.read_csv(files[0])
  1422. return df[metric_name].values
  1423. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1424. ses_paths = {
  1425. "control": os.path.join(base_path, "ses-1"),
  1426. "acute": os.path.join(base_path, "ses-2")
  1427. }
  1428. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1429. #subject IDs present in both sessions
  1430. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1431. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1432. common_ids = sorted(list(ids_ses1 & ids_ses2))
  1433. print(f"Subjects in both control and acute: {len(common_ids)}")
  1434. metric_names = ["Degree_centrality", "Closeness", "Clustering"]
  1435. real_kappa_means = {}
  1436. null_kappa_distributions = {}
  1437. pvals_uncorrected = {}
  1438. cohen_ds = {}
  1439. # loop over each metric
  1440. for metric_name in metric_names:
  1441. #load metric vectors
  1442. control_all = []
  1443. acute_all = []
  1444. for subject_id in common_ids:
  1445. control = load_metric_vector(subject_id, "control", metric_name)
  1446. acute = load_metric_vector(subject_id, "acute", metric_name)
  1447. control_all.append(control)
  1448. acute_all.append(acute)
  1449. control_all = np.array(control_all)
  1450. acute_all = np.array(acute_all)
  1451. #COMPUTE REAL KAPPAS
  1452. real_kappas = []
  1453. model = LinearRegression()
  1454. for i in range(len(common_ids)):
  1455. x = control_all[i].reshape(-1, 1)
  1456. y = acute_all[i] - control_all[i]
  1457. model.fit(x, y)
  1458. real_kappas.append(model.coef_[0])
  1459. real_kappas = np.array(real_kappas)
  1460. real_kappa_mean = np.mean(real_kappas)
  1461. real_kappa_means[metric_name] = real_kappa_mean
  1462. print(f"{metric_name} - Real mean kappa (acute vs control): {real_kappa_mean:.4f}")
  1463. n_iterations = 10000
  1464. null_kappa_means = np.zeros(n_iterations)
  1465. for it in range(n_iterations):
  1466. perm_kappas = []
  1467. for i in range(len(common_ids)):
  1468. x = control_all[i].reshape(-1, 1)
  1469. delta = acute_all[i] - control_all[i]
  1470. if np.random.rand() < 0.5:
  1471. delta = -delta
  1472. model.fit(x, delta)
  1473. perm_kappas.append(model.coef_[0])
  1474. null_kappa_means[it] = np.mean(perm_kappas)
  1475. null_kappa_distributions[metric_name] = null_kappa_means
  1476. #p-value (two-tailed), this counts how many null values are as extreme or more extreme than the absolute value of my observed effect.
  1477. p_value_two_tailed = (np.sum(np.abs(null_kappa_means) >= np.abs(real_kappa_mean)) + 1) / (n_iterations + 1)
  1478. pvals_uncorrected[metric_name] = p_value_two_tailed
  1479. #Cohen's d
  1480. mean_null = np.mean(null_kappa_means)
  1481. std_null = np.std(null_kappa_means, ddof=1)
  1482. cohen_d = (real_kappa_mean - mean_null) / std_null
  1483. cohen_ds[metric_name] = cohen_d
  1484. #FDR CORRECTION
  1485. pvals_list = [pvals_uncorrected[m] for m in metric_names]
  1486. rejects, pvals_corrected = fdrcorrection(pvals_list, alpha=0.05)
  1487. print("\n==== FDR-corrected results (acute vs control, α = 0.05) ====")
  1488. for i, metric_name in enumerate(metric_names):
  1489. print(f"{metric_name}:\n"
  1490. f" Real κ = {real_kappa_means[metric_name]:.4f}\n"
  1491. f" Uncorrected p = {pvals_uncorrected[metric_name]:.4f}\n"
  1492. f" FDR-corrected p = {pvals_corrected[i]:.4f}\n"
  1493. f" Significant after FDR: {rejects[i]}\n"
  1494. f" Cohen's d = {cohen_ds[metric_name]:.2f}\n")
  1495. #plot each metric
  1496. for i, metric_name in enumerate(metric_names):
  1497. plt.figure(figsize=(10, 5))
  1498. plt.hist(null_kappa_distributions[metric_name], bins=30, alpha=0.7, edgecolor="black")
  1499. plt.axvline(real_kappa_means[metric_name], color='red', linestyle='dashed', linewidth=4)
  1500. plt.text(real_kappa_means[metric_name], plt.ylim()[1]*0.7,
  1501. f"Real mean κ = {real_kappa_means[metric_name]:.3f}\n"
  1502. f"Cohen's d = {cohen_ds[metric_name]:.2f}",
  1503. color='black', fontsize=10, ha='left', va='top')
  1504. plt.xlabel("κ", fontsize=20)
  1505. plt.ylabel("Frequency", fontsize=20)
  1506. plt.title(f"Permutation test: {metric_name.replace('_', ' ')} (acute vs control)")
  1507. plt.tick_params(axis='x', labelsize = 20)
  1508. plt.tick_params(axis='y', labelsize = 20)
  1509. # plt.legend()
  1510. plt.tight_layout()
  1511. plt.show()
  1512. # %%
  1513. ## the "mixed WITHIN PAIRS" aproach ##
  1514. #real vectors from our csv files
  1515. def load_metric_vector(subject_id, session, metric_name):
  1516. folder = os.path.join(ses_paths[session], subject_id)
  1517. files = glob.glob(os.path.join(folder, metrics_pattern))
  1518. if not files:
  1519. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1520. df = pd.read_csv(files[0])
  1521. return df[metric_name].values
  1522. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1523. ses_paths = {
  1524. "control": os.path.join(base_path, "ses-1"),
  1525. "chronic": os.path.join(base_path, "ses-3")
  1526. }
  1527. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1528. #subject IDs present in both sessions
  1529. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1530. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1531. common_ids = sorted(list(ids_ses1 & ids_ses3))
  1532. print(f"Subjects in both control and chronic: {len(common_ids)}")
  1533. metric_names = ["Degree_centrality", "Closeness", "Clustering"]
  1534. real_kappa_means = {}
  1535. null_kappa_distributions = {}
  1536. pvals_uncorrected = {}
  1537. cohen_ds = {}
  1538. # loop over each metric
  1539. for metric_name in metric_names:
  1540. #load metric vectors
  1541. control_all = []
  1542. chronic_all = []
  1543. for subject_id in common_ids:
  1544. control = load_metric_vector(subject_id, "control", metric_name)
  1545. chronic = load_metric_vector(subject_id, "chronic", metric_name)
  1546. control_all.append(control)
  1547. chronic_all.append(chronic)
  1548. control_all = np.array(control_all)
  1549. chronic_all = np.array(chronic_all)
  1550. #COMPUTE REAL KAPPAS
  1551. real_kappas = []
  1552. model = LinearRegression()
  1553. for i in range(len(common_ids)):
  1554. x = control_all[i].reshape(-1, 1)
  1555. y = chronic_all[i] - control_all[i]
  1556. model.fit(x, y)
  1557. real_kappas.append(model.coef_[0])
  1558. real_kappas = np.array(real_kappas)
  1559. real_kappa_mean = np.mean(real_kappas)
  1560. real_kappa_means[metric_name] = real_kappa_mean
  1561. print(f"{metric_name} - Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  1562. #PERMUTATIONS - COMPUTE "NEW" KAPPAS
  1563. n_iterations = 10000
  1564. null_kappa_means = np.zeros(n_iterations)
  1565. for it in range(n_iterations):
  1566. perm_kappas = []
  1567. for i in range(len(common_ids)):
  1568. x = control_all[i].reshape(-1, 1)
  1569. delta = chronic_all[i] - control_all[i]
  1570. if np.random.rand() < 0.5:
  1571. delta = -delta
  1572. model.fit(x, delta)
  1573. perm_kappas.append(model.coef_[0])
  1574. null_kappa_means[it] = np.mean(perm_kappas)
  1575. null_kappa_distributions[metric_name] = null_kappa_means
  1576. p_value_two_tailed = (np.sum(np.abs(null_kappa_means) >= np.abs(real_kappa_mean)) + 1) / (n_iterations + 1)
  1577. pvals_uncorrected[metric_name] = p_value_two_tailed
  1578. #Cohen's d
  1579. mean_null = np.mean(null_kappa_means)
  1580. std_null = np.std(null_kappa_means, ddof=1)
  1581. cohen_d = (real_kappa_mean - mean_null) / std_null
  1582. cohen_ds[metric_name] = cohen_d
  1583. #FDR CORRECTION
  1584. pvals_list = [pvals_uncorrected[m] for m in metric_names]
  1585. rejects, pvals_corrected = fdrcorrection(pvals_list, alpha=0.05)
  1586. print("\n==== FDR-corrected results (chronic vs control, α = 0.05) ====")
  1587. for i, metric_name in enumerate(metric_names):
  1588. print(f"{metric_name}:\n"
  1589. f" Real κ = {real_kappa_means[metric_name]:.4f}\n"
  1590. f" Uncorrected p = {pvals_uncorrected[metric_name]:.4f}\n"
  1591. f" FDR-corrected p = {pvals_corrected[i]:.4f}\n"
  1592. f" Significant after FDR: {rejects[i]}\n"
  1593. f" Cohen's d = {cohen_ds[metric_name]:.2f}\n")
  1594. #plot each metric
  1595. for i, metric_name in enumerate(metric_names):
  1596. plt.figure(figsize=(10, 5))
  1597. plt.hist(null_kappa_distributions[metric_name], bins=30, alpha=0.7, edgecolor="black")
  1598. plt.axvline(real_kappa_means[metric_name], color='red', linestyle='dashed', linewidth=4)
  1599. plt.text(real_kappa_means[metric_name], plt.ylim()[1]*0.7,
  1600. f"Real mean κ = {real_kappa_means[metric_name]:.3f}\n"
  1601. f"Cohen's d = {cohen_ds[metric_name]:.2f}",
  1602. color='black', fontsize=10, ha='left', va='top')
  1603. plt.xlabel("κ", fontsize=20)
  1604. plt.ylabel("Frequency", fontsize=20)
  1605. plt.title(f"Permutation test: {metric_name.replace('_', ' ')} (chronic vs control)")
  1606. plt.tick_params(axis='x', labelsize = 20)
  1607. plt.tick_params(axis='y', labelsize = 20)
  1608. # plt.legend()
  1609. plt.tight_layout()
  1610. plt.show()
  1611. # %% [markdown]
  1612. # ## permutations with histogram of real values
  1613. # %% [markdown]
  1614. # Degree centrality
  1615. # %%
  1616. ###the "mixed WITHIN PAIRS" aproach with the histogram of real values ##
  1617. #real degree vectors from our csv files
  1618. def load_degree_vector(subject_id, session):
  1619. folder = os.path.join(ses_paths[session], subject_id)
  1620. files = glob.glob(os.path.join(folder, metrics_pattern))
  1621. if not files:
  1622. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1623. df = pd.read_csv(files[0])
  1624. return df["Degree_centrality"].values
  1625. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1626. ses_paths = {
  1627. "control": os.path.join(base_path, "ses-1"),
  1628. "chronic": os.path.join(base_path, "ses-3")
  1629. }
  1630. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1631. #subject IDs present in both sessions
  1632. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1633. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1634. common_ids = sorted(list(ids_ses1 & ids_ses3))
  1635. print(f"Subjects in both control and chronic: {len(common_ids)}")
  1636. #load degree vectors
  1637. deg_control_all = []
  1638. deg_chronic_all = []
  1639. for subject_id in common_ids:
  1640. deg_control = load_degree_vector(subject_id, "control")
  1641. deg_chronic = load_degree_vector(subject_id, "chronic")
  1642. deg_control_all.append(deg_control)
  1643. deg_chronic_all.append(deg_chronic)
  1644. deg_control_all = np.array(deg_control_all)
  1645. deg_chronic_all = np.array(deg_chronic_all)
  1646. #COMPUTE REAL KAPPAS
  1647. real_kappas = []
  1648. model = LinearRegression()
  1649. for i in range(len(common_ids)):
  1650. x = deg_control_all[i].reshape(-1, 1)
  1651. y = deg_chronic_all[i] - deg_control_all[i]
  1652. model.fit(x, y)
  1653. real_kappas.append(model.coef_[0])
  1654. real_kappas = np.array(real_kappas)
  1655. real_kappa_mean = np.mean(real_kappas)
  1656. print(f"Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  1657. #PERMUTATIONS - COMPUTE "NEW" KAPPAS
  1658. n_iterations = 10000
  1659. null_kappa_means = np.zeros(n_iterations)
  1660. for it in range(n_iterations):
  1661. perm_kappas = []
  1662. for i in range(len(common_ids)):
  1663. x = deg_control_all[i].reshape(-1, 1)
  1664. delta = deg_chronic_all[i] - deg_control_all[i]
  1665. if np.random.rand() < 0.5:
  1666. delta = -delta
  1667. model.fit(x, delta)
  1668. perm_kappas.append(model.coef_[0])
  1669. null_kappa_means[it] = np.mean(perm_kappas)
  1670. #load real participant kappa values
  1671. real_kappa_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject/kappas_degree.csv"
  1672. kappa_df = pd.read_csv(real_kappa_path, sep=",")
  1673. real_participant_kappas = kappa_df["κ_Chronic_vs_Control"].dropna().values # drop NAs just in case
  1674. # null_mean = np.mean(null_kappa_means)
  1675. # extreme_count = np.sum(np.abs(null_kappa_means - null_mean) >= np.abs(real_kappa_mean - null_mean))
  1676. # p_value_two_sided = extreme_count / n_iterations
  1677. # print(f"Two-sided P-value: {p_value_two_sided:.4f}")
  1678. #plot
  1679. fig, ax1 = plt.subplots(figsize=(10, 5))
  1680. #histogram of permuted kappa means (null distribution)
  1681. color1 = "darkorange"
  1682. counts1, bins1, patches1 = ax1.hist(real_participant_kappas, bins=12, alpha=0.8, color=color1, edgecolor="black")
  1683. ax1.set_ylabel("Frequency (real participants)", color=color1, fontsize=20)
  1684. ax1.tick_params(axis='y', labelcolor=color1, labelsize=20)
  1685. ax1.set_ylim(0, 8)
  1686. ax1.tick_params(axis='x', labelsize=20)
  1687. #secondary axis for real participants' kappa distribution
  1688. ax2 = ax1.twinx()
  1689. color2 = "grey"
  1690. counts2, bins2, patches2 = ax2.hist(null_kappa_means, bins=20, alpha=0.6, color=color2, edgecolor="black")
  1691. ax2.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2, label=f"Real κ mean = {real_kappa_mean:.3f}")
  1692. ax2.set_xlabel("κ value")
  1693. ax2.set_ylabel("Frequency (permutations)", color=color2, fontsize=20)
  1694. ax2.tick_params(axis='y', labelcolor=color2, labelsize=20)
  1695. #add legends for both histograms
  1696. lines1, labels1 = ax1.get_legend_handles_labels()
  1697. lines2, labels2 = ax2.get_legend_handles_labels()
  1698. ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left", fontsize=14)
  1699. # # Add p-value text to plot
  1700. # ax1.text(0.95, 0.95, f'p = {p_value_two_sided:.4f}', transform=ax1.transAxes,
  1701. # fontsize=12, verticalalignment='top', horizontalalignment='right',
  1702. # bbox=dict(facecolor='white', alpha=0.8, edgecolor='black'))
  1703. plt.title("κ values distibution using degree - chronic vs control\nPermutation null ditribution vs Real participant values")
  1704. plt.tight_layout()
  1705. plt.show()
  1706. # %% [markdown]
  1707. # Closeness centrality
  1708. # %%
  1709. ## the "mixed WITHIN PAIRS" aproach with the histogram of real values ##
  1710. #real closeness vectors from our csv files
  1711. def load_closeness_vector(subject_id, session):
  1712. folder = os.path.join(ses_paths[session], subject_id)
  1713. files = glob.glob(os.path.join(folder, metrics_pattern))
  1714. if not files:
  1715. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1716. df = pd.read_csv(files[0])
  1717. return df["Closeness"].values
  1718. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1719. ses_paths = {
  1720. "control": os.path.join(base_path, "ses-1"),
  1721. "chronic": os.path.join(base_path, "ses-3")
  1722. }
  1723. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1724. #subject IDs present in both sessions
  1725. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1726. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  1727. common_ids = sorted(list(ids_ses1 & ids_ses3))
  1728. print(f"Subjects in both control and chronic: {len(common_ids)}")
  1729. #load degree vectors
  1730. closeness_control_all = []
  1731. closeness_chronic_all = []
  1732. for subject_id in common_ids:
  1733. closeness_control = load_closeness_vector(subject_id, "control")
  1734. closeness_chronic = load_closeness_vector(subject_id, "chronic")
  1735. closeness_control_all.append(closeness_control)
  1736. closeness_chronic_all.append(closeness_chronic)
  1737. closeness_control_all = np.array(closeness_control_all)
  1738. closeness_chronic_all = np.array(closeness_chronic_all)
  1739. #COMPUTE REAL KAPPAS
  1740. real_kappas = []
  1741. model = LinearRegression()
  1742. for i in range(len(common_ids)):
  1743. x = closeness_control_all[i].reshape(-1, 1)
  1744. y = closeness_chronic_all[i] - closeness_control_all[i]
  1745. model.fit(x, y)
  1746. real_kappas.append(model.coef_[0])
  1747. real_kappas = np.array(real_kappas)
  1748. real_kappa_mean = np.mean(real_kappas)
  1749. print(f"Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  1750. #PERMUTATIONS - COMPUTE "NEW" KAPPAS
  1751. n_iterations = 10000
  1752. null_kappa_means = np.zeros(n_iterations)
  1753. for it in range(n_iterations):
  1754. perm_kappas = []
  1755. for i in range(len(common_ids)):
  1756. x = closeness_control_all[i].reshape(-1, 1)
  1757. delta = closeness_chronic_all[i] - closeness_control_all[i]
  1758. if np.random.rand() < 0.5:
  1759. delta = -delta
  1760. model.fit(x, delta)
  1761. perm_kappas.append(model.coef_[0])
  1762. null_kappa_means[it] = np.mean(perm_kappas)
  1763. # Load real participant kappa values
  1764. real_kappa_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject/kappas_closeness.csv"
  1765. kappa_df = pd.read_csv(real_kappa_path, sep=",") # Assuming it's tab-separated
  1766. real_participant_kappas = kappa_df["Chronic_vs_Control"].dropna().values # drop NAs just in case
  1767. # null_mean = np.mean(null_kappa_means)
  1768. # extreme_count = np.sum(np.abs(null_kappa_means - null_mean) >= np.abs(real_kappa_mean - null_mean))
  1769. # p_value_two_sided = extreme_count / n_iterations
  1770. # print(f"Two-sided P-value: {p_value_two_sided:.4f}")
  1771. # Create the plot
  1772. fig, ax1 = plt.subplots(figsize=(10, 5))
  1773. # Histogram of permuted kappa means (null distribution)
  1774. color1 = "darkorange"
  1775. counts1, bins1, patches1 = ax1.hist(real_participant_kappas, bins=12, alpha=0.8, color=color1, edgecolor="black")
  1776. ax1.set_ylabel("Frequency (real participants)", color=color1, fontsize=20)
  1777. ax1.tick_params(axis='y', labelcolor=color1, labelsize=20)
  1778. ax1.set_ylim(0, 8)
  1779. ax1.tick_params(axis='x', labelsize=20)
  1780. # Secondary axis for real participants' kappa distribution
  1781. ax2 = ax1.twinx()
  1782. color2 = "grey"
  1783. counts2, bins2, patches2 = ax2.hist(null_kappa_means, bins=20, alpha=0.6, color=color2, edgecolor="black")
  1784. ax2.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2, label=f"Real κ mean = {real_kappa_mean:.3f}")
  1785. ax2.set_xlabel("κ value")
  1786. ax2.set_ylabel("Frequency (permutations)", color=color2, fontsize=20)
  1787. ax2.tick_params(axis='y', labelcolor=color2, labelsize=20)
  1788. # Add legends for both histograms
  1789. lines1, labels1 = ax1.get_legend_handles_labels()
  1790. lines2, labels2 = ax2.get_legend_handles_labels()
  1791. ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left", fontsize=14)
  1792. # # Add p-value text to plot
  1793. # ax1.text(0.95, 0.95, f'p = {p_value_two_sided:.4f}', transform=ax1.transAxes,
  1794. # fontsize=12, verticalalignment='top', horizontalalignment='right',
  1795. # bbox=dict(facecolor='white', alpha=0.8, edgecolor='black'))
  1796. plt.title("κ values distibution using closeness - chronic vs control\nPermutation null ditribution vs Real participant values")
  1797. plt.tight_layout()
  1798. plt.show()
  1799. # %% [markdown]
  1800. # Clustering coefficient
  1801. # %%
  1802. ###the "mixed WITHIN PAIRS" aproach with the histogram of real values ##
  1803. #real clustering vectors from our csv files
  1804. def load_clustering_vector(subject_id, session):
  1805. folder = os.path.join(ses_paths[session], subject_id)
  1806. files = glob.glob(os.path.join(folder, metrics_pattern))
  1807. if not files:
  1808. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1809. df = pd.read_csv(files[0])
  1810. return df["Clustering"].values
  1811. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1812. ses_paths = {
  1813. "control": os.path.join(base_path, "ses-1"),
  1814. "acute": os.path.join(base_path, "ses-2")
  1815. }
  1816. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  1817. #subject IDs present in both sessions
  1818. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  1819. ids_ses2 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["acute"], "sub-*"))}
  1820. common_ids = sorted(list(ids_ses1 & ids_ses2))
  1821. print(f"Subjects in both control and acute: {len(common_ids)}")
  1822. #load degree vectors
  1823. clustering_control_all = []
  1824. clustering_acute_all = []
  1825. for subject_id in common_ids:
  1826. clustering_control = load_clustering_vector(subject_id, "control")
  1827. clustering_acute = load_clustering_vector(subject_id, "acute")
  1828. clustering_control_all.append(clustering_control)
  1829. clustering_acute_all.append(clustering_acute)
  1830. clustering_control_all = np.array(clustering_control_all)
  1831. clustering_acute_all = np.array(clustering_acute_all)
  1832. #COMPUTE REAL KAPPAS
  1833. real_kappas = []
  1834. model = LinearRegression()
  1835. for i in range(len(common_ids)):
  1836. x = clustering_control_all[i].reshape(-1, 1)
  1837. y = clustering_acute_all[i] - clustering_control_all[i]
  1838. model.fit(x, y)
  1839. real_kappas.append(model.coef_[0])
  1840. real_kappas = np.array(real_kappas)
  1841. real_kappa_mean = np.mean(real_kappas)
  1842. print(f"Real mean kappa (acute vs control): {real_kappa_mean:.4f}")
  1843. #PERMUTATIONS - COMPUTE "NEW" KAPPAS
  1844. n_iterations = 10000
  1845. null_kappa_means = np.zeros(n_iterations)
  1846. for it in range(n_iterations):
  1847. perm_kappas = []
  1848. for i in range(len(common_ids)):
  1849. x = clustering_control_all[i].reshape(-1, 1)
  1850. delta = clustering_acute_all[i] - clustering_control_all[i]
  1851. if np.random.rand() < 0.5:
  1852. delta = -delta
  1853. model.fit(x, delta)
  1854. perm_kappas.append(model.coef_[0])
  1855. null_kappa_means[it] = np.mean(perm_kappas)
  1856. # Load real participant kappa values
  1857. real_kappa_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/HDI_kappa_results_within_subject/kappas_clustering.csv"
  1858. kappa_df = pd.read_csv(real_kappa_path, sep=",")
  1859. real_participant_kappas = kappa_df["Acute_vs_Control"].dropna().values
  1860. # null_mean = np.mean(null_kappa_means)
  1861. # extreme_count = np.sum(np.abs(null_kappa_means - null_mean) >= np.abs(real_kappa_mean - null_mean))
  1862. # p_value_two_sided = extreme_count / n_iterations
  1863. # print(f"Two-sided P-value: {p_value_two_sided:.4f}")
  1864. # Create the plot
  1865. fig, ax1 = plt.subplots(figsize=(10, 5))
  1866. # Histogram of permuted kappa means (null distribution)
  1867. color1 = "darkorange"
  1868. counts1, bins1, patches1 = ax1.hist(real_participant_kappas, bins=12, alpha=0.8, color=color1, edgecolor="black")
  1869. ax1.set_ylabel("Frequency (real participants)", color=color1, fontsize=20)
  1870. ax1.tick_params(axis='y', labelcolor=color1, labelsize=20)
  1871. ax1.set_ylim(0, 8)
  1872. ax1.tick_params(axis='x', labelsize=20)
  1873. # Secondary axis for real participants' kappa distribution
  1874. ax2 = ax1.twinx()
  1875. color2 = "grey"
  1876. counts2, bins2, patches2 = ax2.hist(null_kappa_means, bins=20, alpha=0.6, color=color2, edgecolor="black")
  1877. ax2.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2, label=f"Real κ mean = {real_kappa_mean:.3f}")
  1878. ax2.set_xlabel("κ value")
  1879. ax2.set_ylabel("Frequency (permutations)", color=color2, fontsize=20)
  1880. ax2.tick_params(axis='y', labelcolor=color2, labelsize=20)
  1881. # Add legends for both histograms
  1882. lines1, labels1 = ax1.get_legend_handles_labels()
  1883. lines2, labels2 = ax2.get_legend_handles_labels()
  1884. ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left", fontsize=14)
  1885. # # Add p-value text to plot
  1886. # ax1.text(0.95, 0.95, f'p = {p_value_two_sided:.4f}', transform=ax1.transAxes,
  1887. # fontsize=12, verticalalignment='top', horizontalalignment='right',
  1888. # bbox=dict(facecolor='white', alpha=0.8, edgecolor='black'))
  1889. plt.title("κ values distibution using clustering - acute vs control\nPermutation null ditribution vs Real participant values")
  1890. plt.tight_layout()
  1891. plt.show()
  1892. # %%
  1893. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1894. sessions = {
  1895. "control": "ses-1",
  1896. "acute": "ses-2",
  1897. "chronic": "ses-3"
  1898. }
  1899. metrics_list = ["Degree_centrality", "Closeness", "Clustering"]
  1900. session_pairs = [
  1901. ("acute", "control"),
  1902. ("chronic", "control"),
  1903. ("chronic", "acute")
  1904. ]
  1905. # Custom labels for each comparison
  1906. comparison_labels = {
  1907. ("acute", "control"): "TSD vs RW",
  1908. ("chronic", "control"): "CSR vs RW",
  1909. ("chronic", "acute"): "CSR vs TSD"
  1910. }
  1911. n_iterations = 10000
  1912. def load_metric_vector(subject_id, session, metric):
  1913. folder = os.path.join(base_path, sessions[session], subject_id)
  1914. pattern = os.path.join(folder, "Graphs/wAALours/graph_metrics/*_metrics.csv")
  1915. files = glob.glob(pattern)
  1916. if not files:
  1917. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  1918. df = pd.read_csv(files[0])
  1919. return df[metric].values
  1920. # Initialize plot with metrics as rows, comparisons as columns
  1921. fig, axes = plt.subplots(len(metrics_list), len(session_pairs), figsize=(18, 12))
  1922. fig.subplots_adjust(hspace=0.4, wspace=0.3)
  1923. # Loop over all combinations
  1924. for col_idx, (ses1, ses_ref) in enumerate(session_pairs):
  1925. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(base_path, sessions[ses1], "sub-*"))}
  1926. ids_ses_ref = {os.path.basename(p) for p in glob.glob(os.path.join(base_path, sessions[ses_ref], "sub-*"))}
  1927. common_ids = sorted(list(ids_ses1 & ids_ses_ref))
  1928. print(f"{ses1} vs {ses_ref}: {len(common_ids)} subjects")
  1929. for row_idx, metric in enumerate(metrics_list):
  1930. vec_ref_all = []
  1931. vec_test_all = []
  1932. for subject_id in common_ids:
  1933. vec_ref = load_metric_vector(subject_id, ses_ref, metric)
  1934. vec_test = load_metric_vector(subject_id, ses1, metric)
  1935. vec_ref_all.append(vec_ref)
  1936. vec_test_all.append(vec_test)
  1937. vec_ref_all = np.array(vec_ref_all)
  1938. vec_test_all = np.array(vec_test_all)
  1939. group_ref_mean = np.mean(vec_ref_all, axis=0)
  1940. # Real kappa differences
  1941. model = LinearRegression()
  1942. real_diffs = []
  1943. for i in range(len(common_ids)):
  1944. delta_within = vec_test_all[i] - vec_ref_all[i]
  1945. model.fit(vec_ref_all[i].reshape(-1, 1), delta_within)
  1946. kappa_within = model.coef_[0]
  1947. delta_group = vec_test_all[i] - group_ref_mean
  1948. model.fit(group_ref_mean.reshape(-1, 1), delta_group)
  1949. kappa_group = model.coef_[0]
  1950. real_diffs.append(kappa_within - kappa_group)
  1951. real_diffs = np.array(real_diffs)
  1952. real_mean_diff = np.mean(real_diffs)
  1953. # Permutations
  1954. null_diffs = np.zeros(n_iterations)
  1955. for it in range(n_iterations):
  1956. diffs = []
  1957. for i in range(len(common_ids)):
  1958. perm_test = np.random.permutation(vec_test_all[i])
  1959. delta_within = perm_test - vec_ref_all[i]
  1960. model.fit(vec_ref_all[i].reshape(-1, 1), delta_within)
  1961. kappa_within = model.coef_[0]
  1962. delta_group = perm_test - group_ref_mean
  1963. model.fit(group_ref_mean.reshape(-1, 1), delta_group)
  1964. kappa_group = model.coef_[0]
  1965. diffs.append(kappa_within - kappa_group)
  1966. null_diffs[it] = np.mean(diffs)
  1967. # P-value
  1968. p_value = np.mean(null_diffs <= real_mean_diff)
  1969. # Plot
  1970. ax = axes[row_idx, col_idx]
  1971. ax.hist(null_diffs, bins=30, alpha=0.7, color='grey')
  1972. ax.axvline(real_mean_diff, color='red', linestyle='dashed', linewidth=2,
  1973. label=f"Real = {real_mean_diff:.3f}\nP = {p_value:.4f}")
  1974. ax.set_title(f"{comparison_labels[(ses1, ses_ref)]}", fontsize=10)
  1975. if col_idx == 0:
  1976. ax.set_ylabel(f"{metric}\nFrequency")
  1977. else:
  1978. ax.set_ylabel("")
  1979. ax.set_xlabel("κ_within − κ_group")
  1980. ax.legend()
  1981. plt.suptitle("Permutation Test: κ_within − κ_groupmean", fontsize=16, y=1.02)
  1982. plt.tight_layout()
  1983. plt.show()
  1984. # %%
  1985. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  1986. sessions = {
  1987. "control": "ses-1",
  1988. "acute": "ses-2",
  1989. "chronic": "ses-3"
  1990. }
  1991. metrics_list = ["Degree_centrality", "Closeness", "Clustering"]
  1992. session_pairs = [
  1993. ("acute", "control"),
  1994. ("chronic", "control"),
  1995. ("chronic", "acute")
  1996. ]
  1997. # Custom labels
  1998. comparison_labels = {
  1999. ("acute", "control"): "TSD vs RW",
  2000. ("chronic", "control"): "CSR vs RW",
  2001. ("chronic", "acute"): "CSR vs TSD"
  2002. }
  2003. n_iterations = 10000
  2004. # Load metric vector
  2005. def load_metric_vector(subject_id, session, metric):
  2006. folder = os.path.join(base_path, sessions[session], subject_id)
  2007. pattern = os.path.join(folder, "Graphs/wAALours/graph_metrics/*_metrics.csv")
  2008. files = glob.glob(pattern)
  2009. if not files:
  2010. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  2011. df = pd.read_csv(files[0])
  2012. return df[metric].values
  2013. # Create subplot: metrics = rows, comparisons = columns
  2014. fig, axes = plt.subplots(len(metrics_list), len(session_pairs), figsize=(18, 12))
  2015. fig.subplots_adjust(hspace=0.4, wspace=0.3)
  2016. # Loop over each comparison and metric
  2017. for col_idx, (ses1, ses_ref) in enumerate(session_pairs):
  2018. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(base_path, sessions[ses1], "sub-*"))}
  2019. ids_ses_ref = {os.path.basename(p) for p in glob.glob(os.path.join(base_path, sessions[ses_ref], "sub-*"))}
  2020. common_ids = sorted(list(ids_ses1 & ids_ses_ref))
  2021. print(f"{ses1} vs {ses_ref}: {len(common_ids)} subjects")
  2022. for row_idx, metric in enumerate(metrics_list):
  2023. vec_ref_all = []
  2024. vec_test_all = []
  2025. for subject_id in common_ids:
  2026. vec_ref = load_metric_vector(subject_id, ses_ref, metric)
  2027. vec_test = load_metric_vector(subject_id, ses1, metric)
  2028. vec_ref_all.append(vec_ref)
  2029. vec_test_all.append(vec_test)
  2030. vec_ref_all = np.array(vec_ref_all)
  2031. vec_test_all = np.array(vec_test_all)
  2032. # Compute real kappas
  2033. model = LinearRegression()
  2034. real_kappas = []
  2035. for i in range(len(common_ids)):
  2036. x = vec_ref_all[i].reshape(-1, 1)
  2037. y = vec_test_all[i] - vec_ref_all[i]
  2038. model.fit(x, y)
  2039. real_kappas.append(model.coef_[0])
  2040. real_kappas = np.array(real_kappas)
  2041. real_kappa_mean = np.mean(real_kappas)
  2042. # Permutation: mix subjects
  2043. null_kappa_means = np.zeros(n_iterations)
  2044. for it in range(n_iterations):
  2045. perm_kappas = []
  2046. perm_indices = np.random.permutation(len(common_ids))
  2047. for i in range(len(common_ids)):
  2048. x = vec_ref_all[i].reshape(-1, 1)
  2049. y = vec_test_all[perm_indices[i]] - vec_ref_all[i]
  2050. model.fit(x, y)
  2051. perm_kappas.append(model.coef_[0])
  2052. null_kappa_means[it] = np.mean(perm_kappas)
  2053. # P-value
  2054. # p_value = np.mean(null_kappa_means <= real_kappa_mean)
  2055. # Two-tailed p-value
  2056. null_mean = np.mean(null_kappa_means)
  2057. p_value = np.mean(np.abs(null_kappa_means - null_mean) >= np.abs(real_kappa_mean - null_mean))
  2058. # Plotting
  2059. ax = axes[row_idx, col_idx]
  2060. ax.hist(null_kappa_means, bins=30, alpha=0.7, color='grey')
  2061. ax.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2,
  2062. label=f"Real κ = {real_kappa_mean:.3f}\nP = {p_value:.4f}")
  2063. ax.set_title(f"{comparison_labels[(ses1, ses_ref)]}", fontsize=10)
  2064. if col_idx == 0:
  2065. ax.set_ylabel(f"{metric}\nFrequency")
  2066. else:
  2067. ax.set_ylabel("")
  2068. ax.set_xlabel("Mean κ (shuffled)")
  2069. ax.legend()
  2070. plt.suptitle("Permutation Test: Mean κ (unpaired)", fontsize=16, y=1.02)
  2071. plt.tight_layout()
  2072. plt.show()
  2073. # %%
  2074. ## the "mix the order of patients" aproach ##
  2075. #real degree vectors from our csv files
  2076. def load_degree_vector(subject_id, session):
  2077. folder = os.path.join(ses_paths[session], subject_id)
  2078. files = glob.glob(os.path.join(folder, metrics_pattern))
  2079. if not files:
  2080. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  2081. df = pd.read_csv(files[0])
  2082. return df["Degree_centrality"].values
  2083. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2084. ses_paths = {
  2085. "control": os.path.join(base_path, "ses-1"),
  2086. "chronic": os.path.join(base_path, "ses-3")
  2087. }
  2088. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  2089. #subject IDs present in both sessions
  2090. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  2091. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  2092. common_ids = sorted(list(ids_ses1 & ids_ses3))
  2093. print(f"Subjects in both control and chronic: {len(common_ids)}")
  2094. #load degree vectors
  2095. deg_control_all = []
  2096. deg_chronic_all = []
  2097. for subject_id in common_ids:
  2098. deg_control = load_degree_vector(subject_id, "control")
  2099. deg_chronic = load_degree_vector(subject_id, "chronic")
  2100. deg_control_all.append(deg_control)
  2101. deg_chronic_all.append(deg_chronic)
  2102. deg_control_all = np.array(deg_control_all)
  2103. deg_chronic_all = np.array(deg_chronic_all)
  2104. #COMPUTE REAL KAPPAS
  2105. real_kappas = []
  2106. model = LinearRegression()
  2107. for i in range(len(common_ids)):
  2108. x = deg_control_all[i].reshape(-1, 1)
  2109. y = deg_chronic_all[i] - deg_control_all[i]
  2110. model.fit(x, y)
  2111. real_kappas.append(model.coef_[0])
  2112. real_kappas = np.array(real_kappas)
  2113. real_kappa_mean = np.mean(real_kappas)
  2114. print(f"Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  2115. #PERMUTATIONS - RANDOMLY BREAK SUBJECT PAIRINGS BETWEEN CHRONIC AND CONTROL
  2116. n_iterations = 10000
  2117. null_kappa_means = np.zeros(n_iterations)
  2118. for it in range(n_iterations):
  2119. perm_kappas = []
  2120. # random permutation of chronic subjects (break the subject pairing)
  2121. permuted_chronic_indices = np.random.permutation(len(common_ids))
  2122. for i in range(len(common_ids)):
  2123. x = deg_control_all[i].reshape(-1, 1)
  2124. y = deg_chronic_all[permuted_chronic_indices[i]] - deg_control_all[i]
  2125. model.fit(x, y)
  2126. perm_kappas.append(model.coef_[0])
  2127. null_kappa_means[it] = np.mean(perm_kappas)
  2128. #plot
  2129. plt.figure(figsize=(10, 5))
  2130. plt.hist(null_kappa_means, bins=30, alpha=0.7)
  2131. plt.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2,
  2132. label=f"Real mean κ = {real_kappa_mean:.3f}")
  2133. plt.xlabel("Mean kappa (permutation distribution)")
  2134. plt.ylabel("Frequency")
  2135. plt.title("Permutations - distribution of mean kappa (chronic vs control) using degree\n (controls in correct order, chronics random)")
  2136. plt.legend()
  2137. plt.tight_layout()
  2138. plt.show()
  2139. # %%
  2140. ## the "mixed nodes" aproach ##
  2141. #real degree vectors from our csv files
  2142. def load_degree_vector(subject_id, session):
  2143. folder = os.path.join(ses_paths[session], subject_id)
  2144. files = glob.glob(os.path.join(folder, metrics_pattern))
  2145. if not files:
  2146. raise FileNotFoundError(f"No metrics file found for {subject_id} in {session}")
  2147. df = pd.read_csv(files[0])
  2148. return df["Degree_centrality"].values
  2149. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2150. ses_paths = {
  2151. "control": os.path.join(base_path, "ses-1"),
  2152. "chronic": os.path.join(base_path, "ses-3")
  2153. }
  2154. metrics_pattern = "Graphs/wAALours/graph_metrics/*_metrics.csv"
  2155. # subject IDs present in both sessions
  2156. ids_ses1 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["control"], "sub-*"))}
  2157. ids_ses3 = {os.path.basename(p) for p in glob.glob(os.path.join(ses_paths["chronic"], "sub-*"))}
  2158. common_ids = sorted(list(ids_ses1 & ids_ses3))
  2159. print(f"Subjects in both control and chronic: {len(common_ids)}")
  2160. # load degree vectors
  2161. deg_control_all = []
  2162. deg_chronic_all = []
  2163. for subject_id in common_ids:
  2164. deg_control = load_degree_vector(subject_id, "control")
  2165. deg_chronic = load_degree_vector(subject_id, "chronic")
  2166. deg_control_all.append(deg_control)
  2167. deg_chronic_all.append(deg_chronic)
  2168. deg_control_all = np.array(deg_control_all)
  2169. deg_chronic_all = np.array(deg_chronic_all)
  2170. # COMPUTE REAL KAPPAS
  2171. real_kappas = []
  2172. model = LinearRegression()
  2173. for i in range(len(common_ids)):
  2174. x = deg_control_all[i].reshape(-1, 1)
  2175. y = deg_chronic_all[i] - deg_control_all[i]
  2176. model.fit(x, y)
  2177. real_kappas.append(model.coef_[0])
  2178. real_kappas = np.array(real_kappas)
  2179. real_kappa_mean = np.mean(real_kappas)
  2180. print(f"Real mean kappa (chronic vs control): {real_kappa_mean:.4f}")
  2181. # PERMUTATIONS - shuffle node order in ONE of the vectors (e.g., chronic)
  2182. n_iterations = 10000
  2183. null_kappa_means = np.zeros(n_iterations)
  2184. for it in range(n_iterations):
  2185. perm_kappas = []
  2186. for i in range(len(common_ids)):
  2187. x = deg_control_all[i] # do not shuffle
  2188. y = deg_chronic_all[i].copy()
  2189. #shuffle node order in chronic session
  2190. y_shuffled = np.random.permutation(y)
  2191. delta = y_shuffled - x #mismatch in node correspondence, break the pairs
  2192. model.fit(x.reshape(-1, 1), delta)
  2193. perm_kappas.append(model.coef_[0])
  2194. null_kappa_means[it] = np.mean(perm_kappas)
  2195. # plot
  2196. plt.figure(figsize=(10, 5))
  2197. plt.hist(null_kappa_means, bins=30, alpha=0.7)
  2198. plt.axvline(real_kappa_mean, color='red', linestyle='dashed', linewidth=2,
  2199. label=f"Real κ = {real_kappa_mean:.3f}")
  2200. plt.xlabel("Mean kappa (permutation distribution)")
  2201. plt.ylabel("Frequency")
  2202. plt.title("Permutations - distribution of mean kappa (chronic vs control) using degree\n(node-wise shuffling WITHIN pairs)")
  2203. plt.legend()
  2204. plt.tight_layout()
  2205. plt.show()
  2206. # %% [markdown]
  2207. # ## Covariate constraint manifold learning
  2208. # %% [markdown]
  2209. # CCML degree centrality
  2210. # %%
  2211. metric = "Degree_centrality"
  2212. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2213. metric_filename_pattern = "*_metrics.csv"
  2214. #kappas file
  2215. kappa_all = pd.read_csv(os.path.join(base_path, "HDI_kappa_results_within_subject", "kappas_degree.csv"))
  2216. kappa_all.set_index("Subject_ID", inplace=True)
  2217. #subject directories
  2218. control_dir = os.path.join(base_path, "ses-2")
  2219. patient_dir = os.path.join(base_path, "ses-3")
  2220. control_subjects = sorted(glob.glob(os.path.join(control_dir, "sub-*")))
  2221. patient_subjects = sorted(glob.glob(os.path.join(patient_dir, "sub-*")))
  2222. #extract the metric for each subject
  2223. def extract_metric(subject_paths):
  2224. data = []
  2225. ids = []
  2226. for subj_path in subject_paths:
  2227. pattern = os.path.join(subj_path, "Graphs/wAALours/graph_metrics", metric_filename_pattern)
  2228. files = glob.glob(pattern)
  2229. if not files:
  2230. continue
  2231. df = pd.read_csv(files[0])
  2232. if metric not in df.columns:
  2233. continue
  2234. data.append(df[metric].values)
  2235. ids.append(os.path.basename(subj_path))
  2236. return np.array(data), ids
  2237. #extract data
  2238. control_data, control_ids = extract_metric(control_subjects)
  2239. patient_data, patient_ids = extract_metric(patient_subjects)
  2240. #just in case - clean subject IDs to match kappa file format
  2241. control_ids_cleaned = [cid.replace("sub-", "") for cid in control_ids]
  2242. patient_ids_cleaned = [pid.replace("sub-", "") for pid in patient_ids]
  2243. #extract matching kappa values
  2244. kappa_control_values = kappa_all.loc[["sub-" + cid for cid in control_ids_cleaned], "κ_Acute_vs_Control"].values
  2245. kappa_patient_values = kappa_all.loc[["sub-" + pid for pid in patient_ids_cleaned], "κ_Chronic_vs_Control"].values
  2246. #construct covariate vector
  2247. cov = np.concatenate([kappa_control_values, kappa_patient_values])
  2248. #group labels: 0 = control, 1 = patient
  2249. labels = [0] * len(control_ids) + [1] * len(patient_ids)
  2250. #subject names
  2251. subject_names = [f"T{i}" for i in range(len(control_ids))] + [f"C{i}" for i in range(len(patient_ids))]
  2252. #combine metric data
  2253. X = np.vstack([control_data, patient_data])
  2254. #save outputs for CCML (exactly the same format as in Sophie's tutorial code)
  2255. output_dir = os.path.join(base_path, "CCML_inputs_within_subjects")
  2256. os.makedirs(output_dir, exist_ok=True)
  2257. df_X = pd.DataFrame(X, index=subject_names)
  2258. df_X.to_csv(os.path.join(output_dir, f"{metric}_chronic_and_acute_vs_control.csv"))
  2259. df_cov = pd.DataFrame(cov, index=subject_names, columns=["HDI_kappa"])
  2260. df_cov.to_csv(os.path.join(output_dir, f"HDI_kappa_{metric}_chronic_and_acute_vs_control.csv"))
  2261. df_labels = pd.DataFrame(labels, index=subject_names, columns=["Group"])
  2262. df_labels.to_csv(os.path.join(output_dir, f"group_labels_{metric}_chronic_and_acute_vs_control.csv"))
  2263. print("Files for CCML saved in:", output_dir)
  2264. # %% [markdown]
  2265. # %%
  2266. def f_coord1(x,X2,cov2,alpha2,dist2):
  2267. X2 = np.vstack([X2,x])
  2268. cov2 = cov2.reshape(X2.shape)
  2269. X_tmp2 = np.hstack((alpha2*cov2,X2))
  2270. D2 = sk.metrics.pairwise_distances(X_tmp2)
  2271. return np.linalg.norm(dist2-D2)
  2272. def f_glob(X2,cov2,alpha2,dist2):
  2273. cov2 = cov2.reshape(X2.shape)
  2274. X_tmp2 = np.vstack((alpha2*cov2,X2)).T
  2275. D2 = sk.metrics.pairwise_distances(X_tmp2)
  2276. return np.linalg.norm(dist2-D2)
  2277. def f_alpha(alpha,X2,cov2,dist2):
  2278. cov2 = cov2.reshape(X2.shape)
  2279. X_tmp = np.vstack((alpha*cov2,X2))
  2280. D2 = sk.metrics.pairwise_distances(X_tmp.T)
  2281. return np.linalg.norm(dist2-D2)
  2282. dfX = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/Degree_centrality_chronic_and_acute_vs_control.csv", index_col=0)
  2283. X = dfX.to_numpy()
  2284. dfcov = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/HDI_kappa_Degree_centrality_chronic_and_acute_vs_control.csv", index_col=0)
  2285. cov = dfcov.to_numpy()
  2286. labels_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/group_labels_Degree_centrality_chronic_and_acute_vs_control.csv"
  2287. labels_df = pd.read_csv(labels_path, index_col=0)
  2288. Indiv = labels_df.index.tolist()
  2289. l = labels_df["Group"].values
  2290. #compute Euclidean distance matrix
  2291. D = sk.metrics.pairwise.pairwise_distances(X)
  2292. #initialisation aleatoire
  2293. #list_extract = sk.utils.shuffle(np.arange(X.shape[0]))
  2294. #initialize with the most remote couple of points
  2295. ll = []
  2296. ll.append(np.where(D==np.max(D))[0][0])
  2297. ll.append(np.where(D==np.max(D))[1][0])
  2298. # Add sequentially the furthest point of the previous set
  2299. while(len(ll)!=D.shape[0]):
  2300. D_tmp = D[ll]
  2301. D_tmp[:,ll] = 0
  2302. ll.append(np.where(D_tmp == np.max(D_tmp))[1][0])
  2303. list_extract = np.array(ll)
  2304. # Initialize alpha parameter with the classic Isomap
  2305. iso = sk.manifold.Isomap(n_components=2,n_neighbors=4)
  2306. iso.fit(X)
  2307. tmp = iso.embedding_[:,0]
  2308. dist = iso.dist_matrix_
  2309. delta_1 = np.max(tmp) -np.min(tmp)
  2310. delta_2 = np.max(cov) -np.min(cov)
  2311. alpha = delta_1/delta_2
  2312. #Initialize the first point at 0
  2313. X_tmp = np.zeros([1])
  2314. for i in range(1,X.shape[0]):
  2315. print("one coord",i)
  2316. cov_tmp = cov[list_extract[0:i+1]]
  2317. dist_tmp = dist[list_extract[0:i+1]][:,list_extract[0:i+1]]
  2318. opt_test = 10000
  2319. #Multi-start opitmisation
  2320. for j in range(-5,5):
  2321. xtmptmp,B,tmp,tmp1,tmp3 = opt.fmin(f_coord1,j,(X_tmp,cov_tmp,alpha,dist_tmp),xtol=0.000000001, ftol=0.000000001,maxiter=1000000,maxfun=1000000,disp = 0 , full_output = 1)
  2322. if opt_test>B:
  2323. xtmp = xtmptmp
  2324. opt_test = B
  2325. print(j)
  2326. X_tmp = np.vstack([X_tmp, xtmp])
  2327. # We can add an additional global optimisation before the estimation of the alpha coefficient
  2328. # X_tmp2,B,tmp,tmp1,tmp3 = opt.fmin(f_glob,X_tmp,(cov_tmp,alpha,dist_tmp), xtol=0.000000001, ftol=0.000000001,maxiter=1000000,maxfun=1000000,disp = 1 , full_output = 1)
  2329. # print "all_coord"
  2330. # X_tmp = X_tmp2.reshape(-1,1)
  2331. alpha,B,tmp,tmp1,tmp3 = opt.fmin(f_alpha,alpha,(X_tmp,cov_tmp,dist_tmp), xtol=0.000000001, ftol=0.00000001,disp = 1 , full_output = 1)
  2332. X_tmp2,B,tmp,tmp1,tmp3 = opt.fmin(f_glob,X_tmp,(cov_tmp,alpha,dist_tmp), xtol=0.000000000001, ftol=0.00000000001,maxiter=1000000,maxfun=1000000,disp = 1 , full_output = 1)
  2333. print("all_coord")
  2334. X_tmp = X_tmp2.reshape(-1,1)
  2335. # Merge the covariate modulated by alpha and the first component
  2336. cov2 = cov.reshape(X_tmp.shape)
  2337. X_iso = np.hstack((alpha*cov2[list_extract],X_tmp))
  2338. # Re-order the rows of the matrix to correspond to the initial order of the subject
  2339. I = np.argsort(list_extract)
  2340. X_iso_f = X_iso[I]
  2341. import sys
  2342. sys.path.append('/Users/patrycjascislewska/Analizy_neuro/Graphs/tutorials/TP2_Graphs_ILCB_summer_School')
  2343. import PlotFunction as pf
  2344. ###### Visualization
  2345. Xt = cov
  2346. scaling = 5
  2347. grid_x , grid_y = np.mgrid[-1:1:1000j,-1:1:1000j]*scaling
  2348. grid_lin = griddata(X_iso_f,Xt,(grid_x,grid_y),method='linear')
  2349. grid_lin = grid_lin.reshape(1000,1000)
  2350. pf.scatter_2D(X_iso_f, l,Indiv)
  2351. #plt.imshow(grid_lin.T, extent=(-1,1,-1,1), origin='lower')
  2352. plt.imshow(grid_lin.T,extent=(-scaling,scaling,-scaling,scaling), origin='lower')
  2353. plt.colorbar().ax.tick_params(labelsize=20)
  2354. plt.title('Degree: TSD and CSR compared to RW', fontsize=16)
  2355. plt.tick_params(labelsize=20)
  2356. # %%
  2357. # calculation of difference between two clusters in CCML - for acute and chronic
  2358. group_acute = labels_df["Group"] == 0
  2359. group_chronic = labels_df["Group"] == 1
  2360. #extract group coordinates
  2361. X_acute = X_iso_f[group_acute.values]
  2362. X_chronic = X_iso_f[group_chronic.values]
  2363. #calculate observed centroid distance - distance between two clusters
  2364. d_obs = np.linalg.norm(X_acute.mean(axis=0) - X_chronic.mean(axis=0))
  2365. #permutations
  2366. n_perm = 10000
  2367. d_perm = []
  2368. group_labels = labels_df["Group"].values
  2369. for _ in range(n_perm):
  2370. perm_labels = np.random.permutation(group_labels) #randomly mix the labels of acutes and chronics
  2371. perm_acute = X_iso_f[perm_labels == 0]
  2372. perm_chronic = X_iso_f[perm_labels == 1]
  2373. d = np.linalg.norm(perm_acute.mean(axis=0) - perm_chronic.mean(axis=0))
  2374. d_perm.append(d)
  2375. #p-value
  2376. p_val = np.mean(np.array(d_perm) >= d_obs) #greater than or equal to the observed distance
  2377. print(f"Observed centroid distance = {d_obs:.4f}")
  2378. print(f"P-value (permutation test) = {p_val:.4f}")
  2379. #histogram of permuted distances
  2380. plt.figure(figsize=(8, 5))
  2381. plt.hist(d_perm, bins=50, edgecolor='black', alpha=0.7, color='grey')
  2382. plt.axvline(d_obs, color='red', linestyle='--', linewidth=2, label=f'Observed distance = {d_obs:.3f}\np-value = {p_val:.4f}')
  2383. plt.title("Permutation test - centroid distance", fontsize=20)
  2384. plt.xlabel("Centroid distance", fontsize=18)
  2385. plt.ylabel("Frequency", fontsize=18)
  2386. plt.legend(fontsize=14)
  2387. plt.grid(True, linestyle='--', alpha=0.5)
  2388. plt.tight_layout()
  2389. plt.show()
  2390. # %% [markdown]
  2391. # CCML closeness centrality
  2392. # %%
  2393. metric = "Closeness"
  2394. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2395. metric_filename_pattern = "*_metrics.csv"
  2396. #kappas file
  2397. kappa_all = pd.read_csv(os.path.join(base_path, "HDI_kappa_results_within_subject", "kappas_closeness.csv"))
  2398. kappa_all.set_index("Subject_ID", inplace=True)
  2399. #subject directories
  2400. control_dir = os.path.join(base_path, "ses-2")
  2401. patient_dir = os.path.join(base_path, "ses-3")
  2402. control_subjects = sorted(glob.glob(os.path.join(control_dir, "sub-*")))
  2403. patient_subjects = sorted(glob.glob(os.path.join(patient_dir, "sub-*")))
  2404. #extract the metric for each subject
  2405. def extract_metric(subject_paths):
  2406. data = []
  2407. ids = []
  2408. for subj_path in subject_paths:
  2409. pattern = os.path.join(subj_path, "Graphs/wAALours/graph_metrics", metric_filename_pattern)
  2410. files = glob.glob(pattern)
  2411. if not files:
  2412. continue
  2413. df = pd.read_csv(files[0])
  2414. if metric not in df.columns:
  2415. continue
  2416. data.append(df[metric].values)
  2417. ids.append(os.path.basename(subj_path))
  2418. return np.array(data), ids
  2419. #extract data
  2420. control_data, control_ids = extract_metric(control_subjects)
  2421. patient_data, patient_ids = extract_metric(patient_subjects)
  2422. #just in case - clean subject IDs to match kappa file format
  2423. control_ids_cleaned = [cid.replace("sub-", "") for cid in control_ids]
  2424. patient_ids_cleaned = [pid.replace("sub-", "") for pid in patient_ids]
  2425. #extract matching kappa values
  2426. kappa_control_values = kappa_all.loc[["sub-" + cid for cid in control_ids_cleaned], "Acute_vs_Control"].values
  2427. kappa_patient_values = kappa_all.loc[["sub-" + pid for pid in patient_ids_cleaned], "Chronic_vs_Control"].values
  2428. #construct covariate vector
  2429. cov = np.concatenate([kappa_control_values, kappa_patient_values])
  2430. #group labels: 0 = control, 1 = patient
  2431. labels = [0] * len(control_ids) + [1] * len(patient_ids)
  2432. #subject names
  2433. subject_names = [f"T{i}" for i in range(len(control_ids))] + [f"C{i}" for i in range(len(patient_ids))]
  2434. #combine metric data
  2435. X = np.vstack([control_data, patient_data])
  2436. #save outputs for CCML (exactly the same format as in Sophie's tutorial code)
  2437. output_dir = os.path.join(base_path, "CCML_inputs_within_subjects")
  2438. os.makedirs(output_dir, exist_ok=True)
  2439. df_X = pd.DataFrame(X, index=subject_names)
  2440. df_X.to_csv(os.path.join(output_dir, f"{metric}_chronic_and_acute_vs_control.csv"))
  2441. df_cov = pd.DataFrame(cov, index=subject_names, columns=["HDI_kappa"])
  2442. df_cov.to_csv(os.path.join(output_dir, f"HDI_kappa_{metric}_chronic_and_acute_vs_control.csv"))
  2443. df_labels = pd.DataFrame(labels, index=subject_names, columns=["Group"])
  2444. df_labels.to_csv(os.path.join(output_dir, f"group_labels_{metric}_chronic_and_acute_vs_control.csv"))
  2445. print("Files for CCML saved in:", output_dir)
  2446. # %%
  2447. def f_coord1(x,X2,cov2,alpha2,dist2):
  2448. X2 = np.vstack([X2,x])
  2449. cov2 = cov2.reshape(X2.shape)
  2450. X_tmp2 = np.hstack((alpha2*cov2,X2))
  2451. D2 = sk.metrics.pairwise_distances(X_tmp2)
  2452. return np.linalg.norm(dist2-D2)
  2453. def f_glob(X2,cov2,alpha2,dist2):
  2454. cov2 = cov2.reshape(X2.shape)
  2455. X_tmp2 = np.vstack((alpha2*cov2,X2)).T
  2456. D2 = sk.metrics.pairwise_distances(X_tmp2)
  2457. return np.linalg.norm(dist2-D2)
  2458. def f_alpha(alpha,X2,cov2,dist2):
  2459. cov2 = cov2.reshape(X2.shape)
  2460. X_tmp = np.vstack((alpha*cov2,X2))
  2461. D2 = sk.metrics.pairwise_distances(X_tmp.T)
  2462. return np.linalg.norm(dist2-D2)
  2463. dfX = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/Closeness_chronic_and_acute_vs_control.csv", index_col=0)
  2464. X = dfX.to_numpy()
  2465. dfcov = pd.read_csv("/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/HDI_kappa_Closeness_chronic_and_acute_vs_control.csv", index_col=0)
  2466. cov = dfcov.to_numpy()
  2467. labels_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/CCML_inputs_within_subjects/group_labels_Closeness_chronic_and_acute_vs_control.csv"
  2468. labels_df = pd.read_csv(labels_path, index_col=0)
  2469. Indiv = labels_df.index.tolist()
  2470. l = labels_df["Group"].values
  2471. #compute Euclidean distance matrix
  2472. D = sk.metrics.pairwise.pairwise_distances(X)
  2473. #initialisation aleatoire
  2474. #list_extract = sk.utils.shuffle(np.arange(X.shape[0]))
  2475. #initialize with the most remote couple of points
  2476. ll = []
  2477. ll.append(np.where(D==np.max(D))[0][0])
  2478. ll.append(np.where(D==np.max(D))[1][0])
  2479. # Add sequentially the furthest point of the previous set
  2480. while(len(ll)!=D.shape[0]):
  2481. D_tmp = D[ll]
  2482. D_tmp[:,ll] = 0
  2483. ll.append(np.where(D_tmp == np.max(D_tmp))[1][0])
  2484. list_extract = np.array(ll)
  2485. # Initialize alpha parameter with the classic Isomap
  2486. iso = sk.manifold.Isomap(n_components=2,n_neighbors=4)
  2487. iso.fit(X)
  2488. tmp = iso.embedding_[:,0]
  2489. dist = iso.dist_matrix_
  2490. delta_1 = np.max(tmp) -np.min(tmp)
  2491. delta_2 = np.max(cov) -np.min(cov)
  2492. alpha = delta_1/delta_2
  2493. #Initialize the first point at 0
  2494. X_tmp = np.zeros([1])
  2495. for i in range(1,X.shape[0]):
  2496. print("one coord",i)
  2497. cov_tmp = cov[list_extract[0:i+1]]
  2498. dist_tmp = dist[list_extract[0:i+1]][:,list_extract[0:i+1]]
  2499. opt_test = 10000
  2500. #Multi-start opitmisation
  2501. for j in range(-5,5):
  2502. xtmptmp,B,tmp,tmp1,tmp3 = opt.fmin(f_coord1,j,(X_tmp,cov_tmp,alpha,dist_tmp),xtol=0.000000001, ftol=0.000000001,maxiter=1000000,maxfun=1000000,disp = 0 , full_output = 1)
  2503. if opt_test>B:
  2504. xtmp = xtmptmp
  2505. opt_test = B
  2506. print(j)
  2507. X_tmp = np.vstack([X_tmp, xtmp])
  2508. # We ca add an additional global optimisation before the estimation of the alpha coefficient
  2509. # X_tmp2,B,tmp,tmp1,tmp3 = opt.fmin(f_glob,X_tmp,(cov_tmp,alpha,dist_tmp), xtol=0.000000001, ftol=0.000000001,maxiter=1000000,maxfun=1000000,disp = 1 , full_output = 1)
  2510. # print "all_coord"
  2511. # X_tmp = X_tmp2.reshape(-1,1)
  2512. alpha,B,tmp,tmp1,tmp3 = opt.fmin(f_alpha,alpha,(X_tmp,cov_tmp,dist_tmp), xtol=0.000000001, ftol=0.00000001,disp = 1 , full_output = 1)
  2513. X_tmp2,B,tmp,tmp1,tmp3 = opt.fmin(f_glob,X_tmp,(cov_tmp,alpha,dist_tmp), xtol=0.000000000001, ftol=0.00000000001,maxiter=1000000,maxfun=1000000,disp = 1 , full_output = 1)
  2514. print("all_coord")
  2515. X_tmp = X_tmp2.reshape(-1,1)
  2516. # Merge the covariate modulated by alpha and the first component
  2517. cov2 = cov.reshape(X_tmp.shape)
  2518. X_iso = np.hstack((alpha*cov2[list_extract],X_tmp))
  2519. # Re-order the rows of the matrix to correspond to the initial order of the subject
  2520. I = np.argsort(list_extract)
  2521. X_iso_f = X_iso[I]
  2522. import sys
  2523. sys.path.append('/Users/patrycjascislewska/Analizy_neuro/Graphs/tutorials/TP2_Graphs_ILCB_summer_School')
  2524. import PlotFunction as pf
  2525. ###### Visualization
  2526. Xt = cov
  2527. scaling = 5
  2528. grid_x , grid_y = np.mgrid[-1:1:1000j,-1:1:1000j]*scaling
  2529. grid_lin = griddata(X_iso_f,Xt,(grid_x,grid_y),method='linear')
  2530. grid_lin = grid_lin.reshape(1000,1000)
  2531. pf.scatter_2D(X_iso_f, l,Indiv)
  2532. #plt.imshow(grid_lin.T, extent=(-1,1,-1,1), origin='lower')
  2533. plt.imshow(grid_lin.T,extent=(-scaling,scaling,-scaling,scaling), origin='lower')
  2534. plt.colorbar().ax.tick_params(labelsize=20)
  2535. plt.title('Closeness: CSR and TSD compared to RW')
  2536. plt.tick_params(labelsize=20)
  2537. # %% [markdown]
  2538. # CCML clustering coefficient
  2539. # %%
  2540. metric = "Clustering"
  2541. base_path = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2542. metric_filename_pattern = "*_metrics.csv"
  2543. #kappas file
  2544. kappa_all = pd.read_csv(os.path.join(base_path, "HDI_kappa_results_within_subject", "kappas_clustering.csv"))
  2545. kappa_all.set_index("Subject_ID", inplace=True)
  2546. #subject directories
  2547. control_dir = os.path.join(base_path, "ses-2")
  2548. patient_dir = os.path.join(base_path, "ses-3")
  2549. control_subjects = sorted(glob.glob(os.path.join(control_dir, "sub-*")))
  2550. patient_subjects = sorted(glob.glob(os.path.join(patient_dir, "sub-*")))
  2551. #extract the metric for each subject
  2552. def extract_metric(subject_paths):
  2553. data = []
  2554. ids = []
  2555. for subj_path in subject_paths:
  2556. pattern = os.path.join(subj_path, "Graphs/wAALours/graph_metrics", metric_filename_pattern)
  2557. files = glob.glob(pattern)
  2558. if not files:
  2559. continue
  2560. df = pd.read_csv(files[0])
  2561. if metric not in df.columns:
  2562. continue
  2563. data.append(df[metric].values)
  2564. ids.append(os.path.basename(subj_path))
  2565. return np.array(data), ids
  2566. #extract data
  2567. control_data, control_ids = extract_metric(control_subjects)
  2568. patient_data, patient_ids = extract_metric(patient_subjects)
  2569. #just in case - clean subject IDs to match kappa file format
  2570. control_ids_cleaned = [cid.replace("sub-", "") for cid in control_ids]
  2571. patient_ids_cleaned = [pid.replace("sub-", "") for pid in patient_ids]
  2572. #extract matching kappa values
  2573. kappa_control_values = kappa_all.loc[["sub-" + cid for cid in control_ids_cleaned], "Acute_vs_Control"].values
  2574. kappa_patient_values = kappa_all.loc[["sub-" + pid for pid in patient_ids_cleaned], "Chronic_vs_Control"].values
  2575. #construct covariate vector
  2576. cov = np.concatenate([kappa_control_values, kappa_patient_values])
  2577. #group labels: 0 = control, 1 = patient
  2578. labels = [0] * len(control_ids) + [1] * len(patient_ids)
  2579. #subject names
  2580. subject_names = [f"A{i}" for i in range(len(control_ids))] + [f"Ch{i}" for i in range(len(patient_ids))]
  2581. #combine metric data
  2582. X = np.vstack([control_data, patient_data])
  2583. #save outputs for CCML (exactly the same format as in Sophie's tutorial code)
  2584. output_dir = os.path.join(base_path, "CCML_inputs_within_subjects")
  2585. os.makedirs(output_dir, exist_ok=True)
  2586. df_X = pd.DataFrame(X, index=subject_names)
  2587. df_X.to_csv(os.path.join(output_dir, f"{metric}_chronic_and_acute_vs_control.csv"))
  2588. df_cov = pd.DataFrame(cov, index=subject_names, columns=["HDI_kappa"])
  2589. df_cov.to_csv(os.path.join(output_dir, f"HDI_kappa_{metric}_chronic_and_acute_vs_control.csv"))
  2590. df_labels = pd.DataFrame(labels, index=subject_names, columns=["Group"])
  2591. df_labels.to_csv(os.path.join(output_dir, f"group_labels_{metric}_chronic_and_acute_vs_control.csv"))
  2592. print("Files for CCML saved in:", output_dir)
  2593. # %% [markdown]
  2594. # ## Chord plots
  2595. # %%
  2596. #### individual subjects, 200 edges (from adjacency matrix), size of node corresponds to the number of connecitons
  2597. #### color coding = networks
  2598. import holoviews as hv
  2599. from holoviews import opts
  2600. hv.extension('bokeh')
  2601. base_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA"
  2602. roi_info_csv = "/Users/patrycjascislewska/Analizy_neuro/Graphs/AAL_ours_89_regions_list.csv"
  2603. out_dir = "/Users/patrycjascislewska/Analizy_neuro/Graphs/DATA/graph_metrics/chord_diagrams"
  2604. sessions = {
  2605. "control": "ses-1_AICHA_atlas",
  2606. "acute": "ses-2_AICHA_atlas",
  2607. "chronic": "ses-3_AICHA_atlas"
  2608. }
  2609. ##ROI TO NETWORK MAPPING
  2610. roi_networks = {
  2611. 'PreGy_L': 'SMN', 'PreGy_R': 'SMN', 'SMA_L': 'SMN', 'SMA_R': 'SMN',
  2612. 'RolandOperc_L': 'SMN', 'RolandOperc_R': 'SMN', 'Postcentral_L': 'SMN', 'Postcentral_R': 'SMN',
  2613. 'ParacentralLob_L': 'SMN', 'ParacentralLob_R': 'SMN',
  2614. 'FrontSup_L': 'FPN', 'FrontSup_R': 'FPN', 'FrontMid_L': 'FPN', 'FrontMid_R': 'FPN',
  2615. 'FrontInfOperc_L': 'FPN', 'FrontInfOperc_R': 'FPN', 'FrontInfTri_L': 'FPN', 'FrontInfTri_R': 'FPN',
  2616. 'FrontInfOrb_L': 'FPN', 'FrontInfOrb_R': 'FPN', 'FrontSupMed_L': 'FPN', 'FrontSupMed_R': 'FPN',
  2617. 'CingAnt_L': 'DMN', 'CingAnt_R': 'DMN', 'CingMid_L': 'DMN', 'CingMid_R': 'DMN',
  2618. 'CingPost_L': 'DMN', 'CingPost_R': 'DMN', 'Precuneus_L': 'DMN', 'Precuneus_R': 'DMN',
  2619. 'Angular_L': 'DMN', 'Angular_R': 'DMN', 'FrontMedOrb_L': 'DMN', 'FrontMedOrb_R': 'DMN',
  2620. 'TempMid_L': 'DMN', 'TempMid_R': 'DMN',
  2621. 'Insula_L': 'Salience', 'Insula_R': 'Salience', 'Olfactory_L': 'Salience', 'Olfactory_R': 'Salience',
  2622. 'Calcarine_L': 'VN', 'Calcarine_R': 'VN', 'Cuneus_L': 'VN', 'Cuneus_R': 'VN',
  2623. 'Lingual_L': 'VN', 'Lingual_R': 'VN', 'Occipital_L': 'VN', 'Occipial_R': 'VN',
  2624. 'Fusiform_L': 'VN', 'Fusiform_R': 'VN',
  2625. 'Amygdala_L': 'Limbic', 'Amygdala_R': 'Limbic', 'Hippocampus_L': 'Limbic', 'Hippocampus_R': 'Limbic',
  2626. 'ParaHippoc_L': 'Limbic', 'ParaHippoc_R': 'Limbic', 'TempPole_L': 'Limbic', 'TempPole_R': 'Limbic',
  2627. 'Heschl_L': 'Auditory', 'Heschl_R': 'Auditory', 'TempSup_L': 'Auditory', 'TempSup_R': 'Auditory',
  2628. 'TempInf_L': 'Language', 'TempInf_R': 'Language',
  2629. 'Caudate_L': 'Subcortical', 'Caudate_R': 'Subcortical', 'Putamen_L': 'Subcortical',
  2630. 'Putamen_R': 'Subcortical', 'Pallidum_L': 'Subcortical', 'Pallidum_R': 'Subcortical',
  2631. 'Thalamus_L': 'Subcortical', 'Thalamus_R': 'Subcortical',
  2632. 'ParietalSup_L': 'DAN', 'ParietalSup_R': 'DAN', 'ParietalInf_L': 'DAN', 'ParietalInf_R': 'DAN',
  2633. 'SupraMarginal_L': 'DAN', 'SupraMarginal_R': 'DAN',
  2634. 'Cereb_I_II_L': 'Cerebellum', 'Cereb_I_II_R': 'Cerebellum',
  2635. 'Cereb_III_VI_L': 'Cerebellum', 'Cereb_III_VI_R': 'Cerebellum',
  2636. 'Cereb_VII_X_L': 'Cerebellum', 'Cereb_VII_X_R': 'Cerebellum',
  2637. 'Vermis': 'Cerebellum',
  2638. 'FrontSupOrb_L': 'Other', 'FrontSupOrb_R': 'Other',
  2639. 'FrontMidOrb_L': 'Other', 'FrontMidOrb_R': 'Other'
  2640. }
  2641. #ROI names and sort by atlas Node_number
  2642. roi_df = pd.read_csv(roi_info_csv, sep=";")
  2643. roi_df = roi_df.sort_values("Node_number")
  2644. original_region_names = roi_df["Region"].tolist()
  2645. nodes_df = pd.DataFrame({
  2646. 'name': original_region_names,
  2647. 'network': [roi_networks.get(roi, 'Other') for roi in original_region_names]
  2648. })
  2649. nodes_df = nodes_df.sort_values(by=['network', 'name']).reset_index(drop=True)
  2650. region_names = nodes_df['name'].tolist()
  2651. n_rois = len(region_names)
  2652. def find_all_participants():
  2653. participants = set()
  2654. for ses_folder in sessions.values():
  2655. ses_path = os.path.join(base_dir, ses_folder)
  2656. for root, dirs, files in os.walk(ses_path):
  2657. for file in files:
  2658. if file.startswith("Adj_mat") and file.endswith("200.txt"):
  2659. parts = root.split(os.sep)
  2660. for p in parts:
  2661. if p.startswith("sub-"):
  2662. participants.add(p)
  2663. return sorted(participants)
  2664. #I used adj matrix with 200 edges from R script from Veronica, because chart with 400 edges was to dense
  2665. def get_matrix(sub_id, session_folder):
  2666. path = os.path.join(base_dir, session_folder, sub_id, "Graphs", "wAALours")
  2667. try:
  2668. files = [f for f in os.listdir(path) if f.startswith("Adj_mat") and f.endswith("200.txt")]
  2669. if not files:
  2670. return None
  2671. matrix = np.loadtxt(os.path.join(path, files[0]))
  2672. if matrix.shape != (n_rois, n_rois):
  2673. return None
  2674. return matrix.astype(int)
  2675. except:
  2676. return None
  2677. def create_chord(matrix, title):
  2678. reordered_indices = [original_region_names.index(name) for name in region_names]
  2679. matrix = matrix[np.ix_(reordered_indices, reordered_indices)]
  2680. edges = []
  2681. connected_nodes = set()
  2682. for i in range(matrix.shape[0]):
  2683. for j in range(i + 1, matrix.shape[1]):
  2684. if matrix[i, j] == 1:
  2685. source = region_names[i]
  2686. target = region_names[j]
  2687. network_src = roi_networks.get(source, 'Other')
  2688. network_tgt = roi_networks.get(target, 'Other')
  2689. edges.append((source, target, 1, network_src))
  2690. connected_nodes.add(source)
  2691. connected_nodes.add(target)
  2692. for name in region_names:
  2693. if name not in connected_nodes:
  2694. edges.append((name, name, 0.01, roi_networks.get(name, 'Other')))
  2695. edges_df = pd.DataFrame(edges, columns=["source", "target", "value", "network_src"])
  2696. connection_counts = edges_df[['source', 'target']].stack().value_counts()
  2697. nodes_with_size = nodes_df.copy()
  2698. nodes_with_size['connections'] = nodes_with_size['name'].map(connection_counts).fillna(0)
  2699. nodes_with_size['size'] = nodes_with_size['connections'] * 4
  2700. chord = hv.Chord((edges_df, hv.Dataset(nodes_with_size, kdims='name')))
  2701. return chord.opts(
  2702. opts.Chord(
  2703. labels='name',
  2704. node_color='network',
  2705. edge_color='network_src',
  2706. cmap='Set1',
  2707. edge_cmap='Set1',
  2708. edge_alpha=0.8,
  2709. node_size='size',
  2710. width=650,
  2711. height=650,
  2712. title=title,
  2713. colorbar=False,
  2714. tools=['hover']
  2715. )
  2716. )
  2717. #MAIN
  2718. participants = find_all_participants()
  2719. print(f"Found {len(participants)} participants")
  2720. os.makedirs(out_dir, exist_ok=True)
  2721. for sub_id in participants:
  2722. chords = []
  2723. for condition, session_folder in sessions.items():
  2724. mat = get_matrix(sub_id, session_folder)
  2725. if mat is not None:
  2726. title = f"{condition.capitalize()} – {sub_id}"
  2727. chords.append(create_chord(mat, title))
  2728. else:
  2729. print(f"Missing or invalid matrix for {sub_id} in {session_folder}")
  2730. if chords:
  2731. layout = hv.Layout(chords).cols(3)
  2732. filename = os.path.join(out_dir, f"{sub_id}_chord_diagram_all_sessions_200_sized_colored.html")
  2733. hv.save(layout, filename, backend='bokeh')
  2734. print(f"Saved {filename}")
  2735. else:
  2736. print(f"No valid matrices for {sub_id}, skipping.")
  2737. print("All participant-level multi-session chord diagrams saved.")

Graphs_nodal_global_metrics_HDI_CCML.ipynb at commit af790e1, no license · at the source

Overview

  1. University of Warsaw, Faculty of Biology, Institute of Experimental Zoology, Warsaw, Poland
  2. Université Grenoble Alpes, CNRS, Inria, Grenoble INP, LJK, 38000 Grenoble, France
  3. Université Grenoble Alpes, Inserm, U1216, Grenoble Institut Neurosciences, 38000 Grenoble, France
  4. INSERM U1214, Toulouse Neuroimaging Center, CHU Purpan, 31059 Toulouse, France
  5. Laboratory of Emotions Neurobiology, Nencki Institute of Experimental Biology, Polish Academy of Sciences, 02-093 Warsaw, Poland
  6. Department of Cognitive Neuroscience and Neuroergonomics, Institute of Applied Psychology, Jagiellonian University, 30-348 Kraków, Poland
  7. Centre for Brain Research, Jagiellonian University, 31-501 Kraków, Poland
Journal: Sleep, volume 49, issue 5, article zsag030
Dates: received 20 October 2025; accepted 29 January 2026; published online 3 February 2026; in print May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1093/sleep/zsag030 · PMID 41631633 · PMCID PMC13163182 · OpenAlex W7127334101
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism), systems (subfield)
Methods: Statistics, Machine learning, Connectivity, Preprocessing, Spectral & time-frequency, Graphs, fMRI & imaging
Keywords: fMRI, sleep deprivation, graphs, functional connectivity, hub disruption index, machine learning, sleepiness, chronotype
MeSH: Brain*, Magnetic Resonance Imaging*, Nerve Net*, Sleep Deprivation*, Adult, Brain Mapping, Female, Humans, Longitudinal Studies, Male, Wakefulness, Young Adult (* major topic)
Topic: Sleep and Work-Related Fatigue (Experimental and Cognitive Psychology, Psychology), according to OpenAlex
Funding: Excellence Initiative-Research University (BOB-IDUB-622-412/2025, 2020-2026); Inria and including CNRS; Agence Nationale de la Recherche under the France 2030 program (ANR-23-IACL-0006); French government grant; Ministry of Science and Higher Education; Polish National Science Centre (2018/29/B/HS6/01934)
Citations: not cited yet (Europe PMC); 81 references in the paper

Abstract

Study Objectives: Sleep loss significantly disrupts cognitive and emotional functioning, yet the neural consequences of different types of sleep deprivation remain unclear.

Methods: In a within-subject resting-state functional magnetic resonance imaging study, we examined how acute total sleep deprivation (TSD) and chronic sleep restriction (CSR) alter intrinsic functional brain organization in 28 healthy adults scanned under three conditions: rested wakefulness (RW), after one night of TSD, and after five nights of CSR.

To quantify network-level disruption, we applied graph-theoretical analyses, including a novel within-subject adaptation of the Hub Disruption Index and Covariate-Constrained Manifold Learning (CCML), an unsupervised embedding technique sensitive to subject-level covariates. Moreover, we assessed subjective sleep quality, sleepiness, and circadian traits.

Results: Both TSD and CSR were associated with a consistent reorganization of graph topology relative to RW. Furthermore, direct comparisons revealed that TSD and CSR affect different brain hubs. Regional changes in degree, closeness, and clustering coefficients were most prominent in subsystems of the default mode network, frontoparietal network, and cerebellum. These differences were also captured in CCML embeddings, supporting the hypothesis that acute and chronic sleep deprivation exert divergent effects on brain connectivity. Findings were robust across graph thresholds, brain atlases, and nodal metrics. Moreover, these results were further supported by subjective measures—sleepiness was associated with reduced network integration in RW, and circadian phenotype emerged as a key determinant of individual sensitivity to sleep loss.

Conclusions: Our results show that TSD and CSR induce divergent alterations in brain functional organization, offering new insights into their neural impact.

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

PatrycjaScislewska/sleep_deprivation_graphs

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: af790e173734b687d184ee821b8c2b63bf8947ab, 20 November 2025
Languages: Jupyter (3)
Size: 183 files, 3 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, 3 notebooks
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (3 files), statsmodels (3 files), Matplotlib (2 files), pandas (2 files), SciPy (2 files), NetworkX (1 file), scikit-learn (1 file), seaborn (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
5 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;
  • 3 scripts, each with its path and the digest of its content;
  • 10 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 availability

Time series, atlases and code used in this study are available in the following GitHub repository: https://github.com/PatrycjaScislewska/sleep_deprivation_graphs/

All preprocessing steps can be fully reproduced using the following GitHub repository: https://github.com/veronicamunoz/rs_graph_processing

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, issue, pages, dates, 6 authors, 8 keywords, 12 MeSH terms, 6 funders, 80 references.

Cite

This paper

Scislewska, P., Cabrera Vazquez, A., Szatkowska, I., Kontrymowicz-Ogińska, H., Achard, S., & Domagalik, A. (2026). Divergent disruption of brain networks following total and chronic sleep loss: a longitudinal fMRI study. Sleep, 49(5), zsag030. https://doi.org/10.1093/sleep/zsag030

BibTeX

@article{scislewska2026divergent,
author = {Scislewska, Patrycja and Cabrera Vazquez, Arturo and Szatkowska, Iwona and Kontrymowicz-Ogińska, Halszka and Achard, Sophie and Domagalik, Aleksandra},
title = {{Divergent disruption of brain networks following total and chronic sleep loss: a longitudinal fMRI study}},
journal = {Sleep},
year = {2026},
month = may,
volume = {49},
number = {5},
pages = {zsag030},
publisher = {Oxford University Press},
issn = {0161-8105},
doi = {10.1093/sleep/zsag030},
url = {https://doi.org/10.1093/sleep/zsag030},
pmid = {41631633},
pmcid = {PMC13163182}
}

RIS

TY - JOUR
AU - Scislewska, Patrycja
AU - Cabrera Vazquez, Arturo
AU - Szatkowska, Iwona
AU - Kontrymowicz-Ogińska, Halszka
AU - Achard, Sophie
AU - Domagalik, Aleksandra
TI - Divergent disruption of brain networks following total and chronic sleep loss: a longitudinal fMRI study
T2 - Sleep
J2 - Sleep
PY - 2026
DA - 2026/05/01
VL - 49
IS - 5
SP - zsag030
SN - 0161-8105
PB - Oxford University Press
DO - 10.1093/sleep/zsag030
UR - https://doi.org/10.1093/sleep/zsag030
LA - en
ER -

CSL-JSON

{
"id": "10.1093/sleep/zsag030",
"type": "article-journal",
"title": "Divergent disruption of brain networks following total and chronic sleep loss: a longitudinal fMRI study",
"container-title": "Sleep",
"author": [
{
"family": "Scislewska",
"given": "Patrycja"
},
{
"family": "Cabrera Vazquez",
"given": "Arturo"
},
{
"family": "Szatkowska",
"given": "Iwona"
},
{
"family": "Kontrymowicz-Ogińska",
"given": "Halszka"
},
{
"family": "Achard",
"given": "Sophie"
},
{
"family": "Domagalik",
"given": "Aleksandra"
}
],
"container-title-short": "Sleep",
"volume": "49",
"issue": "5",
"page": "zsag030",
"DOI": "10.1093/sleep/zsag030",
"PMID": "41631633",
"PMCID": "PMC13163182",
"ISSN": "0161-8105",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/sleep/zsag030",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
1
]
]
}
}

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.1038/s41467-026-75661-x [code]
A neural signature of sleep deprivation in the human brain.
Journal: Nature communications
In common: statsmodels, seaborn, scikit-learn, 4 other tools, 5 references
[2] doi:10.1162/imag.a.1278 [code]
Young and old adult brains experience opposite effects of acute sleep restriction on the functional connectivity network.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: NetworkX, statsmodels, seaborn, 4 other tools, fMRI, 4 references
[3] doi:10.1002/hbm.70522 [code]
Investigating Emotional Reactivity in Experienced Users of Psychedelics: A Cross-Sectional fMRI Study.
Journal: Human brain mapping
In common: fMRI, 4 references, author Aleksandra Domagalik
[4] doi:10.1073/pnas.2531706123 [code]
Metabolism-weighted brain connectome reveals synaptic integration and vulnerability to neurodegeneration.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: NetworkX, statsmodels, seaborn, 5 other tools, 3 references
[5] doi:10.1016/j.isci.2026.116173 [code]
Hierarchical sparse spatiotemporal graph neural network for brain graph classification.
Journal: iScience
In common: NetworkX, seaborn, scikit-learn, 4 other tools, fMRI, 3 references
[6] doi:10.1016/j.patter.2026.101560 [code]
Automating region selection with genetic algorithms for energy landscape analyses of brain dynamics.
Journal: Patterns (New York, N.Y.)
In common: NetworkX, statsmodels, seaborn, 4 other tools, fMRI, 3 references
[7] doi:10.3389/fnins.2026.1873417 [code]
Functional brain network alterations in a rat model of dental malocclusion.
Journal: Frontiers in neuroscience
In common: seaborn, scikit-learn, pandas, 3 other tools, fMRI, systems, 4 references
[8] doi:10.1038/s41467-026-75959-w [code]
Charting higher-order models of brain function beyond pairwise interactions.
Journal: Nature communications
In common: NetworkX, statsmodels, seaborn, 5 other tools, 2 references
[9] doi:10.1016/j.nicl.2026.104012 [code]
Structural-functional multilayer brain network properties and outcome of combined repetitive transcranial magnetic stimulation and psychotherapy for obsessive-compulsive disorder.
Journal: NeuroImage. Clinical
In common: NetworkX, statsmodels, seaborn, 5 other tools, fMRI, 2 references
[10] doi:10.1002/alz.71365 [code]
Benchmarking speech biomarkers of Alzheimer's against cognitive and neural measures.
Journal: Alzheimer's & dementia : the journal of the Alzheimer's Association
In common: statsmodels, seaborn, scikit-learn, 4 other tools, fMRI, 3 references

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.