Designer indicators for two-photon recording of subthreshold voltage dynamics.
The 5 matches
- [1] § Methods › JEDI3hyp optical tracking of brain state in the mouse cortex › Pupil phase binning: Δ2–10-Hz Hilbert transform amplitude ↔ vip-som/pupil_phase_binning.py, lines 338–392 · score 0.86 · Hamming bandpass filter, Hilbert transform, phase binned, 0.1–1 Hz, pupil phase, 2–10
- [2] § Methods › JEDI3hyp optical tracking of brain state in the mouse cortex › Pupil phase binning: fluorescence response amplitude ↔ vip-som/pupil_phase_binning.py, lines 35–63 · score 0.68 · pupil phase, low pass filter, fluorescence traces, Hamming, AOD, amplitude
- [3] § Methods › JEDI3hyp optical tracking of brain state in the mouse cortex › Pupil phase binning: fluorescence response amplitude ↔ vip-som/pupil_phase_binning.py, lines 66–139 · score 0.59 · phase binned fluorescence, pupil period, selective, constriction, dilation, amplitude
- [4] § Methods › JEDI3hyp optical tracking of brain state in the mouse cortex › Preprocessing of voltage imaging data ↔ jedi3_reso_meso.py, lines 65–153 · score 0.58 · segmented masks, motion correction, Somatodendritic, somatic, field, scans
- [5] § Methods › JEDI3hyp optical tracking of brain state in the mouse cortex › Pupil phase binning: fluorescence response amplitude ↔ vip-som/pupil_phase_binning.py, lines 463–480 · score 0.57 · constriction medians, constriction period, binned, pupil, phase, dilation
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 626 lines · 34 KB · no license · 4 matches
- import psth_functions
- from pipeline import aod
- import shared
- import numpy as np
- import scipy.stats as stats
- import datajoint as dj
- import scipy.signal as signal
- import clocktools as ct
- import warnings
- with warnings.catch_warnings():
- warnings.simplefilter('ignore')
- pupil = dj.create_virtual_module(module_name = 'pupil', schema_name = 'pipeline_eye')
- treadmill = dj.create_virtual_module(module_name = 'treadmill', schema_name = 'pipeline_treadmill')
- # pupilphasebins5 has 20s rolling median data
- schema = dj.schema('rob_pupilphasebins5', locals(), create_tables=True)
- @schema
- class SomaArea(dj.Computed):
- definition = """ # computes area of somata
- -> aod.Fluorescence.Trace
- ---
- area_um2 : float # Area in microns**2
- """
- def make(self, key):
- print(key)
- pixels = (aod.Segmentation.Mask & key).fetch1('pixels')
- n_pixels = len(pixels)
- um_h, px_h, um_w, px_w = (aod.ScanInfo.ROI & key).fetch1('um_height', 'px_height', 'um_width', 'px_width')
- area_um2 = um_h/px_h * um_w/px_w * n_pixels
- self.insert1({**key, 'area_um2': area_um2})
- @schema
- class NormedTraceStats(dj.Computed):
- definition = """ # statistics on normalized trace
- -> aod.Fluorescence.Trace
- ---
- cv : float # coefficient of variation
- tv : float # total variation
- """
- def make(self, key):
- fluorescence_filter_method = "5Hz Hamming Lowpass"
- print(key)
- ## Fetching pipe and basic scan information
- pipe = psth_functions.fetch_pipe(key)
- scan_times = psth_functions.get_fluorescence_times(key, pipe)
- fps = 1 / np.median(np.diff(scan_times))
- # Fetching fluorescence trace, making a copy to process separately into 2-10 Hz Hilbert transform amplitude. Original trace will be low-pass filtered & binned
- trace = -(pipe.Fluorescence.Trace & key).fetch1('trace')
- trace_copy = trace.copy()
- # Basic processing of fluorescence trace
- filter_table_row = (shared.FilterMethod & f'filter_method = "{fluorescence_filter_method}"')
- trace = filter_table_row.run_filter_with_renan(signal=trace, signal_freq=fps)
- trace = psth_functions.normalize_dFF(trace, fps)
- cv = stats.variation(trace)
- tv = sum(abs(np.diff(trace)))/((len(trace)-1)*(np.max(trace)-np.min(trace)))
- self.insert1({**key, 'cv': cv, 'tv': tv})
- @schema
- class PupilPhaseBinsBeta(dj.Computed):
- definition = """ #
- -> aod.Fluorescence.Trace
- -> shared.FilterMethod
- ---
- phase_bin_edges : blob # An arrray containing the pupil phase bin edges
- pupil_radius_avg : mediumblob # an array containing the averaged pupil phase aligned 0.1-1 Hz filtered pupil radius
- pupil_phase_avg : mediumblob # an array containing the averaged pupil phase aligned 0.1-1 Hz filtered pupil phase
- fluorescence_avg : mediumblob # an array containing the averaged pupil phase aligned filtered fluorescence
- hilbert_phase_avg : mediumblob # an array containing the averaged pupil phase aligned filtered (0.1-1 Hz) 2-10 Hz Hilbert
- treadmill_avg : mediumblob # an array containing the averaged pupil phase aligned treadmill trace
- mean_dilation_minus_constriction : float # mean of dilation period fluorescences minus constriction fluorescences
- median_dilation_minus_constriction : float # median of dilation period fluorescences minus constriction fluorescences
- preferred_direction=NULL : varchar(256) # preferred direction of averaged pupil-phase aligned binned fluorescence
- direction_selectivity_pvalue=NULL : varchar(256) # preferred direction of averaged pupil-phase aligned binned fluorescence
- preferred_phase=NULL : varchar(256) # preferred phase of averaged pupil-phase aligned binned fluorescence
- pupil_period_nums : int # Number of pupil periods in the PSTH
- """
- class PeriodFragments(dj.Part):
- definition = """#
- -> PupilPhaseBinsBeta
- period_idx : int # period_idx obtained from pupil.PupilPeriods.DilationConstriction table
- period_type : enum('dilation', 'constriction') # pupil period type
- ---
- pupil_radius_bin_avg : mediumblob # pupil radius binned from -pi to 0 for dilation periods (period_delta>0) or 0 to pi for constriction periods (period_delta<0)
- pupil_phase_bin_avg : mediumblob # pupil phase binned from -pi to 0 for dilation periods (period_delta>0) or 0 to pi for constriction periods (period_delta<0)
- fluorescence_bin_avg : mediumblob # trace fluorescence values within this pupil period *binned to pupil phase*
- hilbert_ampl_bin_avg : mediumblob # filtered (0.1-1 Hz) 2-10 Hz Hilbert transform amplitude values within this pupil period *binned to pupil phase*
- treadmill_bin_avg : mediumblob # filtered (0.1-1 Hz) 2-10 Hz Hilbert transform amplitude values within this pupil period *binned to pupil phase*
- pupil_radius_fragment : mediumblob # values of pupil_radius within this pupil periods *time*
- fluorescence_fragment : mediumblob # values of fluorescence_trace within this pupil periods *time*
- hilbert_phase_fragment : mediumblob # values of hilbert_phase within this pupil periods *time*
- treadmill_fragment : mediumblob # values of treadmill trace within this pupil periods *time*
- trace_mean : float # fluorescence trace mean within this pupil periods *time*
- trace_max : float # fluorescence trace max within this pupil periods *time*
- trace_min : float # fluorescence trace min within this pupil periods *time*
- is_running=NULL : boolean # boolean value declaring whether this is a running period #within this current make function all running periods are discarded. currently an arbirtrary attribute
- """
- #@property
- #def key_source(self):
- # return (mario_dendrites.Cell.Compartment & dj.Not("compartment_type='dead_pixels'"))
- # def directionality(key):
- # mask_keys = (mario_dendrites.Cell.Compartment & key & dj.Not('compartment_type="dead_pixels"')).fetch('KEY')
- # #mask_keys = (mario_dendrites.PupilPhaseBins & mask_keys).fetch('KEY')
- # for i, mask_key in enumerate(mask_keys):
- # print(mask_key)
- # if len((mario_dendrites.PupilPhaseBins.Binned & mask_key & 'pupil_period_type="dilation"').fetch('fluorescence_bins')) == 0:
- # break
- # dilation_fluorescence_avg = np.nanmean(list((mario_dendrites.PupilPhaseBins.Binned & mask_key & 'pupil_period_type="dilation"').fetch('fluorescence_bins')))
- # constriction_fluorescence_avg = np.nanmean(list((mario_dendrites.PupilPhaseBins.Binned & mask_key & 'pupil_period_type="constriction"').fetch('fluorescence_bins')))
- # print(dilation_fluorescence_avg, constriction_fluorescence_avg)
- # if dilation_fluorescence_avg > constriction_fluorescence_avg:
- # mod_directionality = 'positive'
- # elif dilation_fluorescence_avg < constriction_fluorescence_avg:
- # mod_directionality = 'negative'
- # mask_key['directionality'] = mod_directionality
- # mask_key['fluorescence_dilation_avg'] = dilation_fluorescence_avg
- # mask_key['fluorescence_constriction_avg'] = constriction_fluorescence_avg
- # mario_dendrites.PupilPhaseBins.ModulationDirectionality.insert1(mask_key, skip_duplicates=True)
- # #fluorescence_avg = psth_functions.baseline([fluorescence_avg])
- # #fluorescence_avg = fluorescence_avg[0]
- def _rolling_average(array, window_size):
- # Function to calculate moving average using numpy
- array = np.append(array, array[-window_size:])
- i = 0
- # Initialize an empty list to store moving averages
- moving_averages = []
- # Loop through the array t o
- #consider every window of size 3
- while i < len(array)-window_size:
- # Calculate the average of current window
- window_average = np.sum(array[
- i:i+window_size]) / window_size
- # Store the average of current
- # window in moving average list
- moving_averages.append(window_average)
- # Shift window to right by one position
- i += 1
- return moving_averages
- # def plot(key, attribute, moving_avg):
- # file_text = f"{key['animal_id']-key['session']-key['scan_idx']}_pupil_phased_fluorescence.png"
- # mask_keys = (mario_dendrites.Cell.Compartment & key & dj.Not('compartment_type="dead_pixels"')).fetch('KEY')
- # bin_edges = (PupilPhaseBinsBeta & mask_keys[0]).fetch1('phase_bin_edges')
- # pupil_radius_avg = (PupilPhaseBinsBeta & mask_keys[0]).fetch1('pupil_radius_avg')
- # #print(np.shape(bin_edges))
- # pupil_periods_radius_avg_fragments = (PupilPhaseBinsBeta.PeriodFragments & mask_keys[0]).fetch('pupil_radius_bin_avg')
- # pupil_periods_radius_sem = stats.sem(list(pupil_periods_radius_avg_fragments), nan_policy='omit')
- # #print(np.shape(pupil_periods_radius_sem))
- # if len(mask_keys) == 1:
- # fig, axes = plt.subplots(2, 1, figsize=(9, 9), facecolor="white", subplot_kw=dict(box_aspect=0.75), sharex=True)
- # axis_fontsize = 18
- # scale_fontsize = 10
- # subplot_fontsize = 10
- # xtick_label_size=18
- # ytick_label_size=14
- # if len(mask_keys) > 1:
- # fig, axes = plt.subplots(len(mask_keys)+1, 1, figsize=(10, 30), facecolor="white", subplot_kw=dict(box_aspect=0.75), sharex=True)
- # axis_fontsize = 18
- # subplot_fontsize = 16
- # scale_fontsize = 12
- # xtick_label_size=18
- # ytick_label_size=14
- # axes[0].set_title('Average Filtered Pupil Radius Binned by Phase', fontsize=subplot_fontsize)
- # axes[0].set_ylabel("Change in Pupil Radius", fontsize=axis_fontsize)
- # axes[0].set_xticks([])
- # axes[0].set_yticks([])
- # axes[0].axvline(0,color='k')
- # #axes[0].axhline(0,color='0.8')
- # print(len(bin_edges), len(pupil_radius_avg))
- # axes[0].plot(
- # bin_edges[:int(len(bin_edges)/2)+1],
- # pupil_radius_avg[:int(len(bin_edges)/2)+1],
- # color="red",
- # label="dilation",
- # )
- # axes[0].fill_between(
- # bin_edges[:int(len(bin_edges)/2)+1],
- # pupil_radius_avg[:int(len(bin_edges)/2)+1]
- # - pupil_periods_radius_sem[:int(len(bin_edges)/2)+1],
- # pupil_radius_avg[:int(len(bin_edges)/2)+1]
- # + pupil_periods_radius_sem[:int(len(bin_edges)/2)+1],
- # color="red",
- # alpha=0.1,
- # )
- # axes[0].plot(
- # bin_edges[int(len(bin_edges)/2):],
- # pupil_radius_avg[int(len(bin_edges)/2):],
- # color="blue",
- # label="constriction",
- # )
- # axes[0].fill_between(
- # bin_edges[int(len(bin_edges)/2):],
- # pupil_radius_avg[int(len(bin_edges)/2):]
- # - pupil_periods_radius_sem[int(len(bin_edges)/2):],
- # pupil_radius_avg[int(len(bin_edges)/2):]
- # + pupil_periods_radius_sem[int(len(bin_edges)/2):],
- # color="blue",
- # alpha=0.1,
- # )
- # for i, a in enumerate(axes.flatten()):
- # a.tick_params(axis='x', which = 'major', labelsize=xtick_label_size)
- # a.tick_params(axis='y', which = 'major', labelsize=ytick_label_size)
- # if i == 0:
- # a.legend(loc='upper right',
- # frameon=False,
- # fontsize=scale_fontsize
- # )
- # # elif i>0:
- # # a.legend(loc='upper right')
- # if attribute == 'hilbert_ampl':
- # attribute_type = 'hilbert_phase_avg'
- # bin_attribute_type = 'hilbert_ampl_bin_avg'
- # attribute_plot_title = '2-10 Hz Hilbert Amplitude'
- # y_axis_label = 'Δ 2-10 Hz Hilbert Amplitude'
- # baseline=False
- # if attribute == 'fluorescence':
- # attribute_type = 'fluorescence_avg'
- # bin_attribute_type = 'fluorescence_bin_avg'
- # attribute_plot_title = '5 Hz Lowpass Fluorescence'
- # y_axis_label = '"Fluorescence\n (F_norm)"'
- # baseline=True
- # axes[1].set_title(f'Average {attribute_plot_title}\n by Pupil Phase Bins', fontsize=subplot_fontsize)
- # for i, mask_key in enumerate(mask_keys):
- # compartment_type = (mario_dendrites.Cell.Compartment & mask_key).fetch1('compartment_type')
- # fluorescence_avg = (mario_dendrites.PupilPhaseBinsBeta & mask_key).fetch1(f'{attribute_type}')
- # fluorescence_fragments = (mario_dendrites.PupilPhaseBinsBeta.PeriodFragments & mask_key).fetch(f'{bin_attribute_type}')
- # ####
- # ####
- # fluorescence_sem = stats.sem(list(fluorescence_fragments), nan_policy='omit')
- # if moving_avg == False:
- # fluorescence_array = (mario_dendrites.PupilPhaseBinsBeta & mask_key).fetch1(f'{attribute_type}')
- # if moving_avg == 'sma': #simple moving average of window_size=3
- # fluorescence_array = (mario_dendrites.PupilPhaseBinsBeta & mask_key).fetch(f'{attribute_type}')
- # fluorescence_avg = np.array(mario_dendrites.PupilPhaseBinsBeta._rolling_average(fluorescence_avg, 3))
- # fluorescence_sem = np.array(mario_dendrites.PupilPhaseBinsBeta._rolling_average(fluorescence_sem, 3))
- # if baseline == True:
- # fluorescence_avg = fluorescence_avg-np.mean(fluorescence_avg)
- # ####
- # ####
- # if compartment_type == 'distal dendrite':
- # trace_color = 'forestgreen'
- # if compartment_type == 'apical dendrite':
- # trace_color = 'lightseagreen'
- # if compartment_type == 'soma':
- # trace_color = 'coral'
- # axes[i+1].set_ylabel(y_axis_label, fontsize=axis_fontsize)
- # axes[i+1].axvline(0,color='k')
- # if baseline == True or attribute == 'hilbert_ampl':
- # axes[i+1].axhline(0,color='0.8')
- # axes[i+1].set_xticks([])
- # axes[i+1].plot(bin_edges, fluorescence_avg,
- # color=trace_color,
- # label=compartment_type)
- # axes[i+1].legend(loc='lower right',
- # frameon=False,
- # fontsize=scale_fontsize
- # )
- # axes[i+1].fill_between(bin_edges,
- # fluorescence_avg
- # - fluorescence_sem,
- # fluorescence_avg
- # + fluorescence_sem,
- # color=trace_color,
- # alpha=0.1,
- # )
- # axes[i+1].xaxis.set_major_locator(plt.MultipleLocator(np.pi/ 2))
- # axes[i+1].xaxis.set_major_formatter(plt.FuncFormatter(psth_functions.multiple_formatter()))
- # plt.xlim(-np.pi, np.pi)
- # #plt.legend()
- # plt.savefig('hi.png')
- # plt.show()
- # plt.close()
- def trace_preprocessing(key, fluorescence_filter_method):
- fluorescence_filter_method = "5Hz Hamming Lowpass"
- ## Fetching pipe and basic scan information
- pipe = psth_functions.fetch_pipe(key)
- scan_times = psth_functions.get_fluorescence_times(key, pipe)
- fps = 1 / np.median(np.diff(scan_times))
- # Fetching fluorescence trace, making a copy to process separately into 2-10 Hz Hilbert transform amplitude. Original trace will be low-pass filtered & binned
- trace = -(pipe.Fluorescence.Trace & key).fetch1('trace')
- trace_copy = trace.copy()
- # Basic processing of fluorescence trace
- filter_table_row = (shared.FilterMethod & f'filter_method = "{fluorescence_filter_method}"')
- trace = filter_table_row.run_filter_with_renan(signal=trace, signal_freq=fps)
- trace = psth_functions.normalize_dFF(trace, fps)
- # Basic processing of fluorescence trace into filtered 2-10 Hz Hilbert transform amplitude
- hilbert_filt_one = (shared.FilterMethod & 'filter_method = "2 - 10Hz Hamming Bandpass"')
- print(hilbert_filt_one)
- hilbert_trace = hilbert_filt_one.run_filter_with_renan(signal=trace_copy, signal_freq=fps)
- hilbert_trace = np.abs(signal.hilbert(hilbert_trace))
- #hilbert_filt_two = (shared.FilterMethod & 'filter_method = "0.1 - 1Hz Hamming Bandpass"')
- #hilbert_trace = hilbert_filt_two.run_filter_with_renan(signal=hilbert_trace, signal_freq=fps)
- #### behavior preprocessing
- radius, pupil_fps = (pupil.ProcessedPupil & key & 'pupil_method_id=1').fetch1('filtered_pupil_radius', 'pupil_sampling_rate')
- nan_filter_table_row = (shared.FilterMethod & 'filter_method = "NaN Filler"') # Simply filling in NaNs
- radius = nan_filter_table_row.run_filter(signal=radius, signal_freq=pupil_fps)
- #####
- #####
- low_bandpass_filter = (shared.FilterMethod & 'filter_method = "0.1 - 1Hz Hamming Bandpass"') # standard 0.1-1Hz Hamming Lowpass Filter
- radius = low_bandpass_filter.run_filter(signal=radius, signal_freq=pupil_fps)
- radius_time = (pupil.Eye & key).fetch1('eye_time')
- radius = psth_functions.interpolate_to_new_timestamps(radius_time, radius, scan_times)
- # computing the hilbert transform of filtered pupil radius and subsequently its phase
- radius_hilbert = signal.hilbert(radius)
- radius_phase = np.angle(radius_hilbert)
- #####
- #####
- # fetching basic treadmill velocity trace attributes, e.g, sampling rate, treadmill_times, and treadmill velocity values
- treadmill_trace, treadmill_time = (treadmill.Treadmill & key).fetch1('treadmill_vel', 'treadmill_time')
- # interpolating missing values in pupil radius, otherwise interpolating to new timestamps won't work or won't work well
- treadmill_trace = psth_functions.interpolate_nans(treadmill_trace)
- treadmill_trace = psth_functions.interpolate_to_new_timestamps(treadmill_time, treadmill_trace, scan_times)
- return trace, hilbert_trace, radius, radius_phase, treadmill_trace
- def fetch_nonrunning_pupil_periods(key): #, pupil_method_id, run_filter_method, running_method_id): ## arguments to consider having to compute non_running_pupil_periods dictionaries containing frame_idxs across different pupil and running methods
- pupil_method_id=1
- running_method_id = 1
- run_filter_method = '0.5sec Median Filter'
- running_period_padding = 3
- # fetching pipe and scan_times of particular scan_key field to then appropriately convert pupil_period_onset times to fluorescence frame idxs to then be able to iterate per pupil period across non_running_frame_idxs
- pipe = psth_functions.fetch_pipe(key)
- scan_times = psth_functions.get_fluorescence_times(key, pipe)
- pupil_period_list = []
- #fetching all pupil periods and select attributes to make into a list of dictionaries that we'll iterate through and discard ones that fall near or within running periods
- period_idx, period_onset, period_offset, period_delta, period_duration = (pupil.PupilPeriods.DilationConstriction & key & f'pupil_method_id={pupil_method_id}' & f'period_offset<{scan_times[-1]}'& 'period_method_id=1').fetch('period_idx', 'period_onset', 'period_offset', 'period_delta', 'period_duration')
- start_stop_times_list = [[val[0], val[1]] for val in zip(period_onset, period_offset)]
- print('start stop times length:', len(start_stop_times_list))
- list_of_pupil_periods_idxs = ct.convert_clocks(key,
- start_stop_times_list,
- source_format= 'times',
- source_type= 'fluorescence-behavior',
- target_format= 'indices',
- target_type= 'fluorescence-behavior',
- drop_single_idx=True,
- debug=True)
- # creating a dictionary of fetched pupil periods to iterate through shortly
- print('pupil_period_idxs length:', len(list_of_pupil_periods_idxs))
- list_of_pupil_periods = [{f'period_idx': val[0],
- 'period_onset': val[1],
- 'period_offset': val[2],
- 'period_delta': val[3],
- 'period_duration': val[4],
- } for val in zip(period_idx, period_onset, period_offset, period_delta, period_duration)]
- print('pupil periods length', len(list_of_pupil_periods))
- for i, pupil_period in enumerate(list_of_pupil_periods):
- pupil_period['period_onset_idx'] = list_of_pupil_periods_idxs[i][0]
- pupil_period['period_offset_idx'] = list_of_pupil_periods_idxs[i][-1]
- #fetching run_onsets and run_offsets that we'll iterate through individually to check whether all pupil_periods fall within that time frame
- run_onsets, run_offsets = (treadmill.Running.Period & dict(key, run_filter_method = f'{run_filter_method}', running_method_id = running_method_id)).fetch('run_onset', 'run_offset')
- # iterating through each pupil period & determining which one's fall within running periods to then discard those using a boolean mask
- pupil_period_idxs_to_delete = []
- for i in range(len(run_onsets)):
- run_start=run_onsets[i]-running_period_padding
- run_stop=run_offsets[i]+running_period_padding
- for j, pupil_period_dict in enumerate(list_of_pupil_periods):
- period_onset, period_offset = pupil_period_dict['period_onset'], pupil_period_dict['period_offset']
- if period_onset >= run_start and period_offset <= run_stop:
- pupil_period_idxs_to_delete.append(j)
- # making a boolean mask of len(list_of_pupil_periods) of all True values. Iterating through all pupil_period_idxs_to_delete & making those specific array index values = False
- pupil_periods_list_boolean_mask = np.full(len(list_of_pupil_periods), True)
- # iterates through pupil period indeces list to remove and modifies boolean mask to make into False values
- for pupil_periods_idxs in pupil_period_idxs_to_delete:
- pupil_periods_list_boolean_mask[pupil_periods_idxs] = False
- pupil_periods_list_boolean_mask[pupil_periods_idxs+1] = False # accounts for open ended indexing, otherwise you would discard all but one period index that falls within running_periods
- # passes boolean mask to list of pupil periods to remove pupil periods that fall within running period windows
- list_of_pupil_periods = np.array(list_of_pupil_periods)[pupil_periods_list_boolean_mask]
- return list_of_pupil_periods
- def get_directionality_stats(mean_traces, directions):
- mean_traces = np.array(mean_traces)
- directions = np.array(directions)
- constriction_periods = (directions == 'constriction')
- dilation_periods = (directions == 'dilation')
- constriction_median = np.median(mean_traces[constriction_periods])
- dilation_median = np.median(mean_traces[dilation_periods])
- constriction_mean = np.mean(mean_traces[constriction_periods])
- dilation_mean = np.mean(mean_traces[dilation_periods])
- median_diff = dilation_median - constriction_median
- mean_diff = dilation_mean - constriction_mean
- if constriction_median > dilation_median:
- preferred_direction = 'F(constriction) > F(dilation)'
- else:
- preferred_direction = 'F(dilation) >= F(constriction)'
- pvalue = stats.kruskal(mean_traces[constriction_periods], mean_traces[dilation_periods]).pvalue
- return mean_diff, median_diff, preferred_direction, pvalue
- def make(self, key):
- print(key)
- fluorescence_filter_method = "5Hz Hamming Lowpass"
- trace, hilbert_trace, radius, radius_phase, treadmill_trace = PupilPhaseBinsBeta.trace_preprocessing(key, fluorescence_filter_method)
- non_running_pupil_periods = PupilPhaseBinsBeta.fetch_nonrunning_pupil_periods(key)
- ###########
- ###########
- bin_half_width = np.pi/128
- bin_centers = np.linspace(-np.pi+bin_half_width, np.pi-bin_half_width, 65)
- bin_edges = np.linspace(-np.pi, np.pi, 65)
- period_dict_static_key = {**key,
- 'filter_method': fluorescence_filter_method,
- }
- per_period_dict_static_key = period_dict_static_key.copy()
- pupil_period_nums = len(non_running_pupil_periods)
- all_period_pupil_radius_bin_avgs = []
- all_period_pupil_phase_bin_avgs = []
- all_period_fluorescence_bin_avgs = []
- all_period_hilbert_ampl_bin_avgs = []
- all_period_treadmill_bin_avgs = []
- all_periods_keys_list = []
- for i, pupil_period in enumerate(non_running_pupil_periods):
- period_idx = pupil_period['period_idx']
- period_delta = pupil_period['period_delta']
- period_onset_idx = pupil_period['period_onset_idx']
- period_offset_idx = pupil_period['period_offset_idx']+1
- pupil_radius_fragment = radius[period_onset_idx:period_offset_idx]
- pupil_phase_fragment = radius_phase[period_onset_idx:period_offset_idx]
- fluorescence_fragment = trace[period_onset_idx:period_offset_idx]
- hilbert_phase_fragment = hilbert_trace[period_onset_idx:period_offset_idx]
- treadmill_fragment = treadmill_trace[period_onset_idx:period_offset_idx]
- fluorescence_trace_mean = np.mean(fluorescence_fragment)
- fluorescence_trace_max = np.max(fluorescence_fragment)
- fluorescence_trace_min = np.min(fluorescence_fragment)
- current_period_radius_bin_values = np.zeros(65)
- current_period_phase_bin_values = np.zeros(65)
- current_period_fluorescence_bin_values = np.zeros(65)
- current_period_hilbert_bin_values = np.zeros(65)
- current_period_treadmill_bin_values = np.zeros(65)
- if period_delta > 0:
- period_type = 'dilation'
- if period_delta < 0:
- period_type = 'constriction'
- for bin_idx, bin_center in enumerate(bin_centers):
- pupil_mask = np.logical_and(pupil_phase_fragment >= bin_center - bin_half_width , pupil_phase_fragment < bin_center + bin_half_width)
- if len(pupil_mask) != len(fluorescence_fragment):
- print(f'mismatch in fluorescence_fragment & boolean mask: {len(pupil_mask)-len(fluorescence_fragment)}')
- if np.abs(len(pupil_mask)-len(fluorescence_fragment)) < 5:
- print(f'trimming the pupil_mask & trace fragments')
- pupil_mask = pupil_mask[:len(fluorescence_fragment)]
- pupil_radius_fragment = pupil_radius_fragment[:len(fluorescence_fragment)]
- pupil_phase_fragment = pupil_phase_fragment[:len(fluorescence_fragment)]
- fluorescence_fragment = fluorescence_fragment[:len(fluorescence_fragment)]
- hilbert_phase_fragment = hilbert_phase_fragment[:len(fluorescence_fragment)]
- treadmill_fragment = treadmill_fragment[:len(fluorescence_fragment)]
- current_bin_radius_avg = np.nanmean(pupil_radius_fragment[pupil_mask])
- current_bin_phase_avg = np.nanmean(pupil_phase_fragment[pupil_mask])
- current_bin_fluorescence_avg = np.nanmean(fluorescence_fragment[pupil_mask])
- current_bin_hilbert_avg = np.nanmean(hilbert_phase_fragment[pupil_mask])
- current_bin_treadmill_avg = np.nanmean(treadmill_fragment[pupil_mask])
- current_period_radius_bin_values[bin_idx] = current_bin_radius_avg
- current_period_phase_bin_values[bin_idx] = current_bin_phase_avg
- current_period_fluorescence_bin_values[bin_idx] = current_bin_fluorescence_avg
- current_period_hilbert_bin_values[bin_idx] = current_bin_hilbert_avg
- current_period_treadmill_bin_values[bin_idx] = current_bin_treadmill_avg
- current_period_key = {**per_period_dict_static_key,
- 'period_idx': period_idx,
- 'period_type': period_type,
- 'pupil_radius_bin_avg': current_period_radius_bin_values,
- 'pupil_phase_bin_avg': current_period_phase_bin_values,
- 'fluorescence_bin_avg': current_period_fluorescence_bin_values,
- 'hilbert_ampl_bin_avg': current_period_hilbert_bin_values,
- 'treadmill_bin_avg': current_period_treadmill_bin_values,
- 'pupil_radius_fragment' : pupil_radius_fragment,
- 'fluorescence_fragment' : fluorescence_fragment,
- 'hilbert_phase_fragment': hilbert_phase_fragment,
- 'treadmill_fragment': treadmill_fragment,
- 'trace_mean': fluorescence_trace_mean,
- 'trace_max' : fluorescence_trace_max,
- 'trace_min' : fluorescence_trace_min,
- }
- all_period_pupil_radius_bin_avgs.append(current_period_radius_bin_values)
- all_period_pupil_phase_bin_avgs.append(current_period_phase_bin_values)
- all_period_fluorescence_bin_avgs.append(current_period_fluorescence_bin_values)
- all_period_hilbert_ampl_bin_avgs.append(current_period_hilbert_bin_values)
- all_period_treadmill_bin_avgs.append(current_period_treadmill_bin_values)
- all_periods_keys_list.append(current_period_key)
- #print(f"shape of radius average array for all periods. should be (n_periods, n_bins) {np.reshape(all_periods_radius_list, (len(list_of_pupil_periods), len(period_bin_edges)))}")
- phase_binned_radius_all_periods_avg = np.nanmean(all_period_pupil_radius_bin_avgs, axis=0)
- phase_binned_phase_all_periods_avg = np.nanmean(all_period_pupil_phase_bin_avgs, axis=0)
- phase_binned_fluorescence_all_periods_avg = np.nanmean(all_period_fluorescence_bin_avgs, axis=0)
- phase_binned_fluorescence_hilbert_all_periods_avg = np.nanmean(all_period_hilbert_ampl_bin_avgs, axis=0)
- phase_binned_treadmill_all_periods_avg = np.nanmean(all_period_treadmill_bin_avgs, axis=0)
- all_trace_means = [k['trace_mean'] for k in all_periods_keys_list]
- all_pupil_directions = [k['period_type'] for k in all_periods_keys_list]
- mean_diff, median_diff, preferred_direction, direction_selectivity_pvalue = PupilPhaseBinsBeta.get_directionality_stats(all_trace_means, all_pupil_directions)
- period_dict_static_key = {**period_dict_static_key,
- 'mean_dilation_minus_constriction': mean_diff,
- 'median_dilation_minus_constriction': median_diff,
- 'preferred_direction': preferred_direction,
- 'direction_selectivity_pvalue': direction_selectivity_pvalue,
- 'phase_bin_edges' : bin_edges,
- 'pupil_radius_avg' : phase_binned_radius_all_periods_avg,
- 'pupil_phase_avg' : phase_binned_phase_all_periods_avg,
- 'fluorescence_avg' : phase_binned_fluorescence_all_periods_avg,
- 'hilbert_phase_avg': phase_binned_fluorescence_hilbert_all_periods_avg,
- 'treadmill_avg' : phase_binned_treadmill_all_periods_avg,
- 'pupil_period_nums': pupil_period_nums,
- }
- print(period_dict_static_key.keys())
- print(all_periods_keys_list[0].keys())
- self.insert1(period_dict_static_key)
- self.PeriodFragments.insert(all_periods_keys_list)
pupil_phase_binning.py at commit 80deb0b, no license · at the source
Overview
and 14 other authors
Ryan G Natan3, Jian Zhong8, Ryan Kroeger1, Robert G Law1, Noura Hakam1, Cameron L Smith1, Ming Hu1, Shanii Tabb5, Brice Bathellier7, Barna Dudok1,5, Na Ji3,8,9,10, Laurent Bourdieu2, Jacob Reimer1,11,12, François St-Pierre1,4,11,12,1313 affiliations
- Department of Neuroscience, Baylor College of Medicine, Houston, TX USA
- Institut de Biologie de l’Ecole Normale Supérieure (IBENS), Ecole Normale Supérieure, CNRS, INSERM, Université PSL, Paris, France
- Department of Neuroscience, University of California, Berkeley, Berkeley, CA USA
- Systems, Synthetic and Physical Biology program, Rice University, Houston, TX USA
- Department of Neurology, Baylor College of Medicine, Houston, TX USA
- Department of Chemical and Biomolecular Engineering, Rice University, Houston, TX USA
- Université Paris Cité, Institut Pasteur, AP-HP, INSERM, CNRS, Fondation Pour l’Audition, Institut de l’Audition, IHU reConnect, Paris, France
- Department of Physics, University of California, Berkeley, Berkeley, CA USA
- Helen Wills Neuroscience Institute, University of California, Berkeley, Berkeley, CA USA
- Molecular Biophysics and Integrated Bioimaging Division, Lawrence Berkeley National Laboratory, Berkeley, CA USA
- Department of Electrical and Computer Engineering, Rice University, Houston, TX USA
- Center for Neuroscience and Artificial Intelligence, Baylor College of Medicine, Houston, TX USA
- Department of Biochemistry and Molecular Pharmacology, Baylor College of Medicine, Houston, TX USA
Abstract
Subthreshold voltage dynamics are essential for neuronal information integration, yet they remain technically challenging to measure in vivo. While genetically encoded voltage indicators are emerging as powerful tools for voltage recording, they lack the sensitivity to detect millivolt-scale subthreshold fluctuations with two-photon microscopy—the method of choice for deep-tissue recording. To overcome this limitation, we engineered two genetically encoded voltage indicators, JEDI3sub and JEDI3hyp, with enhanced subthreshold voltage detection under two-photon excitation. In the mouse brain, JEDI3sub enabled simultaneous tracking of subthreshold optical tuning from over 100 cells simultaneously, while JEDI3hyp captured subthreshold dynamics associated with sharp-wave ripples in hippocampal PV+ interneurons. Moreover, JEDI3hyp supported prolonged imaging of brain-state-dependent, millivolt-scale subthreshold voltage changes across deep-layer somas, fine dendritic structures and diverse cell types. By enabling sensitive reporting of subthreshold voltage dynamics, JEDI3 indicators open previously unexplored avenues for dissecting neural information processing in health and disease.
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 5 matches between paragraphs and lines of code.
reimerlab/jedi3-paper
80deb0b9c79ca92db257d5f84ed8d433ab4fe8e0, 13 February 2026Availability: 1 check, the latest on 29 September 2026: the link answers
- 29 September 2026: the link answers
11 files
- JEDI3_reso_meso_plot_not
ebook.ipynb , Jupyter, 568 lines - jedi3_reso_meso.py, Python, 1,245 lines, 1 match
- vip-som/
JEDI_final_manuscript_fi , Jupyter, 1,069 linesgures_stats.ipynb - vip-som/
clocktools.py , Python, 818 lines - vip-som/
dataset.py , Python, 1,154 lines - vip-som/
mario_utils.py , Python, 3 lines - vip-som/
psth_functions.py , Python, 2,698 lines - vip-som/
pupil_phase_binning.py , Python, 626 lines, 4 matches - vip-som/
shared.py , Python, 451 lines - vip-som/
units.py , Python, 5 lines - README.md, Text, 2 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;
- 10 scripts, each with its path and the digest of its content;
- 5 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
- zenodo:17537897, at Zenodo; found in “Data availability”
Data availability
The GenBank accession numbers are 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, 29 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 34 authors, 4 keywords, 8 MeSH terms, 6 funders, 87 references, 5 RRIDs.
Cite
This paper
Land, M. A., Galdamez, M., Villette, V., Zhu, J., Lu, X., Marosi, M., Yang, S., Foran, G., McDonald, A. J., Dong, X., Zaabout, E., Liu, H., Liu, Z., Colbert, K. L., Lai, S., Shorey, M., Lourdiane, A. S. G., Ayon, A., Bradley, J., . . . St-Pierre, F. (2026). Designer indicators for two-photon recording of subthreshold voltage dynamics. Nature methods, 23(5), 986-997. https://
BibTeX
@article{land2026designe
author = {Land, Michelle A and Galdamez, Mario and Villette, Vincent and Zhu, Jun and Lu, Xiaoyu and Marosi, Mate and Yang, Shuyuan and Foran, Gregory and McDonald, Alex J and Dong, Xiaoyu and Zaabout, Elsayed and Liu, Haixin and Liu, Zhuohe and Colbert, Kevin L and Lai, Shujuan and Shorey, Matthew and Lourdiane, Anthony S G and Ayon, Annick and Bradley, Jonathan and Mailhes-Hamon, Caroline and Natan, Ryan G and Zhong, Jian and Kroeger, Ryan and Law, Robert G and Hakam, Noura and Smith, Cameron L and Hu, Ming and Tabb, Shanii and Bathellier, Brice and Dudok, Barna and Ji, Na and Bourdieu, Laurent and Reimer, Jacob and St-Pierre, François},
title = {{Designer indicators for two-photon recording of subthreshold voltage dynamics}},
journal = {Nature methods},
year = {2026},
month = apr,
volume = {23},
number = {5},
pages = {986--997},
publisher = {Nature Portfolio},
issn = {1548-7091},
doi = {10.1038/
url = {https://
pmid = {41986688},
pmcid = {PMC13577304}
}
RIS
TY - JOUR
AU - Land, Michelle A
AU - Galdamez, Mario
AU - Villette, Vincent
AU - Zhu, Jun
AU - Lu, Xiaoyu
AU - Marosi, Mate
AU - Yang, Shuyuan
AU - Foran, Gregory
AU - McDonald, Alex J
AU - Dong, Xiaoyu
AU - Zaabout, Elsayed
AU - Liu, Haixin
AU - Liu, Zhuohe
AU - Colbert, Kevin L
AU - Lai, Shujuan
AU - Shorey, Matthew
AU - Lourdiane, Anthony S G
AU - Ayon, Annick
AU - Bradley, Jonathan
AU - Mailhes-Hamon, Caroline
AU - Natan, Ryan G
AU - Zhong, Jian
AU - Kroeger, Ryan
AU - Law, Robert G
AU - Hakam, Noura
AU - Smith, Cameron L
AU - Hu, Ming
AU - Tabb, Shanii
AU - Bathellier, Brice
AU - Dudok, Barna
AU - Ji, Na
AU - Bourdieu, Laurent
AU - Reimer, Jacob
AU - St-Pierre, François
TI - Designer indicators for two-photon recording of subthreshold voltage dynamics
T2 - Nature methods
J2 - Nat Methods
PY - 2026
DA - 2026/
VL - 23
IS - 5
SP - 986
EP - 997
SN - 1548-7091
PB - Nature Portfolio
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Designer indicators for two-photon recording of subthreshold voltage dynamics",
"container-title": "Nature methods",
"author": [
{
"family": "Land",
"given": "Michelle A"
},
{
"family": "Galdamez",
"given": "Mario"
},
{
"family": "Villette",
"given": "Vincent"
},
{
"family": "Zhu",
"given": "Jun"
},
{
"family": "Lu",
"given": "Xiaoyu"
},
{
"family": "Marosi",
"given": "Mate"
},
{
"family": "Yang",
"given": "Shuyuan"
},
{
"family": "Foran",
"given": "Gregory"
},
{
"family": "McDonald",
"given": "Alex J"
},
{
"family": "Dong",
"given": "Xiaoyu"
},
{
"family": "Zaabout",
"given": "Elsayed"
},
{
"family": "Liu",
"given": "Haixin"
},
{
"family": "Liu",
"given": "Zhuohe"
},
{
"family": "Colbert",
"given": "Kevin L"
},
{
"family": "Lai",
"given": "Shujuan"
},
{
"family": "Shorey",
"given": "Matthew"
},
{
"family": "Lourdiane",
"given": "Anthony S G"
},
{
"family": "Ayon",
"given": "Annick"
},
{
"family": "Bradley",
"given": "Jonathan"
},
{
"family": "Mailhes-Hamon",
"given": "Caroline"
},
{
"family": "Natan",
"given": "Ryan G"
},
{
"family": "Zhong",
"given": "Jian"
},
{
"family": "Kroeger",
"given": "Ryan"
},
{
"family": "Law",
"given": "Robert G"
},
{
"family": "Hakam",
"given": "Noura"
},
{
"family": "Smith",
"given": "Cameron L"
},
{
"family": "Hu",
"given": "Ming"
},
{
"family": "Tabb",
"given": "Shanii"
},
{
"family": "Bathellier",
"given": "Brice"
},
{
"family": "Dudok",
"given": "Barna"
},
{
"family": "Ji",
"given": "Na"
},
{
"family": "Bourdieu",
"given": "Laurent"
},
{
"family": "Reimer",
"given": "Jacob"
},
{
"family": "St-Pierre",
"given": "François"
}
],
"container-title-short":
"volume": "23",
"issue": "5",
"page": "986-997",
"DOI": "10.1038/
"PMID": "41986688",
"PMCID": "PMC13577304",
"ISSN": "1548-7091",
"publisher": "Nature Portfolio",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
15
]
]
}
}
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.1016/j.isci.2026.117010 [code]
- Deep learning-assisted mapping of dendritic spines using sequential 2D two-photon calcium imaging.Journal: iScienceIn common: seaborn, pandas, SciPy, 2 other tools, optical imaging (calcium, voltage, 2-photon), histology / microscopy, 4 references
- [2] doi:10.1038/s41467-026-71667-7
- Behavioural states control binocular vision through input-specific mechanisms.Journal: Nature communicationsIn common: mouse, 7 references
- [3] doi:10.7554/elife.108352 [code]
- Analysis of dendritic input currents during place field dynamics.Journal: eLifeIn common: pandas, Matplotlib, NumPy, 5 references
- [4] doi:10.1038/s41586-026-10907-8 [code]
- Procognitive restoration of PV neuron plasticity in neurodevelopmental disorders.Journal: NatureIn common: pandas, SciPy, Matplotlib, 1 other tool, 4 references
- [5] doi:10.1111/ejn.70582 [code]
- Multifiber Array-Based Photometry System for Multiregional Functional Mapping in the Mouse Brain.Journal: The European journal of neuroscienceIn common: pandas, SciPy, Matplotlib, 1 other tool, optical imaging (calcium, voltage, 2-photon), mouse, 4 references
- [6] doi:10.1038/s41593-026-02388-9 [code]
- Hippocampal CA3 connectomics reveals a gradient of mossy fiber inputs and selective feedforward inhibition onto pyramidal cells.Journal: Nature neuroscienceIn common: Plotly, seaborn, pandas, 3 other tools, histology / microscopy, mouse, 2 references
- [7] doi:10.1016/j.xpro.2026.104678 [code]
- Protocol for longitudinal two-photon calcium imaging and holographic optogenetic manipulation to investigate memory in mice.Journal: STAR protocolsIn common: seaborn, pandas, SciPy, 2 other tools, optical imaging (calcium, voltage, 2-photon), histology / microscopy, mouse, 2 references
- [8] doi:10.1038/s41592-026-03179-7 [code]
- Voltage imaging of neurons distributed across entire brains of larval zebrafish.Journal: Nature methodsIn common: SciPy, Matplotlib, NumPy, optical imaging (calcium, voltage, 2-photon), histology / microscopy, 4 references
- [9] doi:10.1038/s41467-026-73389-2 [code]
- Physics-informed multi-encoder adaptive optics enables rapid aberration correction for intravital microscopy of deep complex tissue.Journal: Nature communicationsIn common: SciPy, Matplotlib, NumPy, optical imaging (calcium, voltage, 2-photon), histology / microscopy, mouse, 3 references
- [10] doi:10.1016/j.crmeth.2026.101421 [code]
- EthoPy provides an accessible platform for reproducible behavioral neuroscience.Journal: Cell reports methodsIn common: Plotly, seaborn, pandas, 3 other tools, mouse, 2 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, 10 scripts, and 5 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:f2ecd8b0b1ef10f5…
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.
