OSCR

Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds.

Code ↔ Paper

18 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 18 matches
  1. [1] § Materials and methods › Data analysis › Similarity kernel and diffusion maps. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 314–324 · score 0.85 · Laplace Beltrami approximation, IAN weighted graph, Diffusion maps, Laplacian, outliers, embedding
  2. [2] § Materials and methods › Data analysis › Similarity kernel and diffusion maps. ↔ encoding-manifold/encoding-manifold.ipynb, lines 263–272 · score 0.84 · Laplace Beltrami approximation, IAN weighted graph, Diffusion maps, Laplacian, embedding, matrix
  3. [3] § Materials and methods › Data analysis › Additional metrics. ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 340–404 · score 0.79 · VISam, VISrl, drifting gratings, temporal frequencies, VISal, VISpm
  4. [4] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 340–404 · score 0.75 · VISam, VISrl, drifting grating, VISal, VISpm, ISI
  5. [5] § Results ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 622–696 · score 0.71 · VISam, VISrl, VISal, VISpm, static gratings, phases
  6. [6] § Materials and methods › Dataset ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 418–439 · score 0.69 · visual space prior, stimulus warping, monitor, mouse
  7. [7] § Materials and methods › Data analysis › Additional metrics. ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 458–534 · score 0.66 · VISam, VISrl, VISal, VISpm, metrics, spiking
  8. [8] § Materials and methods › Data analysis › Neural encoding space. ↔ CNNs/build-CNN-manifold-resnet50_block3.ipynb, lines 10–152 · score 0.63 · tensor components, neural matrices, split, lowest, reconstruction, error
  9. [9] § Materials and methods › Data analysis › Tensor decomposition. ↔ permuted-decomposition/matlab/run_permcp.m, lines 1–95 · score 0.62 · direct optimization, Tensor Toolbox, modified, permutation
  10. [10] § Materials and methods › Data analysis › Natural scene vs. static grating selectivity ratio. ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 622–696 · score 0.61 · Brain Observatory, static gratings, stimulus class, phase, orientation, spikes
  11. [11] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 281–391 · score 0.61 · Improved Sheather Jones, static gratings, bandwidth, algorithm, kernel, smoothed
  12. [12] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 262–318 · score 0.60 · Improved Sheather Jones, drifting gratings, bandwidth, algorithm, kernel, smoothed
  13. [13] § Materials and methods › Data analysis › Neural encoding space. ↔ CNNs/build-CNN-manifold-resnet50_block3.ipynb, lines 10–152 · score 0.59 · neural matrix, stimulus response, linear, vectors, product, encoding
  14. [14] § Materials and methods › Data analysis › Natural scene filtering. ↔ allen-data-analysis/filtering-natural-scenes/ffttools.py, lines 621–687 · score 0.57 · band pass, inner, radius, outer, Filtered, natural scenes
  15. [15] § Materials and methods › Data analysis › Neural encoding space. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 56–154 · score 0.56 · neural matrix, stimulus response, linear, vectors, encoding, neuron
  16. [16] § Materials and methods › Data analysis › Neural encoding space. ↔ permuted-decomposition/choosing-n-of-components.ipynb, lines 213–225 · score 0.56 · lowest reconstruction error, components, encoding
  17. [17] § Materials and methods › Data analysis › Natural scene filtering. ↔ allen-data-analysis/filtering-natural-scenes/filtering-natural-scenes.ipynb, lines 72–134 · score 0.55 · band pass, Filtered, cpd, cropped, natural scenes
  18. [18] § Materials and methods › Data analysis › Neural encoding space. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 56–154 · score 0.53 · neural matrix, neural factors, reconstruction, magnitudes, component, tensor

Paper

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

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 696 lines · 24 KB · BSD-2-Clause · 5 matches

  1. # %%
  2. import os
  3. # https://allensdk.readthedocs.io/en/latest/visual_coding_neuropixels.html#
  4. #https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_quickstart.html
  5. from ipywidgets import FloatProgress
  6. import numpy as np
  7. import pandas as pd
  8. import matplotlib.pyplot as plt
  9. import pickle
  10. from allensdk.brain_observatory.ecephys.ecephys_project_cache import EcephysProjectCache
  11. # %% [markdown]
  12. # #### ftns
  13. # %%
  14. #https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_session.html#Stimulus-presentations
  15. from itertools import product
  16. from itertools import product
  17. def get_spike_trains(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size, my_trial_len=None):
  18. trials_info = session.get_stimulus_table(STIM_CLASS)
  19. if trials_info.index.size == 0:
  20. return {}
  21. trial_len = round(np.mean(trials_info['duration'].values),2) #sec
  22. if my_trial_len is not None:
  23. assert my_trial_len <= trial_len
  24. trial_len = my_trial_len
  25. print('trial_len',trial_len)
  26. tot_len = trial_len + prestim_len
  27. time_bin_edges = np.linspace(-prestim_len, trial_len, int(tot_len/bin_size))
  28. NBINS = len(time_bin_edges)-1
  29. binarize = False
  30. if bin_size < .001:
  31. binarize = True
  32. print('NBINS',NBINS)
  33. # look at responses to a certain type of gratings
  34. stim_params = {pname:sorted(set(trials_info[pname].unique()).difference(['null'])) for pname in PARAM_NAMES}
  35. print(f'{STIM_CLASS} params:', stim_params)
  36. #get all param combinations
  37. paramvals_tuples = list(product(*[stim_params[pname] for pname in PARAM_NAMES]))
  38. all_trains = {}
  39. for ii,ui in enumerate(my_units):
  40. if ii % 5 == 0: print(ii,end=' ',flush=True)
  41. all_trains[ui] = {}
  42. for pvals_tup in paramvals_tuples:
  43. row_filter = np.prod(np.stack([trials_info[pname].values == pval for pname,pval in zip(PARAM_NAMES,pvals_tup)],axis=0),axis=0).astype('bool')
  44. sids = trials_info[row_filter].index.values
  45. Ntrials = len(sids)
  46. # print(f'{pvals_tup}, {Ntrials=}')
  47. #TODO compare speed against reading spk times directly: https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_optotagging.html
  48. spike_counts_da = session.presentationwise_spike_counts(
  49. bin_edges=time_bin_edges,
  50. stimulus_presentation_ids=sids,
  51. unit_ids=[ui],
  52. binarize=binarize
  53. )
  54. spike_counts_da = np.squeeze(spike_counts_da.values)
  55. # print(spike_counts_da.shape,spike_counts_da.max(),spike_counts_da.sum())
  56. #re-convert to spike times
  57. unit_trains = []
  58. for triali in range(Ntrials):
  59. train = np.flatnonzero(spike_counts_da[triali]).astype('float32')
  60. unit_trains.append(train * bin_size * 1000) #convert bin number to time (ms)
  61. all_trains[ui][pvals_tup] = unit_trains
  62. print()
  63. return all_trains
  64. def get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size):
  65. trials_info = session.get_stimulus_table(STIM_CLASS)
  66. if trials_info.index.size == 0:
  67. return {}
  68. trial_len = round(np.mean(trials_info['duration'].values),2) #sec
  69. print('trial_len',trial_len)
  70. tot_len = trial_len + prestim_len
  71. time_bin_edges = np.linspace(-prestim_len, trial_len, int(tot_len/bin_size))
  72. NBINS = len(time_bin_edges)-1
  73. binarize = False
  74. if bin_size < .001:
  75. binarize = True
  76. print('NBINS',NBINS)
  77. # look at responses to a certain type of gratings
  78. stim_params = {pname:sorted(set(trials_info[pname].unique()).difference(['null'])) for pname in PARAM_NAMES}
  79. print(f'{STIM_CLASS} params:', stim_params)
  80. #get all param combinations
  81. paramvals_tuples = list(product(*[stim_params[pname] for pname in PARAM_NAMES]))
  82. pref_phases = cache.get_unit_analysis_metrics_for_session(session_id)['pref_phase_sg']
  83. all_trains = {}
  84. for ii,ui in enumerate(my_units):
  85. print(ii,end=' ')
  86. all_trains[ui] = {}
  87. for pvals_tup in paramvals_tuples:
  88. #append this unit's pref phase to the stimulus params tuple
  89. assert ui in pref_phases.index
  90. pvals_tup_with_phase = pvals_tup + (str(pref_phases.loc[ui]),)#must convert phase to str (!?)
  91. PARAM_NAMES_with_phase = PARAM_NAMES + ['phase']
  92. row_filter = np.prod(np.stack(
  93. [trials_info[pname].values == pval for pname,pval in \
  94. zip(PARAM_NAMES_with_phase,pvals_tup_with_phase)],axis=0),axis=0).astype('bool')
  95. sids = trials_info[row_filter].index.values
  96. Ntrials = len(sids)
  97. # print(f'{pvals_tup}, {Ntrials=}')
  98. spike_counts_da = session.presentationwise_spike_counts(
  99. bin_edges=time_bin_edges,
  100. stimulus_presentation_ids=sids,
  101. unit_ids=[ui],
  102. binarize=binarize
  103. )
  104. spike_counts_da = np.squeeze(spike_counts_da.values)
  105. # print(spike_counts_da.shape,spike_counts_da.max(),spike_counts_da.sum())
  106. #re-convert to spike times
  107. unit_trains = []
  108. for triali in range(Ntrials):
  109. train = np.flatnonzero(spike_counts_da[triali]).astype('float32')
  110. unit_trains.append(train * bin_size * 1000) #convert bin number to time (ms)
  111. all_trains[ui][pvals_tup] = unit_trains
  112. print()
  113. return all_trains
  114. def get_train_dicts(all_trains_uid, tf, dirs, trial_len_ms, prestim_len_ms):
  115. trials_traindict = {}
  116. ISI_Nspks = {}
  117. full_traindict = {}
  118. for d in dirs:
  119. ptup = (tf, d,)
  120. assert ptup in all_trains_uid
  121. ISI_Nspks[d] = []
  122. new_trains = []
  123. for train in all_trains_uid[ptup]:
  124. if len(train) == 0:
  125. new_trains.append(np.array([]))
  126. ISI_Nspks[d].append(0)
  127. continue
  128. assert max(train) < trial_len_ms + prestim_len_ms
  129. new_train = train - prestim_len_ms
  130. new_trains.append(new_train[new_train >= 0])
  131. ISI_Nspks[d].append(new_train[new_train < 0].size)
  132. trials_traindict[d] = new_trains
  133. ISI_Nspks[d] = np.asarray(ISI_Nspks[d])
  134. full_traindict[d] = all_trains_uid[ptup]
  135. return full_traindict, trials_traindict, ISI_Nspks
  136. # %%
  137. import scipy as sp
  138. import warnings
  139. def computeResponseStats(traindict, ISI_Nspks, stats_ISI_len, trial_len, verbose=False):
  140. """Runs statistical tests to compare firing rates between the ISI and a given stimulus,
  141. for any period within the stimulus trial with the same length as the ISI.
  142. It performs two comparisons against the ISI FR: one using the maximum FR found for that
  143. interval length across stimulus trials; and another using the minimum FR.
  144. The following one-sided tests are run:
  145. Mann-Whitney U
  146. https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mannwhitneyu.html
  147. and Wilcoxon:
  148. https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wilcoxon.html
  149. ----------------
  150. Arguments:
  151. traindict: dict, {stimulus_direction: list of spike time arrays (ms), one per trial}
  152. ISI_Nspks: dict, {stimulus_direction: list of spike counts, one per trial}
  153. stats_ISI_len: float, length of the ISI interval, in secs, used to compute ISI_Nspks
  154. trial_len: float, total length of each trial, in secs
  155. ----------------
  156. Returns:
  157. stats_results: dict, for each of 'min-interval' and 'max-interval', contains a dict
  158. containing, for each stimulus direction, the p-values found for each tests, as well
  159. as the FRs for the stimulus and the ISI
  160. """
  161. mydirs = traindict.keys()
  162. stats_results = {}
  163. interval_Nspks_for_stats = {'min':{}, 'max':{}}
  164. for d in mydirs:
  165. #combine all trains
  166. data = np.concatenate(traindict[d])
  167. counts,bins = np.histogram(data,np.arange(0,trial_len+stats_ISI_len,stats_ISI_len))
  168. maxfr = -1
  169. minfr = np.inf
  170. for i in range(0,counts.size):
  171. fr = counts[i]
  172. if fr > maxfr:
  173. maxi = i
  174. maxfr = fr
  175. if fr < minfr:
  176. mini = i
  177. minfr = fr
  178. for interval_type,i_,fr_ in [('min',mini,minfr), ('max',maxi,maxfr)]:
  179. interval_Nspks_for_stats[interval_type][d] = []
  180. for train in traindict[d]:
  181. spks_within_interval = train[(train >= stats_ISI_len*i_) & (train < stats_ISI_len*(i_+1))]
  182. interval_Nspks_for_stats[interval_type][d].append( spks_within_interval.size )
  183. interval_Nspks_for_stats[interval_type][d] = np.array(interval_Nspks_for_stats[interval_type][d])
  184. # print(d,interval_type,interval_Nspks_for_stats[interval_type][d])
  185. assert len(interval_Nspks_for_stats[interval_type][d]) == len(traindict[d])
  186. assert interval_Nspks_for_stats[interval_type][d].sum() == fr_
  187. for trial_Nspks_for_stats_,pval_type,alternative in [
  188. (interval_Nspks_for_stats['max'],'maxinterval-pval','greater'),
  189. (interval_Nspks_for_stats['min'],'mininterval-pval','less')]:
  190. stats_results[pval_type] = {}
  191. for d in mydirs:
  192. dir_grayspks = ISI_Nspks[d]
  193. dir_stimspks = trial_Nspks_for_stats_[d]
  194. assert len(dir_stimspks) == len(dir_grayspks)
  195. if len(dir_stimspks) * len(dir_grayspks) == 0:
  196. stats_results[pval_type][d] = (np.inf,0,0)
  197. stats_results[pval_type][d] = {'MANNWHITNEY':np.inf,'WILCOXON':np.inf,'isiFR':0,'stimFR':0}
  198. if verbose: print(f'{d} no spikes')
  199. continue
  200. try:
  201. with warnings.catch_warnings():
  202. warnings.simplefilter("ignore",category=RuntimeWarning)
  203. _, pval = sp.stats.mannwhitneyu(dir_stimspks, dir_grayspks, alternative=alternative)
  204. except:
  205. pval = np.inf
  206. try:
  207. with warnings.catch_warnings():
  208. warnings.simplefilter("ignore",category=RuntimeWarning)
  209. warnings.simplefilter("ignore",category=UserWarning)
  210. _, wpval = sp.stats.wilcoxon(dir_stimspks, dir_grayspks, alternative=alternative)
  211. except:
  212. wpval = np.inf
  213. stats_results[pval_type][d] = {'MANNWHITNEY':pval,'WILCOXON':wpval,'isiFR':dir_grayspks.mean()/stats_ISI_len,'stimFR':dir_stimspks.mean()/stats_ISI_len}
  214. if verbose: print(f'{d} {pval_type} ({pval:.3f},{wpval:.3f}), gray={dir_grayspks.mean()/stats_ISI_len:.2f}, stim={dir_stimspks.mean()/stats_ISI_len:.2f}')
  215. return stats_results
  216. # %%
  217. import warnings
  218. from KDEpy import FFTKDE
  219. def getResponseCurve(train_dict, total_trial_len, bw=None, samp_interval=1, MINBW=10, MAXBW=50):
  220. """Computes smooth trial-averaged response to a stim in all directions from spike trains
  221. using a kernel density estimator."""
  222. ts = np.arange(0,total_trial_len+samp_interval,samp_interval)
  223. x_ts = .5*(ts[:-1]+ts[1:]) #sample at the midpoints between sampling intervals
  224. all_ISJs = []
  225. fftkde = None
  226. if bw is None:
  227. # if no pre-specified kernel bandwidth,
  228. # auto-estimate within range [MINBW, MAXBW]
  229. bw, fftkde = fitSmoothingKernelBandwidth(train_dict, total_trial_len)
  230. if bw is not None:
  231. bw = max(min(MAXBW,bw),MINBW)
  232. fftkde.bw = bw
  233. else:
  234. bw = MAXBW
  235. for di,d in enumerate(sorted(train_dict)):
  236. full_train = []
  237. for triali,train in enumerate(train_dict[d]):
  238. if train.size == 0: continue
  239. assert max(train) < total_trial_len
  240. full_train += list(train)
  241. data = np.array(full_train)
  242. data.sort()
  243. n = data.size
  244. if fftkde is None:
  245. fftkde = FFTKDE(kernel='gaussian', bw=bw) #initialize kernel density estimator
  246. try:
  247. fftkde = fftkde.fit(data)
  248. #extend sampling one unit before and after trial
  249. ext_x_ts = np.r_[x_ts[0]-samp_interval,x_ts,x_ts[-1]+samp_interval]
  250. #then crop after applying the kernel
  251. y = fftkde.evaluate(ext_x_ts)[1:-1]
  252. except:
  253. #error occurs in the rare cases when there are zero spikes. print out to double-check
  254. # print(f'fftkde failed: {n} data points')
  255. y = np.zeros_like(x_ts)
  256. all_ISJs.append(y*n*1000/len(train_dict[d])) #convert density to spks/sec
  257. return np.array(all_ISJs), x_ts
  258. def fitSmoothingKernelBandwidth(full_traindict, total_trial_len):
  259. """Fits a spike smoothing kernel to spike train data using
  260. the improved Sheather-Jones (ISJ) algorithm:
  261. Z. I. Botev, J. F. Grotowski, and D. P. Kroese.
  262. “Kernel density estimation via diffusion.”
  263. Annals of Statistics, Volume 38, Number 5, pp. 2916-2957, 2010.
  264. https://arxiv.org/pdf/1011.2602.pdf
  265. (see https://kdepy.readthedocs.io/en/latest/index.html
  266. for more information on this implementation)
  267. ---------------
  268. Arguments:
  269. full_traindict: dict, {stimulus_direction: list of spike time arrays, one per trial}
  270. The bandwidth is computed for the stimulus direction that elicited the most spikes.
  271. total_trial_len: float or int, total length of a trial used for the trains; must
  272. use the same time unit as the spike times in `full_traindict`
  273. ---------------
  274. Returns:
  275. opt_bw: float, optimal bandwidth found
  276. fftkde: object, the fitted fftkde object, to be reused when evaluating the kernel
  277. """
  278. # 1) use stimulus direction with max n of spks to estimate optimal bandwidth
  279. maxn = -1
  280. for d, trains in full_traindict.items():
  281. data = np.concatenate(trains)
  282. assert max(data) < total_trial_len
  283. data.sort()
  284. n = data.size
  285. if n > maxn:
  286. maxn = n
  287. bestd = d
  288. data = np.concatenate(full_traindict[bestd])
  289. # 2) fit bw
  290. fftkde = FFTKDE(kernel='gaussian', bw='ISJ')
  291. try:
  292. with warnings.catch_warnings():
  293. warnings.simplefilter("ignore",category=RuntimeWarning)
  294. fftkde = fftkde.fit(data)
  295. opt_bw = fftkde.bw
  296. except:
  297. #print(f'fftkde failed: {n} data points')
  298. opt_bw = None
  299. fftkde = None
  300. return opt_bw, fftkde #return fftkde object as well, to avoid refitting
  301. # %% [markdown]
  302. # #### load cache
  303. # %%
  304. # Example cache directory path, it determines where downloaded data will be stored
  305. output_dir = 'ecephys_cache_dir/'
  306. # this path determines where downloaded data will be stored
  307. manifest_path = os.path.join(output_dir, "manifest.json")
  308. cache = EcephysProjectCache.from_warehouse(manifest=manifest_path)
  309. print(cache.get_all_session_types())
  310. # %%
  311. sessions = cache.get_session_table()
  312. brain_observatory_type_sessions = sessions[sessions["session_type"] == "brain_observatory_1.1"]
  313. print(len(brain_observatory_type_sessions))
  314. brain_observatory_type_sessions.head()
  315. # %%
  316. from glob import glob
  317. my_session_ids = [int(dirname.split('session_')[1]) for dirname in glob('ecephys_cache_dir/session_*')]
  318. print(my_session_ids)
  319. # %% [markdown]
  320. # ### get gratings imgs
  321. # %%
  322. from allensdk.brain_observatory.stimulus_info import get_spatial_grating
  323. #aspect = 1920/1200
  324. aspect = 1174/918 #same as nat scene
  325. # 21.93" wide monitor positioned at 15 cm away from the mouse's right eye and
  326. # spanned 120° x 95° of visual space prior to stimulus warping
  327. #orig_px_per_deg = 1920/120 #tot_pixels/tot_degs
  328. height = 918//2
  329. width = aspect * height
  330. grat_imgs = []
  331. for cycle_per_deg in [.02, .04, .08, 0.16, 0.32]:
  332. for ori in [0,30,60,90,120,150]:
  333. for phase in [0, 0.25, 0.5, 0.75]:
  334. tot_cycles = cycle_per_deg * 120# 1920/px_per_deg#tot_pixels/px_per_deg
  335. pix_per_cycle = width/tot_cycles
  336. grat_imgs.append( get_spatial_grating(height=height, aspect_ratio=aspect, ori=ori, pix_per_cycle=pix_per_cycle, phase=phase) )
  337. # %%
  338. f, axes = plt.subplots(11,11,figsize=(7.5*aspect,7.5))
  339. shuffled_ix = np.random.choice(range(len(grat_imgs)), len(grat_imgs), False)
  340. for i in range(len(grat_imgs)):
  341. ax = axes.ravel()[i]
  342. image = grat_imgs[shuffled_ix[i]]
  343. ax.imshow(image, cmap=plt.cm.gray)
  344. ax.set(xticks=[],yticks=[])
  345. for i in range(len(grat_imgs),11**2):
  346. ax = axes.ravel()[i]
  347. ax.axis('off')
  348. plt.subplots_adjust(wspace=0.2, hspace=0.2)
  349. plt.show()
  350. # %% [markdown]
  351. # ### collect trial N spks
  352. # %%
  353. ### COLLECTING SPIKE COUNTS -- ALL PHASES
  354. AREAs = ['VISal','VISrl','VISam','VISpm', 'VISp', 'VISl']
  355. datadir = 'data'
  356. STIM_CLASS = 'static_gratings'
  357. PARAM_NAMES = ['spatial_frequency', 'orientation', 'phase']
  358. prestim_len = 0 #sec
  359. bin_size = .0005 #sec
  360. for session_i in range(brain_observatory_type_sessions.index.values.size):
  361. session_id = brain_observatory_type_sessions.index.values[session_i]
  362. print(f'\n* {session_i}: session_id',session_id,flush=True)
  363. skip = True
  364. for AREA in AREAs:
  365. fname = f's{session_id}_{AREA}_{STIM_CLASS}_allPhases'
  366. if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
  367. print(AREA,'saved previously.')
  368. else:
  369. skip = False
  370. break
  371. if skip:
  372. print('skipping...')
  373. continue
  374. session = cache.get_session_data(session_id)
  375. if STIM_CLASS not in session.stimulus_names:
  376. print(f'{STIM_CLASS} not found.')
  377. continue
  378. for AREA in AREAs:
  379. fname = f's{session_id}_{AREA}_{STIM_CLASS}_allPhases'
  380. if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
  381. print(AREA,'saved previously.')
  382. continue
  383. my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
  384. Nunits = len(my_units)
  385. print(AREA,'Nunits',Nunits)
  386. if Nunits == 0:
  387. continue
  388. pref_phases = cache.get_unit_analysis_metrics_for_session(session_id)['pref_phase_sg']
  389. #compute for all cells
  390. all_trains = get_spike_trains(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
  391. if not all_trains:
  392. print('0 trains')
  393. continue
  394. uis = sorted(all_trains)
  395. stims = sorted(all_trains[uis[0]])
  396. stim_ntrials = [len(all_trains[uis[0]][si]) for si in stims]
  397. X = None
  398. prefPhases = []
  399. for ii,ui in enumerate(uis):
  400. print(ii,end=' ',flush=True)
  401. prefPhase = pref_phases.loc[ui]
  402. all_frs = []
  403. for si in stims:
  404. all_frs += list(map(len,all_trains[ui][si]))
  405. if X is None:
  406. X = np.asarray(all_frs)[None,:]
  407. else:
  408. X = np.concatenate([X,np.asarray(all_frs)[None,:]])
  409. prefPhases.append(prefPhase)
  410. print()
  411. cellData = {'uis':uis, 'stims':stims, 'stim_ntrials':stim_ntrials, 'prefPhases':prefPhases}
  412. with open(f'{datadir}/{fname}_trial_info.pkl', 'wb') as f:
  413. pickle.dump(cellData, f)
  414. np.save(f'{datadir}/{fname}_trial_data.npy',X)
  415. print(fname,'saved.')
  416. # %%
  417. ### COLLECTING SPK COUNTS -- PREF PHASE
  418. AREAs = ['VISp','VISl','VISal','VISrl','VISam','VISpm']
  419. datadir = 'data'
  420. STIM_CLASS = 'static_gratings'
  421. PARAM_NAMES = ['spatial_frequency', 'orientation']
  422. prestim_len = 0 #sec
  423. bin_size = .0005 #sec
  424. trial_len_ms = 250
  425. prestim_len_ms = prestim_len * 1000
  426. dirs = [0.0, 30.0, 60.0, 90.0, 120.0, 150.0]
  427. bw = 25
  428. samp_interval = 10
  429. NDIRS = 6
  430. SFs = [0.02, 0.04, 0.08, 0.16, 0.32]
  431. for session_i in range(brain_observatory_type_sessions.index.values.size):
  432. session_id = brain_observatory_type_sessions.index.values[session_i]
  433. print(f'\n* {session_i}: session_id',session_id,flush=True)
  434. skip = True
  435. for AREA in AREAs:
  436. fname = f's{session_id}_{AREA}_{STIM_CLASS}_prefPhase'
  437. if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
  438. print(AREA,'saved previously.')
  439. else:
  440. skip = False
  441. break
  442. if skip:
  443. print('skipping...')
  444. continue
  445. session = cache.get_session_data(session_id)
  446. if STIM_CLASS not in session.stimulus_names:
  447. print(f'{STIM_CLASS} not found.')
  448. continue
  449. for AREA in AREAs:
  450. fname = f's{session_id}_{AREA}_{STIM_CLASS}_prefPhase'
  451. if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
  452. print(AREA,'saved previously.')
  453. continue
  454. my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
  455. Nunits = len(my_units)
  456. print(AREA,'Nunits',Nunits)
  457. if Nunits == 0:
  458. continue
  459. #compute for all cells
  460. all_trains = get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
  461. if not all_trains:
  462. print('0 trains')
  463. continue
  464. uis = sorted(all_trains)
  465. stims = sorted(product(*[stim_params[pname] for pname in PARAM_NAMES]))
  466. X = []
  467. prefPhases = []
  468. stim_ntrials = []
  469. for ii,ui in enumerate(uis):
  470. print(ii,end=' ',flush=True)
  471. prefPhase = list(all_trains[ui].keys())[0][-1]
  472. assert np.all(np.array(list(all_trains[ui].keys()))[:,2] == prefPhase)
  473. stim_ntrials.append([len(all_trains[ui][si+(prefPhase,)]) for si in stims])
  474. all_frs = []
  475. for si in stims:
  476. all_frs += list(map(len,all_trains[ui][si+(prefPhase,)]))
  477. X.append(np.asarray(all_frs))
  478. prefPhases.append(prefPhase)
  479. print()
  480. cellData = {'uis':uis, 'prefPhases':prefPhases, 'stims':stims, 'stim_ntrials':stim_ntrials}
  481. with open(f'{datadir}/{fname}_trial_info.pkl', 'wb') as f:
  482. pickle.dump(cellData, f)
  483. np.save(f'{datadir}/{fname}_trial_data.npy',X)
  484. print(fname,'saved.')
  485. # %% [markdown]
  486. # ### collect PSTHs
  487. # %%
  488. AREAs = ['VISl','VISal','VISrl','VISam','VISpm','VISp']
  489. datadir = 'data'
  490. STIM_CLASS = 'static_gratings'
  491. PARAM_NAMES = ['spatial_frequency', 'orientation']
  492. prestim_len = 0.05 #sec
  493. bin_size = .0005 #sec
  494. trial_len_ms = 300
  495. prestim_len_ms = prestim_len * 1000
  496. dirs = [0.0, 30.0, 60.0, 90.0, 120.0, 150.0]
  497. bw = 10
  498. samp_interval = 5
  499. NDIRS = 6
  500. SFs = [0.02, 0.04, 0.08, 0.16, 0.32]
  501. for session_i in range(brain_observatory_type_sessions.index.values.size):
  502. session_id = brain_observatory_type_sessions.index.values[session_i]
  503. print(f'\n* {session_i}: session_id',session_id)
  504. skip = True
  505. for AREA in AREAs:
  506. fname = f's{session_id}_{AREA}_{STIM_CLASS}_bw{bw}'
  507. if os.path.isfile(f'{datadir}/{fname}.pkl'):
  508. print(AREA,'saved previously.')
  509. else:
  510. skip = False
  511. break
  512. if skip:
  513. print('skipping...')
  514. continue
  515. session = cache.get_session_data(session_id)
  516. if STIM_CLASS not in session.stimulus_names:
  517. print(f'{STIM_CLASS} not found.')
  518. continue
  519. for AREA in AREAs:
  520. fname = f's{session_id}_{AREA}_{STIM_CLASS}_bw{bw}'
  521. if os.path.isfile(f'{datadir}/{fname}.pkl'):
  522. print(AREA,'saved previously.')
  523. continue
  524. my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
  525. Nunits = len(my_units)
  526. print(AREA,'Nunits',Nunits)
  527. if Nunits == 0:
  528. continue
  529. #compute for all cells
  530. all_trains = get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
  531. if not all_trains:
  532. print('0 trains')
  533. continue
  534. allData = {}
  535. for uid in my_units:
  536. allData[uid] = {}
  537. for tfi,tf in enumerate(SFs):
  538. allData[uid][tf] = {}
  539. full_traindict, trials_traindict, ISI_Nspks = get_train_dicts(all_trains[uid], tf, dirs, trial_len_ms, prestim_len_ms)
  540. all_ISJs, x_ts = getResponseCurve(full_traindict, trial_len_ms+prestim_len_ms, bw, samp_interval)
  541. allData[uid][tf]['psts'] = all_ISJs
  542. if prestim_len_ms > 0:
  543. allData[uid][tf]['stats'] = computeResponseStats(trials_traindict, ISI_Nspks, prestim_len_ms, trial_len_ms)
  544. allData[uid][tf]['n_trials'] = [full_traindict[d] for d in dirs]
  545. with open(f'{datadir}/{fname}.pkl', 'wb') as f:
  546. pickle.dump(allData, f)
  547. print(fname,'saved.')

read_spike_data-static-gratings.ipynb at commit ddccf17, under BSD-2-Clause · at the source

Overview

Authors: Luciano Dyballa1, Greg D. Field2, Michael P. Stryker3,4, Steven W. Zucker5,6
  1. School of Science and Technology, IE University, Madrid, Spain
  2. Jules Stein Eye Institute, Department of Ophthalmology, David Geffen School of Medicine, University of California, Los Angeles, California, United States of America
  3. Department of Physiology, University of California, San Francisco, California, United States of America
  4. Kavli Institute for Fundamental Neuroscience, University of California, San Francisco, California, United States of America
  5. Department of Computer Science, Yale University, New Haven, Connecticut, United States of America
  6. Department of Biomedical Engineering, Yale University, New Haven, Connecticut, United States of America
Institutions: IE University (Spain); Yale University (United States)
Journal: PloS one, volume 21, issue 9, article e0356243
Dates: received 4 February 2026; accepted 1 August 2026; published online 17 September 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1371/journal.pone.0356243 · PMID 42752452 · PMCID PMC13585280 · OpenAlex W7213466761
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: mouse (organism)
Methods: Spectral & time-frequency, Statistics, Smoothing, state filtering, decompositions, Preprocessing, Connectivity, Machine learning, Single-unit activity, calcium imaging
MeSH: Primary Visual Cortex*, Visual Cortex*, Visual Perception*, Animals, Mice, Neurons, Photic Stimulation (* major topic)
Journal subjects: Biology and Life Sciences, Cell Biology, Cellular Types, Animal Cells, Neurons, Neuroscience, Cellular Neuroscience, Physical Sciences, Mathematics, Topology, Manifolds, Cognitive Science, Cognitive Psychology, Perception, Sensory Perception, Vision, Psychology, Social Sciences, Neuronal Tuning, Geometry, Non-Euclidean geometry, Discrete Mathematics, Combinatorics, Permutation, Anatomy, Brain, Visual Cortex, Medicine and Health Sciences
Topic: Visual perception and processing mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: National Institutes of Health (NIH) (EY031059); National Science Foundation (NSF) (1822598); European Commission's Marie Skłodowska-Curie Action (101207931); Jules Stein Eye Institute from Research to Prevent Blindness (NIH) (EY000331)
Citations: cited by 1 paper (Europe PMC); 76 references in the paper

Abstract

A challenge in sensory neuroscience is understanding how populations of neurons operate in concert to represent diverse stimuli. To meet this challenge, we have created “encoding manifolds” that reveal the overall responses of brain areas to diverse stimuli and organize individual neurons in stimulus-response coordinates according to their selectivity and response dynamics. Here we use encoding manifolds to compare the population-level encoding of primary visual cortex (VISp) with that of five higher visual areas (VISam, VISal, VISpm, VISlm, and VISrl), using data from the Allen Institute Visual Coding–Neuropixels dataset from the mouse. We show that the topology of the encoding manifold for VISp and for higher visual areas is continuous, with smooth coordinates along which stimulus selectivity and response dynamics are organized with layer and cell-type specificity. Surprisingly, the manifolds revealed novel relationships between how natural scenes are encoded relative to static gratings—a relationship conserved across visual areas. Namely, neurons preferring natural scenes preferred either low or high spatial frequency gratings, but not intermediate ones. Analyzing responses by cortical layer reveals a preference for gratings concentrated in layer 6, whereas preferences for natural scenes tended to be higher in layers 2/3 and 4. The results demonstrate how machine learning approaches can be used to organize and visualize the structure of sensory coding, thereby revealing novel relationships within and across brain areas and sensory stimuli.

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

Repository

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

dyballa/NeuralEncodingManifolds

License: BSD-2-Clause
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Commit: ddccf17c8dc1e3d129624c05285dd5ec24a97e41, 24 October 2025
Languages: Jupyter (17), Python (5), MATLAB (4)
Size: 542 files, 26 scripts
Software Heritage: not archived
Found in: the text, “Neural encoding manifolds.”
Holds: README, license file, 17 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (22 files), NumPy (22 files), SciPy (13 files), Pillow (5 files), AllenSDK (4 files), pandas (4 files), scikit-learn (4 files), Keras (3 files), TensorFlow (3 files), Plotly (1 file)
Availability: 1 check, the latest on 26 September 2026: the link answers
  • 26 September 2026: the link answers
28 files

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;
  • 26 scripts, each with its path and the digest of its content;
  • 18 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

The data underlying the results presented in the study are available from the Allen Institute Neuropixels dataset: https://portal.brain-map.org/circuits-behavior/visual-coding-neuropixels.

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

Versions

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

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 7 MeSH terms, 4 funders, 65 references.

Cite

This paper

Dyballa, L., Field, G. D., Stryker, M. P., & Zucker, S. W. (2026). Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds. PloS one, 21(9), e0356243. https://doi.org/10.1371/journal.pone.0356243

BibTeX

@article{dyballa2026functional,
author = {Dyballa, Luciano and Field, Greg D. and Stryker, Michael P. and Zucker, Steven W.},
title = {{Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds}},
journal = {PloS one},
year = {2026},
month = sep,
volume = {21},
number = {9},
pages = {e0356243},
publisher = {PLOS},
issn = {1932-6203},
doi = {10.1371/journal.pone.0356243},
url = {https://doi.org/10.1371/journal.pone.0356243},
pmid = {42752452},
pmcid = {PMC13585280}
}

RIS

TY - JOUR
AU - Dyballa, Luciano
AU - Field, Greg D.
AU - Stryker, Michael P.
AU - Zucker, Steven W.
TI - Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds
T2 - PloS one
J2 - PLoS One
PY - 2026
DA - 2026/09/17
VL - 21
IS - 9
SP - e0356243
SN - 1932-6203
PB - PLOS
DO - 10.1371/journal.pone.0356243
UR - https://doi.org/10.1371/journal.pone.0356243
LA - en
ER -

CSL-JSON

{
"id": "10.1371/journal.pone.0356243",
"type": "article-journal",
"title": "Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds",
"container-title": "PloS one",
"author": [
{
"family": "Dyballa",
"given": "Luciano"
},
{
"family": "Field",
"given": "Greg D."
},
{
"family": "Stryker",
"given": "Michael P."
},
{
"family": "Zucker",
"given": "Steven W."
}
],
"container-title-short": "PLoS One",
"volume": "21",
"issue": "9",
"page": "e0356243",
"DOI": "10.1371/journal.pone.0356243",
"PMID": "42752452",
"PMCID": "PMC13585280",
"ISSN": "1932-6203",
"publisher": "PLOS",
"URL": "https://doi.org/10.1371/journal.pone.0356243",
"language": "en",
"issued": {
"date-parts": [
[
2026,
9,
17
]
]
}
}

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.1126/sciadv.aed6417 [code]
Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior.
Journal: Science advances
In common: Plotly, scikit-learn, pandas, 3 other tools, mouse, 4 references
[2] doi:10.1186/s13059-026-04177-w [code]
Genomic sequence evolution underlying human neocortical interareal diversification.
Journal: Genome biology
In common: AllenSDK, TensorFlow, Plotly, 6 other tools, mouse
[3] doi:10.1371/journal.pcbi.1013138 [code]
Hierarchical recurrent temporal prediction as a model of the mammalian dorsal visual pathway.
Journal: PLoS computational biology
In common: AllenSDK, scikit-learn, pandas, 3 other tools, 3 references
[4] doi:10.1038/s41467-026-76939-w [code]
HIPPIE: a generative model for electrophysiological analysis across species, technologies, and modalities.
Journal: Nature communications
In common: AllenSDK, Pillow, scikit-learn, 4 other tools, mouse, 2 references
[5] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: Keras, TensorFlow, Plotly, 6 other tools, mouse
[6] doi:10.1126/sciadv.aed3650 [code]
Truthful visualizations for mass spectrometry imaging enable high-spatial-resolution interactive &lt;i&gt;m/z&lt;/i&gt; mapping and exploration.
Journal: Science advances
In common: Keras, TensorFlow, Pillow, 5 other tools, mouse, 1 reference
[7] doi:10.1523/eneuro.0023-26.2026 [code]
Real-Time Segmentation and Classification of Birdsong Syllables for Learning Experiments.
Journal: eNeuro
In common: Keras, TensorFlow, Plotly, 6 other tools
[8] doi:10.1186/s12880-026-02481-2 [code]
Deep learning-based neuroanatomical profiling reveals population-specific brain changes in multiple sclerosis: a large-scale Middle Eastern study.
Journal: BMC medical imaging
In common: Keras, TensorFlow, Plotly, 6 other tools
[9] doi:10.3389/fnsys.2026.1822122 [code]
Convergence-divergence circuits for multimodal integration of innate and learned opponent valences.
Journal: Frontiers in systems neuroscience
In common: Keras, TensorFlow, Plotly, 6 other tools
[10] doi:10.1038/s41597-025-05174-7 [code]
A large-scale MEG and EEG dataset for object recognition in naturalistic scenes
Journal: —
In common: Keras, TensorFlow, Plotly, 6 other tools

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.