OSCR

Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts.

Code ↔ Paper

3 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 3 matches
  1. [1] § Methods › Beta peak detection ↔ feat_ex/beta/core.py, lines 1–51 · score 0.83 · 15–36 Hz, 10–36, 5–45, 6–14, 1–6, bin
  2. [2] § Results › Bilateral prevalence of STN-LFP beta power is 47.25% per patient ↔ eval/beta_prev_stats.py, lines 798–824 · score 0.74 · Unilateral prevalence, hemisphere prevalence, Bilateral prevalence, Beta peaks, LFP beta, artifact
  3. [3] § Methods › Anatomical reconstruction of microelectrode positions ↔ feat_ex/spatial/volumetric_screen.py, lines 229–246 · score 0.60 · MNI space, DBS lead, microelectrode, shift, trajectories, position

Paper

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

The paper is loaded when this pane is shown.

The authors' code

Python · 1,058 lines · 52 KB · no license · 1 match

  1. '''
  2. Created on Oct 28, 2024
  3. @author: voodoocode
  4. '''
  5. import os
  6. import finnpy.file_io.data_manager as dm
  7. import numpy as np
  8. import matplotlib
  9. matplotlib.use("Qtagg")
  10. import matplotlib.pyplot as plt
  11. import scipy.signal
  12. import functools
  13. import PyQt6.QtWidgets
  14. import multiprocessing
  15. import pyqtgraph
  16. pyqtgraph.setConfigOptions(antialias=True)
  17. import pandas
  18. PLOT_COLORS = ["lightblue", "green", "red", "orange", "purple"]
  19. PLOT_PATTERNS = ["solid", "dotted", "dashed"]
  20. TIMER_DELAY_MS = 1500
  21. PATH = "/mnt/data/Professional/LMU/data/Beta-prevalence/"
  22. MAX_PEAKS = 3
  23. SCALES = [[1, 6], [6, 14], [5, 45], [10, 36], [15, 36]]
  24. REFRESH_TIMER_DELAY_MS = 2000
  25. MRK_CNT_START = 3
  26. LEFT_HEMISPHERE = 0
  27. RIGHT_HEMISPHERE = 1
  28. ALPHA_COLUMN = 1
  29. BETA1_COLUMN = 3
  30. BETA2_COLUMN = 4
  31. PLOT_IDX = 0
  32. CANVAS_IDX = 1
  33. INSIGHT_GAIN = {"Strong" : 3, "Medium" : 2, "Low" : 1, "None" : 0}
  34. FREQ_BAND_CNT = 2
  35. MAX_FILT_BIN = 12
  36. def clicked_color_bttn(bttn, config_idx, hem_idx):
  37. global valid_chs;
  38. curr_color = bttn.styleSheet().split(": ")[1][:-1]
  39. curr_color_idx = PLOT_COLORS.index(curr_color)
  40. while(True):
  41. curr_color_idx += 1
  42. if (curr_color_idx == len(PLOT_COLORS)):
  43. curr_color_idx = 0
  44. if (curr_color_idx == len(valid_chs[hem_idx])):
  45. curr_color_idx = 0
  46. if (valid_chs[hem_idx][curr_color_idx]):
  47. break
  48. bttn.setStyleSheet("background-color: " + PLOT_COLORS[curr_color_idx] + ";")
  49. global box_color
  50. box_color[config_idx][0] = PLOT_COLORS[curr_color_idx]
  51. def get_line_edit(pos):
  52. global mrk_cnt
  53. txt = PyQt6.QtWidgets.QLineEdit()
  54. txt.setAlignment(PyQt6.QtCore.Qt.AlignmentFlag.AlignCenter)
  55. txt.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  56. if (pos == "la"):
  57. txt.setText(str(mrk_cnt[0][0]))
  58. if (pos == "ra"):
  59. txt.setText(str(mrk_cnt[1][0]))
  60. if (pos == "lb"):
  61. txt.setText(str(mrk_cnt[0][1]))
  62. if (pos == "rb"):
  63. txt.setText(str(mrk_cnt[1][1]))
  64. txt.setEnabled(False)
  65. return txt
  66. def clckd_box_peak(config_idx, mrk_idx, box):
  67. global box_peak
  68. if (box.isChecked()):
  69. box_peak[config_idx][mrk_idx] = True
  70. else:
  71. box_peak[config_idx][mrk_idx] = False
  72. def clckd_box_noise(config_idx, mrk_idx, box):
  73. global box_noise
  74. if (box.isChecked()):
  75. box_noise[config_idx][mrk_idx] = True
  76. else:
  77. box_noise[config_idx][mrk_idx] = False
  78. def clckd_box_depth(config_idx, mrk_idx, box):
  79. global box_depth
  80. if (box.isChecked()):
  81. box_depth[config_idx][mrk_idx] = True
  82. else:
  83. box_depth[config_idx][mrk_idx] = False
  84. def clckd_box_lim(config_idx, mrk_idx, box):
  85. global box_lim
  86. if (box.isChecked()):
  87. box_lim[config_idx][mrk_idx] = True
  88. else:
  89. box_lim[config_idx][mrk_idx] = False
  90. def clckd_box_depvar(config_idx, mrk_idx, box):
  91. global box_depvar
  92. if (box.isChecked()):
  93. box_depvar[config_idx][mrk_idx] = True
  94. else:
  95. box_depvar[config_idx][mrk_idx] = False
  96. def clckd_box_specstat(config_idx, mrk_idx, box):
  97. global box_specstat
  98. if (box.isChecked()):
  99. box_specstat[config_idx][mrk_idx] = True
  100. else:
  101. box_specstat[config_idx][mrk_idx] = False
  102. def clckd_box_no_harm(config_idx, mrk_idx, box):
  103. global box_no_harm
  104. if (box.isChecked()):
  105. box_no_harm[config_idx][mrk_idx] = True
  106. else:
  107. box_no_harm[config_idx][mrk_idx] = False
  108. def clckd_box_plausible(config_idx, mrk_idx, box):
  109. global box_plausible
  110. if (box.isChecked()):
  111. box_plausible[config_idx][mrk_idx] = True
  112. else:
  113. box_plausible[config_idx][mrk_idx] = False
  114. def clckd_box_unimod(config_idx, mrk_idx, box):
  115. global box_unimod
  116. if (box.isChecked()):
  117. box_unimod[config_idx][mrk_idx] = True
  118. else:
  119. box_unimod[config_idx][mrk_idx] = False
  120. def clckd_box_unique(config_idx, box):
  121. global box_unique
  122. if (box.isChecked()):
  123. box_unique[config_idx][0] = True
  124. else:
  125. box_unique[config_idx][0] = False
  126. def get_strong_mrk_frame(pos, mrk_idx, layout, show_text, db_name):
  127. if (show_text == True):
  128. box_2_6 = PyQt6.QtWidgets.QCheckBox(" Spatially unimodal"); layout.addWidget(box_2_6, 2, mrk_idx)
  129. box_2_4 = PyQt6.QtWidgets.QCheckBox(" Unique suggestion"); layout.addWidget(box_2_4, 1, mrk_idx)
  130. else:
  131. box_2_6 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_6, 2, mrk_idx)
  132. box_2_4 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_4, 1, mrk_idx)
  133. box_2_4.setVisible(False)
  134. if (db_name == "db1" or db_name == "db2"):
  135. box_2_6.setVisible(False)
  136. if (pos == "la"):
  137. config_idx = 0
  138. elif (pos == "ra"):
  139. config_idx = 1
  140. elif (pos == "lb"):
  141. config_idx = 2
  142. elif (pos == "rb"):
  143. config_idx = 3
  144. global box_unimod, box_unique
  145. box_unimod[config_idx][mrk_idx] = False
  146. box_unique[config_idx][0] = False
  147. box_2_6.stateChanged.connect(functools.partial(clckd_box_unimod, config_idx, mrk_idx, box_2_6))
  148. box_2_4.stateChanged.connect(functools.partial(clckd_box_unique, config_idx, box_2_4))
  149. def get_artifact_mrk_frame(pos, mrk_idx, layout, show_text, db_name):
  150. if (show_text == True):
  151. box_2_2 = PyQt6.QtWidgets.QCheckBox(" Visible in >1 depth"); layout.addWidget(box_2_2,10, mrk_idx)
  152. box_2_6 = PyQt6.QtWidgets.QCheckBox(" Spatially limited"); layout.addWidget(box_2_6, 9, mrk_idx)
  153. box_2_5 = PyQt6.QtWidgets.QCheckBox(" Depth variable amp."); layout.addWidget(box_2_5, 8, mrk_idx)
  154. box_2_3 = PyQt6.QtWidgets.QCheckBox(" Spectrally static"); layout.addWidget(box_2_3, 7, mrk_idx)
  155. #box_2_4 = PyQt6.QtWidgets.QCheckBox(" Spectrally wide"); layout.addWidget(box_2_4, 6, mrk_idx)
  156. box_2_1 = PyQt6.QtWidgets.QCheckBox(" Not a LF harmonic"); layout.addWidget(box_2_1, 5, mrk_idx)
  157. box_2_0 = PyQt6.QtWidgets.QCheckBox(" Inside the STN"); layout.addWidget(box_2_0, 4, mrk_idx)
  158. else:
  159. box_2_2 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_2,10, mrk_idx)
  160. box_2_6 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_6, 9, mrk_idx)
  161. box_2_5 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_5, 8, mrk_idx)
  162. box_2_3 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_3, 7, mrk_idx)
  163. #box_2_4 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_4, 6, mrk_idx)
  164. box_2_1 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_1, 5, mrk_idx)
  165. box_2_0 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_2_0, 4, mrk_idx)
  166. if (db_name == "db1" or db_name == "db2"):
  167. # box_2_5.setVisible(False)
  168. box_2_6.setVisible(False)
  169. box_2_2.setVisible(False)
  170. if (db_name != "db4" and db_name != "db6"):
  171. box_2_0.setVisible(False)
  172. #box_2_0.setVisible(False)
  173. if (pos == "la"):
  174. config_idx = 0
  175. elif (pos == "ra"):
  176. config_idx = 1
  177. elif (pos == "lb"):
  178. config_idx = 2
  179. elif (pos == "rb"):
  180. config_idx = 3
  181. global box_depth, box_lim, box_depvar, box_specstat, box_no_harm, box_plausible
  182. box_depth[config_idx][mrk_idx] = False
  183. box_lim[config_idx][mrk_idx] = False
  184. box_depvar[config_idx][mrk_idx] = False
  185. box_specstat[config_idx][mrk_idx] = False
  186. box_no_harm[config_idx][mrk_idx] = False
  187. box_plausible[config_idx][mrk_idx] = False
  188. box_2_2.stateChanged.connect(functools.partial(clckd_box_depth, config_idx, mrk_idx, box_2_2))
  189. box_2_6.stateChanged.connect(functools.partial(clckd_box_lim, config_idx, mrk_idx, box_2_6))
  190. box_2_5.stateChanged.connect(functools.partial(clckd_box_depvar, config_idx, mrk_idx, box_2_5))
  191. box_2_3.stateChanged.connect(functools.partial(clckd_box_specstat, config_idx, mrk_idx, box_2_3))
  192. box_2_1.stateChanged.connect(functools.partial(clckd_box_no_harm, config_idx, mrk_idx, box_2_1))
  193. box_2_0.stateChanged.connect(functools.partial(clckd_box_plausible, config_idx, mrk_idx, box_2_0))
  194. def get_anything_mrk_frame(pos, mrk_idx, layout, show_text):
  195. if (show_text == True):
  196. if (pos[1] == "a"):
  197. box_1_1 = PyQt6.QtWidgets.QCheckBox(" Alpha 8-12Hz"); layout.addWidget(box_1_1, 13, mrk_idx)
  198. else:
  199. box_1_1 = PyQt6.QtWidgets.QCheckBox(" Beta 12-32Hz"); layout.addWidget(box_1_1, 13, mrk_idx)
  200. box_1_2 = PyQt6.QtWidgets.QCheckBox(" Above noise level"); layout.addWidget(box_1_2, 12, mrk_idx)
  201. else:
  202. if (pos[1] == "a"):
  203. box_1_1 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_1_1, 13, mrk_idx)
  204. else:
  205. box_1_1 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_1_1, 13, mrk_idx)
  206. box_1_2 = PyQt6.QtWidgets.QCheckBox(); layout.addWidget(box_1_2, 12, mrk_idx)
  207. if (pos == "la"):
  208. config_idx = 0
  209. elif (pos == "ra"):
  210. config_idx = 1
  211. elif (pos == "lb"):
  212. config_idx = 2
  213. elif (pos == "rb"):
  214. config_idx = 3
  215. global box_peak, box_noise
  216. box_peak[config_idx][mrk_idx] = False
  217. box_noise[config_idx][mrk_idx] = False
  218. box_1_1.stateChanged.connect(functools.partial(clckd_box_peak, config_idx, mrk_idx, box_1_1))
  219. box_1_2.stateChanged.connect(functools.partial(clckd_box_noise, config_idx, mrk_idx, box_1_2))
  220. def get_config_tabs(pos, parent_layout, db_name):
  221. global mrk_cnt
  222. if (pos == "la"):
  223. loc_mrk_cnt = mrk_cnt[0][0]
  224. if (pos == "ra"):
  225. loc_mrk_cnt = mrk_cnt[1][0]
  226. if (pos == "lb"):
  227. loc_mrk_cnt = mrk_cnt[0][1]
  228. if (pos == "rb"):
  229. loc_mrk_cnt = mrk_cnt[1][1]
  230. main_wid = PyQt6.QtWidgets.QWidget(objectName = "config chkbxs")
  231. main_wid.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  232. main_layout = PyQt6.QtWidgets.QGridLayout(); main_wid.setLayout(main_layout)
  233. main_layout.setContentsMargins(0, 0, 0, 0); main_layout.setSpacing(5)
  234. for mrk_idx in range(loc_mrk_cnt):
  235. get_anything_mrk_frame(pos, mrk_idx, main_layout, True if (mrk_idx == (len(range(loc_mrk_cnt)) - 1)) else False)
  236. get_artifact_mrk_frame(pos, mrk_idx, main_layout, True if (mrk_idx == (len(range(loc_mrk_cnt)) - 1)) else False, db_name)
  237. get_strong_mrk_frame(pos, mrk_idx, main_layout, True if (mrk_idx == (len(range(loc_mrk_cnt)) - 1)) else False, db_name)
  238. for mrk_idx in range(loc_mrk_cnt):
  239. main_layout.addWidget(PyQt6.QtWidgets.QLabel(str(mrk_idx + 1)), 14, mrk_idx)
  240. border_wid0 = PyQt6.QtWidgets.QLabel("- Target identification -"); border_wid0.setEnabled(False); main_layout.addWidget(border_wid0, 0, 0, 1, 4)
  241. border_wid1 = PyQt6.QtWidgets.QLabel("- Artifact rejection -"); border_wid1.setEnabled(False); main_layout.addWidget(border_wid1, 3, 0, 1, 4)
  242. border_wid2 = PyQt6.QtWidgets.QLabel("- Peak detection -"); border_wid2.setEnabled(False); main_layout.addWidget(border_wid2, 11, 0, 1, 4)
  243. parent_layout.addWidget(main_wid)
  244. def info_bttn_clicked(bttn, config_idx):
  245. global box_insight
  246. if (bttn.text() == "Insight gain: None"):
  247. bttn.setText("Insight gain: Strong")
  248. box_insight[config_idx][0] = "Strong"
  249. elif (bttn.text() == "Insight gain: Strong"):
  250. bttn.setText("Insight gain: Medium")
  251. box_insight[config_idx][0] = "Medium"
  252. elif (bttn.text() == "Insight gain: Medium"):
  253. bttn.setText("Insight gain: Low")
  254. box_insight[config_idx][0] = "Low"
  255. elif (bttn.text() == "Insight gain: Low"):
  256. bttn.setText("Insight gain: None")
  257. box_insight[config_idx][0] = "None"
  258. def get_mrk_result(pos,
  259. parent_layout):
  260. global box_insight
  261. main_wid = PyQt6.QtWidgets.QWidget(objectName = "Insights")
  262. layout = PyQt6.QtWidgets.QVBoxLayout(); main_wid.setLayout(layout)
  263. if (pos == "la"):
  264. config_idx = 0
  265. elif (pos == "ra"):
  266. config_idx = 1
  267. elif (pos == "lb"):
  268. config_idx = 2
  269. elif (pos == "rb"):
  270. config_idx = 3
  271. layout.addWidget(get_line_edit(pos))
  272. bttn = PyQt6.QtWidgets.QPushButton("Insight gain: None");
  273. bttn.clicked.connect(functools.partial(info_bttn_clicked, bttn, config_idx))
  274. box_insight[config_idx][0] = "None"
  275. layout.addWidget(bttn)
  276. layout.setContentsMargins(0, 0, 0, 0); layout.setSpacing(5)
  277. parent_layout.addWidget(main_wid)
  278. def get_bttns(pos,
  279. parent_layout):
  280. bttn_wid = PyQt6.QtWidgets.QWidget(objectName = "config bttns")
  281. bttn_layout = PyQt6.QtWidgets.QHBoxLayout(); bttn_wid.setLayout(bttn_layout)
  282. bttn_layout.setContentsMargins(0, 0, 0, 0); bttn_layout.setSpacing(5)
  283. global mrk_cnt, box_color
  284. if (pos == "la"):
  285. for mrk_idx in range(mrk_cnt[0][0]):
  286. bttn = PyQt6.QtWidgets.QToolButton()
  287. bttn.setText(str(mrk_idx + 1))
  288. bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  289. bttn.setStyleSheet("background-color: " + PLOT_COLORS[valid_chs[0].index(True)] + ";")
  290. bttn.clicked.connect(functools.partial(clicked_color_bttn, bttn, 0, 0))
  291. box_color[0][mrk_idx] = PLOT_COLORS[valid_chs[0].index(True)]
  292. bttn_layout.addWidget(bttn)
  293. if (pos == "ra"):
  294. for mrk_idx in range(mrk_cnt[1][0]):
  295. bttn = PyQt6.QtWidgets.QToolButton()
  296. bttn.setText(str(mrk_idx + 1))
  297. bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  298. bttn.setStyleSheet("background-color: " + PLOT_COLORS[valid_chs[1].index(True)] + ";")
  299. bttn.clicked.connect(functools.partial(clicked_color_bttn, bttn, 1, 1))
  300. box_color[1][mrk_idx] = PLOT_COLORS[valid_chs[1].index(True)]
  301. bttn_layout.addWidget(bttn)
  302. if (pos == "lb"):
  303. for mrk_idx in range(mrk_cnt[0][1]):
  304. bttn = PyQt6.QtWidgets.QToolButton()
  305. bttn.setText(str(mrk_idx + 1))
  306. bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  307. bttn.setStyleSheet("background-color: " + PLOT_COLORS[valid_chs[0].index(True)] + ";")
  308. bttn.clicked.connect(functools.partial(clicked_color_bttn, bttn, 2, 0))
  309. box_color[2][mrk_idx] = PLOT_COLORS[valid_chs[0].index(True)]
  310. bttn_layout.addWidget(bttn)
  311. if (pos == "rb"):
  312. for mrk_idx in range(mrk_cnt[1][1]):
  313. bttn = PyQt6.QtWidgets.QToolButton()
  314. bttn.setText(str(mrk_idx + 1))
  315. bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  316. bttn.setStyleSheet("background-color: " + PLOT_COLORS[valid_chs[1].index(True)] + ";")
  317. bttn.clicked.connect(functools.partial(clicked_color_bttn, bttn, 3, 1))
  318. box_color[3][mrk_idx] = PLOT_COLORS[valid_chs[1].index(True)]
  319. bttn_layout.addWidget(bttn)
  320. parent_layout.addWidget(bttn_wid)
  321. def get_config_block(pos,
  322. parent_layout = None,
  323. config_wid = None,
  324. db_name = ""):
  325. if (config_wid is None):
  326. config_wid = PyQt6.QtWidgets.QWidget(objectName = "config panel")
  327. parent_layout.addWidget(config_wid)
  328. config_wid.setMinimumWidth(196)
  329. layout = PyQt6.QtWidgets.QVBoxLayout()
  330. config_wid.setLayout(layout)
  331. layout.setContentsMargins(0, 0, 0, 0)
  332. layout.setSpacing(5)
  333. freq_label = PyQt6.QtWidgets.QLabel("Alpha" if (pos[1] == "a") else "Beta")
  334. freq_label.setAlignment(PyQt6.QtCore.Qt.AlignmentFlag.AlignCenter)
  335. layout.addWidget(freq_label)
  336. get_mrk_result(pos, layout)
  337. get_bttns(pos, layout)
  338. get_config_tabs(pos, layout, db_name)
  339. global config_wids
  340. if (pos == "la"):
  341. config_wids[0][0] = config_wid
  342. if (pos == "ra"):
  343. config_wids[1][0] = config_wid
  344. if (pos == "lb"):
  345. config_wids[0][1] = config_wid
  346. if (pos == "rb"):
  347. config_wids[1][1] = config_wid
  348. def get_config_panel(hem_idx,
  349. parent_layout):
  350. main_wid = PyQt6.QtWidgets.QWidget(objectName = "config panels")
  351. layout = PyQt6.QtWidgets.QVBoxLayout()
  352. main_wid.setLayout(layout)
  353. get_config_block("la" if (hem_idx == 0) else "ra", layout)
  354. a_space = PyQt6.QtWidgets.QWidget(objectName = "config panel separator")
  355. a_space.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Expanding)
  356. layout.addWidget(a_space)
  357. get_config_block("lb" if (hem_idx == 0) else "rb", layout)
  358. parent_layout.addWidget(main_wid)
  359. def get_psds(raw_data, fs, win_szs, depth_cnt, ch_cnt, db_name, scale_db):
  360. hem_cnt = len(raw_data)
  361. bins = [[[[np.ones((int(win_sz * fs / 2 + 1))) * np.nan for _ in range(depth_cnt[hem_idx])] for _ in range(ch_cnt[hem_idx])] for hem_idx in range(hem_cnt)] for win_sz in win_szs]
  362. pwrs = [[[[np.ones((int(win_sz * fs / 2 + 1))) * np.nan for _ in range(depth_cnt[hem_idx])] for _ in range(ch_cnt[hem_idx])] for hem_idx in range(hem_cnt)] for win_sz in win_szs]
  363. ref_bins = [None for _ in range(len(win_szs))]
  364. for hem_idx in range(hem_cnt):
  365. for ch_idx in range(ch_cnt[hem_idx]):
  366. for depth_idx in range(depth_cnt[hem_idx]):
  367. for (win_sz_idx, win_sz) in enumerate(win_szs):
  368. if (raw_data[hem_idx][ch_idx][depth_idx] is None):
  369. continue
  370. (bins[win_sz_idx][hem_idx][ch_idx][depth_idx], pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx]) = scipy.signal.welch(raw_data[hem_idx][ch_idx][depth_idx], fs, "hann", nperseg = int(fs * win_sz), noverlap = int(fs//2 * win_sz), nfft = int(fs * win_sz), detrend = "linear", return_onesided = True)
  371. #===========================================================
  372. # if (db_name == "db4"):#Remove a filter starting at 10 Hz
  373. # loc_bins = bins[win_sz_idx][hem_idx][ch_idx][depth_idx]
  374. # loc_pwrs = pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx]
  375. #
  376. # def _db4_filter_func(x):
  377. # r = 5.6*1e3; c = 1*1e-6
  378. # f_cutoff = 1/(2 * np.pi * r * c)
  379. # order = 1
  380. #
  381. # return 1/(1 + np.power(f_cutoff/x, 2*order))
  382. # corr_fctr = 1/_db4_filter_func(loc_bins[1:])
  383. #
  384. # loc_pwrs[1:] *= corr_fctr
  385. #
  386. # bins[win_sz_idx][hem_idx][ch_idx][depth_idx] = loc_bins
  387. # pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx] = loc_pwrs
  388. #
  389. # if (db_name == "db6"):#Remove a filter starting at 200 Hz
  390. # loc_bins = bins[win_sz_idx][hem_idx][ch_idx][depth_idx]
  391. # loc_pwrs = pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx]
  392. #
  393. # def _db6_filter_func(x, f_cutoff = 200, order = 1):
  394. # return 1/(1 + np.power(f_cutoff/x, 2*order))
  395. # corr_fctr = 1/_db6_filter_func(loc_bins[1:])
  396. #
  397. # #=======================================================
  398. # # plt.plot(_db6_filter_func(loc_bin[1:(max_filt_bin_idx + 1)]))
  399. # # plt.show(block = True)
  400. # #=======================================================
  401. #
  402. # loc_pwrs[1:] *= corr_fctr
  403. #
  404. # bins[win_sz_idx][hem_idx][ch_idx][depth_idx] = loc_bins
  405. # pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx] = loc_pwrs
  406. #===========================================================
  407. if (ref_bins[win_sz_idx] is None and bins[win_sz_idx][hem_idx][ch_idx][depth_idx] is not None):
  408. ref_bins[win_sz_idx] = bins[win_sz_idx][hem_idx][ch_idx][depth_idx]
  409. if (scale_db):
  410. for hem_idx in range(hem_cnt):
  411. for ch_idx in range(ch_cnt[hem_idx]):
  412. for depth_idx in range(depth_cnt[hem_idx]):
  413. for (win_sz_idx, win_sz) in enumerate(win_szs):
  414. pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx] = 20 * np.log10(pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx])
  415. #===========================================================================
  416. # #Normalization is here
  417. # for (win_sz_idx, win_sz) in enumerate(win_szs):
  418. # for hem_idx in range(hem_cnt):
  419. # for ch_idx in range(ch_cnt[hem_idx]):
  420. # loc_pwr = pwrs[win_sz_idx][hem_idx][ch_idx]
  421. # loc_pwr = np.asarray(loc_pwr)
  422. #
  423. # ref_data = list()
  424. # for depth_idx in range(depth_cnt[hem_idx]):
  425. # ref_data.append(pandas.Series(loc_pwr[depth_idx]).rolling(window = 5, center = True).mean())
  426. # ref_data = np.asarray(ref_data)
  427. # loc_pwr /= np.nanmedian(ref_data, axis = 0)
  428. # loc_pwr = np.nan_to_num(loc_pwr)
  429. #
  430. # #loc_pwr /= np.nanmedian(loc_pwr, axis = 0)
  431. #
  432. # loc_pwr = loc_pwr.tolist()
  433. # pwrs[win_sz_idx][hem_idx][ch_idx] = loc_pwr
  434. #===========================================================================
  435. for (win_sz_idx, win_sz) in enumerate(win_szs):
  436. for hem_idx in range(hem_cnt):
  437. for ch_idx in range(ch_cnt[hem_idx]):
  438. for depth_idx in range(depth_cnt[hem_idx]):
  439. if (np.isnan(pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx][0])):
  440. bins[win_sz_idx][hem_idx][ch_idx][depth_idx] = None
  441. pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx] = None
  442. ref_indices = [[None for _ in range(len(win_szs))] for _ in range(len(SCALES))]
  443. for scale_idx in range(len(SCALES)):
  444. for win_sz_idx in range(len(win_szs)):
  445. start = np.argmin(np.abs(ref_bins[win_sz_idx] - SCALES[scale_idx][0]))
  446. end = np.argmin(np.abs(ref_bins[win_sz_idx] - SCALES[scale_idx][1]))
  447. ref_indices[scale_idx][win_sz_idx] = [start, end]
  448. return (bins, pwrs, ref_indices)
  449. class Custom_plot_widget(pyqtgraph.PlotWidget):
  450. def __init__(self, lab_wid, db_name, f_name, pat_name, scale_idx, depth_idx, hem_idx, hem_wid):
  451. super().__init__()
  452. self.lab_wid = lab_wid
  453. self.db_name = db_name
  454. self.f_name = f_name
  455. self.pat_name = pat_name
  456. self.scale_idx = scale_idx
  457. self.depth_idx = depth_idx
  458. self.hem_idx = hem_idx
  459. self.hem_wid = hem_wid
  460. def mouseMoveEvent(self, ev):
  461. pos = self.plotItem.vb.mapSceneToView(PyQt6.QtCore.QPointF(ev.pos().x(), ev.pos().y()))
  462. x_pos = pos.x()
  463. y_pos = pos.y()
  464. self.lab_wid.setText("Database: %s File: %s Patient: %s X-Pos: %2.2f Y-Pos: %f" % (self.db_name, self.f_name, self.pat_name, x_pos, y_pos))
  465. #return pyqtgraph.PlotWidget.mouseMoveEvent(self, ev)
  466. def update_mrk(self, x_pos):
  467. freq_idx = 0 if (self.scale_idx == 1) else 1
  468. global mrk_cnt; global mrk_pos; global mrk_depth; global scatters; global plotters
  469. mrk_cnt[self.hem_idx][freq_idx] += 1
  470. if (mrk_cnt[self.hem_idx][freq_idx] > MAX_PEAKS):
  471. mrk_cnt[self.hem_idx][freq_idx] = 0
  472. for plot_idx in range(MAX_PEAKS)[::-1]:
  473. for sub_idx in range(len(scatters[self.hem_idx][freq_idx][plot_idx])):
  474. parent = scatters[self.hem_idx][freq_idx][plot_idx][sub_idx][0]
  475. scttr = scatters[self.hem_idx][freq_idx][plot_idx][sub_idx][1]
  476. annot = scatters[self.hem_idx][freq_idx][plot_idx][sub_idx][2]
  477. parent.removeItem(scttr); parent.removeItem(annot)
  478. return
  479. mrk_pos[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1] = x_pos
  480. mrk_depth[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1] = self.depth_idx
  481. scatters[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1] = list()
  482. if (freq_idx == 0):
  483. y_pos = (plotters[self.hem_idx][self.depth_idx][1].plotItem.viewRange()[1][0] + plotters[self.hem_idx][self.depth_idx][1].plotItem.viewRange()[1][1])/2
  484. ax = plotters[self.hem_idx][self.depth_idx][1]
  485. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  486. annot = pyqtgraph.TextItem(str(mrk_cnt[self.hem_idx][freq_idx])); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  487. scatters[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1].append((ax, scttr, annot))
  488. else:
  489. y_pos = (plotters[self.hem_idx][self.depth_idx][3].plotItem.viewRange()[1][0] + plotters[self.hem_idx][self.depth_idx][3].plotItem.viewRange()[1][1])/2
  490. ax = plotters[self.hem_idx][self.depth_idx][3]
  491. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  492. annot = pyqtgraph.TextItem(str(mrk_cnt[self.hem_idx][freq_idx])); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  493. scatters[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1].append((ax, scttr, annot))
  494. y_pos = (plotters[self.hem_idx][self.depth_idx][4].plotItem.viewRange()[1][0] + plotters[self.hem_idx][self.depth_idx][4].plotItem.viewRange()[1][1])/2
  495. ax = plotters[self.hem_idx][self.depth_idx][4]
  496. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  497. annot = pyqtgraph.TextItem(str(mrk_cnt[self.hem_idx][freq_idx])); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  498. scatters[self.hem_idx][freq_idx][mrk_cnt[self.hem_idx][freq_idx] - 1].append((ax, scttr, annot))
  499. def mousePressEvent(self, ev):
  500. if (ev.button() != PyQt6.QtCore.Qt.MouseButton.LeftButton):
  501. return
  502. pos_xy = self.plotItem.vb.mapSceneToView(PyQt6.QtCore.QPointF(ev.pos().x(), ev.pos().y()))
  503. x_pos = pos_xy.x()
  504. if (self.scale_idx == 1):
  505. if (x_pos > 12 or x_pos < 8):
  506. return
  507. if (self.scale_idx == 3 or self.scale_idx == 4):
  508. if (x_pos > 32 or x_pos < 12):
  509. return
  510. if (self.scale_idx == 0 or self.scale_idx == 2):
  511. return
  512. self.update_mrk(x_pos)
  513. if (self.hem_idx == 0):
  514. config_type = "la" if (self.scale_idx == 1) else "lb"
  515. else:
  516. config_type = "ra" if (self.scale_idx == 1) else "rb"
  517. global config_wids
  518. if (self.hem_idx == 0):
  519. old_config_wid = config_wids[0][0] if (self.scale_idx == 1) else config_wids[0][1]
  520. else:
  521. old_config_wid = config_wids[1][0] if (self.scale_idx == 1) else config_wids[1][1]
  522. new_config_wid = PyQt6.QtWidgets.QWidget(objectName = "config panel")
  523. get_config_block(config_type, config_wid = new_config_wid, db_name = self.db_name)
  524. parent = old_config_wid.parent()
  525. parent.layout().replaceWidget(old_config_wid, new_config_wid)
  526. old_config_wid.close()
  527. parent.update()
  528. #return pyqtgraph.PlotWidget.mousePressEvent(self, ev)
  529. def get_central_plots(hem_idx, depth_cnt, ch_cnt, win_szs, bins, pwrs, ref_indices,
  530. lab_wid, db_name, f_name, pat_name,
  531. parent_layout, hem_wid):
  532. global plotters
  533. bttns = [[None for _ in range(len(SCALES))] for _ in range(depth_cnt)]
  534. main_wid = PyQt6.QtWidgets.QScrollArea()
  535. inner_wid = PyQt6.QtWidgets.QWidget()
  536. main_wid.setWidgetResizable(True)
  537. main_wid.setWidget(inner_wid)
  538. layout = PyQt6.QtWidgets.QGridLayout()
  539. layout.setSpacing(0)
  540. layout.setContentsMargins(0, 0, 0, 0)
  541. refresh_timer = PyQt6.QtCore.QTimer()
  542. inner_wid.setLayout(layout)
  543. for depth_idx in range(depth_cnt):
  544. for (scale_idx, _) in enumerate(SCALES):
  545. data_wid = PyQt6.QtWidgets.QWidget()
  546. data_wid.setFixedHeight(100)
  547. data_layout = PyQt6.QtWidgets.QHBoxLayout()
  548. data_wid.setLayout(data_layout)
  549. data_layout.setSpacing(0)
  550. data_layout.setContentsMargins(0, 0, 0, 0)
  551. data_wid.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Expanding, PyQt6.QtWidgets.QSizePolicy.Policy.Expanding)
  552. plotter = Custom_plot_widget(lab_wid, db_name, f_name, pat_name, scale_idx, depth_idx, hem_idx, hem_wid)
  553. plotter.setDragMode(PyQt6.QtWidgets.QGraphicsView.DragMode.NoDrag)
  554. data_layout.addWidget(plotter)
  555. plotters[hem_idx][depth_idx][scale_idx] = plotter
  556. plotter.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Expanding, PyQt6.QtWidgets.QSizePolicy.Policy.Expanding)
  557. plotter.setContentsMargins(0, 0, 0, 0);
  558. plotter.getAxis("left").hide()
  559. plotter.getAxis("bottom").hide()
  560. plotter.setFixedHeight(100)
  561. bttn = PyQt6.QtWidgets.QToolButton(); data_layout.addWidget(bttn); bttn.setText("X")
  562. bttns[depth_idx][scale_idx] = bttn
  563. bttn.clicked.connect(functools.partial(bttn_clicked, bttn, refresh_timer))
  564. bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  565. layout.addWidget(data_wid, depth_idx, scale_idx)
  566. refresh_timer.timeout.connect(functools.partial(refresh_timer_expires, plotters, bttns, ch_cnt, depth_cnt, win_szs, bins, pwrs, ref_indices, hem_idx))
  567. refresh_timer.setSingleShot(True)
  568. parent_layout.addWidget(main_wid)
  569. return bttns
  570. def get_central_area(depth_cnt, db_name, f_name, pat_name, hem_idx,
  571. ch_cnt, win_szs, bins, pwrs, ref_indices,
  572. parent_layout, hem_wid):
  573. global is_valid
  574. wid = PyQt6.QtWidgets.QWidget()
  575. layout = PyQt6.QtWidgets.QVBoxLayout()
  576. wid.setLayout(layout)
  577. settings_wid = PyQt6.QtWidgets.QWidget()
  578. settings_layout = PyQt6.QtWidgets.QHBoxLayout()
  579. settings_wid.setLayout(settings_layout)
  580. cursor_pos_wid = PyQt6.QtWidgets.QLabel("Database: %s File: %s Patient: %s X-Pos: Y-Pos: " % (db_name, f_name, pat_name))
  581. cursor_pos_wid.setAlignment(PyQt6.QtCore.Qt.AlignmentFlag.AlignCenter)
  582. cursor_pos_wid.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Expanding, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  583. valid_bttn = PyQt6.QtWidgets.QPushButton(("valid" if (is_valid[hem_idx] == True) else "invalid"))
  584. valid_bttn.pressed.connect(functools.partial(valid_bttn_clicked, valid_bttn, hem_idx))
  585. valid_bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  586. #valid_bttn.setMaximumWidth(60)
  587. settings_layout.addWidget((cursor_pos_wid))
  588. settings_layout.addWidget((valid_bttn))
  589. settings_layout.setContentsMargins(0, 0, 0, 0)
  590. layout.addWidget((settings_wid))
  591. bttns = get_central_plots(hem_idx, depth_cnt, ch_cnt, win_szs, bins, pwrs, ref_indices,
  592. cursor_pos_wid, db_name, f_name, pat_name,
  593. layout, hem_wid)
  594. parent_layout.addWidget(wid)
  595. return (bttns, cursor_pos_wid)
  596. def bttn_clicked(bttn, refresh_timer):
  597. if (bttn.text() == "X"):
  598. bttn.setText("-")
  599. else:
  600. bttn.setText("X")
  601. refresh_timer.stop()
  602. refresh_timer.start(REFRESH_TIMER_DELAY_MS)
  603. #===============================================================================
  604. # def mouse_moved(lab_wid, db_name, f_name, pat_name, event):
  605. # print("A")
  606. #
  607. # try:
  608. # x_pos = float(event.xdata)
  609. # y_pos = float(event.ydata)
  610. # except:
  611. # return
  612. #
  613. # lab_wid.setText("Database: %s File: %s Patient: %s X-Pos: %2.2f Y-Pos: %f" % (db_name, f_name, pat_name, x_pos, y_pos))
  614. # #lab_wid.setText("X-Pos: %2.2f Y-Pos: %f" % (x_pos, y_pos))
  615. #
  616. # #===========================================================================
  617. # # lab_wid.setText("X-Pos: %2.2f" % (event.xdata,))
  618. # #===========================================================================
  619. #===============================================================================
  620. def draw_data(plotter, bttns, hem_cnt, ch_cnt, depth_cnt, win_szs, bins, pwrs, ref_indices, scale_db):
  621. zoom_scales_min = [[999,999,999,999,999], [999,999,999,999,999]]
  622. zoom_scales_max = [[-999,-999,-999,-999,-999], [-999,-999,-999,-999,-999]]
  623. for hem_idx in range(hem_cnt):
  624. for depth_idx in range(depth_cnt[hem_idx]):
  625. for (scale_idx, _) in enumerate(SCALES):
  626. ax = plotter[hem_idx][depth_idx][scale_idx]
  627. ax.clear()
  628. ax.setXRange(SCALES[scale_idx][0], SCALES[scale_idx][1])
  629. for ch_idx in range(ch_cnt[hem_idx]):
  630. for win_sz_idx in range(len(win_szs)):
  631. if (bins[win_sz_idx][hem_idx][ch_idx][depth_idx] is not None):
  632. x_vals = bins[win_sz_idx][hem_idx][ch_idx][depth_idx][ref_indices[scale_idx][win_sz_idx][0]:ref_indices[scale_idx][win_sz_idx][1]]
  633. y_vals = pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx][ref_indices[scale_idx][win_sz_idx][0]:ref_indices[scale_idx][win_sz_idx][1]]
  634. loc_y_vals = np.copy(y_vals)
  635. loc_y_vals = np.pad(loc_y_vals, 2, mode = "edge")
  636. loc_y_vals = np.asarray(pandas.Series(loc_y_vals).rolling(window = 3, center = True).mean())
  637. loc_y_vals = loc_y_vals[2:-2]
  638. ax.plot(x_vals, y_vals, pen = pyqtgraph.mkPen(PLOT_COLORS[ch_idx], width = 0.75))
  639. ax.plot(x_vals, loc_y_vals, pen = pyqtgraph.mkPen(PLOT_COLORS[ch_idx], width = 0.75), linestyle = "--")
  640. if (scale_db):
  641. if (bttns[hem_idx][depth_idx][scale_idx].text() == "X"):
  642. if (np.abs(np.min(y_vals)) > 1e-5):
  643. zoom_scales_min[hem_idx][scale_idx] = np.min([zoom_scales_min[hem_idx][scale_idx], np.min(y_vals)])
  644. zoom_scales_max[hem_idx][scale_idx] = np.max([zoom_scales_max[hem_idx][scale_idx], np.max(y_vals)])
  645. else:
  646. if (bttns[hem_idx][depth_idx][scale_idx].text() == "X"):
  647. zoom_scales_min[hem_idx][scale_idx] = np.min([zoom_scales_min[hem_idx][scale_idx], np.min(y_vals)])
  648. zoom_scales_max[hem_idx][scale_idx] = np.max([zoom_scales_max[hem_idx][scale_idx], np.max(y_vals)])
  649. for hem_idx in range(hem_cnt):
  650. for depth_idx in range(depth_cnt[hem_idx]):
  651. for (scale_idx, _) in enumerate(SCALES):
  652. ax = plotter[hem_idx][depth_idx][scale_idx]
  653. ax.setYRange(zoom_scales_min[hem_idx][scale_idx], zoom_scales_max[hem_idx][scale_idx])
  654. def refresh_timer_expires(plotters, bttns, ch_cnt, depth_cnt, win_szs,
  655. bins, pwrs, ref_indices, hem_idx):
  656. zoom_scales_max = [-999, -999, -999, -999, -999]
  657. zoom_scales_min = [999, 999, 999, 999, 999]
  658. for depth_idx in range(depth_cnt):
  659. for (scale_idx, _) in enumerate(SCALES):
  660. ax = plotters[hem_idx][depth_idx][scale_idx]
  661. ax.clear()
  662. ax.setXRange(SCALES[scale_idx][0], SCALES[scale_idx][1])
  663. for ch_idx in range(ch_cnt):
  664. for win_sz_idx in range(len(win_szs)):
  665. if (bins[win_sz_idx][hem_idx][ch_idx][depth_idx] is not None):
  666. x_vals = bins[win_sz_idx][hem_idx][ch_idx][depth_idx][ref_indices[scale_idx][win_sz_idx][0]:ref_indices[scale_idx][win_sz_idx][1]]
  667. y_vals = pwrs[win_sz_idx][hem_idx][ch_idx][depth_idx][ref_indices[scale_idx][win_sz_idx][0]:ref_indices[scale_idx][win_sz_idx][1]]
  668. #ax.plot(x_vals, y_vals, pen = pyqtgraph.mkPen(PLOT_COLORS[ch_idx], width = 0.75))
  669. loc_y_vals = np.copy(y_vals)
  670. loc_y_vals = np.pad(loc_y_vals, 2, mode = "edge")
  671. loc_y_vals = np.asarray(pandas.Series(loc_y_vals).rolling(window = 3, center = True).mean())
  672. loc_y_vals = loc_y_vals[2:-2]
  673. ax.plot(x_vals, y_vals, pen = pyqtgraph.mkPen(PLOT_COLORS[ch_idx], width = 0.75))
  674. ax.plot(x_vals, loc_y_vals, pen = pyqtgraph.mkPen(PLOT_COLORS[ch_idx], width = 0.75), linestyle = "--")
  675. if (bttns[depth_idx][scale_idx].text() == "X"):
  676. zoom_scales_min[scale_idx] = np.min([zoom_scales_min[scale_idx], np.min(y_vals)])
  677. zoom_scales_max[scale_idx] = np.max([zoom_scales_max[scale_idx], np.max(y_vals)])
  678. for depth_idx in range(depth_cnt):
  679. for (scale_idx, _) in enumerate(SCALES):
  680. ax = plotters[hem_idx][depth_idx][scale_idx]
  681. ax.setYRange(zoom_scales_min[scale_idx], zoom_scales_max[scale_idx])
  682. global mrk_cnt; global mrk_pos; global mrk_depth; global scatters
  683. for freq_band_idx in range(FREQ_BAND_CNT):
  684. for mrk_idx in range(mrk_cnt[hem_idx][freq_band_idx]):
  685. x_pos = mrk_pos[hem_idx][freq_band_idx][mrk_idx]
  686. scatters[hem_idx][freq_band_idx][mrk_idx] = list()
  687. if (freq_band_idx == 0):
  688. y_pos = (plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][1].plotItem.viewRange()[1][0]+plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][1].plotItem.viewRange()[1][1])/2
  689. ax = plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][1]
  690. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  691. annot = pyqtgraph.TextItem(str(mrk_idx + 1)); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  692. scatters[hem_idx][freq_band_idx][mrk_idx].append((ax, scttr, annot))
  693. else:
  694. y_pos = (plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][3].plotItem.viewRange()[1][0]+plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][3].plotItem.viewRange()[1][1])/2
  695. ax = plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][3]
  696. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  697. annot = pyqtgraph.TextItem(str(mrk_idx + 1)); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  698. scatters[hem_idx][freq_band_idx][mrk_idx].append((ax, scttr, annot))
  699. y_pos = (plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][4].plotItem.viewRange()[1][0]+plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][4].plotItem.viewRange()[1][1])/2
  700. ax = plotters[hem_idx][mrk_depth[hem_idx][freq_band_idx][mrk_idx]][4]
  701. scttr = ax.plot([x_pos, x_pos], [y_pos, y_pos], color = "red", symbol = "o", symbolPen = pyqtgraph.mkPen("red"), symbolBrush = pyqtgraph.mkBrush("red"))
  702. annot = pyqtgraph.TextItem(str(mrk_idx + 1)); ax.addItem(annot); annot.setPos(x_pos, y_pos)
  703. scatters[hem_idx][freq_band_idx][mrk_idx].append((ax, scttr, annot))
  704. def on_close(wid, f_name, pat_name, db_name, cursor_pos_wid, hem_cnt):
  705. HEM_TO_STR = {0 : "l", 1 : "r"}
  706. for hem_idx in range(hem_cnt):
  707. wid.setCurrentIndex(hem_idx)
  708. cursor_pos_wid[hem_idx].setText("Database: %s X-Pos: Y-Pos: " % (db_name,))
  709. os.makedirs(PATH + "beta_pictures/" + db_name + "/", exist_ok = True)
  710. if (f_name == "n/a"):
  711. wid.grab().save(PATH + "beta_pictures/" + db_name + "/" + pat_name + "_" + HEM_TO_STR[hem_idx] + ".png")
  712. elif(pat_name == "n/a"):
  713. wid.grab().save(PATH + "beta_pictures/" + db_name + "/" + f_name + "_" + HEM_TO_STR[hem_idx] + ".png")
  714. else:
  715. wid.grab().save(PATH + "beta_pictures/" + db_name + "/" + pat_name + "_" + f_name + "_" + HEM_TO_STR[hem_idx] + ".png")
  716. print_output(f_name, pat_name, db_name)
  717. def get_pos_output(pos):
  718. global mrk_cnt; global mrk_pos; global mrk_depth; global config_wids
  719. if (pos == "la"):
  720. loc_mrk_cnt = mrk_cnt[0][0]
  721. loc_mrk_pos = mrk_pos[0][0]
  722. loc_mrk_depth = mrk_depth[0][0]
  723. loc_conf = 0
  724. elif (pos == "ra"):
  725. loc_mrk_cnt = mrk_cnt[1][0]
  726. loc_mrk_pos = mrk_pos[1][0]
  727. loc_mrk_depth = mrk_depth[1][0]
  728. loc_conf = 1
  729. elif (pos == "lb"):
  730. loc_mrk_cnt = mrk_cnt[0][1]
  731. loc_mrk_pos = mrk_pos[0][1]
  732. loc_mrk_depth = mrk_depth[0][1]
  733. loc_conf = 2
  734. elif (pos == "rb"):
  735. loc_mrk_cnt = mrk_cnt[1][1]
  736. loc_mrk_pos = mrk_pos[1][1]
  737. loc_mrk_depth = mrk_depth[1][1]
  738. loc_conf = 3
  739. global box_color, box_peak, box_noise, box_depth, box_lim, box_depvar, box_specstat, box_specwide, box_no_harm, box_unimod, box_plausible, box_unique, box_insight
  740. mrk_chs = [-1 for _ in range(3)]
  741. for loc_mrk_idx in range(loc_mrk_cnt):
  742. mrk_chs[loc_mrk_idx] = PLOT_COLORS.index(box_color[loc_conf][loc_mrk_idx])
  743. return ([INSIGHT_GAIN[box_insight[loc_conf][0]], loc_mrk_cnt, box_unique[loc_conf][0]], [loc_mrk_pos, loc_mrk_depth, mrk_chs,
  744. box_peak[loc_conf], box_noise[loc_conf], box_depth[loc_conf],
  745. box_lim[loc_conf], box_depvar[loc_conf], box_specstat[loc_conf],
  746. box_specwide[loc_conf], box_no_harm[loc_conf], box_plausible[loc_conf],
  747. box_unimod[loc_conf]])
  748. def print_output(f_name, pat_name, db_name):
  749. global is_valid
  750. (la_file_stats, la_mrk_stats) = get_pos_output("la")
  751. (ra_file_stats, ra_mrk_stats) = get_pos_output("ra")
  752. (lb_file_stats, lb_mrk_stats) = get_pos_output("lb")
  753. (rb_file_stats, rb_mrk_stats) = get_pos_output("rb")
  754. file = open(PATH + "beta_pictures/" + "0_per_file_lfp_beta.txt", "a")
  755. line = "\t".join([f_name, pat_name, db_name,
  756. str(la_file_stats[0]), str(la_file_stats[1]), str(la_file_stats[2]),
  757. str(ra_file_stats[0]), str(ra_file_stats[1]), str(ra_file_stats[2]),
  758. str(lb_file_stats[0]), str(lb_file_stats[1]), str(lb_file_stats[2]),
  759. str(rb_file_stats[0]), str(rb_file_stats[1]), str(rb_file_stats[2])])
  760. line += "\t" + str(is_valid[0]) + "\t" + str(is_valid[1])
  761. file.write(line + "\n")
  762. file.close()
  763. file = open(PATH + "beta_pictures/" + "0_per_peak_lfp_beta.txt", "a")
  764. for mrk_idx in range(la_file_stats[1]):
  765. line = "\t".join([f_name, pat_name, db_name, "la",
  766. str(la_mrk_stats[ 0][mrk_idx]), str(la_mrk_stats[ 1][mrk_idx]), str(la_mrk_stats[ 2][mrk_idx]), str(la_mrk_stats[ 3][mrk_idx]),
  767. str(la_mrk_stats[ 4][mrk_idx]), str(la_mrk_stats[ 5][mrk_idx]), str(la_mrk_stats[ 6][mrk_idx]), str(la_mrk_stats[ 7][mrk_idx]),
  768. str(la_mrk_stats[ 8][mrk_idx]), str(la_mrk_stats[ 9][mrk_idx]), str(la_mrk_stats[10][mrk_idx]), str(la_mrk_stats[11][mrk_idx]),
  769. str(la_mrk_stats[12][mrk_idx])])
  770. file.write(line + "\n")
  771. for mrk_idx in range(ra_file_stats[1]):
  772. line = "\t".join([f_name, pat_name, db_name, "ra",
  773. str(ra_mrk_stats[ 0][mrk_idx]), str(ra_mrk_stats[ 1][mrk_idx]), str(ra_mrk_stats[ 2][mrk_idx]), str(ra_mrk_stats[ 3][mrk_idx]),
  774. str(ra_mrk_stats[ 4][mrk_idx]), str(ra_mrk_stats[ 5][mrk_idx]), str(ra_mrk_stats[ 6][mrk_idx]), str(ra_mrk_stats[ 7][mrk_idx]),
  775. str(ra_mrk_stats[ 8][mrk_idx]), str(ra_mrk_stats[ 9][mrk_idx]), str(ra_mrk_stats[10][mrk_idx]), str(ra_mrk_stats[11][mrk_idx]),
  776. str(ra_mrk_stats[12][mrk_idx])])
  777. file.write(line + "\n")
  778. for mrk_idx in range(lb_file_stats[1]):
  779. line = "\t".join([f_name, pat_name, db_name, "lb",
  780. str(lb_mrk_stats[ 0][mrk_idx]), str(lb_mrk_stats[ 1][mrk_idx]), str(lb_mrk_stats[ 2][mrk_idx]), str(lb_mrk_stats[ 3][mrk_idx]),
  781. str(lb_mrk_stats[ 4][mrk_idx]), str(lb_mrk_stats[ 5][mrk_idx]), str(lb_mrk_stats[ 6][mrk_idx]), str(lb_mrk_stats[ 7][mrk_idx]),
  782. str(lb_mrk_stats[ 8][mrk_idx]), str(lb_mrk_stats[ 9][mrk_idx]), str(lb_mrk_stats[10][mrk_idx]), str(lb_mrk_stats[11][mrk_idx]),
  783. str(lb_mrk_stats[12][mrk_idx])])
  784. file.write(line + "\n")
  785. for mrk_idx in range(rb_file_stats[1]):
  786. line = "\t".join([f_name, pat_name, db_name, "rb",
  787. str(rb_mrk_stats[ 0][mrk_idx]), str(rb_mrk_stats[ 1][mrk_idx]), str(rb_mrk_stats[ 2][mrk_idx]), str(rb_mrk_stats[ 3][mrk_idx]),
  788. str(rb_mrk_stats[ 4][mrk_idx]), str(rb_mrk_stats[ 5][mrk_idx]), str(rb_mrk_stats[ 6][mrk_idx]), str(rb_mrk_stats[ 7][mrk_idx]),
  789. str(rb_mrk_stats[ 8][mrk_idx]), str(rb_mrk_stats[ 9][mrk_idx]), str(rb_mrk_stats[10][mrk_idx]), str(rb_mrk_stats[11][mrk_idx]),
  790. str(rb_mrk_stats[12][mrk_idx])])
  791. file.write(line + "\n")
  792. file.close()
  793. HEM_NAMES = ["left hemisphere", "right hemisphere"]
  794. def valid_bttn_clicked(bttn, hem_idx):
  795. global is_valid;
  796. if (is_valid[hem_idx]):
  797. is_valid[hem_idx] = False
  798. bttn.setText("invalid")
  799. else:
  800. is_valid[hem_idx] = True
  801. bttn.setText("valid")
  802. def run_internal(f_name, pat_name, db_name, raw_data, fs, win_szs, scale_db):
  803. hem_cnt = len(raw_data)
  804. freq_cnt = 2
  805. ch_cnt = [0, 0]; depth_cnt = [0, 0]
  806. if (raw_data[0] is not None):
  807. ch_cnt[0] = len(raw_data[0])
  808. if (raw_data[0][0] is not None):
  809. depth_cnt[0] = len(raw_data[0][0])
  810. if (raw_data[1] is not None):
  811. ch_cnt[1] = len(raw_data[1])
  812. if (raw_data[1][0] is not None):
  813. depth_cnt[1] = len(raw_data[1][0])
  814. global mrk_cnt; mrk_cnt = [[0 for _ in range(freq_cnt)] for _ in range (hem_cnt)]
  815. global mrk_pos; mrk_pos = [[[0 for _ in range(MAX_PEAKS)] for _ in range(freq_cnt)] for _ in range(hem_cnt)]
  816. global mrk_depth; mrk_depth = [[[-1 for _ in range(MAX_PEAKS)] for _ in range(freq_cnt)] for _ in range(hem_cnt)]
  817. global scatters; scatters = [[[None for _ in range(MAX_PEAKS)] for _ in range(freq_cnt)] for _ in range(hem_cnt)]
  818. global config_wids; config_wids = [[None for _ in range(freq_cnt)] for _ in range(hem_cnt)]
  819. global plotters; plotters = [[[None for _ in range(len(SCALES))] for _ in range(depth_cnt[hem_idx])] for hem_idx in range(hem_cnt)]
  820. global valid_chs; valid_chs = [[np.asarray([(raw_data[hem_idx][ch_idx][depth_idx] is not None) for depth_idx in range(depth_cnt[hem_idx])]).any() for ch_idx in range(ch_cnt[hem_idx])] for hem_idx in range(hem_cnt)]
  821. global is_valid; is_valid = [True, True]
  822. global box_color; box_color = [[-1 for _ in range(3)] for _ in range(4)]
  823. global box_peak; box_peak = [[-1 for _ in range(3)] for _ in range(4)]; global box_noise; box_noise = [[-1 for _ in range(3)] for _ in range(4)];
  824. global box_depth; box_depth = [[-1 for _ in range(3)] for _ in range(4)]; global box_lim; box_lim = [[-1 for _ in range(3)] for _ in range(4)];
  825. global box_depvar; box_depvar = [[-1 for _ in range(3)] for _ in range(4)]; global box_specstat; box_specstat = [[-1 for _ in range(3)] for _ in range(4)]
  826. global box_specwide; box_specwide = [[-1 for _ in range(3)] for _ in range(4)]; global box_no_harm; box_no_harm = [[-1 for _ in range(3)] for _ in range(4)];
  827. global box_unimod; box_unimod = [[-1 for _ in range(3)] for _ in range(4)]; global box_plausible; box_plausible = [[-1 for _ in range(3)] for _ in range(4)];
  828. global box_unique; box_unique = [[-1 for _ in range(1)] for _ in range(4)]; global box_insight; box_insight = [[-1 for _ in range(1)] for _ in range(4)]
  829. qapp = PyQt6.QtWidgets.QApplication([])
  830. app = PyQt6.QtWidgets.QMainWindow()
  831. (bins, pwrs, ref_indices) = get_psds(raw_data, fs, win_szs, depth_cnt, ch_cnt, db_name, scale_db)
  832. main_wid = PyQt6.QtWidgets.QWidget()
  833. layout = PyQt6.QtWidgets.QVBoxLayout()
  834. main_wid.setLayout(layout)
  835. #===========================================================================
  836. # settings_wid = PyQt6.QtWidgets.QWidget()
  837. # layout.addWidget(settings_wid)
  838. # settings_layout = PyQt6.QtWidgets.QHBoxLayout()
  839. # settings_layout.setContentsMargins(0, 0, 0, 0)
  840. # settings_layout.setSpacing(0)
  841. # settings_wid.setLayout(settings_layout)
  842. # settings_buffer = PyQt6.QtWidgets.QWidget()
  843. # settings_buffer.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Expanding, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  844. # valid_bttn = PyQt6.QtWidgets.QPushButton("valid" if (is_valid) else "invalid")
  845. # valid_bttn.pressed.connect(functools.partial(valid_bttn_clicked, valid_bttn))
  846. # valid_bttn.setSizePolicy(PyQt6.QtWidgets.QSizePolicy.Policy.Minimum, PyQt6.QtWidgets.QSizePolicy.Policy.Minimum)
  847. # valid_bttn.setMaximumWidth(100)
  848. # settings_layout.addWidget(settings_buffer, PyQt6.QtCore.Qt.AlignmentFlag.AlignRight)
  849. # settings_layout.addWidget(valid_bttn, PyQt6.QtCore.Qt.AlignmentFlag.AlignRight)
  850. #===========================================================================
  851. tab_wid = PyQt6.QtWidgets.QTabWidget(); app.setCentralWidget(main_wid)
  852. layout.addWidget(tab_wid)
  853. bttns = [None for _ in range(hem_cnt)]; cursor_pos_wids = [None for _ in range(hem_cnt)]
  854. for hem_idx in range(hem_cnt):
  855. hem_wid = PyQt6.QtWidgets.QWidget(objectName = "hem_wid")
  856. hem_layout = PyQt6.QtWidgets.QHBoxLayout()
  857. hem_wid.setLayout(hem_layout)
  858. get_config_panel(hem_idx, hem_layout)
  859. (bttns[hem_idx], cursor_pos_wids[hem_idx]) = get_central_area(depth_cnt[hem_idx], db_name, f_name, pat_name, hem_idx,
  860. ch_cnt[hem_idx], win_szs, bins, pwrs, ref_indices,
  861. hem_layout, hem_wid)
  862. tab_wid.addTab(hem_wid, HEM_NAMES[hem_idx])
  863. draw_data(plotters, bttns, hem_cnt, ch_cnt, depth_cnt, win_szs, bins, pwrs, ref_indices, scale_db)
  864. app.showMaximized()
  865. app.activateWindow()
  866. app.raise_()
  867. for hem_idx in range(hem_cnt):
  868. for depth_idx in range(depth_cnt[hem_idx]):
  869. for (scale_idx, _) in enumerate(SCALES):
  870. bttns[hem_idx][depth_idx][scale_idx].setFixedHeight(30)
  871. bttns[hem_idx][depth_idx][scale_idx].setFixedWidth(25)
  872. qapp.aboutToQuit.connect(functools.partial(on_close, tab_wid, f_name, pat_name, db_name, cursor_pos_wids, hem_cnt))
  873. qapp.exec()
  874. def run(f_name, pat_name, db_name, raw_data, fs, win_szs, scale_db = True, mode = "release"):
  875. if (mode == "release"):
  876. thread = multiprocessing.Process(target = run_internal, args = (f_name, pat_name, db_name, raw_data, fs, win_szs, scale_db))
  877. thread.start()
  878. thread.join()
  879. if (mode == "debug"):
  880. run_internal(f_name, pat_name, db_name, raw_data, fs, win_szs, scale_db)

core.py at commit 7c9f1e9, no license · at the source

Overview

Authors: Maximilian Scherer1, Nina Wiedemann1, Kai Bötzel1, Matthias Löhle2,3, Thomas Kriesen4, Daniel Cantré5, Jan Hinnerk Mehrkens6, René Reese2, Thomas Koeglsperger1,7
  1. Department of Neurology, LMU University Hospital, LMU Munich, Munich, Germany
  2. Department of Neurology, Rostock University Medical Center, Rostock, Germany
  3. German Center for Neurodegenerative Diseases (DZNE) Rostock/Greifswald, Rostock, Germany
  4. Department of Neurosurgery, Rostock University Medical Center, Rostock, Germany
  5. Institute and Policlinic of Radiology, Pediatric Radiology and Neuroradiology, Rostock University Medical Center, Rostock, Germany
  6. Department of Neurosurgery, LMU University Hospital, LMU Munich, Munich, Germany
  7. Department of Translational Brain Research, German Center for Neurodegenerative Diseases (DZNE), Munich, Germany
Journal: NPJ Parkinson's disease, volume 12, issue 1, article 222
Dates: received 12 February 2026; accepted 8 August 2026; published online 16 September 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41531-026-01531-4 · PMID 42749741 · PMCID PMC13582961 · OpenAlex W7213334037
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: extracellular electrophysiology (units, LFP) (modality), human (organism), Parkinson's (population), clinical / translational (subfield)
Methods: Spectral & time-frequency, Connectivity, Preprocessing, Smoothing, state filtering, decompositions
Keywords: Biomarkers, Neurology, Neuroscience
Topic: Neurological disorders and treatments (Neurology, Medicine), according to OpenAlex
Citations: not cited yet (Europe PMC); 60 references in the paper

Abstract

Elevated subthalamic (STN) beta (12–32 Hz) local field potentials (LFP) are a hallmark of Parkinson’s disease and closely tied to synchronized spiking. The biomarker guides deep brain stimulation (DBS) lead implantation and is under investigation to inform DBS programming and adaptive DBS, yet its prevalence has not been independently assessed. This study assesses the biomarkers prevalence and investigating its relationship to synchronized beta-bursting neurons. STN LFPs and spiking activity of n = 156 patients with Parkinson’s disease from seven DBS centers recorded via microelectrodes were examined and spectral LFP peaks classified. The distribution of phase angles between beta LFP and beta-bursting neurons was explored. Beta LFPs were bilaterally expressed in 47.25% of patients (65.59% of hemispheres). LFP-synchronized neuronal spiking clustered in two peaks, 182.84° phase shifted. This applied on the group level and for individual patients. Beta-LFP-informed approaches mandate reliable biomarker expression, a requirement not met by 52.75% of patients. This is particularly troublesome for intraoperative guidance wherein the cause of biomarker absence, including DBS lead misplacement or a balanced ratio of phasic and antiphasic spiking neurons, cannot be determined. While STN-LFP beta remains a valuable biomarker, multiple biomarkers should be evaluated simultaneously to increase robustness.

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

Repositories

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

MaximilianScherer/2026_MS_beta_prevalence

License: none: the authors keep all their rights
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Commit: 7c9f1e9088ffed9e8d845ccf5034c349fa39cb4f, 15 December 2025
Languages: Python (34), MATLAB (2)
Size: 40 files, 36 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: environment (pyproject.toml)
Not found: README, license file, CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (22 files), SciPy (13 files), Matplotlib (8 files), pandas (3 files), h5py (2 files), LMFIT (1 file), MNE-Python (1 file), Neo (1 file), NiBabel (1 file), scikit-image (1 file), scikit-learn (1 file)
Availability: 1 check, the latest on 26 September 2026: the link answers
  • 26 September 2026: the link answers
36 files

Zenodo 17951551

License: CC-BY-4.0
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Size: 2 files
Software Heritage: not checked
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 26 September 2026: the link answers (HTTP 200)
  • 26 September 2026: the link answers (HTTP 200)
At the source:

Zenodo 17940236

License: CC-BY-4.0
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (22 files), SciPy (13 files), Matplotlib (8 files), pandas (3 files), h5py (2 files), LMFIT (1 file), MNE-Python (1 file), Neo (1 file), NiBabel (1 file), scikit-image (1 file), scikit-learn (1 file)
Availability: 1 check, the latest on 26 September 2026: the link answers (HTTP 200)
  • 26 September 2026: the link answers (HTTP 200)
36 files
At the source:

Code availability

Python code written to support this evaluation has been made available at: https://codeberg.org/MaximilianScherer/2026_MS_beta_prevalence.git60.

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

Tracing map

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

What the map holds:

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

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

Data

Datasets cited

Data availability

Data from cohorts 1 to 3 are publicly available33,46,51. Data from cohorts 4 & 5 may be shared upon request. Graphical documentations for all LFP/spike evaluations individually, as well as microelectrode reconstructions, are available at zenodo59.

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 Alexander von Humboldt-Stiftung

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 9 authors, 3 keywords, 57 references.

Cite

This paper

Scherer, M., Wiedemann, N., Bötzel, K., Löhle, M., Kriesen, T., Cantré, D., Mehrkens, J. H., Reese, R., & Koeglsperger, T. (2026). Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts. NPJ Parkinson's disease, 12(1), 222. https://doi.org/10.1038/s41531-026-01531-4

BibTeX

@article{scherer2026inconsistent,
author = {Scherer, Maximilian and Wiedemann, Nina and Bötzel, Kai and Löhle, Matthias and Kriesen, Thomas and Cantré, Daniel and Mehrkens, Jan Hinnerk and Reese, René and Koeglsperger, Thomas},
title = {{Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts}},
journal = {NPJ Parkinson's disease},
year = {2026},
month = sep,
volume = {12},
number = {1},
pages = {222},
publisher = {Nature Publishing Group},
issn = {2373-8057},
doi = {10.1038/s41531-026-01531-4},
url = {https://doi.org/10.1038/s41531-026-01531-4},
pmid = {42749741},
pmcid = {PMC13582961}
}

RIS

TY - JOUR
AU - Scherer, Maximilian
AU - Wiedemann, Nina
AU - Bötzel, Kai
AU - Löhle, Matthias
AU - Kriesen, Thomas
AU - Cantré, Daniel
AU - Mehrkens, Jan Hinnerk
AU - Reese, René
AU - Koeglsperger, Thomas
TI - Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts
T2 - NPJ Parkinson's disease
J2 - NPJ Parkinsons Dis
PY - 2026
DA - 2026/09/16
VL - 12
IS - 1
SP - 222
SN - 2373-8057
PB - Nature Publishing Group
DO - 10.1038/s41531-026-01531-4
UR - https://doi.org/10.1038/s41531-026-01531-4
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41531-026-01531-4",
"type": "article-journal",
"title": "Inconsistent subthalamic local field potential beta activity amid in- and antiphasic neuronal bursts",
"container-title": "NPJ Parkinson's disease",
"author": [
{
"family": "Scherer",
"given": "Maximilian"
},
{
"family": "Wiedemann",
"given": "Nina"
},
{
"family": "Bötzel",
"given": "Kai"
},
{
"family": "Löhle",
"given": "Matthias"
},
{
"family": "Kriesen",
"given": "Thomas"
},
{
"family": "Cantré",
"given": "Daniel"
},
{
"family": "Mehrkens",
"given": "Jan Hinnerk"
},
{
"family": "Reese",
"given": "René"
},
{
"family": "Koeglsperger",
"given": "Thomas"
}
],
"container-title-short": "NPJ Parkinsons Dis",
"volume": "12",
"issue": "1",
"page": "222",
"DOI": "10.1038/s41531-026-01531-4",
"PMID": "42749741",
"PMCID": "PMC13582961",
"ISSN": "2373-8057",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41531-026-01531-4",
"language": "en",
"issued": {
"date-parts": [
[
2026,
9,
16
]
]
}
}

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/s41531-026-01380-1 [code]
Identifying maximal beta power from directional subthalamic local field potentials in Parkinson's disease.
Journal: NPJ Parkinson's disease
In common: MNE-Python, scikit-learn, pandas, 3 other tools, extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 4 references
[2] doi:10.1038/s41591-026-04432-4 [code]
Activity-dependent adaptive deep brain stimulation improves gait in Parkinson's disease.
Journal: Nature medicine
In common: MNE-Python, h5py, scikit-learn, 3 other tools, Parkinson's, clinical / translational, 4 references
[3] doi:10.1002/ana.78206 [code]
Multimodal Image Guidance in Subthalamic Deep Brain Stimulation for Parkinson's Disease.
Journal: Annals of neurology
In common: h5py, NiBabel, pandas, 3 other tools, Parkinson's, clinical / translational, 3 references
[4] doi:10.1016/j.cnp.2026.08.008 [code]
Subthalamic beta activity and neuroimaging concordance in deep brain stimulation: electrode placement and clinical outcomes.
Journal: Clinical neurophysiology practice
In common: pandas, SciPy, Matplotlib, 1 other tool, extracellular electrophysiology (units, LFP), clinical / translational, 4 references
[5] doi:10.1093/braincomms/fcag328 [code]
Subthalamic stimulation modulates working memory-related cortical dynamics in Parkinson's disease.
Journal: Brain communications
In common: MNE-Python, pandas, SciPy, 2 other tools, Parkinson's, 3 references
[6] doi:10.7554/elife.110588 [code]
Opening the black box toward a modular approach to spike sorting.
Journal: eLife
In common: Neo, scikit-image, h5py, 5 other tools, extracellular electrophysiology (units, LFP)
[7] doi:10.1038/s42003-026-10011-7 [code]
Learning brain dynamics across distinct scaling regimes reveals psychiatric signatures.
Journal: Communications biology
In common: LMFIT, scikit-image, NiBabel, 5 other tools
[8] doi:10.1007/s12021-026-09803-3 [code]
NeuroFusion: A Unified Framework for Generalized Visual Stimulus Decoding from fMRI Across Datasets and Subjects.
Journal: Neuroinformatics
In common: MNE-Python, scikit-image, h5py, 6 other tools
[9] doi:10.1117/1.nph.13.2.025001 [code]
Surface-based image reconstruction optimization for high-density functional near-infrared spectroscopy.
Journal: Neurophotonics
In common: MNE-Python, scikit-image, h5py, 6 other tools
[10] doi:10.1038/s41593-026-02228-w [code]
Circuit response to neuromodulation characterized with simultaneous deep brain stimulation and precision neuroimaging in humans.
Journal: Nature neuroscience
In common: NiBabel, scikit-learn, pandas, 3 other tools, Parkinson's, 2 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.