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

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: 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 (how the code is found and 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: Deep Brain Stimulation and Movement Disorders (Clinical 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.1073/pnas.2205881119 [code]
Single-neuron bursts encode pathological oscillations in subcortical nuclei of patients with Parkinson's disease and essential tremor.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: LMFIT, Neo, scikit-image, 5 other tools, extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 9 references
[2] doi:10.1016/j.ebiom.2025.105988 [code]
Beyond beta rhythms: subthalamic aperiodic broadband power scales with Parkinson's disease severity-a cross-sectional multicentre study.
Journal: EBioMedicine
In common: MNE-Python, NiBabel, pandas, 3 other tools, Parkinson's, 7 references
[3] doi:10.1038/s41531-024-00773-4 [code]
Clinico-physiological correlates of Parkinson's disease from multi-resolution basal ganglia recordings.
Journal: NPJ Parkinson's disease
In common: Neo, scikit-learn, pandas, 3 other tools, extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 6 references
[4] doi:10.1038/s41531-026-01273-3 [code]
Decoding the impact of visual states on adaptive deep brain stimulation feedback signals in movement disorders.
Journal: NPJ Parkinson's disease
In common: MNE-Python, NiBabel, scikit-learn, 4 other tools, extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 5 references
[5] doi:10.1038/s41531-025-01243-1 [code]
The graded effect of propofol in electrophysiology-guided navigation during deep brain stimulation surgery.
Journal: NPJ Parkinson's disease
In common: Neo, MNE-Python, scikit-learn, 4 other tools, Parkinson's, clinical / translational, 4 references
[6] doi:10.1080/17582024.2024.2404386
Current and future applications of local field potential-guided programming for Parkinson's disease with the Percept™ rechargeable neurostimulator.
Journal: Neurodegenerative disease management
In common: extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 8 references
[7] doi:10.1162/netn_a_00265 [code]
FiNN: A toolbox for neurophysiological network analysis.
Journal: Network neuroscience (Cambridge, Mass.)
In common: LMFIT, MNE-Python, scikit-image, 6 other tools, 2 references
[8] doi:10.1038/s41531-026-01380-1 [code]
Identifying maximal beta power from directional subthalamic local field potentials in Parkinson's disease.
Journal: NPJ Parkinson's disease
In common: MNE-Python, scikit-learn, pandas, 3 other tools, extracellular electrophysiology (units, LFP), Parkinson's, clinical / translational, 4 references
[9] doi:10.1126/sciadv.adx6849 [code]
Differential modulation of movement speed with state-dependent deep brain stimulation in Parkinson's disease.
Journal: Science advances
In common: MNE-Python, NiBabel, scikit-learn, 4 other tools, Parkinson's, 4 references
[10] doi:10.1002/hbm.26190 [code]
Direct modulation index: A measure of phase amplitude coupling for neurophysiology data.
Journal: Human brain mapping
In common: LMFIT, MNE-Python, scikit-image, 6 other tools, 1 reference

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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4.svg)](https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4.svg
   :target: https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4"><img src="https://openscicode.org/badge/doi/10.1038/s41531-026-01531-4.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.