OSCR

Planar, spiral, and concentric traveling waves distinguish behavioral states in human memory.

Code ↔ Paper

6 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 6 matches
  1. [1] § Methods › Identification of traveling waves ↔ TW_Code/par_funcs_fine.py, lines 163–268 · score 0.79 · circular linear regression, vector length, circular correlation, cos, sin, predicted
  2. [2] § Methods › Identification of traveling waves ↔ TW_Code/par_funcs_fine.py, lines 163–268 · score 0.71 · phase offset, predicted phase, Circular linear, angle, regression
  3. [3] § Methods › Validating CICA using simulations ↔ TW_Code/wave_spatial_autocorrelation_ica.ipynb, lines 89–132 · score 0.64 · circular shift, spatial autocorrelation, permutation, rotational wave, noise
  4. [4] § Methods › Electrophysiological recordings and preprocessing ↔ TW_Code/RAM_helpers.py, lines 291–436 · score 0.63 · electrode location, clinical, configuration, strips, depth, contacts
  5. [5] § Methods › Identification of oscillations ↔ TW_Code/Anup-th1-grids-2D-fine-CPCAM.ipynb, lines 324–400 · score 0.54 · normalized power spectrum, peak frequencies, algorithm, cluster, electrode
  6. [6] § Methods › Identification of oscillations ↔ TW_Code/par_funcs_fine.py, lines 22–49 · score 0.51 · standard deviation, power spectrum, fit, peaks, electrode

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 · 464 lines · 16 KB · no license · 3 matches

  1. """
  2. A set of functions that you
  3. """
  4. import numpy as np
  5. import statsmodels.api as sm
  6. import pdb
  7. import numexpr
  8. from scipy.signal import argrelmax
  9. from scipy.stats import ttest_ind
  10. from xarray import concat
  11. from ptsa.data.timeseries import TimeSeries
  12. from ptsa.data.filters import MorletWaveletFilter
  13. from tqdm import tqdm
  14. import pycircstat
  15. from numpy import exp
  16. from scipy.optimize import curve_fit
  17. import matplotlib.pyplot as plt
  18. from math import cos,sin
  19. def par_find_peaks_by_chan(p_spect_array, frequencies, std_thresh=1.):
  20. """
  21. Parameters
  22. ----------
  23. p_spect_array: numpy.ndarray
  24. An array with dimensions frequencies x channels
  25. frequencies: numpy.ndarray
  26. An array of the frequencies used
  27. std_thresh: float
  28. Threshold in number of standard deviations above the corrected power spectra to be counted as a peak
  29. Returns
  30. -------
  31. peaks_all_chans: numpy.ndarray with type bool
  32. An array of booleans the same shape as p_spect_array, specifying if there is a peak at a given frequency
  33. and electrode
  34. """
  35. peaks_all_chans = np.zeros(p_spect_array.shape).astype(bool)
  36. for i, chan_data in enumerate(p_spect_array.T):
  37. x = sm.tools.tools.add_constant(np.log10(frequencies))
  38. model_res = sm.RLM(chan_data, x).fit()
  39. peak_inds = argrelmax(model_res.resid)
  40. peaks = np.zeros(x.shape[0], dtype=bool)
  41. peaks[peak_inds] = True
  42. above_thresh = model_res.resid > (np.std(model_res.resid) * std_thresh)
  43. peaks_all_chans[:,i] = peaks & above_thresh
  44. return peaks_all_chans
  45. def par_robust_reg(info):
  46. """
  47. Parallelizable robust regression function
  48. info: two element list. first element, power spectra: # freqs x # elecs. Second element: log transformed freqs
  49. returns intercepts, slopes, resids
  50. """
  51. p_spects = info[0]
  52. x = sm.tools.tools.add_constant(info[1])
  53. # holds slope of fit line
  54. slopes = np.empty((p_spects.shape[1]))
  55. slopes[:] = np.nan
  56. # holds residuals
  57. resids = np.empty((p_spects.shape[0], p_spects.shape[1]))
  58. resids[:] = np.nan
  59. # holds intercepts
  60. intercepts = np.empty((p_spects.shape[1]))
  61. intercepts[:] = np.nan
  62. # holds mean height of fit line
  63. bband_power = np.empty((p_spects.shape[1]))
  64. bband_power[:] = np.nan
  65. # loop over every electrode
  66. for i, y in enumerate(p_spects.T):
  67. model_res = sm.RLM(y, x).fit()
  68. intercepts[i] = model_res.params[0]
  69. slopes[i] = model_res.params[1]
  70. bband_power[i] = model_res.fittedvalues.mean()
  71. resids[:, i] = model_res.resid
  72. return intercepts, slopes, resids, bband_power
  73. def par_robust_reg_no_low_freqs(info):
  74. """
  75. Parallelizable robust regression function
  76. info: two element list. first element, power spectra: # freqs x # elecs. Second element: log transformed freqs
  77. returns intercepts, slopes, resids
  78. """
  79. p_spects = info[0]
  80. x = sm.tools.tools.add_constant(info[1])
  81. freq_inds = info[2]
  82. # holds slope of fit line
  83. slopes = np.empty((p_spects.shape[1]))
  84. slopes[:] = np.nan
  85. # holds residuals
  86. resids = np.empty((p_spects.shape[0], p_spects.shape[1]))
  87. resids[:] = np.nan
  88. # holds intercepts
  89. intercepts = np.empty((p_spects.shape[1]))
  90. intercepts[:] = np.nan
  91. # holds mean height of fit line
  92. bband_power = np.empty((p_spects.shape[1]))
  93. bband_power[:] = np.nan
  94. # loop over every electrode
  95. for i, y in enumerate(p_spects.T):
  96. model_res = sm.RLM(y[freq_inds], x[freq_inds]).fit()
  97. intercepts[i] = model_res.params[0]
  98. slopes[i] = model_res.params[1]
  99. bband_power[i] = model_res.fittedvalues.mean()
  100. resids[:, i] = y - ((x[:, 1]*model_res.params[1]) + model_res.params[0])
  101. return intercepts, slopes, resids, bband_power
  102. def my_local_max(arr):
  103. """
  104. Returns indices of local maxima in a 1D array. Unlike scipy.signal.argrelmax, this does not ignore consecutive
  105. values that are peaks. It finds the last repetition.
  106. """
  107. b1 = arr[:-1] <= arr[1:]
  108. b2 = arr[:-1] > arr[1:]
  109. k = np.where(b1[:-1] & b2[1:])[0] + 1
  110. if arr[0] > arr[1]:
  111. k = np.append(k, 0)
  112. if arr[-1] > arr[-2]:
  113. k = np.append(k, len(arr) - 1)
  114. return k
  115. def par_find_peaks(info):
  116. """
  117. Parallelizable peak picking function, uses robust reg but returns
  118. """
  119. p_spect = info[0]
  120. x = sm.tools.tools.add_constant(info[1])
  121. model_res = sm.RLM(p_spect, x).fit()
  122. peak_inds = my_local_max(model_res.resid)
  123. peaks = np.zeros(x.shape[0], dtype=bool)
  124. peaks[peak_inds] = True
  125. above_thresh = model_res.resid > np.std(model_res.resid)
  126. peaks = peaks & above_thresh
  127. return peaks
  128. def circ_lin_regress(phases, coords, theta_r, params):
  129. """
  130. Performs 2D circular linear regression.
  131. This is ported from Honghui's matlab code.
  132. :param phases:
  133. :param coords:
  134. :return:
  135. """
  136. n = phases.shape[1]
  137. pos_x = np.expand_dims(coords[:, 0], 1)
  138. pos_y = np.expand_dims(coords[:, 1], 1)
  139. # compute predicted phases for angle and phase offset
  140. x = np.expand_dims(phases, 2) - params[:, 0] * pos_x - params[:, 1] * pos_y
  141. # Compute resultant vector length. This is faster than calling pycircstat.resultant_vector_length
  142. # now = time.time()
  143. x1 = numexpr.evaluate('sum(cos(x) / n, axis=1)')
  144. x1 = numexpr.evaluate('x1 ** 2')
  145. x2 = numexpr.evaluate('sum(sin(x) / n, axis=1)')
  146. x2 = numexpr.evaluate('x2 ** 2')
  147. Rs = numexpr.evaluate('-sqrt(x1 + x2)')
  148. # for each time and event, find the parameters with the smallest -R
  149. min_vals = theta_r[np.argmin(Rs, axis=1)]
  150. sl = min_vals[:, 1] * np.array([np.cos(min_vals[:, 0]), np.sin((min_vals[:, 0]))])
  151. offs = np.arctan2(np.sum(np.sin(phases.T - sl[0, :] * pos_x - sl[1, :] * pos_y), axis=0),
  152. np.sum(np.cos(phases.T - sl[0, :] * pos_x - sl[1, :] * pos_y), axis=0))
  153. pos_circ = np.mod(sl[0, :] * pos_x + sl[1, :] * pos_y + offs, 2 * np.pi)
  154. # compute circular correlation coefficient between actual phases and predicited phases
  155. circ_corr_coef = pycircstat.corrcc(phases.T, pos_circ, axis=0)
  156. # compute adjusted r square
  157. # pdb.set_trace()
  158. r2_adj = circ_corr_coef ** 2
  159. # r2_adj = 1 - ((1 - circ_corr_coef ** 2) * (n - 1)) / (n - 4)
  160. wave_ang = min_vals[:, 0]
  161. wave_freq = min_vals[:, 1]
  162. #re-estimate finer parameters
  163. wave_ang_fine = []
  164. wave_freq_fine = []
  165. r2_adj_fine = []
  166. offs_fine = []
  167. for i_trial in range(0, np.shape(wave_ang)[0], 1):
  168. wave_ang_deg = np.degrees(wave_ang[i_trial])
  169. wave_freq_deg = np.degrees(wave_freq[i_trial])
  170. thetas_tmp = np.radians(np.arange(np.max([0, wave_ang_deg-2.5]), np.min([wave_ang_deg+2.5, 359.99]), 0.05))
  171. rs_tmp = np.radians(np.arange(np.max([0, wave_freq_deg-0.5]), np.min([wave_freq_deg+0.5, 17.99]), 0.05))
  172. theta_r_tmp = np.stack([(x, y) for x in thetas_tmp for y in rs_tmp])
  173. params_tmp = np.stack([theta_r_tmp[:, 1] * np.cos(theta_r_tmp[:, 0]), theta_r_tmp[:, 1] * np.sin(theta_r_tmp[:, 0])], -1)
  174. # compute predicted phases for angle and phase offset
  175. x = np.expand_dims(np.expand_dims(phases[i_trial, :], 1), 0) - params_tmp[:, 0] * pos_x - params_tmp[:, 1] * pos_y
  176. # Compute resultant vector length. This is faster than calling pycircstat.resultant_vector_length
  177. # now = time.time()
  178. x1 = numexpr.evaluate('sum(cos(x) / n, axis=1)')
  179. x1 = numexpr.evaluate('x1 ** 2')
  180. x2 = numexpr.evaluate('sum(sin(x) / n, axis=1)')
  181. x2 = numexpr.evaluate('x2 ** 2')
  182. Rs = numexpr.evaluate('-sqrt(x1 + x2)')
  183. # for each time and event, find the parameters with the smallest -R
  184. min_vals = theta_r_tmp[np.argmin(Rs, axis=1)]
  185. sl = min_vals[:, 1] * np.array([np.cos(min_vals[:, 0]), np.sin((min_vals[:, 0]))])
  186. offs_tmp = np.arctan2(np.sum(np.sin(np.expand_dims(phases[i_trial, :], 1) - sl[0, :] * pos_x - sl[1, :] * pos_y), axis=0),
  187. np.sum(np.cos(np.expand_dims(phases[i_trial, :], 1) - sl[0, :] * pos_x - sl[1, :] * pos_y), axis=0))
  188. offs_fine.append(offs_tmp)
  189. pos_circ = np.mod(sl[0, :] * pos_x + sl[1, :] * pos_y + offs_tmp, 2 * np.pi)
  190. # compute circular correlation coefficient between actual phases and predicited phases
  191. circ_corr_coef = pycircstat.corrcc(np.expand_dims(phases[i_trial, :], 1), pos_circ, axis=0)
  192. # compute adjusted r square
  193. # pdb.set_trace()
  194. r2_adj_fine.append(circ_corr_coef ** 2)
  195. # r2_adj = 1 - ((1 - circ_corr_coef ** 2) * (n - 1)) / (n - 4)
  196. wave_ang_fine.append(min_vals[:, 0])
  197. wave_freq_fine.append(min_vals[:, 1])
  198. wave_ang_fine = np.squeeze(np.array(wave_ang_fine))
  199. wave_freq_fine = np.squeeze(np.array(wave_freq_fine))
  200. r2_adj_fine = np.squeeze(np.array(r2_adj_fine))
  201. offs_fine = np.squeeze(np.array(offs_fine))
  202. return wave_ang_fine, wave_freq_fine, r2_adj_fine, offs_fine
  203. def channel_peak(data):
  204. params,rs=findpeak(data)
  205. peaks = np.zeros(len(data)).astype(bool)
  206. #params=[ int(round(i)) for i in params]
  207. if len(params)==5:
  208. if 5<params[3]<195 and params[2]>.3 and params[4]<50:
  209. peaks[int(round(params[3]))]=True
  210. elif len(params)==8:
  211. if 5<params[3]<195 and params[2]>.3 and params[4]<50:
  212. peaks[int(round(params[3]))]=True
  213. if 5<params[6]<195 and params[5]>.3 and params[7]<50:
  214. peaks[int(round(params[6]))]=True
  215. elif len(params)==11:
  216. if 5<params[3]<195 and params[2]>.3 and params[4]<50:
  217. peaks[int(round(params[3]))]=True
  218. if 5<params[6]<195 and params[5]>.3 and params[7]<50:
  219. peaks[int(round(params[6]))]=True
  220. if 5<params[9]<195 and params[8]>.3 and params[10]<50:
  221. peaks[int(round(params[9]))]=True
  222. return peaks
  223. def findpeak(data):
  224. line,peaks,heights=robustfit(data)
  225. #when no peaks found from robustfit, return no peaks, extremely rare ad extremely annoying!!!
  226. if len(peaks)==0 or line[1]>0 or line[0]<0:
  227. return line,0
  228. x=np.array(list(range(len(data))))
  229. y=data
  230. # Just fit 1 peak
  231. initial=[line[1],line[0],1,peaks[0],5]
  232. upper=[0,np.inf,np.inf,199,np.inf]
  233. lower=[-np.inf,0,0,0,0]
  234. best_vals,cov = curve_fit(oneOverF1, x, y,p0=initial,bounds=(lower,upper))
  235. Rs=np.corrcoef(y,oneOverF1(x,*best_vals))[0,1]**2;
  236. Rs=Radjust(Rs,5,200)
  237. res=best_vals
  238. RS=Rs
  239. # if 1 peaks are good enough, return
  240. if RS>.999:
  241. return res,RS
  242. # fit 2 peaks
  243. upper=[0,np.inf,np.inf,199,np.inf,np.inf,199,np.inf]
  244. lower=[-np.inf,0,0,0,0,0,0,0]
  245. if len(peaks)>=2:
  246. initial=[line[1],line[0],1,peaks[0],5,1,peaks[1],5]
  247. best_vals,cov = curve_fit(oneOverF2, x, y,p0=initial,bounds=(lower,upper))
  248. Rs=np.corrcoef(y,oneOverF2(x,*best_vals))[0,1]**2;
  249. Rs=Radjust(Rs,8,200)
  250. if RS<Rs:
  251. res=best_vals
  252. RS=Rs
  253. # fit 2 peaks with interpolation
  254. # first interpolation point
  255. initial=[line[1],line[0],1,peaks[0],5,1,peaks[0]/2,5]
  256. best_vals,cov = curve_fit(oneOverF2, x, y,p0=initial,bounds=(lower,upper))
  257. Rs=np.corrcoef(y,oneOverF2(x,*best_vals))[0,1]**2;
  258. Rs=Radjust(Rs,8,200)
  259. if RS<Rs:
  260. res=best_vals
  261. RS=Rs
  262. # second interpolation point
  263. initial=[line[1],line[0],1,peaks[0],5,1,(peaks[0]+199)/2,5]
  264. best_vals,cov = curve_fit(oneOverF2, x, y,p0=initial,bounds=(lower,upper))
  265. Rs=np.corrcoef(y,oneOverF2(x,*best_vals))[0,1]**2;
  266. Rs=Radjust(Rs,8,200)
  267. if RS<Rs:
  268. res=best_vals
  269. RS=Rs
  270. # if 2 peaks are good enough, return
  271. if RS>.999 or len(peaks)<2:
  272. return res,RS
  273. # fit 3 peaks
  274. upper=[0,np.inf,np.inf,199,199,np.inf,199,199,np.inf,199,199]
  275. lower=[-np.inf,0,0,0,0,0,0,0,0,0,0]
  276. if len(peaks)>2:
  277. initial=[line[1],line[0],1,peaks[0],5,1,peaks[1],5,1,peaks[2],5]
  278. best_vals,cov = curve_fit(oneOverF3, x, y,p0=initial,bounds=(lower,upper))
  279. Rs=np.corrcoef(y,oneOverF3(x,*best_vals))[0,1]**2;
  280. Rs=Radjust(Rs,11,200)
  281. if RS<Rs:
  282. res=best_vals
  283. RS=Rs
  284. # fit 2 peaks plus different interpolation points cause the 3rd peak is not that reliable.
  285. Peak=sorted(peaks[:2])
  286. # first interpolation point
  287. initial=[line[1],line[0],1,Peak[0]/2,5,1,Peak[0],5,1,Peak[1],5]
  288. best_vals,cov = curve_fit(oneOverF3, x, y,p0=initial,bounds=(lower,upper))
  289. Rs=np.corrcoef(y,oneOverF3(x,*best_vals))[0,1]**2;
  290. Rs=Radjust(Rs,11,len(data))
  291. if RS<Rs:
  292. res=best_vals
  293. RS=Rs
  294. # second interpolation point
  295. initial=[line[1],line[0],1,Peak[0],5,1,sum(Peak)/2,5,1,Peak[1],5]
  296. best_vals,cov = curve_fit(oneOverF3, x, y,p0=initial,bounds=(lower,upper))
  297. Rs=np.corrcoef(y,oneOverF3(x,*best_vals))[0,1]**2;
  298. Rs=Radjust(Rs,11,200)
  299. if RS<Rs:
  300. res=best_vals
  301. RS=Rs
  302. # third interpolation point
  303. initial=[line[1],line[0],1,Peak[0],5,1,Peak[1],5,1,Peak[1]/2+99.5,5]
  304. best_vals,cov = curve_fit(oneOverF3, x, y,p0=initial,bounds=(lower,upper))
  305. Rs=np.corrcoef(y,oneOverF3(x,*best_vals))[0,1]**2;
  306. Rs=Radjust(Rs,11,200)
  307. if RS<Rs:
  308. res=best_vals
  309. RS=Rs
  310. return res,RS
  311. def oneOverF1(x,slope,intercept, amp1, cen1, wid1):
  312. return x*slope+intercept+amp1 * exp(-.5*(x-cen1)**2 / wid1**2)
  313. def oneOverF2(x,slope,intercept, amp1, cen1, wid1,amp2, cen2, wid2):
  314. return x*slope+intercept+amp1 * exp(-.5*(x-cen1)**2 / wid1**2)+amp2 * exp(-.5*(x-cen2)**2 / wid2**2)
  315. def oneOverF3(x,slope,intercept, amp1, cen1, wid1,amp2, cen2, wid2,amp3, cen3, wid3):
  316. return x*slope+intercept+amp1 * exp(-.5*(x-cen1)**2 / wid1**2)+amp2 * exp(-.5*(x-cen2)**2 / wid2**2)+amp3 * exp(-.5*(x-cen3)**2 / wid3**2)
  317. def Radjust(Rs,k,n):
  318. return 1-(1-Rs)*(n-1)/(n-k-1)
  319. def robustfit(data):
  320. x = sm.tools.tools.add_constant(np.array(range(200)))
  321. model_res = sm.RLM(data, x).fit()
  322. peak_inds = np.squeeze(argrelmax(model_res.resid))
  323. if peak_inds.size==1:
  324. peak_inds=np.expand_dims(peak_inds,0)
  325. peak_inds=sorted(peak_inds,key=lambda i:model_res.resid[i])[::-1]
  326. return model_res.params,peak_inds[:3],model_res.resid[peak_inds[:3]]
  327. def par_find_peaks_by_chan2(p_spect_array, frequencies, std_thresh=1.):
  328. """
  329. Parameters
  330. ----------
  331. p_spect_array: numpy.ndarray
  332. An array with dimensions frequencies x channels
  333. frequencies: numpy.ndarray
  334. An array of the frequencies used
  335. std_thresh: float
  336. Threshold in number of standard deviations above the corrected power spectra to be counted as a peak
  337. Returns
  338. -------
  339. peaks_all_chans: numpy.ndarray with type bool
  340. An array of booleans the same shape as p_spect_array, specifying if there is a peak at a given frequency
  341. and electrode
  342. """
  343. peaks_all_chans = np.zeros(p_spect_array.shape).astype(bool)
  344. for i, chan_data in enumerate(p_spect_array.T):
  345. peaks_all_chans[:,i] = channel_peak(chan_data)
  346. return peaks_all_chans
  347. def rbar(x):
  348. n=len(x)
  349. x1 = numexpr.evaluate('sum(cos(x) / n, axis=0)')
  350. x1 = numexpr.evaluate('x1 ** 2')
  351. x2 = numexpr.evaluate('sum(sin(x) / n, axis=0)')
  352. x2 = numexpr.evaluate('x2 ** 2')
  353. Rs = numexpr.evaluate('sqrt(x1 + x2)')
  354. return Rs
  355. def circ_hist2(data,n=20,x=0,y=0,radius=1,color='k'):
  356. bins=np.linspace(-np.pi,np.pi,n+1)
  357. counts,bins=np.histogram(data,bins)
  358. counts=radius*counts/np.max(counts)
  359. for i in range(len(counts)):
  360. plt.plot([x,x+counts[i]*cos(bins[i])],[y,y+counts[i]*sin(bins[i])],color=color,linewidth=.3)
  361. plt.plot([x,x+counts[i]*cos(bins[i+1])],[y,y+counts[i]*sin(bins[i+1])],color=color,linewidth=.3)
  362. arch=np.linspace(bins[i],bins[i+1],100)
  363. plt.plot(x+counts[i]*np.cos(arch),y+counts[i]*np.sin(arch),color=color,linewidth=.5)
  364. plt.axis('equal')
  365. def circ_hist(data,n=20,x=0,y=0,radius=1,color='k',alpha=.3):
  366. # circular histogram, data need to be within -pi to pi
  367. bins=np.linspace(-np.pi,np.pi,n+1)
  368. counts,bins=np.histogram(data,bins)
  369. counts=radius*counts/np.max(counts)
  370. for i in range(len(counts)):
  371. X=[x,x+counts[i]*cos(bins[i]),x+counts[i]*cos(bins[i+1]),x]
  372. Y=[y,y+counts[i]*sin(bins[i]),y+counts[i]*sin(bins[i+1]),y]
  373. plt.fill(X,Y,color=color,alpha=alpha,linewidth=0)
  374. plt.axis('equal')
  375. def adjust(rs,n,k):
  376. return 1 - ((1 - rs) * (n - 1)) / (n - 1-k)

par_funcs_fine.py at commit 68587cf, no license · at the source

Overview

  1. Department of Neurology, University of Chicago,Chicago, IL USA
  2. Department of Biomedical Engineering, Columbia University,New York, NY USA
  3. Department of Mathematics, University of Pittsburgh,Pittsburgh, PA USA
Institutions: University of Chicago (United States); Columbia University (United States); University of Pittsburgh (United States)
Journal: Nature communications, volume 17, issue 1, article 5143
Dates: received 21 July 2025; accepted 9 March 2026; published online 11 April 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41467-026-71386-z · PMID 41963323 · PMCID PMC13250086 · OpenAlex W7152990215
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), cognitive (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing
Keywords: Cognitive neuroscience, Neuroscience
MeSH: Brain*, Cerebral Cortex*, Memory*, Adult, Brain Mapping, Female, Humans, Male, Young Adult (* major topic)
Topic: Memory and Neural Mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: National Science Foundation (CAREER)
Citations: cited by 4 papers (Europe PMC); 101 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

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

anupdas777/complex_traveling_waves

License: none: the authors keep all their rights
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 68587cfd4d364699a31687eef41db080acf796e3, 20 May 2025
Languages: Jupyter (3), Python (2)
Size: 20 files, 5 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: 3 notebooks
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (5 files), SciPy (5 files), Matplotlib (4 files), pandas (4 files), scikit-learn (3 files), CircStat (2 files), xarray (2 files), Nilearn (1 file), Pillow (1 file), seaborn (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
5 files

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41467-026-71386-z.

Tracing map

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

What the map holds:

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

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

Data

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

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • no repository, dataset or request procedure was recognized in it

Read it in the paper: doi.org/10.1038/s41467-026-71386-z.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 2 keywords, 9 MeSH terms, 1 funder, 94 references.

Cite

This paper

Das, A., Zabeh, E., Ermentrout, B., & Jacobs, J. (2026). Planar, spiral, and concentric traveling waves distinguish behavioral states in human memory. Nature communications, 17(1), 5143. https://doi.org/10.1038/s41467-026-71386-z

BibTeX

@article{das2026planar,
author = {Das, Anup and Zabeh, Erfan and Ermentrout, Bard and Jacobs, Joshua},
title = {{Planar, spiral, and concentric traveling waves distinguish behavioral states in human memory}},
journal = {Nature communications},
year = {2026},
month = apr,
volume = {17},
number = {1},
pages = {5143},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-71386-z},
url = {https://doi.org/10.1038/s41467-026-71386-z},
pmid = {41963323},
pmcid = {PMC13250086}
}

RIS

TY - JOUR
AU - Das, Anup
AU - Zabeh, Erfan
AU - Ermentrout, Bard
AU - Jacobs, Joshua
TI - Planar, spiral, and concentric traveling waves distinguish behavioral states in human memory
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/04/11
VL - 17
IS - 1
SP - 5143
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-71386-z
UR - https://doi.org/10.1038/s41467-026-71386-z
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-71386-z",
"type": "article-journal",
"title": "Planar, spiral, and concentric traveling waves distinguish behavioral states in human memory",
"container-title": "Nature communications",
"author": [
{
"family": "Das",
"given": "Anup"
},
{
"family": "Zabeh",
"given": "Erfan"
},
{
"family": "Ermentrout",
"given": "Bard"
},
{
"family": "Jacobs",
"given": "Joshua"
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "5143",
"DOI": "10.1038/s41467-026-71386-z",
"PMID": "41963323",
"PMCID": "PMC13250086",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-71386-z",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
11
]
]
}
}

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.7554/elife.108208 [code]
Realistic coupling enables flexible macroscopic traveling waves in the mouse cortex.
Journal: eLife
In common: pandas, NumPy, 17 references
[2] doi:10.3389/fncom.2026.1844662
Quantifying cortex-wide traveling brain waves of complex patterns with a graph-based algorithm.
Journal: Frontiers in computational neuroscience
In common: 14 references
[3] doi:10.7554/elife.100674 [code]
The dominance of large-scale phase dynamics in human cortex, from delta to gamma.
Journal: eLife
In common: SciPy, Matplotlib, NumPy, 10 references
[4] doi:10.1038/s41467-026-72931-6 [code]
Three parsimonious spatiotemporal patterns in cerebellum reveal individual traits in function and behavior.
Journal: Nature communications
In common: Nilearn, statsmodels, seaborn, 5 other tools, cognitive, 5 references
[5] doi:10.7554/elife.106753 [code]
Traveling waves across scales: Different mechanisms but same canonical computation?
Journal: n/a
In common: 10 references
[6] doi:10.1073/pnas.2527296123
Traveling-wave transcranial alternating current stimulation (twtACS) causally links neural timing to cognitive function.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: cognitive, 9 references
[7] doi:10.1371/journal.pcbi.1013488 [code]
Evaluating place cell detection methods in Rats and Humans: Implications for cross-species spatial coding.
Journal: PLoS computational biology
In common: statsmodels, seaborn, scikit-learn, 4 other tools, 1 reference, author Joshua Jacobs
[8] doi:10.1038/s41467-026-75959-w [code]
Charting higher-order models of brain function beyond pairwise interactions.
Journal: Nature communications
In common: xarray, Nilearn, statsmodels, 6 other tools, 2 references
[9] doi:10.1371/journal.pcbi.1013007 [code]
Traveling waves in the human visual cortex: An MEG-EEG model-based approach
Journal: n/a
In common: SciPy, Matplotlib, NumPy, 7 references
[10] doi:10.1016/j.isci.2026.116728 [code]
Awake cortex stabilizes traveling waves for global and reliable information routing.
Journal: iScience
In common: SciPy, NumPy, 7 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.