Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds.
The 18 matches
- [1] § Materials and methods › Data analysis › Similarity kernel and diffusion maps. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 314 to 324 · score 0.85 · Laplace Beltrami approximation, IAN weighted graph, Diffusion maps, Laplacian, outliers, embedding · Report a mismatch
- [2] § Materials and methods › Data analysis › Similarity kernel and diffusion maps. ↔ encoding-manifold/encoding-manifold.ipynb, lines 263 to 272 · score 0.84 · Laplace Beltrami approximation, IAN weighted graph, Diffusion maps, Laplacian, embedding, matrix · Report a mismatch
- [3] § Materials and methods › Data analysis › Additional metrics. ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 340 to 404 · score 0.79 · VISam, VISrl, drifting gratings, temporal frequencies, VISal, VISpm · Report a mismatch
- [4] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 340 to 404 · score 0.75 · VISam, VISrl, drifting grating, VISal, VISpm, ISI · Report a mismatch
- [5] § Results ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 622 to 696 · score 0.71 · VISam, VISrl, VISal, VISpm, static gratings, phases · Report a mismatch
- [6] § Materials and methods › Dataset ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 418 to 439 · score 0.69 · visual space prior, stimulus warping, monitor, mouse · Report a mismatch
- [7] § Materials and methods › Data analysis › Additional metrics. ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 458 to 534 · score 0.66 · VISam, VISrl, VISal, VISpm, metrics, spiking · Report a mismatch
- [8] § Materials and methods › Data analysis › Neural encoding space. ↔ CNNs/build-CNN-manifold-resnet50_block3.ipynb, lines 10 to 152 · score 0.63 · tensor components, neural matrices, split, lowest, reconstruction, error · Report a mismatch
- [9] § Materials and methods › Data analysis › Tensor decomposition. ↔ permuted-decomposition/matlab/run_permcp.m, lines 1 to 95 · score 0.62 · direct optimization, Tensor Toolbox, modified, permutation · Report a mismatch
- [10] § Materials and methods › Data analysis › Natural scene vs. static grating selectivity ratio. ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 622 to 696 · score 0.61 · Brain Observatory, static gratings, stimulus class, phase, orientation, spikes · Report a mismatch
- [11] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 281 to 391 · score 0.61 · Improved Sheather Jones, static gratings, bandwidth, algorithm, kernel, smoothed · Report a mismatch
- [12] § Materials and methods › Data preprocessing ↔ allen-data-analysis/read_spike_data-drifting gratings.ipynb, lines 262 to 318 · score 0.60 · Improved Sheather Jones, drifting gratings, bandwidth, algorithm, kernel, smoothed · Report a mismatch
- [13] § Materials and methods › Data analysis › Neural encoding space. ↔ CNNs/build-CNN-manifold-resnet50_block3.ipynb, lines 10 to 152 · score 0.59 · neural matrix, stimulus response, linear, vectors, product, encoding · Report a mismatch
- [14] § Materials and methods › Data analysis › Natural scene filtering. ↔ allen-data-analysis/filtering-natural-scenes/ffttools.py, lines 621 to 687 · score 0.57 · band pass, inner, radius, outer, Filtered, natural scenes · Report a mismatch
- [15] § Materials and methods › Data analysis › Neural encoding space. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 56 to 154 · score 0.56 · neural matrix, stimulus response, linear, vectors, encoding, neuron · Report a mismatch
- [16] § Materials and methods › Data analysis › Neural encoding space. ↔ permuted-decomposition/choosing-n-of-components.ipynb, lines 213 to 225 · score 0.56 · lowest reconstruction error, components, encoding · Report a mismatch
- [17] § Materials and methods › Data analysis › Natural scene filtering. ↔ allen-data-analysis/filtering-natural-scenes/filtering-natural-scenes.ipynb, lines 72 to 134 · score 0.55 · band pass, Filtered, cpd, cropped, natural scenes · Report a mismatch
- [18] § Materials and methods › Data analysis › Neural encoding space. ↔ allen-data-analysis/encoding-manifold-VISp.ipynb, lines 56 to 154 · score 0.53 · neural matrix, neural factors, reconstruction, magnitudes, component, tensor · 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
Jupyter notebook · 696 lines · 24 KB · BSD-2-Clause · 5 matches
- # %%
- import os
- # https://allensdk.readthedocs.io/en/latest/visual_coding_neuropixels.html#
- #https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_quickstart.html
- from ipywidgets import FloatProgress
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- import pickle
- from allensdk.brain_observatory.ecephys.ecephys_project_cache import EcephysProjectCache
- # %% [markdown]
- # #### ftns
- # %%
- #https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_session.html#Stimulus-presentations
- from itertools import product
- from itertools import product
- def get_spike_trains(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size, my_trial_len=None):
- trials_info = session.get_stimulus_table(STIM_CLASS)
- if trials_info.index.size == 0:
- return {}
- trial_len = round(np.mean(trials_info['duration'].values),2) #sec
- if my_trial_len is not None:
- assert my_trial_len <= trial_len
- trial_len = my_trial_len
- print('trial_len',trial_len)
- tot_len = trial_len + prestim_len
- time_bin_edges = np.linspace(-prestim_len, trial_len, int(tot_len/bin_size))
- NBINS = len(time_bin_edges)-1
- binarize = False
- if bin_size < .001:
- binarize = True
- print('NBINS',NBINS)
- # look at responses to a certain type of gratings
- stim_params = {pname:sorted(set(trials_info[pname].unique()).difference(['null'])) for pname in PARAM_NAMES}
- print(f'{STIM_CLASS} params:', stim_params)
- #get all param combinations
- paramvals_tuples = list(product(*[stim_params[pname] for pname in PARAM_NAMES]))
- all_trains = {}
- for ii,ui in enumerate(my_units):
- if ii % 5 == 0: print(ii,end=' ',flush=True)
- all_trains[ui] = {}
- for pvals_tup in paramvals_tuples:
- 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')
- sids = trials_info[row_filter].index.values
- Ntrials = len(sids)
- # print(f'{pvals_tup}, {Ntrials=}')
- #TODO compare speed against reading spk times directly: https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_optotagging.html
- spike_counts_da = session.presentationwise_spike_counts(
- bin_edges=time_bin_edges,
- stimulus_presentation_ids=sids,
- unit_ids=[ui],
- binarize=binarize
- )
- spike_counts_da = np.squeeze(spike_counts_da.values)
- # print(spike_counts_da.shape,spike_counts_da.max(),spike_counts_da.sum())
- #re-convert to spike times
- unit_trains = []
- for triali in range(Ntrials):
- train = np.flatnonzero(spike_counts_da[triali]).astype('float32')
- unit_trains.append(train * bin_size * 1000) #convert bin number to time (ms)
- all_trains[ui][pvals_tup] = unit_trains
- print()
- return all_trains
- def get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size):
- trials_info = session.get_stimulus_table(STIM_CLASS)
- if trials_info.index.size == 0:
- return {}
- trial_len = round(np.mean(trials_info['duration'].values),2) #sec
- print('trial_len',trial_len)
- tot_len = trial_len + prestim_len
- time_bin_edges = np.linspace(-prestim_len, trial_len, int(tot_len/bin_size))
- NBINS = len(time_bin_edges)-1
- binarize = False
- if bin_size < .001:
- binarize = True
- print('NBINS',NBINS)
- # look at responses to a certain type of gratings
- stim_params = {pname:sorted(set(trials_info[pname].unique()).difference(['null'])) for pname in PARAM_NAMES}
- print(f'{STIM_CLASS} params:', stim_params)
- #get all param combinations
- paramvals_tuples = list(product(*[stim_params[pname] for pname in PARAM_NAMES]))
- pref_phases = cache.get_unit_analysis_metrics_for_session(session_id)['pref_phase_sg']
- all_trains = {}
- for ii,ui in enumerate(my_units):
- print(ii,end=' ')
- all_trains[ui] = {}
- for pvals_tup in paramvals_tuples:
- #append this unit's pref phase to the stimulus params tuple
- assert ui in pref_phases.index
- pvals_tup_with_phase = pvals_tup + (str(pref_phases.loc[ui]),)#must convert phase to str (!?)
- PARAM_NAMES_with_phase = PARAM_NAMES + ['phase']
- row_filter = np.prod(np.stack(
- [trials_info[pname].values == pval for pname,pval in \
- zip(PARAM_NAMES_with_phase,pvals_tup_with_phase)],axis=0),axis=0).astype('bool')
- sids = trials_info[row_filter].index.values
- Ntrials = len(sids)
- # print(f'{pvals_tup}, {Ntrials=}')
- spike_counts_da = session.presentationwise_spike_counts(
- bin_edges=time_bin_edges,
- stimulus_presentation_ids=sids,
- unit_ids=[ui],
- binarize=binarize
- )
- spike_counts_da = np.squeeze(spike_counts_da.values)
- # print(spike_counts_da.shape,spike_counts_da.max(),spike_counts_da.sum())
- #re-convert to spike times
- unit_trains = []
- for triali in range(Ntrials):
- train = np.flatnonzero(spike_counts_da[triali]).astype('float32')
- unit_trains.append(train * bin_size * 1000) #convert bin number to time (ms)
- all_trains[ui][pvals_tup] = unit_trains
- print()
- return all_trains
- def get_train_dicts(all_trains_uid, tf, dirs, trial_len_ms, prestim_len_ms):
- trials_traindict = {}
- ISI_Nspks = {}
- full_traindict = {}
- for d in dirs:
- ptup = (tf, d,)
- assert ptup in all_trains_uid
- ISI_Nspks[d] = []
- new_trains = []
- for train in all_trains_uid[ptup]:
- if len(train) == 0:
- new_trains.append(np.array([]))
- ISI_Nspks[d].append(0)
- continue
- assert max(train) < trial_len_ms + prestim_len_ms
- new_train = train - prestim_len_ms
- new_trains.append(new_train[new_train >= 0])
- ISI_Nspks[d].append(new_train[new_train < 0].size)
- trials_traindict[d] = new_trains
- ISI_Nspks[d] = np.asarray(ISI_Nspks[d])
- full_traindict[d] = all_trains_uid[ptup]
- return full_traindict, trials_traindict, ISI_Nspks
- # %%
- import scipy as sp
- import warnings
- def computeResponseStats(traindict, ISI_Nspks, stats_ISI_len, trial_len, verbose=False):
- """Runs statistical tests to compare firing rates between the ISI and a given stimulus,
- for any period within the stimulus trial with the same length as the ISI.
- It performs two comparisons against the ISI FR: one using the maximum FR found for that
- interval length across stimulus trials; and another using the minimum FR.
- The following one-sided tests are run:
- Mann-Whitney U
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mannwhitneyu.html
- and Wilcoxon:
- https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wilcoxon.html
- ----------------
- Arguments:
- traindict: dict, {stimulus_direction: list of spike time arrays (ms), one per trial}
- ISI_Nspks: dict, {stimulus_direction: list of spike counts, one per trial}
- stats_ISI_len: float, length of the ISI interval, in secs, used to compute ISI_Nspks
- trial_len: float, total length of each trial, in secs
- ----------------
- Returns:
- stats_results: dict, for each of 'min-interval' and 'max-interval', contains a dict
- containing, for each stimulus direction, the p-values found for each tests, as well
- as the FRs for the stimulus and the ISI
- """
- mydirs = traindict.keys()
- stats_results = {}
- interval_Nspks_for_stats = {'min':{}, 'max':{}}
- for d in mydirs:
- #combine all trains
- data = np.concatenate(traindict[d])
- counts,bins = np.histogram(data,np.arange(0,trial_len+stats_ISI_len,stats_ISI_len))
- maxfr = -1
- minfr = np.inf
- for i in range(0,counts.size):
- fr = counts[i]
- if fr > maxfr:
- maxi = i
- maxfr = fr
- if fr < minfr:
- mini = i
- minfr = fr
- for interval_type,i_,fr_ in [('min',mini,minfr), ('max',maxi,maxfr)]:
- interval_Nspks_for_stats[interval_type][d] = []
- for train in traindict[d]:
- spks_within_interval = train[(train >= stats_ISI_len*i_) & (train < stats_ISI_len*(i_+1))]
- interval_Nspks_for_stats[interval_type][d].append( spks_within_interval.size )
- interval_Nspks_for_stats[interval_type][d] = np.array(interval_Nspks_for_stats[interval_type][d])
- # print(d,interval_type,interval_Nspks_for_stats[interval_type][d])
- assert len(interval_Nspks_for_stats[interval_type][d]) == len(traindict[d])
- assert interval_Nspks_for_stats[interval_type][d].sum() == fr_
- for trial_Nspks_for_stats_,pval_type,alternative in [
- (interval_Nspks_for_stats['max'],'maxinterval-pval','greater'),
- (interval_Nspks_for_stats['min'],'mininterval-pval','less')]:
- stats_results[pval_type] = {}
- for d in mydirs:
- dir_grayspks = ISI_Nspks[d]
- dir_stimspks = trial_Nspks_for_stats_[d]
- assert len(dir_stimspks) == len(dir_grayspks)
- if len(dir_stimspks) * len(dir_grayspks) == 0:
- stats_results[pval_type][d] = (np.inf,0,0)
- stats_results[pval_type][d] = {'MANNWHITNEY':np.inf,'WILCOXON':np.inf,'isiFR':0,'stimFR':0}
- if verbose: print(f'{d} no spikes')
- continue
- try:
- with warnings.catch_warnings():
- warnings.simplefilter("ignore",category=RuntimeWarning)
- _, pval = sp.stats.mannwhitneyu(dir_stimspks, dir_grayspks, alternative=alternative)
- except:
- pval = np.inf
- try:
- with warnings.catch_warnings():
- warnings.simplefilter("ignore",category=RuntimeWarning)
- warnings.simplefilter("ignore",category=UserWarning)
- _, wpval = sp.stats.wilcoxon(dir_stimspks, dir_grayspks, alternative=alternative)
- except:
- wpval = np.inf
- stats_results[pval_type][d] = {'MANNWHITNEY':pval,'WILCOXON':wpval,'isiFR':dir_grayspks.mean()/stats_ISI_len,'stimFR':dir_stimspks.mean()/stats_ISI_len}
- 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}')
- return stats_results
- # %%
- import warnings
- from KDEpy import FFTKDE
- def getResponseCurve(train_dict, total_trial_len, bw=None, samp_interval=1, MINBW=10, MAXBW=50):
- """Computes smooth trial-averaged response to a stim in all directions from spike trains
- using a kernel density estimator."""
- ts = np.arange(0,total_trial_len+samp_interval,samp_interval)
- x_ts = .5*(ts[:-1]+ts[1:]) #sample at the midpoints between sampling intervals
- all_ISJs = []
- fftkde = None
- if bw is None:
- # if no pre-specified kernel bandwidth,
- # auto-estimate within range [MINBW, MAXBW]
- bw, fftkde = fitSmoothingKernelBandwidth(train_dict, total_trial_len)
- if bw is not None:
- bw = max(min(MAXBW,bw),MINBW)
- fftkde.bw = bw
- else:
- bw = MAXBW
- for di,d in enumerate(sorted(train_dict)):
- full_train = []
- for triali,train in enumerate(train_dict[d]):
- if train.size == 0: continue
- assert max(train) < total_trial_len
- full_train += list(train)
- data = np.array(full_train)
- data.sort()
- n = data.size
- if fftkde is None:
- fftkde = FFTKDE(kernel='gaussian', bw=bw) #initialize kernel density estimator
- try:
- fftkde = fftkde.fit(data)
- #extend sampling one unit before and after trial
- ext_x_ts = np.r_[x_ts[0]-samp_interval,x_ts,x_ts[-1]+samp_interval]
- #then crop after applying the kernel
- y = fftkde.evaluate(ext_x_ts)[1:-1]
- except:
- #error occurs in the rare cases when there are zero spikes. print out to double-check
- # print(f'fftkde failed: {n} data points')
- y = np.zeros_like(x_ts)
- all_ISJs.append(y*n*1000/len(train_dict[d])) #convert density to spks/sec
- return np.array(all_ISJs), x_ts
- def fitSmoothingKernelBandwidth(full_traindict, total_trial_len):
- """Fits a spike smoothing kernel to spike train data using
- the improved Sheather-Jones (ISJ) algorithm:
- Z. I. Botev, J. F. Grotowski, and D. P. Kroese.
- “Kernel density estimation via diffusion.”
- Annals of Statistics, Volume 38, Number 5, pp. 2916-2957, 2010.
- https://arxiv.org/pdf/1011.2602.pdf
- (see https://kdepy.readthedocs.io/en/latest/index.html
- for more information on this implementation)
- ---------------
- Arguments:
- full_traindict: dict, {stimulus_direction: list of spike time arrays, one per trial}
- The bandwidth is computed for the stimulus direction that elicited the most spikes.
- total_trial_len: float or int, total length of a trial used for the trains; must
- use the same time unit as the spike times in `full_traindict`
- ---------------
- Returns:
- opt_bw: float, optimal bandwidth found
- fftkde: object, the fitted fftkde object, to be reused when evaluating the kernel
- """
- # 1) use stimulus direction with max n of spks to estimate optimal bandwidth
- maxn = -1
- for d, trains in full_traindict.items():
- data = np.concatenate(trains)
- assert max(data) < total_trial_len
- data.sort()
- n = data.size
- if n > maxn:
- maxn = n
- bestd = d
- data = np.concatenate(full_traindict[bestd])
- # 2) fit bw
- fftkde = FFTKDE(kernel='gaussian', bw='ISJ')
- try:
- with warnings.catch_warnings():
- warnings.simplefilter("ignore",category=RuntimeWarning)
- fftkde = fftkde.fit(data)
- opt_bw = fftkde.bw
- except:
- #print(f'fftkde failed: {n} data points')
- opt_bw = None
- fftkde = None
- return opt_bw, fftkde #return fftkde object as well, to avoid refitting
- # %% [markdown]
- # #### load cache
- # %%
- # Example cache directory path, it determines where downloaded data will be stored
- output_dir = 'ecephys_cache_dir/'
- # this path determines where downloaded data will be stored
- manifest_path = os.path.join(output_dir, "manifest.json")
- cache = EcephysProjectCache.from_warehouse(manifest=manifest_path)
- print(cache.get_all_session_types())
- # %%
- sessions = cache.get_session_table()
- brain_observatory_type_sessions = sessions[sessions["session_type"] == "brain_observatory_1.1"]
- print(len(brain_observatory_type_sessions))
- brain_observatory_type_sessions.head()
- # %%
- from glob import glob
- my_session_ids = [int(dirname.split('session_')[1]) for dirname in glob('ecephys_cache_dir/session_*')]
- print(my_session_ids)
- # %% [markdown]
- # ### get gratings imgs
- # %%
- from allensdk.brain_observatory.stimulus_info import get_spatial_grating
- #aspect = 1920/1200
- aspect = 1174/918 #same as nat scene
- # 21.93" wide monitor positioned at 15 cm away from the mouse's right eye and
- # spanned 120° x 95° of visual space prior to stimulus warping
- #orig_px_per_deg = 1920/120 #tot_pixels/tot_degs
- height = 918//2
- width = aspect * height
- grat_imgs = []
- for cycle_per_deg in [.02, .04, .08, 0.16, 0.32]:
- for ori in [0,30,60,90,120,150]:
- for phase in [0, 0.25, 0.5, 0.75]:
- tot_cycles = cycle_per_deg * 120# 1920/px_per_deg#tot_pixels/px_per_deg
- pix_per_cycle = width/tot_cycles
- grat_imgs.append( get_spatial_grating(height=height, aspect_ratio=aspect, ori=ori, pix_per_cycle=pix_per_cycle, phase=phase) )
- # %%
- f, axes = plt.subplots(11,11,figsize=(7.5*aspect,7.5))
- shuffled_ix = np.random.choice(range(len(grat_imgs)), len(grat_imgs), False)
- for i in range(len(grat_imgs)):
- ax = axes.ravel()[i]
- image = grat_imgs[shuffled_ix[i]]
- ax.imshow(image, cmap=plt.cm.gray)
- ax.set(xticks=[],yticks=[])
- for i in range(len(grat_imgs),11**2):
- ax = axes.ravel()[i]
- ax.axis('off')
- plt.subplots_adjust(wspace=0.2, hspace=0.2)
- plt.show()
- # %% [markdown]
- # ### collect trial N spks
- # %%
- ### COLLECTING SPIKE COUNTS -- ALL PHASES
- AREAs = ['VISal','VISrl','VISam','VISpm', 'VISp', 'VISl']
- datadir = 'data'
- STIM_CLASS = 'static_gratings'
- PARAM_NAMES = ['spatial_frequency', 'orientation', 'phase']
- prestim_len = 0 #sec
- bin_size = .0005 #sec
- for session_i in range(brain_observatory_type_sessions.index.values.size):
- session_id = brain_observatory_type_sessions.index.values[session_i]
- print(f'\n* {session_i}: session_id',session_id,flush=True)
- skip = True
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_allPhases'
- if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
- print(AREA,'saved previously.')
- else:
- skip = False
- break
- if skip:
- print('skipping...')
- continue
- session = cache.get_session_data(session_id)
- if STIM_CLASS not in session.stimulus_names:
- print(f'{STIM_CLASS} not found.')
- continue
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_allPhases'
- if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
- print(AREA,'saved previously.')
- continue
- my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
- Nunits = len(my_units)
- print(AREA,'Nunits',Nunits)
- if Nunits == 0:
- continue
- pref_phases = cache.get_unit_analysis_metrics_for_session(session_id)['pref_phase_sg']
- #compute for all cells
- all_trains = get_spike_trains(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
- if not all_trains:
- print('0 trains')
- continue
- uis = sorted(all_trains)
- stims = sorted(all_trains[uis[0]])
- stim_ntrials = [len(all_trains[uis[0]][si]) for si in stims]
- X = None
- prefPhases = []
- for ii,ui in enumerate(uis):
- print(ii,end=' ',flush=True)
- prefPhase = pref_phases.loc[ui]
- all_frs = []
- for si in stims:
- all_frs += list(map(len,all_trains[ui][si]))
- if X is None:
- X = np.asarray(all_frs)[None,:]
- else:
- X = np.concatenate([X,np.asarray(all_frs)[None,:]])
- prefPhases.append(prefPhase)
- print()
- cellData = {'uis':uis, 'stims':stims, 'stim_ntrials':stim_ntrials, 'prefPhases':prefPhases}
- with open(f'{datadir}/{fname}_trial_info.pkl', 'wb') as f:
- pickle.dump(cellData, f)
- np.save(f'{datadir}/{fname}_trial_data.npy',X)
- print(fname,'saved.')
- # %%
- ### COLLECTING SPK COUNTS -- PREF PHASE
- AREAs = ['VISp','VISl','VISal','VISrl','VISam','VISpm']
- datadir = 'data'
- STIM_CLASS = 'static_gratings'
- PARAM_NAMES = ['spatial_frequency', 'orientation']
- prestim_len = 0 #sec
- bin_size = .0005 #sec
- trial_len_ms = 250
- prestim_len_ms = prestim_len * 1000
- dirs = [0.0, 30.0, 60.0, 90.0, 120.0, 150.0]
- bw = 25
- samp_interval = 10
- NDIRS = 6
- SFs = [0.02, 0.04, 0.08, 0.16, 0.32]
- for session_i in range(brain_observatory_type_sessions.index.values.size):
- session_id = brain_observatory_type_sessions.index.values[session_i]
- print(f'\n* {session_i}: session_id',session_id,flush=True)
- skip = True
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_prefPhase'
- if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
- print(AREA,'saved previously.')
- else:
- skip = False
- break
- if skip:
- print('skipping...')
- continue
- session = cache.get_session_data(session_id)
- if STIM_CLASS not in session.stimulus_names:
- print(f'{STIM_CLASS} not found.')
- continue
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_prefPhase'
- if os.path.isfile(f'{datadir}/{fname}_trial_info.pkl'):
- print(AREA,'saved previously.')
- continue
- my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
- Nunits = len(my_units)
- print(AREA,'Nunits',Nunits)
- if Nunits == 0:
- continue
- #compute for all cells
- all_trains = get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
- if not all_trains:
- print('0 trains')
- continue
- uis = sorted(all_trains)
- stims = sorted(product(*[stim_params[pname] for pname in PARAM_NAMES]))
- X = []
- prefPhases = []
- stim_ntrials = []
- for ii,ui in enumerate(uis):
- print(ii,end=' ',flush=True)
- prefPhase = list(all_trains[ui].keys())[0][-1]
- assert np.all(np.array(list(all_trains[ui].keys()))[:,2] == prefPhase)
- stim_ntrials.append([len(all_trains[ui][si+(prefPhase,)]) for si in stims])
- all_frs = []
- for si in stims:
- all_frs += list(map(len,all_trains[ui][si+(prefPhase,)]))
- X.append(np.asarray(all_frs))
- prefPhases.append(prefPhase)
- print()
- cellData = {'uis':uis, 'prefPhases':prefPhases, 'stims':stims, 'stim_ntrials':stim_ntrials}
- with open(f'{datadir}/{fname}_trial_info.pkl', 'wb') as f:
- pickle.dump(cellData, f)
- np.save(f'{datadir}/{fname}_trial_data.npy',X)
- print(fname,'saved.')
- # %% [markdown]
- # ### collect PSTHs
- # %%
- AREAs = ['VISl','VISal','VISrl','VISam','VISpm','VISp']
- datadir = 'data'
- STIM_CLASS = 'static_gratings'
- PARAM_NAMES = ['spatial_frequency', 'orientation']
- prestim_len = 0.05 #sec
- bin_size = .0005 #sec
- trial_len_ms = 300
- prestim_len_ms = prestim_len * 1000
- dirs = [0.0, 30.0, 60.0, 90.0, 120.0, 150.0]
- bw = 10
- samp_interval = 5
- NDIRS = 6
- SFs = [0.02, 0.04, 0.08, 0.16, 0.32]
- for session_i in range(brain_observatory_type_sessions.index.values.size):
- session_id = brain_observatory_type_sessions.index.values[session_i]
- print(f'\n* {session_i}: session_id',session_id)
- skip = True
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_bw{bw}'
- if os.path.isfile(f'{datadir}/{fname}.pkl'):
- print(AREA,'saved previously.')
- else:
- skip = False
- break
- if skip:
- print('skipping...')
- continue
- session = cache.get_session_data(session_id)
- if STIM_CLASS not in session.stimulus_names:
- print(f'{STIM_CLASS} not found.')
- continue
- for AREA in AREAs:
- fname = f's{session_id}_{AREA}_{STIM_CLASS}_bw{bw}'
- if os.path.isfile(f'{datadir}/{fname}.pkl'):
- print(AREA,'saved previously.')
- continue
- my_units = session.units[session.units["ecephys_structure_acronym"].values == AREA].index.values
- Nunits = len(my_units)
- print(AREA,'Nunits',Nunits)
- if Nunits == 0:
- continue
- #compute for all cells
- all_trains = get_spike_trains_pref_phase(my_units, STIM_CLASS, PARAM_NAMES, prestim_len, bin_size)
- if not all_trains:
- print('0 trains')
- continue
- allData = {}
- for uid in my_units:
- allData[uid] = {}
- for tfi,tf in enumerate(SFs):
- allData[uid][tf] = {}
- full_traindict, trials_traindict, ISI_Nspks = get_train_dicts(all_trains[uid], tf, dirs, trial_len_ms, prestim_len_ms)
- all_ISJs, x_ts = getResponseCurve(full_traindict, trial_len_ms+prestim_len_ms, bw, samp_interval)
- allData[uid][tf]['psts'] = all_ISJs
- if prestim_len_ms > 0:
- allData[uid][tf]['stats'] = computeResponseStats(trials_traindict, ISI_Nspks, prestim_len_ms, trial_len_ms)
- allData[uid][tf]['n_trials'] = [full_traindict[d] for d in dirs]
- with open(f'{datadir}/{fname}.pkl', 'wb') as f:
- pickle.dump(allData, f)
- print(fname,'saved.')
read_spike_data-static-gratings.ipynb at commit ddccf17, under BSD-2-Clause · at the source
Overview
- School of Science and Technology, IE University, Madrid, Spain
- Jules Stein Eye Institute, Department of Ophthalmology, David Geffen School of Medicine, University of California, Los Angeles, California, United States of America
- Department of Physiology, University of California, San Francisco, California, United States of America
- Kavli Institute for Fundamental Neuroscience, University of California, San Francisco, California, United States of America
- Department of Computer Science, Yale University, New Haven, Connecticut, United States of America
- Department of Biomedical Engineering, Yale University, New Haven, Connecticut, United States of America
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/
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
ddccf17c8dc1e3d129624c05285dd5ec24a97e41, 24 October 2025Availability: 2 checks, the latest on 30 October 2026: the link answers
- 30 October 2026: the link answers
- 26 September 2026: the link answers
28 files
- CNNs/
build-CNN-manifold-resne , Jupyter, 822 lines, 2 matchest50_block3.ipynb - CNNs/
classification-examples- , Jupyter, 72 linesResNet50.ipynb - CNNs/
sampling-active-neurons. , Jupyter, 365 linesipynb - CNNs/
test-set-activities-ResN , Jupyter, 263 lineset50.ipynb - CNNs/
utils.py , Python, 289 lines - allen-data-analysis/
build-tensor-drifting-gr , Jupyter, 328 linesatings.ipynb - allen-data-analysis/
encoding-manifold-VISp.i , Jupyter, 333 lines, 3 matchespynb - allen-data-analysis/
filtering-natural-scenes , Python, 688 lines, 1 match/ ffttools.py - allen-data-analysis/
filtering-natural-scenes , Jupyter, 199 lines, 1 match/ filtering-natural-scenes .ipynb - allen-data-analysis/
load-factorization-VISp- , Jupyter, 256 linesdg.ipynb - allen-data-analysis/
load-factorization-VISp- , Jupyter, 258 linessg.ipynb - allen-data-analysis/
read_spike_data-drifting , Jupyter, 488 lines, 3 matchesgratings.ipynb - allen-data-analysis/
read_spike_data-natural_ , Jupyter, 528 linesscenes.ipynb - allen-data-analysis/
read_spike_data-static-g , Jupyter, 696 lines, 5 matchesratings.ipynb - allen-data-analysis/
waveform-classification. , Jupyter, 144 linesipynb - creating-the-tensor/
creating-the-tensor.ipyn , Jupyter, 302 linesb - creating-the-tensor/
utils.py , Python, 223 lines - encoding-manifold/
encoding-manifold.ipynb , Jupyter, 289 lines, 1 match - encoding-manifold/
utils.py , Python, 61 lines - permuted-decomposition/
choosing-n-of-components , Jupyter, 225 lines, 1 match.ipynb - permuted-decomposition/
matlab/ , MATLAB, 220 linesperm_cp_opt.m - permuted-decomposition/
matlab/ , MATLAB, 164 linesperm_tt_cp_fg.m - permuted-decomposition/
matlab/ , MATLAB, 62 linesperm_tt_cp_fun.m - permuted-decomposition/
matlab/ , MATLAB, 106 lines, 1 matchrun_permcp.m - permuted-decomposition/
plotting-components.ipyn , Jupyter, 123 linesb - permuted-decomposition/
utils.py , Python, 61 lines - LICENSE, License, 24 lines
- README.md, Text, 42 lines
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
- portal.brain-map.org/
circuits-behavior/ , at Allen Brain Map; found in “Data Availability”visual-coding-neuropixel s
Data Availability
The data underlying the results presented in the study are available from the Allen Institute Neuropixels dataset: https://
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://
BibTeX
@article{dyballa2026func
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/
url = {https://
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/
VL - 21
IS - 9
SP - e0356243
SN - 1932-6203
PB - PLOS
DO - 10.1371/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1371/
"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":
"volume": "21",
"issue": "9",
"page": "e0356243",
"DOI": "10.1371/
"PMID": "42752452",
"PMCID": "PMC13585280",
"ISSN": "1932-6203",
"publisher": "PLOS",
"URL": "https://
"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.1073/pnas.2317773121 [code]
- Population encoding of stimulus features along the visual hierarchy.Journal: Proceedings of the National Academy of Sciences of the United States of AmericaIn common: AllenSDK, Keras, Plotly, 7 other tools, mouse, 9 references, 3 authors
- [2] doi:10.1371/journal.pcbi.1012297 [code]
- Decoding dynamic visual scenes across the brain hierarchy.Journal: PLoS computational biologyIn common: AllenSDK, Keras, TensorFlow, 4 other tools, portal.brain-map.org/circuits-behavior/visual-coding-neuropixels, 4 references
- [3] doi:10.1038/s41597-025-06115-0 [code]
- Mouse Hippocampal Sharp-Wave Ripple Dataset Curated From Public Neuropixels Datasets.Journal: Scientific dataIn common: AllenSDK, Plotly, scikit-learn, 4 other tools, portal.brain-map.org/circuits-behavior/visual-coding-neuropixels, mouse, 3 references
- [4] doi:10.1038/s41467-025-56733-w [code]
- Deciphering neuronal variability across states reveals dynamic sensory encoding.Journal: Nature communicationsIn common: AllenSDK, Plotly, scikit-learn, 4 other tools, portal.brain-map.org/circuits-behavior/visual-coding-neuropixels, mouse, 3 references
- [5] doi:10.7554/elife.87736 [code]
- Running modulates primate and rodent visual cortex differently.Journal: eLifeIn common: AllenSDK, pandas, SciPy, 2 other tools, portal.brain-map.org/circuits-behavior/visual-coding-neuropixels, mouse, 4 references
- [6] doi:10.1016/j.isci.2025.114395 [code]
- Locomotion selectively enhances visual speed encoding in mouse medial higher visual areas.Journal: iScienceIn common: AllenSDK, pandas, SciPy, 1 other tool, mouse, 7 references
- [7] doi:10.1038/s41467-025-62069-2 [code]
- Temporal coding carries more stable cortical visual representations than firing rate over time.Journal: Nature communicationsIn common: Keras, TensorFlow, scikit-learn, 4 other tools, mouse, 6 references
- [8] doi:10.1038/s41467-022-29656-z [code]
- Diversity of spatiotemporal coding reveals specialized visual processing streams in the mouse cortex.Journal: Nature communicationsIn common: mouse, 10 references
- [9] doi:10.1038/s41467-025-58003-1 [code]
- Broadband visual stimuli improve neuronal representation and sensory perception.Journal: Nature communicationsIn common: pandas, SciPy, Matplotlib, 1 other tool, mouse, 8 references
- [10] doi:10.1371/journal.pcbi.1011506 [code]
- Mouse visual cortex as a limited resource system that self-learns an ecologically-general representation.Journal: PLoS computational biologyIn common: AllenSDK, Keras, Pillow, 5 other tools, mouse, 4 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 26 scripts, and 18 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:fcb613d051379b85…
Add the badge to the code's README
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 -->
[](https://openscicode.org/badge/doi/10.1371/journal.pone.0356243)
<!-- oscr-badge:end -->reStructuredText
.. oscr-badge:start
.. image:: https://openscicode.org/badge/doi/10.1371/journal.pone.0356243.svg
:target: https://openscicode.org/badge/doi/10.1371/journal.pone.0356243
:alt: OSCR
.. oscr-badge:endHTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1371/journal.pone.0356243"><img src="https://openscicode.org/badge/doi/10.1371/journal.pone.0356243.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".
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)
Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
