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–324 · score 0.85 · Laplace Beltrami approximation, IAN weighted graph, Diffusion maps, Laplacian, outliers, embedding
- [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] § 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] § 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] § Results ↔ allen-data-analysis/read_spike_data-static-gratings.ipynb, lines 622–696 · score 0.71 · VISam, VISrl, VISal, VISpm, static gratings, phases
- [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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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
- # %%
- 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: 1 check, the latest on 26 September 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.1126/sciadv.aed6417 [code]
- Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior.Journal: Science advancesIn 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 biologyIn 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 biologyIn 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 communicationsIn 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 biologyIn 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 advancesIn 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: eNeuroIn 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 imagingIn 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 neuroscienceIn 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 scenesJournal: —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.
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 its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
