OSCR

An Intraoperative EEG Biomarker for Postoperative Delirium Predicting Based on Interpretable Deep Learning Framework.

Code ↔ Paper

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

The 8 matches
  1. [1] § Results › Best Temporal Filter Captures an EEG Signature of POD Individuals ↔ plot/Figure4.ipynb, lines 254–318 · score 0.69 · gradient boosting machine, multilayer perceptron, random forest, 10 %, training, accuracies
  2. [2] § Results › Best Temporal Filter Captures an EEG Signature of POD Individuals ↔ plot/figure_3.ipynb, lines 282–359 · score 0.69 · gradient boosting machine, multilayer perceptron, random forest, 10 %, training, accuracies
  3. [3] § Materials and Methods › Filter Analysis ↔ plot/Figure4.ipynb, lines 254–318 · score 0.68 · multilayer perceptron, gradient boosting, random forest, training, model
  4. [4] § Materials and Methods › Filter Analysis ↔ plot/figure_3.ipynb, lines 282–359 · score 0.68 · multilayer perceptron, gradient boosting, random forest, training, model
  5. [5] § Results › A Novel EEG Waveform as a Potential Biomarker for POD Early Warning ↔ plot/Figure5.ipynb, lines 187–248 · score 0.63 · power spectral density, confidence interval, target wave, 12 Hz
  6. [6] § Results › A Novel EEG Waveform as a Potential Biomarker for POD Early Warning ↔ plot/Figure5.ipynb, lines 561–662 · score 0.58 · biomarker events, confidence intervals, brain regions, bootstrap, min, POD
  7. [7] § Results › A Novel EEG Waveform as a Potential Biomarker for POD Early Warning ↔ plot/Figure5.ipynb, lines 561–662 · score 0.53 · confidence interval, brain regions, event, bootstrap, min, biomarkers
  8. [8] § Results › A Novel EEG Waveform as a Potential Biomarker for POD Early Warning ↔ supp_plot/supp_figure_4.ipynb, lines 74–140 · score 0.52 · power spectral density, target wave, PSD, POD, 12 Hz

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 · 662 lines · 25 KB · no license · 3 matches

  1. # %% [markdown]
  2. # # Figure 5
  3. # %%
  4. import numpy as np
  5. import pandas as pd
  6. import matplotlib.pyplot as plt
  7. import seaborn as sns
  8. import torch
  9. import torch.nn.functional as F
  10. from scipy import stats
  11. import os
  12. import pickle
  13. from sklearn.preprocessing import StandardScaler
  14. import mne
  15. from matplotlib.colors import Normalize
  16. from matplotlib.colors import TwoSlopeNorm
  17. from scipy.stats import bootstrap
  18. import matplotlib as mpl
  19. # %% [markdown]
  20. # # Figure 5A
  21. # %%
  22. import os
  23. import pickle
  24. import numpy as np
  25. import matplotlib.pyplot as plt
  26. import matplotlib.patches as patches
  27. def highlight_interval_visualization(load_path, signal_data_path, subject_ids, save_root, sampling_rate=125):
  28. """
  29. Visualizes highlighted intervals and signal data for different channel positions.
  30. A plot is generated for each minute of data.
  31. Arguments:
  32. - load_path: str, path to the highlighted interval data (pickle format).
  33. - signal_data_path: str, path to processed signal data (pickle format, organized by channel).
  34. - subject_ids: list, list of subject IDs to visualize.
  35. - save_root: str, directory to save the generated images.
  36. - sampling_rate: int, the EEG data sampling rate (default is 125Hz).
  37. Returns:
  38. - None: This function saves visualization results as image files in the specified folder.
  39. """
  40. # Load the highlight regions and signal data from the provided paths
  41. with open(load_path, 'rb') as file:
  42. highlight_regions_per_channel = pickle.load(file)
  43. with open(signal_data_path, 'rb') as file:
  44. signal_data = pickle.load(file)
  45. # Configure plot appearance
  46. plt.rcParams.update({
  47. 'font.family': 'Times New Roman', # Recommended journal font
  48. 'font.size': 12,
  49. 'axes.labelsize': 14,
  50. 'axes.linewidth': 1.5,
  51. 'legend.fontsize': 10,
  52. 'xtick.labelsize': 12,
  53. 'ytick.labelsize': 12,
  54. 'pdf.fonttype': 42,
  55. 'ps.fonttype': 42,
  56. 'figure.dpi': 300
  57. })
  58. # Process each subject
  59. for subject_id in subject_ids:
  60. # Extract highlight intervals for the current subject
  61. subject_highlight = {}
  62. for channel, highlights in highlight_regions_per_channel.items():
  63. highlight_data = next((item for item in highlights if item['subject_id'] == subject_id), None)
  64. if highlight_data:
  65. subject_highlight[channel] = highlight_data['highlight_intervals']
  66. # Extract signal data for the current subject
  67. subject_signal = {}
  68. for channel, signals in signal_data.items():
  69. signal_data_item = next((item for item in signals if item['subject_id'] == subject_id), None)
  70. if signal_data_item:
  71. subject_signal[channel] = signal_data_item['processed_data']
  72. # Calculate the number of minutes of signal data
  73. num_samples_per_minute = sampling_rate * 10
  74. total_samples = len(next(iter(subject_signal.values()))) # Get the number of samples from one channel
  75. num_minutes = total_samples // num_samples_per_minute
  76. # Generate one plot for each minute
  77. for minute in range(num_minutes):
  78. fig, ax = plt.subplots(figsize=(6.5, 8)) # Standard size for Nature journal (inches)
  79. plt.subplots_adjust(left=0.15, right=0.95, top=0.93, bottom=0.08) # Adjust layout margins
  80. num_channels = len(subject_signal)
  81. bar_height = 0.8
  82. # Plot the signal data for each channel
  83. for channel_id, signal in subject_signal.items():
  84. signal_start = minute * num_samples_per_minute
  85. signal_end = (minute + 1) * num_samples_per_minute
  86. signal_segment = signal[signal_start:signal_end]
  87. signal_segment = normalize_signal(signal_segment)
  88. time_segment = np.linspace(signal_start / sampling_rate, signal_end / sampling_rate, len(signal_segment))
  89. # Plot the signal waveform for the current channel
  90. ax.plot(time_segment, signal_segment + channel_id - 0.5,
  91. color="#2f2f2f", alpha=0.95, linewidth=1.2, solid_capstyle='round')
  92. # Plot the highlighted intervals for the current channel
  93. if channel_id not in subject_highlight:
  94. continue
  95. channel_highlights = subject_highlight[channel_id]
  96. for highlight in channel_highlights:
  97. start, end = highlight
  98. start_time = start / sampling_rate
  99. end_time = end / sampling_rate
  100. # Check if the highlight interval falls within the current minute's time window
  101. if start_time >= signal_start / sampling_rate and end_time <= signal_end / sampling_rate:
  102. ax.add_patch(patches.Rectangle(
  103. (start_time, channel_id - 0.5),
  104. end_time - start_time,
  105. bar_height,
  106. color='orange',
  107. alpha=0.45,
  108. edgecolor="orange",
  109. linewidth=0.7,
  110. linestyle="-"
  111. ))
  112. # Customize the plot appearance
  113. channel_labels = ["Fp1", "Fp2", "Fz", "F3", "F4", "F7", "F8", "FCz", "FC3", "FC4", "FT7", "FT8", "Cz",
  114. "C3", "C4", "T3", "T4", "CP3", "CP4", "TP7", "TP8", "Pz", "P3", "P4", "T5", "T6", "Oz", "O1", "O2"]
  115. ax.set_xlim(minute * 10, (minute + 1) * 10)
  116. ax.set_ylim(-0.5, num_channels - 0.5)
  117. ax.set_yticks(range(num_channels))
  118. ax.set_yticklabels(channel_labels, fontstyle='italic', color="black")
  119. ax.yaxis.set_tick_params(width=0.5)
  120. ax.xaxis.set_tick_params(width=0.5)
  121. ax.set_xlabel("Time (s)", labelpad=3)
  122. ax.set_ylabel("EEG Channels", labelpad=3)
  123. ax.spines['right'].set_visible(False)
  124. ax.spines['top'].set_visible(False)
  125. ax.spines['left'].set_linewidth(0.5)
  126. ax.spines['bottom'].set_linewidth(0.5)
  127. ax.invert_yaxis()
  128. # Title the plot
  129. ax.set_title(f"Subject {subject_id} | Time window: {minute + 1}", fontweight='semibold', pad=12)
  130. # Save the plot as an image
  131. save_path = os.path.join(save_root, f"{subject_id}_second_{minute + 1}.png")
  132. plt.savefig(save_path, dpi=600, bbox_inches='tight', pil_kwargs={'compression': 'tiff_lzw'})
  133. plt.close()
  134. def normalize_signal(signal):
  135. """Normalize the signal data to be between -1 and 1."""
  136. return (signal - np.min(signal)) / (np.max(signal) - np.min(signal)) * 2 - 1
  137. # If necessary, please contact the corresponding author to obtain the data.
  138. # %% [markdown]
  139. # # Figure 5B
  140. # %%
  141. # 读取 .npz 文件
  142. data = np.load('../dataset/figure5_B_plot_variables.npz')
  143. # 获取文件中的变量
  144. freqs_tar = data['freqs_tar']
  145. psd_tar = data['psd_tar']
  146. ci_lower_tar = data['ci_lower_tar']
  147. ci_upper_tar = data['ci_upper_tar']
  148. freqs_ori = data['freqs_ori']
  149. psd_ori = data['psd_ori']
  150. ci_lower_ori = data['ci_lower_ori']
  151. ci_upper_ori = data['ci_upper_ori']
  152. mean_psd_tar = data['mean_psd_tar']
  153. mean_psd_ori = data['mean_psd_ori']
  154. freqs_show = data['freqs_show']
  155. sig_freqs = data['sig_freqs']
  156. pvals_corrected = data['pvals_corrected']
  157. ci_lower_diff = data['ci_lower_diff']
  158. ci_upper_diff = data['ci_upper_diff']
  159. # %%
  160. import matplotlib.pyplot as plt
  161. import numpy as np
  162. # Define frequency limit and mask for frequency range
  163. freq_limit = 30
  164. freq_mask = freqs_tar <= freq_limit
  165. freqs_show = freqs_tar[freq_mask]
  166. # Update plotting parameters for a professional look
  167. plt.rcParams.update({
  168. 'font.family': 'DejaVu Sans',
  169. 'font.size': 12, # Base font size
  170. 'axes.labelsize': 14, # Axis label font size
  171. 'axes.linewidth': 1.5, # Axis line width
  172. 'legend.fontsize': 10, # Legend font size
  173. 'xtick.labelsize': 12, # X-axis tick label font size
  174. 'ytick.labelsize': 12, # Y-axis tick label font size
  175. 'pdf.fonttype': 42, # Ensure the output text is editable
  176. 'ps.fonttype': 42,
  177. 'figure.dpi': 300 # High resolution for the figure
  178. })
  179. # Create the plot with specified figure size
  180. fig, ax = plt.subplots(figsize=(4.3*1.5, 3*1.5))
  181. # Plot the target wave (mean and confidence intervals)
  182. ax.plot(freqs_tar[freq_mask], mean_psd_tar, color='orange', alpha=0.75, label='Target wave', zorder=1)
  183. ax.fill_between(freqs_tar[freq_mask], ci_lower_tar, ci_upper_tar, color='orange', alpha=0.25)
  184. # Plot the original signal (mean and confidence intervals)
  185. ax.plot(freqs_tar[freq_mask], mean_psd_ori, color='gray', alpha=0.75, label='Original signal', zorder=2)
  186. ax.fill_between(freqs_tar[freq_mask], ci_lower_ori, ci_upper_ori, color='gray', alpha=0.25)
  187. # Plot the confidence interval of the difference between target and original signals
  188. ax.fill_between(freqs_tar[freq_mask], ci_lower_diff, ci_upper_diff, color='red', alpha=0.25, label='95% CI of difference', zorder=3)
  189. # Highlight the significant frequencies with stars
  190. sig_freqs = freqs_show[pvals_corrected < 0.05] # Frequencies with p-values < 0.05
  191. sig_freqs = sig_freqs[sig_freqs < 16] # Limit to frequencies below 16 Hz
  192. if len(sig_freqs) > 0:
  193. ax.plot(sig_freqs, [np.max([mean_psd_tar, mean_psd_ori]) * 1.1] * len(sig_freqs),
  194. lw=0, marker='*', color='k', markersize=8, label='p < 0.05')
  195. # Adjust plot formatting
  196. ax.set_xlim(-0.5, freq_limit + 0.5)
  197. ax.set_xlabel('Frequency (Hz)') # X-axis label
  198. ax.set_ylabel('Power (a.u.)') # Y-axis label with units
  199. # Customize grid if needed (commented out for clarity)
  200. # ax.grid(True, linestyle=':', alpha=0.6) # Optional grid lines for better visualization
  201. # Add legend
  202. legend = ax.legend(frameon=True, loc='upper right')
  203. legend.get_frame().set_linewidth(1.2) # Set legend border width
  204. legend.get_frame().set_edgecolor('whitesmoke') # Set legend border color
  205. # Adjust layout to fit the plot nicely
  206. plt.tight_layout()
  207. # Show the plot (if running interactively)
  208. # plt.show()
  209. # %%
  210. from scipy.signal import butter, filtfilt, welch
  211. from scipy.stats import t
  212. # Define the mapping of regions to channels in a dictionary
  213. net_dict = {
  214. 'Forehead': list(range(0, 2)),
  215. 'Frontal': list(range(2, 12)),
  216. 'Center': [12, 13, 14, 17, 18],
  217. 'Temporal': [15, 16, 19, 20, 24, 25],
  218. 'Parietal': [21, 22, 23],
  219. 'Occipital': [26, 27, 28],
  220. 'All': list(range(0, 29)) # Mapping includes all channels
  221. }
  222. fs = 125 # Sampling frequency
  223. def calculate_power_per_subject(detection_datas, region_dict):
  224. """
  225. Calculate the total power of EEG signals in different frequency bands for each subject,
  226. averaged over specified highlight intervals for each region.
  227. Arguments:
  228. - detection_datas: Data for the EEG signals with highlight intervals.
  229. - region_dict: Mapping of regions to channel indices.
  230. Returns:
  231. - subject_region_mean_intervals: List of dictionaries with average power for each region per subject.
  232. """
  233. subject_region_mean_intervals = []
  234. # Iterate over each subject's data
  235. for j, subject_data in enumerate(detection_datas[0]): # Assuming the subject data is in the first element
  236. subject_id = subject_data['subject_id']
  237. # Store the region-wise mean power for each subject
  238. region_mean_per_subject = {}
  239. # Iterate through the regions and their channels
  240. for region, channels in region_dict.items():
  241. highlight_intervals_per_region = []
  242. # Iterate through each channel in the region
  243. for channel in channels:
  244. highlight_intervals = detection_datas[channel][j]['highlight_intervals']
  245. # Process each highlight interval
  246. for highlight_interval in highlight_intervals:
  247. # Normalize the highlight interval
  248. segment_normalized = highlight_interval / np.max(np.abs(highlight_interval))
  249. # Calculate the power spectrum using Welch's method
  250. frequencies, psd = welch(segment_normalized, fs=fs, nperseg=100)
  251. # Calculate the total power of the signal
  252. total_power = np.trapz(psd, frequencies) / len(highlight_interval) * fs
  253. highlight_intervals_per_region.append(total_power)
  254. # Compute the average power for the region across channels
  255. region_mean_per_subject[region] = np.mean(highlight_intervals_per_region)
  256. # Store the region-wise power results for the subject
  257. subject_region_mean_intervals.append(region_mean_per_subject)
  258. return subject_region_mean_intervals
  259. def calculate_center_frequency_per_subject(detection_datas, region_dict):
  260. """
  261. Calculate the center frequency of EEG signals for each subject, averaged over highlight intervals
  262. for each region.
  263. Arguments:
  264. - detection_datas: Data for the EEG signals with highlight intervals.
  265. - region_dict: Mapping of regions to channel indices.
  266. Returns:
  267. - subject_region_mean_intervals: List of dictionaries with average center frequency for each region per subject.
  268. """
  269. subject_region_mean_intervals = []
  270. # Iterate over each subject's data
  271. for j, subject_data in enumerate(detection_datas[0]):
  272. subject_id = subject_data['subject_id']
  273. # Store the region-wise center frequency for each subject
  274. region_mean_per_subject = {}
  275. # Iterate through the regions and their channels
  276. for region, channels in region_dict.items():
  277. highlight_intervals_per_region = []
  278. # Iterate through each channel in the region
  279. for channel in channels:
  280. highlight_intervals = detection_datas[channel][j]['highlight_intervals']
  281. # Process each highlight interval
  282. for highlight_interval in highlight_intervals:
  283. # Calculate the power spectral density using Welch's method
  284. frequencies, psd = welch(highlight_interval, fs=fs, nperseg=100)
  285. # Calculate the center frequency as the weighted average of the frequencies
  286. center_frequency = np.sum(frequencies * psd) / np.sum(psd)
  287. highlight_intervals_per_region.append(center_frequency)
  288. # Compute the average center frequency for the region across channels
  289. region_mean_per_subject[region] = np.mean(highlight_intervals_per_region)
  290. # Store the region-wise center frequency results for the subject
  291. subject_region_mean_intervals.append(region_mean_per_subject)
  292. return subject_region_mean_intervals
  293. def calculate_highlight_intervals_per_subject(detection_datas, region_dict):
  294. """
  295. Calculate the number of highlight intervals for each region in the EEG data for each subject.
  296. Arguments:
  297. - detection_datas: Data for the EEG signals with highlight intervals.
  298. - region_dict: Mapping of regions to channel indices.
  299. Returns:
  300. - subject_region_mean_intervals: List of dictionaries with the number of highlight intervals per region per subject.
  301. """
  302. subject_region_mean_intervals = []
  303. # Iterate over each subject's data
  304. for j, subject_data in enumerate(detection_datas[0]):
  305. subject_id = subject_data['subject_id']
  306. # Store the region-wise highlight interval count for each subject
  307. region_mean_per_subject = {}
  308. # Iterate through the regions and their channels
  309. for region, channels in region_dict.items():
  310. highlight_intervals_per_region = []
  311. # Iterate through each channel in the region
  312. for channel in channels:
  313. highlight_intervals = detection_datas[channel][j]['highlight_intervals']
  314. # Count the number of highlight intervals for the channel
  315. highlight_intervals_per_region.append(len(highlight_intervals))
  316. # Compute the average number of highlight intervals for the region
  317. region_mean_per_subject[region] = np.mean(highlight_intervals_per_region)
  318. # Store the region-wise highlight interval counts for the subject
  319. subject_region_mean_intervals.append(region_mean_per_subject)
  320. return subject_region_mean_intervals
  321. def extract_region_means(subject_region_means, region_name):
  322. """
  323. Extract the region-specific means for all subjects.
  324. Arguments:
  325. - subject_region_means: List of dictionaries containing region-wise means for each subject.
  326. - region_name: The region to extract means for.
  327. Returns:
  328. - np.array: Array of region-specific means across subjects.
  329. """
  330. return np.array([subject[region_name] for subject in subject_region_means])
  331. def mean_difference(data, n_group_A):
  332. """
  333. Calculate the difference in means between two groups.
  334. Arguments:
  335. - data: Combined data from both groups.
  336. - n_group_A: The number of samples in group A.
  337. Returns:
  338. - The difference in means between the two groups.
  339. """
  340. group_A = data[:n_group_A]
  341. group_B = data[n_group_A:]
  342. return np.mean(group_A) - np.mean(group_B)
  343. def bootstrap_region_comparison(subject_region_mean_A, subject_region_mean_B, region_dict, confidence_level=0.95, n_resamples=10000):
  344. """
  345. Perform a bootstrap test to compare the means of two groups for each region.
  346. Arguments:
  347. - subject_region_mean_A: Region means for group A.
  348. - subject_region_mean_B: Region means for group B.
  349. - region_dict: Mapping of regions to channel indices.
  350. - confidence_level: Confidence level for the bootstrap test (default is 0.95).
  351. - n_resamples: Number of bootstrap resamples (default is 10,000).
  352. Returns:
  353. - results: Dictionary with mean differences, confidence intervals, and p-values for each region.
  354. """
  355. results = {}
  356. # Iterate over each region in the dictionary
  357. for region in region_dict.keys():
  358. # Extract region means for both groups
  359. group_A = extract_region_means(subject_region_mean_A, region)
  360. group_B = extract_region_means(subject_region_mean_B, region)
  361. # Record the sample size of group A
  362. n_group_A = len(group_A)
  363. # Combine data from both groups
  364. data = np.concatenate([group_A, group_B])
  365. # Perform the bootstrap test
  366. res = bootstrap(
  367. (data,),
  368. lambda x: mean_difference(x, n_group_A),
  369. confidence_level=confidence_level,
  370. n_resamples=n_resamples,
  371. method='BCa',
  372. paired=False,
  373. alternative='two-sided'
  374. )
  375. # Save the results for this region
  376. results[region] = {
  377. "mean_difference": mean_difference(data, n_group_A),
  378. "confidence_interval": res.confidence_interval,
  379. "p_value": (res.confidence_interval.low > 0) or (res.confidence_interval.high < 0)
  380. }
  381. return results
  382. # %% [markdown]
  383. # # Figure 5C
  384. # %%
  385. results = np.load('../dataset/figure5_C_results.npz', allow_pickle=True)
  386. for region, result in results.items():
  387. result = result.item()
  388. print(f" Region: {region}")
  389. print(f" Mean Difference: {result['mean_difference']}")
  390. print(f" Confidence Interval: {result['confidence_interval']}")
  391. print(f" Significant: {result['p_value']}\n")
  392. violin_data = np.load('../dataset/figure5_C_violin_data.npy', allow_pickle=True)
  393. df_violin = pd.DataFrame(violin_data, columns=["Region", "Group", "Center Frequecy(Hz)"])
  394. # 设置期刊级绘图参数
  395. plt.rcParams.update({
  396. 'font.family': 'DejaVu Sans',
  397. 'font.size': 10,
  398. 'axes.titlesize': 12,
  399. 'axes.labelsize': 11,
  400. 'xtick.labelsize': 10,
  401. 'ytick.labelsize': 10,
  402. 'legend.fontsize': 10,
  403. 'figure.dpi': 300,
  404. 'axes.linewidth': 0.8 # 坐标轴线宽
  405. })
  406. fig, ax = plt.subplots(figsize=(13, 4), constrained_layout=True)
  407. clinical_palette = {"POD": "#E64B35", "non-POD": "#3C5488"}
  408. # 增强型箱线图设计
  409. box = sns.boxplot(
  410. x='Region',
  411. y='Center Frequecy(Hz)',
  412. hue='Group',
  413. data=df_violin,
  414. palette=clinical_palette,
  415. width=0.6,
  416. linewidth=1.2,
  417. flierprops={
  418. 'marker': 'o',
  419. 'markersize': 4,
  420. 'markerfacecolor': 'none',
  421. 'markeredgecolor': 'gray',
  422. 'markeredgewidth': 0.5
  423. },
  424. # boxprops={'facecolor': 'none', 'edgecolor': 'black'},
  425. whiskerprops={'linewidth': 1.2},
  426. medianprops={'color': 'black', 'linewidth': 1.5},
  427. ax=ax
  428. )
  429. # 坐标轴优化
  430. ax.set_ylim(3, 9.5)
  431. ax.set_ylabel("Center Frequecy(Hz)", labelpad=10)
  432. ax.set_xlabel("Brain Regions", labelpad=10)
  433. ax.yaxis.grid(True, linestyle='--', alpha=0.6)
  434. # 高级图例设计
  435. handles, labels = ax.get_legend_handles_labels()
  436. legend = ax.legend(
  437. handles[:2],
  438. ['POD', 'Non-POD'],
  439. frameon=True,
  440. loc='upper right',
  441. bbox_to_anchor=(1.18, 1), # 图例外置防止重叠
  442. ncol=1,
  443. # title='Experimental Group',
  444. title_fontproperties={'weight': 'bold'},
  445. edgecolor='none',
  446. borderpad=0.8
  447. )
  448. # 优化视觉层次
  449. sns.despine(offset=5, trim=True) # 移除顶部和右侧轴线
  450. plt.savefig('/mnt/dataset1/UnonoU/POD-biomarker/plot/figure_5_C', dpi=300, bbox_inches='tight')
  451. plt.show()
  452. # %% [markdown]
  453. # # Figure 5E
  454. # %%
  455. # subject_region_mean_A = calculate_highlight_intervals_per_subject(detection_datas_A, net_dict)
  456. # subject_region_mean_B = calculate_highlight_intervals_per_subject(detection_datas_B, net_dict)
  457. # results = bootstrap_region_comparison(subject_region_mean_A, subject_region_mean_B, net_dict, confidence_level=0.999, n_resamples=1000)
  458. # violin_data = []
  459. # for region in regions:
  460. # data_A = [subject[region] for subject in subject_region_mean_A]
  461. # data_B = [subject[region] for subject in subject_region_mean_B]
  462. # for value in data_A:
  463. # violin_data.append((region, 'POD', value/10))
  464. # for value in data_B:
  465. # violin_data.append((region, 'non-POD', value/10))
  466. # df_violin = pd.DataFrame(violin_data, columns=["Region", "Group", "Biomarker Event Frequency (event/min)"])
  467. results = np.load('../dataset/figure5_E_results_99.9.npz', allow_pickle=True)
  468. print('P<0.001')
  469. for region, result in results.items():
  470. result = result.item()
  471. print(f" Region: {region}")
  472. print(f" Mean Difference: {result['mean_difference']}")
  473. print(f" Confidence Interval: {result['confidence_interval']}")
  474. print(f" Significant: {result['p_value']}\n")
  475. results = np.load('../dataset/figure5_E_results_99.npz', allow_pickle=True)
  476. print('P<0.01')
  477. for region, result in results.items():
  478. result = result.item()
  479. print(f" Region: {region}")
  480. print(f" Mean Difference: {result['mean_difference']}")
  481. print(f" Confidence Interval: {result['confidence_interval']}")
  482. print(f" Significant: {result['p_value']}\n")
  483. violin_data = np.load('../dataset/figure5_E_violin_data.npy', allow_pickle=True)
  484. df_violin = pd.DataFrame(violin_data, columns=["Region", "Group", "Biomarker Event Frequency (event/min)"])
  485. # 设置期刊级绘图参数
  486. plt.rcParams.update({
  487. 'font.family': 'DejaVu Sans',
  488. 'font.size': 10,
  489. 'axes.titlesize': 12,
  490. 'axes.labelsize': 11,
  491. 'xtick.labelsize': 10,
  492. 'ytick.labelsize': 10,
  493. 'legend.fontsize': 10,
  494. 'figure.dpi': 300,
  495. 'axes.linewidth': 0.8 # 坐标轴线宽
  496. })
  497. fig, ax = plt.subplots(figsize=(12, 4), constrained_layout=True)
  498. clinical_palette = {"POD": "#E64B35", "non-POD": "#3C5488"}
  499. # 增强型箱线图设计
  500. box = sns.boxplot(
  501. x='Region',
  502. y='Biomarker Event Frequency (event/min)',
  503. hue='Group',
  504. data=df_violin,
  505. palette=clinical_palette,
  506. width=0.6,
  507. linewidth=1.2,
  508. flierprops={
  509. 'marker': 'o',
  510. 'markersize': 4,
  511. 'markerfacecolor': 'none',
  512. 'markeredgecolor': 'gray',
  513. 'markeredgewidth': 0.5
  514. },
  515. # boxprops={'facecolor': 'none', 'edgecolor': 'black'},
  516. whiskerprops={'linewidth': 1.2},
  517. medianprops={'color': 'black', 'linewidth': 1.5},
  518. ax=ax
  519. )
  520. # 坐标轴优化
  521. # ax.set_ylim(3, 9.5)
  522. ax.set_ylabel("Biomarker Event Frequency (event/min)", labelpad=10)
  523. ax.set_xlabel("Brain Regions", labelpad=10)
  524. ax.yaxis.grid(True, linestyle='--', alpha=0.6)
  525. # 高级图例设计
  526. handles, labels = ax.get_legend_handles_labels()
  527. legend = ax.legend(
  528. handles[:2],
  529. ['POD', 'Non-POD'],
  530. frameon=True,
  531. loc='upper right',
  532. bbox_to_anchor=(1.18, 1), # 图例外置防止重叠
  533. ncol=1,
  534. # title='Experimental Group',
  535. title_fontproperties={'weight': 'bold'},
  536. edgecolor='none',
  537. borderpad=0.8
  538. )
  539. # 优化视觉层次
  540. sns.despine(offset=5, trim=True) # 移除顶部和右侧轴线
  541. plt.savefig('/mnt/dataset1/UnonoU/POD-biomarker/plot/figure_5_E', dpi=300, bbox_inches='tight')
  542. plt.show()

Figure5.ipynb at commit 0f9c56d, no license · at the source

Overview

Authors: Yinuo Zhang1, Yan Zhu1, Xinxin Zhang2,3,4, Xinke Shen1, Xuemiao Tang2,5, Zhihong Lu2,3,4, Chong Lei2,3,4, Mengyu Li2,3,4, Hailong Dong2,3,4, Zhichao Liang1,6, Quanying Liu1, Guangchao Zhao2,3,4
ORCID iDs: Guangchao Zhao
  1. Department of Biomedical Engineering, Southern University of Science and Technology, Shenzhen, Guangdong, China
  2. Department of Anesthesiology and Perioperative Medicine, Xijing Hospital, The Fourth Military Medical University, Xi'an, China
  3. Key Laboratory of Anesthesiology (The Fourth Military Medical University), Ministry of Education, Xi'an, China
  4. Shaanxi Provincial Clinical Research Center for Anesthesiology Medicine, Xi'an, China
  5. Department of Anesthesiology, The Third People's Hospital of Chengdu, Chengdu, Sichuan, China
  6. Center for Neurocognition and Social Behavior, Artificial Intelligence Research Institute, Shenzhen University of Advanced Technology, Shenzhen, Guangdong, China
Journal: MedComm, volume 7, issue 9, article e70980
Dates: received 20 October 2025; accepted 23 June 2026; published online 6 September 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1002/mco2.70980 · PMID 42707129 · PMCID PMC13547085 · OpenAlex W7210277326
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), human (organism), other condition (population), clinical / translational (subfield)
Methods: Spectral & time-frequency, Preprocessing, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning
Keywords: interpretable deep learning framework, multichannel EEG recording, postoperative delirium, spatiotemporal convolutional network
Topic: Intensive Care Unit Cognitive Disorders (Critical Care and Intensive Care Medicine, Medicine), according to OpenAlex
Funding: National Natural Science Foundation of China (2021ZD0200500, 82221001, 82271211, 82293643, 62472206, 82430040, 3254100307); Southern University of Science and Technology; Shenzhen Science and Technology Innovation Commission (KJZD20230923115221044, RCBS20231211090748082); National Science and Technology Major Project (2021ZD0200500, 2025ZD0218300); Basic and Applied Basic Research Foundation of Guangdong Province (2025A1515011645, 2026A1515010121, 2026B1515020099)
Citations: not cited yet (Europe PMC); 36 references in the paper

Abstract

Postoperative delirium (POD) is a common complication in older surgical patients and substantially worsens clinical outcomes, yet existing intraoperative electroencephalography (EEG) monitoring tools lack spatial and temporal specificity, creating a need for interpretable biomarkers. We prospectively analyzed 32‐channel intraoperative EEG from 71 patients aged ≥ 60 undergoing noncardiac surgery, trained an interpretable spatiotemporal convolutional network (ST‐CN), derived a best temporal filter (BTF), and evaluated model performance with region‐specific tests and independent external validation. The ST‐CN classified POD with 97.52% accuracy and an ROC of 0.996. The BTF alone discriminated POD with an AUC of 0.911 and achieved 85.12% accuracy using frontal EEG alone. It captured a distinct 2–12 Hz (δ–θ–α) oscillation in a spindle‐like envelope, which occurred at a significantly higher rate in POD patients (4.62 ± 0.15 vs. 3.88 ± 0.13 waves/min in the frontal region) and differed in central frequency and spectral power. Independent external validation further confirmed robust generalizability, with frontal EEG achieving 89.01% accuracy and an AUC of 0.950. This framework enables accurate, interpretable POD risk stratification and identifies a reproducible frontal EEG biomarker, supporting objective intraoperative early warning and individualized perioperative care.

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

Repository

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

ncclab-sustech/POD-biomarker

License: none: the authors keep all their rights
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Commit: 0f9c56db9a968774970f1494373a6581407b2c88, 13 September 2025
Languages: Jupyter (8), Python (5)
Size: 166 files, 13 scripts
Software Heritage: not archived
Found in: “Data Availability Statement”
Holds: README, 8 notebooks
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (11 files), Matplotlib (9 files), PyTorch (8 files), scikit-learn (7 files), MNE-Python (3 files), pandas (3 files), SciPy (3 files), seaborn (3 files)
Availability: 1 check, the latest on 26 September 2026: the link answers
  • 26 September 2026: the link answers
14 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;
  • 13 scripts, each with its path and the digest of its content;
  • 8 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

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

Data

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

Data Availability Statement

The analysis code has been uploaded to GitHub, the code is available at https://github.com/ncclab‐sustech/POD‐biomarker.git (https://github.com/ncclab-sustech/POD-biomarker.git). Detailed experimental protocols and analysis code are provided in Section 4 Materials and Methods. The data that support the findings of this study are available upon reasonable request from the corresponding author.

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 3, 28 September 2026

  • Funding: added National Natural Science Foundation of China: 2021ZD0200500, 82221001, 82271211, 82293643, 62472206, 82430040, 3254100307; Southern University of Science and Technology; Shenzhen Municipal Science and Technology Innovation Council: KJZD20230923115221044, RCBS20231211090748082; National Science and Technology Major Project: 2021ZD0200500, 2025ZD0218300; Basic and Applied Basic Research Foundation of Guangdong Province: 2025A1515011645, 2026A1515010121, 2026B1515020099

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 12 authors, 4 keywords, 36 references.

Cite

This paper

Zhang, Y., Zhu, Y., Zhang, X., Shen, X., Tang, X., Lu, Z., Lei, C., Li, M., Dong, H., Liang, Z., Liu, Q., & Zhao, G. (2026). An Intraoperative EEG Biomarker for Postoperative Delirium Predicting Based on Interpretable Deep Learning Framework. MedComm, 7(9), e70980. https://doi.org/10.1002/mco2.70980

BibTeX

@article{zhang2026intraoperative,
author = {Zhang, Yinuo and Zhu, Yan and Zhang, Xinxin and Shen, Xinke and Tang, Xuemiao and Lu, Zhihong and Lei, Chong and Li, Mengyu and Dong, Hailong and Liang, Zhichao and Liu, Quanying and Zhao, Guangchao},
title = {{An Intraoperative EEG Biomarker for Postoperative Delirium Predicting Based on Interpretable Deep Learning Framework}},
journal = {MedComm},
year = {2026},
month = sep,
volume = {7},
number = {9},
pages = {e70980},
publisher = {Wiley},
issn = {2688-2663},
doi = {10.1002/mco2.70980},
url = {https://doi.org/10.1002/mco2.70980},
pmid = {42707129},
pmcid = {PMC13547085}
}

RIS

TY - JOUR
AU - Zhang, Yinuo
AU - Zhu, Yan
AU - Zhang, Xinxin
AU - Shen, Xinke
AU - Tang, Xuemiao
AU - Lu, Zhihong
AU - Lei, Chong
AU - Li, Mengyu
AU - Dong, Hailong
AU - Liang, Zhichao
AU - Liu, Quanying
AU - Zhao, Guangchao
TI - An Intraoperative EEG Biomarker for Postoperative Delirium Predicting Based on Interpretable Deep Learning Framework
T2 - MedComm
J2 - MedComm (2020)
PY - 2026
DA - 2026/09/06
VL - 7
IS - 9
SP - e70980
SN - 2688-2663
PB - Wiley
DO - 10.1002/mco2.70980
UR - https://doi.org/10.1002/mco2.70980
LA - en
ER -

CSL-JSON

{
"id": "10.1002/mco2.70980",
"type": "article-journal",
"title": "An Intraoperative EEG Biomarker for Postoperative Delirium Predicting Based on Interpretable Deep Learning Framework",
"container-title": "MedComm",
"author": [
{
"family": "Zhang",
"given": "Yinuo"
},
{
"family": "Zhu",
"given": "Yan"
},
{
"family": "Zhang",
"given": "Xinxin"
},
{
"family": "Shen",
"given": "Xinke"
},
{
"family": "Tang",
"given": "Xuemiao"
},
{
"family": "Lu",
"given": "Zhihong"
},
{
"family": "Lei",
"given": "Chong"
},
{
"family": "Li",
"given": "Mengyu"
},
{
"family": "Dong",
"given": "Hailong"
},
{
"family": "Liang",
"given": "Zhichao"
},
{
"family": "Liu",
"given": "Quanying"
},
{
"family": "Zhao",
"given": "Guangchao"
}
],
"container-title-short": "MedComm (2020)",
"volume": "7",
"issue": "9",
"page": "e70980",
"DOI": "10.1002/mco2.70980",
"PMID": "42707129",
"PMCID": "PMC13547085",
"ISSN": "2688-2663",
"publisher": "Wiley",
"URL": "https://doi.org/10.1002/mco2.70980",
"language": "en",
"issued": {
"date-parts": [
[
2026,
9,
6
]
]
}
}

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.3389/fmed.2026.1794626 [code]
Divergent periodic and aperiodic EEG signatures of Propofol versus sevoflurane anesthesia: a comparative neurophysiological study.
Journal: Frontiers in medicine
In common: seaborn, scikit-learn, pandas, 3 other tools, EEG, clinical / translational, 2 references
[2] doi:10.3389/fncom.2026.1786996 [code]
Schumann-anchored golden ratio organization of human neural oscillations.
Journal: Frontiers in computational neuroscience
In common: MNE-Python, seaborn, scikit-learn, 4 other tools, EEG, 1 reference
[3] doi:10.1371/journal.pcbi.1014302 [code]
Trial-level sequence modeling reveals hidden dynamics of dual-task interference.
Journal: PLoS computational biology
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[4] doi:10.1162/imag.a.1207 [code]
Investigating the temporal dynamics and modeling of mid-level feature representations in humans.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[5] doi:10.1371/journal.pone.0347671 [code]
RMETNet: A cross-subject motor imagery EEG signal classification model based on TSLANet and riemannian geometry features.
Journal: PloS one
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[6] doi:10.1002/hbm.70528 [code]
Explainable AI Insights Into EEG Classification and Its Alignment to Neural Correlates.
Journal: Human brain mapping
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[7] doi:10.1038/s41597-025-05174-7 [code]
A large-scale MEG and EEG dataset for object recognition in naturalistic scenes
Journal: n/a
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[8] doi:10.3390/diagnostics16081186 [code]
The Perioperative Neurocognitive Disorder Prediction Based on AI-Assisted EEG Dynamic Features in Anesthetized Mice.
Journal: Diagnostics (Basel, Switzerland)
In common: EEG, clinical / translational, 1 reference, author Guangchao Zhao
[9] doi:10.3389/fpsyg.2026.1774068 [code]
Analysis of cognitive mechanisms in phoneme perception and pronunciation errors among Korean language learners.
Journal: Frontiers in psychology
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG
[10] doi:10.1186/s13634-026-01330-2 [code]
Leednet: a lightweight network for event detection in EEG signals.
Journal: Journal on advances in signal processing
In common: MNE-Python, PyTorch, seaborn, 5 other tools, EEG

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.