High-speed whole-brain imaging in Drosophila.
The 10 matches
- [1] § Methods › Data analysis › Fourier analyses ↔ analysis_scripts/functions.py, lines 551–685 · score 0.74 · Fourier spectrum, absolute power, stimulus block, fft, scipy, subtracted
- [2] § Results › Resolving neural activity underlying individual song pulses ↔ analysis_scripts/functions.py, lines 786–868 · score 0.74 · 27–28 Hz, Fourier spectrum, absolute power, frame rate, IPI, peak
- [3] § Results › Capturing fast dynamics across the entire brain ↔ analysis_scripts/functions.py, lines 1108–1163 · score 0.64 · Correlation coefficients, highest correlation, ROIs extracted, Auditory stimulus, cutoff, Activity
- [4] § Results › Resolving neural activity underlying individual song pulses ↔ analysis_scripts/functions.py, lines 786–868 · score 0.63 · 27–28 Hz, Fourier spectrum, absolute power, IPI, peak, trace
- [5] § Methods › Data analysis › Identification of stimulus-modulated ROIs ↔ analysis_scripts/functions.py, lines 114–216 · score 0.61 · correlation coefficient, correlated ROIs, auditory stimulus, subset
- [6] § Methods › Data analysis › Identification of stimulus-modulated ROIs ↔ analysis_scripts/Suppl_figs.py, lines 101–167 · score 0.61 · correlation coefficient, convolving, kernel, rise, decay, scored
- [7] § Methods › Image processing and signal extraction ↔ dellaserver_processing/brainviz/roi.py, lines 26–55 · score 0.57 · agglomerative clustering, Ward, linkage, voxel, slice, brain
- [8] § Methods › Animal auditory stimulation and behavior recording ↔ flyvr/fictrac/fictrac_driver.py, lines 92–211 · score 0.57 · fly vr, Fictrac, tracked, setup
- [9] § Methods › Animal auditory stimulation and behavior recording ↔ matlab/audio_calibration/CalibrateSound_WhiteNoise_flyVR.m, lines 128–229 · score 0.55 · fly vr, Auditory stimuli, MATLAB, sound, Audio
- [10] § Results › Capturing fast dynamics across the entire brain ↔ analysis_scripts/Suppl_figs.py, lines 101–167 · score 0.54 · convolved stimulus, Correlation coefficients, kernel, duration, scored, blocks
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 · 1,483 lines · 55 KB · no license · 5 matches
- # -*- coding: utf-8 -*-
- import numpy as np
- import matplotlib.pyplot as plt
- from scipy.stats import zscore
- from scipy.stats import sem
- from scipy.fft import rfftfreq
- import scipy.fft
- from scipy.signal import find_peaks
- import matplotlib
- from scipy.interpolate import interp1d
- from typing import Optional, Tuple
- matplotlib.rcParams['pdf.fonttype'] = 42
- matplotlib.rcParams['ps.fonttype'] = 42
- def create_stim(dffs, start_block_seconds,end_block_seconds ,frame_rate, t_i2c=0):
- """
- Description
- ----------
- This function creates a continuous version of the stimulus (array of 0 or 1) with the same shape as the activity
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block of stimulus in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block of stimulus in seconds
- t_i2c (float)
- The first time point received by I2C. Set to 0 if data is already aligned
- frame_rate (float)
- frame rate of the scope
- ----------
- Returns
- ----------
- continuous_stim
- An array of 0 and 1s when stimulus is off and on respectively
- ----------
- """
- # Get index of start and end
- s = (start_block_seconds)*frame_rate
- e = (end_block_seconds )*frame_rate
- continuous_stim = []
- for ii in range(int((t_i2c)*frame_rate)):
- continuous_stim.append(0)
- for ii in range(dffs.shape[1]-int((t_i2c)*frame_rate)):
- if (s[0]<ii<e[0]) or (s[1]<ii<e[1]) or (s[2]<ii<e[2]) or (s[3]<ii<e[3]) or (s[4]<ii<e[4]) or (s[5]<ii<e[5]) or (s[6]<ii<e[6]) or (s[7]<ii<e[7]) or (s[8]<ii<e[8]) or (s[9]<ii<e[9]) or (s[10]<ii<e[10]) or (s[11]<ii<e[11]) or (s[12]<ii<e[12]):
- continuous_stim.append(1)
- else:
- continuous_stim.append(0)
- return (continuous_stim)
- def create_stim_train(dffs, start_block_seconds,end_block_seconds ,frame_rate, t_i2c=0):
- """
- Description
- ----------
- This function creates a continuous version of the stimulus (array of 0 or 1) with the same shape as the activity
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_i2c (float)
- The first time point received by I2C. Set to 0 is data is already aligned
- frame_rate (float)
- frame rate of the scope
- ----------
- Returns
- ----------
- continuous_stim
- An array of 0 and 1s when stimulus is off and on respectively
- ----------
- """
- # Get index of start and end
- s = (start_block_seconds)*frame_rate
- e = (end_block_seconds )*frame_rate
- continuous_stim = []
- for ii in range(int((t_i2c)*frame_rate)):
- continuous_stim.append(0)
- for ii in range(dffs.shape[1]-int((t_i2c)*frame_rate)):
- if (s[0]<ii<e[0]) or (s[1]<ii<e[1]) or (s[2]<ii<e[2]) or (s[3]<ii<e[3]) or (s[4]<ii<e[4]) or (s[5]<ii<e[5]) or (s[6]<ii<e[6]) or (s[7]<ii<e[7]) or (s[8]<ii<e[8]) or (s[9]<ii<e[9]) or (s[10]<ii<e[10]) or (s[11]<ii<e[11]) or (s[12]<ii<e[12]) or (s[13]<ii<e[13]):
- continuous_stim.append(0.15)
- else:
- continuous_stim.append(0)
- return (continuous_stim)
- def crosscorr_sort(dffs, stimulus,cutoff,frame_rate,max_lag=0):
- """
- Description
- ----------
- This function computes the cross correlation between the calcium activity and the auditory stimulus and extract the top cutoff % of ROIs based on the correlation coefficient.
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- stimulus (np.ndarray)
- array containing the auditory stimulus
- cutoff (float)
- threshold in percentage to use when extracting the top X% based on correlation
- max_lag (float)
- the lag over which to compute the cross correlation
- frame_rate (float)
- frame rate of the scope
- ----------
- Returns
- ----------
- audio_correlated, corr_coeff, correlations, sorted_indices
- array containing the index of the top 'cutoff'% ROIs with the highest correlation coefficient with the stimulus
- corr_coeff
- array containing the correlation coefficient of the extracted ROIs in audio_correlated
- correlations
- array containing the correlation coefficients of all ROIs in dffs
- ----------
- """
- # Normalize the activity and the stimulus
- dffs_mean = dffs.mean(axis=1, keepdims=True)
- dffs_std = dffs.std(axis=1, keepdims=True)
- dffs_normalized = (dffs - dffs_mean) / dffs_std
- stimulus_normalized = (stimulus - stimulus.mean()) / stimulus.std()
- # define index cutoff
- index_cutoff = int(dffs.shape[0] * (cutoff/100))
- # If we don't want any lag
- if max_lag == 0:
- # Compute correlations
- correlations = np.dot(dffs_normalized, stimulus_normalized.T) / dffs_normalized.shape[1]
- correlations = correlations.flatten()
- rois = np.arange(0,dffs.shape[0])
- rois = rois[~np.isnan(correlations)]
- correlations = correlations[~np.isnan(correlations)]
- #sort the correlation coefficients and rois
- sorted_indices = np.argsort(correlations)
- sorted_corr = correlations[sorted_indices]
- sorted_rois = rois[sorted_indices]
- # Extract the top X%
- audio_correlated = sorted_rois[-index_cutoff:]
- corr_coeff = sorted_corr[-index_cutoff:]
- # Result: correlations is a 1D array of shape (number of ROIs,)
- else:
- # Compute correlations for different lags
- num_rois, time_points = dffs.shape
- lags = np.arange(int(-max_lag*frame_rate), int(max_lag*frame_rate) + 1,int(0.5*frame_rate))
- correlation_matrix = np.zeros((num_rois, len(lags)))
- for i, lag in enumerate(lags):
- if lag < 0:
- # Shift auditory stimulus forward
- shifted_stimulus = stimulus_normalized[-lag:]
- activity_subset = dffs_normalized[:, :len(shifted_stimulus)]
- elif lag > 0:
- # Shift auditory stimulus backward
- shifted_stimulus = stimulus_normalized[:-lag]
- activity_subset = dffs_normalized[:, lag:]
- # Compute correlations for the current lag
- correlation_matrix[:, i] = (
- np.dot(activity_subset, shifted_stimulus) / len(shifted_stimulus)
- )
- # Find the best correlation across all lags for each ROI
- correlations = np.max(np.abs(correlation_matrix), axis=1)
- #best_lags = lags[np.argmax(np.abs(correlation_matrix), axis=1)]
- rois = np.arange(0,dffs.shape[0])
- rois = rois[~np.isnan(correlations)]
- correlations = correlations[~np.isnan(correlations)]
- #sort the correlation and rois
- sorted_indices = np.argsort(correlations)
- sorted_corr = correlations[sorted_indices]
- sorted_rois = rois[sorted_indices]
- audio_correlated = sorted_rois[-index_cutoff:]
- corr_coeff = sorted_corr[-index_cutoff:]
- print('number of audio correlated ROIs: {}'.format(len(audio_correlated)))
- return(audio_correlated, corr_coeff, correlations, sorted_indices[-index_cutoff:])
- def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
- if isinstance(cmap, str):
- cmap = plt.get_cmap(cmap)
- new_cmap = matplotlib.colors.LinearSegmentedColormap.from_list(
- 'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
- cmap(np.linspace(minval, maxval, n)))
- return new_cmap
- def compute_mean_time_series_per_block_pair(dffs, time_activity, frame_rate, start_block_seconds, end_block_seconds, t_added, scope):
- """
- Description
- ----------
- This function computes the mean activity across all ROIs during the two presentation of stimulus block with the same frequency.
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- frame_rate (float)
- frame rate of the scope
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_added (float)
- Time to add around each block for visualization purposes
- scope (str)
- 'LB' or '2p' to specify which scope was used to aquire the data
- ----------
- Returns
- ----------
- block_pair_traces (shape: (n_block_pairs, samples_per_block))
- array containing the mean activity across all ROIs during each block of stimulus with different frequencies
- block_pair_sem (shape: (n_block_pairs, samples_per_block))
- array containing the sem of the activity across all ROIs during each block of stimulus with different frequencies
- ----------
- """
- n_blocks = len(start_block_seconds)
- assert n_blocks % 2 == 0, "Number of blocks must be even"
- block_pair_traces = []
- block_pair_sem=[]
- for i in range(0, n_blocks, 2):
- pair_traces = []
- pair_sem = []
- for j in [i, i + 1]: # handle each of the two blocks in the pair
- start_time = start_block_seconds[j]-t_added
- end_time = end_block_seconds[j]+t_added
- samples_per_block = int((end_time-start_time) * frame_rate)
- # Get mask for time points in this block
- mask = (time_activity >= start_time) & (time_activity <= end_time)
- if scope == '2p':
- samples_per_block+=1
- if (i==10) and (j==11):
- mask = np.hstack(( np.array(np.where(mask)[0][0]-1) , np.where(mask)[0] ))
- block_data = dffs[:, mask]
- if block_data.shape[1] != samples_per_block:
- raise ValueError(f"Block {j+1} does not contain {samples_per_block} samples. Got {block_data.shape[1]}.")
- # Average across ROIs
- mean_trace = np.mean(block_data, axis=0)
- pair_traces.append(mean_trace)
- sem_trace = sem(block_data, axis=0)
- pair_sem.append(sem_trace)
- # Average the two blocks
- mean_pair_trace = np.mean(pair_traces, axis=0)
- block_pair_traces.append(mean_pair_trace)
- sem_pair_trace = np.mean(sem_trace, axis=0)
- block_pair_sem.append(sem_pair_trace)
- return np.stack(block_pair_traces, axis=0),np.stack(block_pair_sem, axis=0)
- def extract_single_stimulus_per_block_pair(stimulus, time_audio, frame_rate, start_block_seconds,end_block_seconds,t_added):
- """
- Description
- ----------
- This function extract the auditory stimulus during each block presentation for plotting purposes in other functions.
- ----------
- Parameters
- ----------
- stimulus (np.ndarray)
- array containing the auditory stimulus
- time_audio (np.ndarray)
- array containing the time for the auditory stimulus
- frame_rate (float)
- frame rate of the scope
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_added (float)
- Time to add around each block for visualization purposes
- ----------
- Returns
- ----------
- stim_traces (shape: (n_block_pairs, samples_per_block))
- array containing the stimulus during each block of stimulus of a given frequency
- ----------
- """
- samples_per_block = int((10+t_added+t_added) * frame_rate)
- n_blocks = len(start_block_seconds)
- assert n_blocks % 2 == 0, "Number of blocks must be even"
- stim_traces = []
- for i in range(0, n_blocks, 2): # take the first block in each pair
- start_time = start_block_seconds[i]-t_added
- end_time = end_block_seconds[i] +t_added # 10s block
- mask = (time_audio >= start_time) & (time_audio < end_time)
- stim_block = stimulus[mask]
- if stim_block.shape[0] != samples_per_block:
- raise ValueError(f"Stimulus block {i+1} has {stim_block.shape[0]} samples; expected {samples_per_block}.")
- stim_traces.append(stim_block)
- return np.stack(stim_traces, axis=0)
- def plot_calcium_with_stimulus_overlay(mean_traces,sem_traces, stim_traces, frame_rate,path, scope):
- """
- Description
- ----------
- This function plots the mean activity across all ROIs during the two presentation of stimulus block of a given frequency overlayed with the auditory stimulus.
- ----------
- Parameters
- ----------
- mean_traces (nd.array)
- array containing the mean activity across all ROIs during each block of stimulus with different frequencies
- sem_traces (nd.array)
- array containing the sem of the activity across all ROIs during each block of stimulus with different frequencies
- stim_traces (np.ndarray)
- array containing the stimulus during each block of stimulus of a given frequency
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- frame_rate (float)
- frame rate of the scope
- path (str)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- scope (str)
- 'LB' or '2p' to specify which scope was used to aquire the data
- ----------
- """
- if scope == 'LB':
- col = 'g'
- else:
- col = 'm'
- fr = 1/frame_rate
- n_pairs, samples_per_block = mean_traces.shape
- time_axis = np.arange(0,(samples_per_block)*fr,fr)
- fr_stim = 1/100
- samples_per_block_stim = np.shape(stim_traces[0])[0]
- t_stim = np.arange(0,(float(samples_per_block_stim))*fr_stim,fr_stim)
- for i in range(n_pairs):
- max_trace = np.max(mean_traces[i] + sem_traces[i])
- #min_trace = np.min(mean_traces[i] + sem_traces[i])
- height1 = 0.006
- height2 = 0.085
- plt.figure(figsize = (8,5))
- plt.plot(time_axis, mean_traces[i],color= col,alpha = 1, lw = 2.5)
- plt.fill_between(t_stim,y1=(max_trace*stim_traces[i])+ height1,y2=(max_trace*stim_traces[i])+height2,where =stim_traces[i]>0,color='r',alpha=1)
- plt.fill_between(time_axis,y1=(mean_traces[i] + sem_traces[i]),y2=( mean_traces[i] - sem_traces[i]),color=col,alpha=0.4)
- plt.xlabel('Time (s)', fontsize = 36)
- plt.ylabel('Z(DF/F)', fontsize = 36)
- plt.locator_params(axis='y', nbins=5)
- plt.xlim(0,time_axis[-1])
- plt.xticks(fontsize = 34)
- plt.yticks(fontsize = 34)
- plt.locator_params(axis='y', nbins=3)
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'mean_activity_cluster_block_' + str(i) + '_2p' + '.pdf', transparent = True)
- def fourier_mean_activity_interpolate(dffs,start_block_seconds,end_block_seconds,t_added,frame_rate,time_activity,scope, target_frame_rate,N_target, path,xlim, col):
- """
- Description
- ----------
- This function computes and plots the fourier spectrum of the mean activity for each block pair of a given frequency
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_added (float)
- Time to add around each block for visualization purposes
- frame_rate (float)
- frame rate of the scope
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- scope (str)
- Either 'LB' or '2p'
- Hz_target (float)
- Target frame rate when interpolating 2p
- N_target (float)
- Target number of samples when interpolating 2p
- path (float)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- xlim (float)
- maximum of x axis for plotting
- col (float)
- color for plotting
- ----------
- """
- b = 1 # Index that keeps track of the stimulus blocks
- # Loop through each pair of stimulus blocks
- for k in range(6):
- # Grab first block
- t_sart = start_block_seconds[b]
- t_end = end_block_seconds[b] +t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- if len(dffs.shape)>1:
- activity_block1 = dffs[:,start_act:end_act]
- else:
- activity_block1 = dffs[start_act:end_act]
- # Grab second block
- t_sart = start_block_seconds[b+1]
- t_end = end_block_seconds[b+1] + t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- if len(dffs.shape)>1:
- activity_block2 = dffs[:,start_act:end_act]
- else:
- activity_block2 = dffs[start_act:end_act]
- # append both blocks and take the mean
- act_both_block = np.vstack((activity_block1,activity_block2))
- # Compute the mean
- mean_block = np.mean(act_both_block,axis = 0)
- # subtract the mean
- activity = mean_block-np.mean(mean_block)
- normalize = int(N/2)+1
- #### compute fourier
- fourier = scipy.fft.fft(activity)
- fourier = np.abs(fourier)**2
- ff = scipy.fft.fft(activity)
- ff = np.abs(ff)**2
- # Interpolate 2-photon spectrum to match light bead frequency axis
- if scope == '2p':
- time_inter = rfftfreq(N_target, d = 1/frame_rate)
- freq_2p = rfftfreq(N, d = 1/frame_rate)
- interp_func = interp1d(freq_2p, ff[:normalize], kind='linear')
- fourier_interp = interp_func(time_inter)
- # plot the Fourier spectrums
- N = len(activity)
- plt.figure()
- if scope == '2p':
- plt.plot(rfftfreq(N_target, d = 1/frame_rate),fourier_interp[:int(N_target/2)+1], color = col,lw = 2.5 )
- if scope == 'LB':
- plt.plot(rfftfreq(N, d = 1/target_frame_rate), fourier[:normalize], color = col,lw = 2.5)
- plt.xlabel('Frequency (Hz)',fontsize =36)
- plt.ylabel('Amplitude',fontsize =36)
- plt.xlim(0,xlim)
- plt.xticks(fontsize =34)
- plt.yticks(fontsize =34,)
- plt.locator_params(axis='y', nbins=4)
- #plt.title('Spectrum block {0}Hz'.format(HZ[k]))
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'spectrum_block_' + str(k) + '_'+ scope + '.pdf', transparent = True)
- b=b+2 # Move to the next block pair
- plt.tight_layout()
- def power_ROI(dffs,start_block_seconds,end_block_seconds,t_added,frame_rate,time_activity,scope,N_target):
- """
- Description
- ----------
- This function computes the absolute and fraction of power at each frequencies for each ROIs.
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_added (float)
- Time to add around each block for visualization purposes
- frame_rate (float)
- frame rate of the scope
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- scope (str)
- Either 'LB' or '2p'
- N_target (float)
- Target number of samples when interpolating 2p
- ----------
- Returns
- ----------
- ps (nd.array)
- array containing the absolute power at each frequency for all ROIs
- fracps (nd.array)
- array containing the fraction of power at each frequency for all ROIs
- """
- mean_blocks = []
- ### First we grab the mean activity during of each stimulus block pair to subtract later
- b = 1 # Index that keeps track of the stimulus blocks
- # Loop through each block pairs
- for k in range(6):
- # extract first block in the pair
- t_sart = start_block_seconds[b]
- t_end = end_block_seconds[b] +t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block1 = dffs[:,start_act:end_act]
- # Grab second block in the pair
- t_sart = start_block_seconds[b+1]
- t_end = end_block_seconds[b+1] + t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block2 = dffs[:,start_act:end_act]
- # Combine both blocks
- activity_both_block = np.vstack((activity_block1,activity_block2))
- # store the mean
- mean_blocks.append(np.mean(activity_both_block))
- b +=2 # Move to the next block pair
- HZ = [0.25,0.5,1,2,3,5]
- ps = np.zeros((6,dffs.shape[0])) # will contain the absolute power
- fracps = np.zeros((6,dffs.shape[0])) # will contain the fraction of power
- for roi, i in enumerate(dffs):
- b=1 # Index that keeps track of the stimulus blocks
- # Loop through each block pairs
- for k in range(6):
- # extract first block in the pair
- t_sart = start_block_seconds[b]
- t_end = end_block_seconds[b] +t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block1 = dffs[:,start_act:end_act][roi,:]
- # Grab second block in the pair
- t_sart = start_block_seconds[b+1]
- t_end = end_block_seconds[b+1] + t_added
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block2 = dffs[:,start_act:end_act][roi,:]
- # Combine both blocks
- activity_both_block = np.vstack((activity_block1,activity_block2))
- # Compute the mean
- mean_both_block = np.mean(activity_both_block,axis = 0)
- #### Compute fourier spectrum
- # subtract the overall mean during these two blocks
- ff_mean = scipy.fft.fft(mean_both_block - mean_blocks[k], norm = 'ortho' )
- ff_mean = np.abs(ff_mean)**2
- N_mean = len(mean_both_block)
- normalize = int(N_mean/2)+1
- ### slice frequency range
- f_range = [HZ[k]-0.1, HZ[k]+0.1]
- freq_axis = rfftfreq(N, d = 1/frame_rate)
- f0 = np.argmin(np.abs(freq_axis- f_range[0]))
- f1 = np.argmin(np.abs(freq_axis- f_range[1]))
- f_cutoff = np.argmin(np.abs(freq_axis- 1.1))
- if scope == 'LB':
- p = np.sum(ff_mean[f0:f1])
- if HZ[k]<2:
- totp = np.sum(ff_mean[1:f_cutoff])
- else:
- totp = np.sum(ff_mean[1:normalize])
- if scope == '2p':
- if (HZ[k]-freq_axis[f0])>0.8:
- p = 0
- else:
- p = np.sum(ff_mean[f0:f1])
- totp = np.sum(ff_mean[1:normalize])
- ps[k,roi] = p
- fracp = (p/totp)*100
- fracps[k,roi] = fracp
- return(ps,fracps)
- def plot_power_ROIs(ps,fracps,ps_2p, fracps_2p,path):
- """
- Description
- ----------
- This function plot the fraction of power at each frequency across all ROIs for both 2p and LB
- ----------
- Parameters
- ----------
- ps (nd.array)
- array containing the absolute power at each frequency for all ROIs aquired with LB
- fracps (nd.array)
- array containing the fraction of power at each frequency for all ROIs aquired with LB
- ps_2p (nd.array)
- array containing the absolute power at each frequency for all ROIs aquired with 2p
- fracps_2p (nd.array)
- array containing the fraction of power at each frequency for all ROIs aquired with 2p
- path (str)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- ----------
- """
- HZ = [0.25,0.5,1,2,3,5]
- # Loop through each frequencies
- for k in range(len(ps)):
- plt.figure(figsize=(3.5, 6))
- plt.plot(np.random.normal(0,0.03, size = len(fracps[k])),fracps[k],'g.',alpha = 0.1)
- plt.plot(np.random.normal(0.3,0.03, size = len(fracps_2p[k])),fracps_2p[k],'m.',alpha = 0.1)
- plt.bar([0],np.mean(fracps[k]),yerr = np.std(fracps[k]),capsize = 5, color = 'white',edgecolor = 'g', width = 0.2)
- plt.bar([0.3],np.mean(fracps_2p[k]),yerr = np.std(fracps_2p[k]),capsize = 5, color = 'white', edgecolor = 'm', width = 0.2)
- plt.xticks([])
- plt.ylabel('Fraction of power at {} Hz'.format(HZ[k]), fontsize = 32)
- plt.yticks(fontsize = 32)
- plt.locator_params(axis='y', nbins=4)
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'frac_power' + str(HZ[k]) + '_' + '.pdf', transparent = True)
- def shuffle_within_blocks(dffs, start_block_seconds, end_block_seconds, frame_rate, seed=None):
- """
- This function returns a copy of the activity where, for each stimulus block,
- the time‐points within that block are independently shuffled for each ROI.
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- frame_rate (float)
- frame rate of the scope
- seed : int or None
- If given, seeds the RNG
- Returns
- -------
- shuffled : np.ndarray, shape (n_rois, n_timepoints)
- A copy of `dffs` with time‐points shuffled within each block for each ROI.
- """
- if seed is not None:
- np.random.seed(seed)
- n_rois, n_time = dffs.shape
- shuffled = dffs.copy()
- for start_sec, end_sec in zip(start_block_seconds, end_block_seconds):
- # Convert seconds to integer frame indices
- start_idx = int(np.floor(start_sec * frame_rate))
- end_idx = int(np.ceil (end_sec * frame_rate))
- # Clip to valid range
- start_idx = max(start_idx, 0)
- end_idx = min(end_idx, n_time)
- block_len = end_idx - start_idx
- if block_len <= 1:
- continue
- # For each ROI, shuffle the values within [start_idx:end_idx]
- for r in range(n_rois):
- block_vals = dffs[r, start_idx:end_idx]
- permuted = block_vals[np.random.permutation(block_len)]
- shuffled[r, start_idx:end_idx] = permuted
- return shuffled
- def fourier_and_peaks_mean(dffs,start_block_seconds,end_block_seconds,frame_rate,time_activity, path):
- """
- Description
- ----------
- This function computes the absolute power at 27-28Hz and the fourier spectrum for each ROIs
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- frame_rate (float)
- frame rate of the scope
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- path (str)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- ----------
- Returns
- ----------
- ps (nd.array)
- array containing the absolute power at each frequency for all ROIs
- ff_all_roi (nd.array)
- array containing the mean Fourier spectrum for each ROI
- """
- HZ = 27.77 # frequency of pulses within the stimulus (36ms IPI)
- ps = []
- mean_roi = np.zeros((dffs.shape[0],2*int(frame_rate) ))
- ff_all_roi = np.zeros((dffs.shape[0],2*int(frame_rate) )) # Contains the mean ff spectrum of each ROI
- # First we compute the mean activity over all blocks for each ROIs to subtract later
- for roi in range(dffs.shape[0]):
- ## Loop through the blocks
- for k in range(2,14):
- # Grab block
- t_sart = start_block_seconds[k]
- t_end = end_block_seconds[k]
- N = int((t_end-t_sart)*frame_rate)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = (np.abs(t_sart-time_activity)).argmin()
- end_act = (np.abs(t_end-time_activity)).argmin()
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- # store the block
- if k == 2:
- activity_block = dffs[roi, start_act:end_act]
- else:
- activity_block = np.vstack((activity_block,dffs[roi, start_act:end_act]))
- ## take the mean across all block for each roi
- mean_across_blocks = np.mean(activity_block, axis = 0)
- mean_roi[roi,:] = mean_across_blocks
- # compute the mean of all these means
- overall_mean = np.mean(mean_roi)
- # compute fourier
- for roi in range(mean_roi.shape[0]):
- # extract activity of each roi and subtract the overall mean
- activity_fourier = mean_roi[roi,:] - overall_mean
- N = len(activity_fourier)
- #Slice frequency range
- f_range = [HZ -0.77, HZ +0.33] # Frequency range of interesest (27-28Hz)
- freq_axis = rfftfreq(N, d = 1/frame_rate)
- f0 = np.argmin(np.abs(freq_axis- f_range[0]))
- f1 = np.argmin(np.abs(freq_axis- f_range[1]))
- #Compute fourier
- ff = np.abs(scipy.fft.fft(activity_fourier, norm = 'ortho' ))**2
- if roi == 0:
- ff_all_roi = ff
- else:
- ff_all_roi = np.vstack((ff_all_roi,ff))
- #compute abstolute power
- p = np.sum(ff[f0:f1])
- ps.append(p)
- return ps , ff_all_roi
- def plot_power_ROIs_all(ps,ps_shuffled,ps_off, path,title):
- """
- Description
- ----------
- This function plots the absolute at 27-28Hz for the three groups (stim on, stim off and shuffled activity)
- ----------
- Parameters
- ----------
- ps (nd.array)
- array containing the absolute power at 27-28Hz for all ROIs when stimulus is on
- ps_shuffled (nd.array)
- array containing the absolute power at 27-28Hz for all ROIs when stimulus is on and the activity is shuffled
- ps_off (nd.array)
- array containing the absolute power at 27-28Hz for all ROIs when stimulus is off
- path (str)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- ----------
- """
- plt.figure(figsize=(11, 6))
- plt.plot(np.random.normal(0.1,0.025, size = len(ps)),ps,'g.',alpha = 0.3)#+'.'
- plt.plot(np.random.normal(0.9,0.025, size = len(ps_shuffled)),ps_shuffled,'m.',alpha = 0.3)#+'.'
- plt.plot(np.random.normal(0.5,0.025, size = len(ps_off)),ps_off,'k.',alpha = 0.3)#+'.'
- plt.bar([0.1],np.mean(ps),yerr = np.std(ps),capsize = 5, color = 'white',edgecolor = 'g', width = 0.1)
- plt.bar([0.9],np.mean(ps_shuffled),yerr = np.std(ps_shuffled),capsize = 5, color = 'white',edgecolor = 'm', width = 0.1)
- plt.bar([0.5],np.mean(ps_off),yerr = np.std(ps_off),capsize = 5, color = 'white',edgecolor = 'k', width = 0.1)
- plt.xticks([])
- plt.yticks(fontsize = 22)
- plt.ylabel('Absolute power at [27-28] Hz', fontsize = 20)
- plt.locator_params(axis='y', nbins=3)
- plt.ylim(0,0.9)
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'absolute_power'+ title +'.pdf', transparent = True)
- def plot_power_ROIs_all_no_shuffle(ps,ps_off, path,title):
- """
- Description
- ----------
- This function plots the absolute at 27-28Hz for the three groups (stim on, stim off)
- ----------
- Parameters
- ----------
- ps (nd.array)
- array containing the absolute power at 27-28Hz for all ROIs when stimulus is on
- ps_off (nd.array)
- array containing the absolute power at 27-28Hz for all ROIs when stimulus is off
- path (str)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- ----------
- """
- plt.figure(figsize=(11, 6))
- plt.plot(np.random.normal(0.25,0.025, size = len(ps)),ps,'g.',alpha = 0.3)#+'.'
- plt.plot(np.random.normal(0.6,0.025, size = len(ps_off)),ps_off,'k.',alpha = 0.3)#+'.'
- plt.bar([0.25],np.mean(ps),yerr = np.std(ps),capsize = 5, color = 'white',edgecolor = 'g', width = 0.1)
- plt.bar([0.6],np.mean(ps_off),yerr = np.std(ps_off),capsize = 5, color = 'white',edgecolor = 'k', width = 0.1)
- plt.xticks([])
- plt.yticks(fontsize = 22)
- plt.ylabel('Absolute power at [27-28] Hz', fontsize = 20)
- plt.locator_params(axis='y', nbins=3)
- plt.ylim(0,0.9)
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'absolute_power'+ title +'.pdf', transparent = True)
- def peaks_fourier_ROI_combine(dffs,start_block_seconds,end_block_seconds,t_added,Hz,time_activity,scope,N_target,top_peaks,path_fig):
- """
- Description
- ----------
- This function computes the fourier of each roi for each block and extract the peak frequency of the spectrum.
- It also plots the fourier spectrum for each block of a representative ROI.
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- start_block_seconds (np.ndarray)
- array containing the start of each block in seconds
- end_block_seconds (np.ndarray)
- array containing the end of each block in seconds
- t_added (float)
- Time to add around each block for visualization purposes
- frame_rate (float)
- frame rate of the scope
- time_activity (np.ndarray)
- array containing the time for each calcium trace
- scope (str)
- 'LB' or '2p' to specify which scope was used to aquire the data
- N_target (float)
- Target number of samples when interpolating 2p
- top_peaks (int)
- the number of peaks to extract per spectrum
- path_fig (float)
- Path to folder where to save the plots. If set to None, plots won't be saved.
- ----------
- Returns
- ----------
- peak_freq (np.ndarray)
- contains the absolute peak frequency of the Fourier for each block of each ROI.
- ----------
- """
- if scope == 'LB':
- roi_to_plot = 1596
- col = 'g'
- else:
- roi_to_plot = 890
- col = 'm'
- mean_blocks = []
- # First we grab mean of each block to subtract later
- b = 1 # Index that keeps track of the stimulus blocks
- for k in range(6):
- # Grab first block
- t_sart = start_block_seconds[b]
- t_end = end_block_seconds[b] +t_added
- N = int((t_end-t_sart)*Hz)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block1 = zscore(dffs[:,start_act:end_act],axis = 1)
- # Grab second block
- t_sart = start_block_seconds[b+1]
- t_end = end_block_seconds[b+1] + t_added
- N = int((t_end-t_sart)*Hz)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block2 = zscore(dffs[:,start_act:end_act],axis = 1)
- # Combine both blocks
- activity_both_block = np.vstack((activity_block1,activity_block2))
- mean_blocks.append(np.mean(activity_both_block))
- b +=2 # move to next pair
- HZ = [0.25,0.5,1,2,3,5]
- peak_freq = np.zeros((6,top_peaks*dffs.shape[0]))
- for roi, i in enumerate(dffs):
- b=1 # Index that keeps track of the stimulus blocks
- for k in range(6):
- #extract first block
- t_sart = start_block_seconds[b]
- t_end = end_block_seconds[b] +t_added
- N = int((t_end-t_sart)*Hz)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block1 = zscore(dffs[:,start_act:end_act][roi,:])
- # Grab second block
- t_sart = start_block_seconds[b+1]
- t_end = end_block_seconds[b+1] + t_added
- N = int((t_end-t_sart)*Hz)
- t_act = np.linspace(t_sart,t_end,N)
- start_act = np.argwhere((t_sart-time_activity)<0.001)[0][0]
- end_act = np.argwhere((t_end-time_activity)<0.001)[0][0]
- if (end_act-start_act)>(len(t_act)):
- end_act = end_act - ((end_act-start_act)-len(t_act))
- activity_block2 = zscore(dffs[:,start_act:end_act][roi,:])
- #Combine both blocks
- activity_both_block = np.vstack((activity_block1,activity_block2))
- mean_both_block = np.mean(activity_both_block,axis = 0)
- # subtract the overall mean during these two blocks
- ff_mean = np.abs(scipy.fft.fft(mean_both_block - mean_blocks[k] ))**2 #
- N_mean = len(mean_both_block)
- normalize = int(N_mean/2)+1
- # Interpolate 2-photon spectrum to match light bead frequency axis
- if scope == '2p':
- # Frequency axes
- time_inter = rfftfreq(N_target, d = 1/Hz)
- freq_2p = rfftfreq(N, d = 1/Hz)
- interp_func = interp1d(freq_2p, ff_mean[:normalize], kind='linear')
- ff_mean = interp_func(time_inter)
- normalize = int(N_target/2)+1
- if roi == roi_to_plot:
- plt.figure()
- plt.plot(rfftfreq(N_target, d = 1/Hz), ff_mean[:normalize], color = col)
- plt.xlabel('Frequency[Hz]',fontsize = 24)
- plt.ylabel('Amplitude',fontsize = 24)
- plt.xticks(fontsize =22)
- plt.yticks(fontsize =22)
- plt.tight_layout()
- if path_fig != None:
- plt.savefig(path_fig + 'spectrum_ROI_' + str(roi) + '_' + str(HZ[k]) + '_LB.pdf', transparent = True)
- # extract peaks
- if scope == 'LB':
- peaks = find_peaks(ff_mean[:normalize],prominence = 0.7)
- else:
- peaks = find_peaks(ff_mean,prominence = 0.7)
- if len(peaks[0])>0:
- # Find the index from the maximum peak
- i_max_peak = peaks[0][np.argmax(ff_mean[peaks[0]])]
- #second_highest_peak_index = peaks[0][np.argpartition(ff_mean[peaks[0]],-2)[-2]]
- # Find the x value from that index
- x_max = rfftfreq(N_target, d = 1/Hz)[i_max_peak]
- peak_freq[k,roi] = x_max
- #if top_peaks == 2:
- # peak_freq[k,roi+dffs.shape[0]] = rfftfreq(N_target, d = 1/Hz) [second_highest_peak_index]
- b=b+2 # move to next pair
- return(peak_freq)
- def crosscorr_sort_corr(dffs,stimulus,threshold_test,cutoff,frame_rate):
- """
- Description
- ----------
- This function computes extract the number of ROIs for each correlation coefficient between activity and the auditory stimulus
- ----------
- Parameters
- ----------
- dffs (np.ndarray)
- Array containing the calcium activity over time, each row is an ROI.
- stimulus (np.ndarray)
- array containing the auditory stimulus
- threshold_test (np.ndarray)
- Contains all of the coefficient values over which to extract the number of ROIs
- cutoff (float)
- threshold in percentage to use when extracting the top X% based on correlation
- max_lag (float)
- the lag over which to compute the cross correlation
- frame_rate (float)
- frame rate of the scope
- ----------
- Returns
- ----------
- n_roi
- the number of ROIs extracted for each correlation coefficient
- corr_coeff
- array containing the correlation coefficient of the top 'cutoff'% of ROIs with the highest correlation with the stimulus
- ----------
- """
- # Normalize dffs and the stimulus
- dffs_mean = dffs.mean(axis=1, keepdims=True)
- dffs_std = dffs.std(axis=1, keepdims=True)
- dffs_normalized = (dffs - dffs_mean) / dffs_std
- stimulus_normalized = (stimulus - stimulus.mean()) / stimulus.std()
- # Compute correlations
- correlations = np.dot(dffs_normalized, stimulus_normalized.T) / dffs_normalized.shape[1]
- correlations = correlations.flatten()
- rois = np.arange(0,dffs.shape[0])
- rois = rois[~np.isnan(correlations)]
- correlations = correlations[~np.isnan(correlations)]
- #sort the correlation and roi index
- sorted_indices = np.argsort(correlations)
- sorted_corr = correlations[sorted_indices]
- index_cutoff = int(dffs.shape[0] * (cutoff/100))
- corr_coeff = sorted_corr[-index_cutoff:]
- n_roi = []
- for t in threshold_test:
- n_roi.append( np.where(sorted_corr>t)[0].shape[0] )
- return(n_roi, corr_coeff)
- def assign_depths(n_rois: int, slice_depths: np.ndarray):
- """
- Assigns depths to each ROI given the total number of ROIs and slice depths.
- Parameters
- ----------
- n_rois : int
- Total number of ROIs (number of rows in your 2D array).
- slice_depths : np.ndarray
- 1D array of shape (n_slices,) containing the depth for each slice.
- Returns
- -------
- roi_depths : np.ndarray
- 1D array of shape (n_rois,) where each entry is the depth of the slice
- corresponding to that ROI.
- """
- n_slices = len(slice_depths)
- rois_per_slice = n_rois // n_slices # assumes equal number of ROIs per slice
- if n_rois % n_slices != 0:
- raise ValueError("Number of ROIs is not evenly divisible by number of slices.")
- # Repeat each slice depth rois_per_slice times
- roi_depths = np.repeat(slice_depths, rois_per_slice)
- return roi_depths
- def plot_distribution_peaks_fourier_ROIs(freq_block,scope, color, path):
- HZ = [0.25,0.5,1,2,3,5]
- # define bins
- if scope == 'LB':
- bins=np.arange(0,14.040,0.40)
- xlim = 14
- if scope == '2p':
- bins = np.arange(0,1.1,0.08)
- xlim = 1.1
- # Plot histogram
- for k in range(len(freq_block)):
- plt.figure()
- plt.hist(freq_block[k], bins=bins, color = color)
- plt.xticks(fontsize = 22)
- plt.yticks(fontsize = 22)
- plt.xlabel('Frequency (Hz)', fontsize = 24)
- plt.ylabel('Count', fontsize = 24)
- plt.xlim(0,xlim)
- plt.tight_layout()
- if path != None:
- plt.savefig(path + 'hist_fourier_peaks_' + str(HZ[k]) + '_' + scope + '.pdf', transparent = True)
- def circular_shift_null_corr_prestandardized(
- dffs_z: np.ndarray, # (n_rois, T) mean-zero (z-scored) ROI traces
- stim_z: np.ndarray, # (T,) mean-zero (z-scored) stimulus
- n_shuffles: int,
- exclude_lags: int = 0, # ignored when 'shifts' is provided
- seed: Optional[int] = None,
- batch_size: int = 256, # shifts per batch (tune for RAM/BLAS)
- dtype: np.dtype = np.float32,
- shifts: Optional[np.ndarray] = None, # (n_shuffles,) specific circular shifts to use
- ) -> Tuple[np.ndarray, np.ndarray]:
- """
- Compute Pearson correlations between each ROI and circularly-shifted stimulus versions.
- If 'shifts' is provided (ints in [0, T)), it is used directly and 'exclude_lags' is ignored.
- Otherwise, draws 'n_shuffles' random shifts, optionally excluding small lags.
- Returns
- -------
- r_null : (n_rois, n_shuffles) correlations (one column per shift)
- shifts : (n_shuffles,) the shift used for each column (np.int64)
- """
- X = np.asarray(dffs_z, dtype=dtype, order="C")
- s = np.asarray(stim_z, dtype=dtype).reshape(-1)
- n_rois, T = X.shape
- if s.shape[0] != T:
- raise ValueError("stim_z length must equal number of columns in dffs_z")
- Xnorm = np.linalg.norm(X, axis=1, keepdims=True).astype(dtype)
- Xnorm[Xnorm == 0] = 1.0
- Xu = X / Xnorm
- s_norm = float(np.linalg.norm(s))
- if s_norm == 0:
- return np.zeros((n_rois, n_shuffles), dtype=dtype), np.zeros(n_shuffles, dtype=np.int64)
- s_u = s / s_norm
- if shifts is not None:
- shifts = np.asarray(shifts, dtype=np.int64).ravel()
- if shifts.size != n_shuffles:
- raise ValueError("len(shifts) must equal n_shuffles.")
- shifts %= T
- else:
- rng = np.random.default_rng(seed)
- if exclude_lags <= 0:
- shifts = rng.integers(0, T, size=n_shuffles, endpoint=False, dtype=np.int64)
- else:
- mask = np.ones(T, dtype=bool)
- mask[:exclude_lags+1] = False
- if exclude_lags > 0:
- mask[T-exclude_lags:] = False
- allowed = np.nonzero(mask)[0].astype(np.int64)
- if allowed.size == 0:
- raise ValueError("Exclusion window too large: no shifts remain.")
- shifts = rng.choice(allowed, size=n_shuffles, replace=True)
- r_null = np.empty((n_rois, n_shuffles), dtype=dtype)
- t = np.arange(T, dtype=np.int64)[:, None]
- for start in range(0, n_shuffles, batch_size):
- end = min(start + batch_size, n_shuffles)
- k = shifts[start:end]
- idx = (t - k[None, :]) % T
- S_batch = s_u[idx]
- r_null[:, start:end] = Xu @ S_batch
- return r_null, shifts
- def allowed_circ_shifts(T, fs, period_sec, E_sec):
- period = int(round(period_sec * fs))
- E = int(round(E_sec * fs))
- allowed = np.ones(T, dtype=bool)
- if period > 0:
- n_mult = int(np.ceil(T / period)) + 1
- for k in range(n_mult):
- center = (k * period) % T
- lo = (center - E) % T
- hi = (center + E) % T
- if lo <= hi:
- allowed[lo:hi+1] = False
- else:
- allowed[:hi+1] = False
- allowed[lo:] = False
- if not np.any(allowed):
- raise ValueError("Exclusion too wide—no shifts left.")
- return np.flatnonzero(allowed)
- def block_permute_null_corr_prestandardized(
- dffs_z: np.ndarray, # (n_rois, T), mean-zero (z-scored)
- stim_z: np.ndarray, # (T,), mean-zero (z-scored)
- fs: float, # Hz
- n_shuffles: int,
- block_sec: float = 10.0, # seconds
- jitter_within_block: int = 0,# ±samples to circularly roll inside each block
- seed: Optional[int] = None,
- batch_size: int = 128,
- dtype: np.dtype = np.float32,
- forbid_identity_perm: bool = True,
- ) -> Tuple[np.ndarray, np.ndarray]:
- """
- Permute stimulus in contiguous blocks (optionally with within-block jitter) and
- compute Pearson r for each ROI vs each permuted stimulus.
- Returns:
- r_null : (n_rois, n_shuffles)
- perms : (n_shuffles, n_blocks)
- """
- X = np.asarray(dffs_z, dtype=dtype, order="C")
- s = np.asarray(stim_z, dtype=dtype).reshape(-1)
- n_rois, T = X.shape
- if s.shape[0] != T:
- raise ValueError("stim_z length must equal number of columns in dffs_z")
- Xnorm = np.linalg.norm(X, axis=1, keepdims=True).astype(dtype)
- Xnorm[Xnorm == 0] = 1.0
- Xu = X / Xnorm
- s_norm = float(np.linalg.norm(s))
- if s_norm == 0:
- return np.zeros((n_rois, n_shuffles), dtype=dtype), np.empty((n_shuffles, 0), dtype=np.int64)
- s_u = s / s_norm
- block_len = int(round(block_sec * fs))
- if block_len <= 0:
- raise ValueError("block_sec too small for the given fs")
- BI = _make_block_indices(T, block_len) # (block_len, n_blocks)
- block_len_eff, n_blocks = BI.shape
- rng = np.random.default_rng(seed)
- r_null = np.empty((n_rois, n_shuffles), dtype=dtype)
- perms_used = np.empty((n_shuffles, n_blocks), dtype=np.int64)
- for start in range(0, n_shuffles, batch_size):
- end = min(start + batch_size, n_shuffles)
- B = end - start
- perms = np.empty((B, n_blocks), dtype=np.int64)
- for b in range(B):
- if forbid_identity_perm and n_blocks == 1:
- perms[b] = np.array([0], dtype=np.int64)
- else:
- while True:
- p = rng.permutation(n_blocks)
- if not forbid_identity_perm or np.any(p != np.arange(n_blocks)):
- break
- perms[b] = p
- if jitter_within_block > 0:
- J = int(jitter_within_block)
- jit = rng.integers(-J, J + 1, size=(B, n_blocks), endpoint=True, dtype=np.int64)
- else:
- jit = np.zeros((B, n_blocks), dtype=np.int64)
- idx_batch = np.empty((T, B), dtype=np.int64)
- for b in range(B):
- cols = []
- for j in range(n_blocks):
- col = BI[:, perms[b, j]]
- if jitter_within_block != 0:
- col = np.roll(col, jit[b, j], axis=0)
- cols.append(col)
- idx_b = np.concatenate(cols, axis=0)[:T]
- idx_batch[:, b] = idx_b
- S_batch = s_u[idx_batch]
- r_null[:, start:end] = Xu @ S_batch
- perms_used[start:end] = perms
- return r_null, perms_used
- def _make_block_indices(T: int, block_len: int) -> np.ndarray:
- """
- Return BI of shape (block_len, n_blocks) with absolute indices per block.
- Pads the final block by repeating its last index so each column has block_len rows.
- """
- if block_len <= 0:
- raise ValueError("block_len must be positive")
- n_blocks = int(np.ceil(T / block_len))
- pad = n_blocks * block_len - T
- idx = np.arange(T, dtype=np.int64)
- if pad > 0:
- idx = np.concatenate([idx, np.full(pad, idx[-1], dtype=np.int64)])
- return idx.reshape(n_blocks, block_len).T # (block_len, n_blocks)
- def permutation_pvals_one_sided(r_obs, r_null, alternative="greater"):
- """
- One-sided permutation p-values with finite-sample correction.
- p_i = (1 + #{ r_null >= r_obs_i }) / (N_i + 1) if alternative == "greater"
- p_i = (1 + #{ r_null <= r_obs_i }) / (N_i + 1) if alternative == "less"
- Parameters
- ----------
- r_obs : array-like, shape (n_rois,)
- Observed statistics (e.g., correlations) per ROI.
- r_null : array-like, shape (n_rois, n_perm) or (n_perm,)
- Null statistics from permutations. If 2D, each row is that ROI's null.
- If 1D, a pooled null used for all ROIs.
- alternative : {"greater","less"}, default "greater"
- Direction of the one-sided test.
- Returns
- -------
- pvals : ndarray, shape (n_rois,)
- One-sided permutation p-values.
- """
- r_obs = np.asarray(r_obs, dtype=np.float64).reshape(-1)
- r_null = np.asarray(r_null, dtype=np.float64)
- if alternative not in ("greater", "less"):
- raise ValueError("alternative must be 'greater' or 'less'.")
- if r_null.ndim == 1:
- valid = ~np.isnan(r_null)
- N = int(valid.sum())
- if N == 0:
- return np.ones_like(r_obs)
- rn = r_null[valid][None, :]
- if alternative == "greater":
- counts = (rn >= r_obs[:, None]).sum(axis=1)
- else:
- counts = (rn <= r_obs[:, None]).sum(axis=1)
- pvals = (1.0 + counts) / (N + 1.0)
- return pvals
- elif r_null.ndim == 2:
- if r_null.shape[0] != r_obs.shape[0]:
- raise ValueError("For per-ROI nulls, r_null must have shape (n_rois, n_perm).")
- valid = ~np.isnan(r_null)
- N = valid.sum(axis=1).astype(np.int64)
- if alternative == "greater":
- ge = (r_null >= r_obs[:, None]) & valid
- else:
- ge = (r_null <= r_obs[:, None]) & valid
- counts = ge.sum(axis=1)
- denom = N + 1.0
- denom[denom == 0] = np.inf
- pvals = (1.0 + counts) / denom
- pvals[np.isinf(denom)] = 1.0
- return pvals
- else:
- raise ValueError("r_null must be 1D (pooled) or 2D (per-ROI).")
functions.py at commit 55570f4, no license · at the source
Overview
- Princeton Neuroscience Institute, Princeton University, Princeton, NJ USA
- Center for the Physics of Biological Function, Princeton University, Princeton, NJ USA
- Department of Psychology, University of Washington, Seattle, WA USA
- Department of Physics, Princeton University, Princeton, NJ USA
- Bezos Center for Neural Circuit Dynamics, Princeton University, Princeton, NJ USA
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repositories
Its files are read in the Code ↔ Paper reader above, with 10 matches between paragraphs and lines of code.
murthylab/fly-vr
1a8705e9d9dd9b94d25addee484f2d966ccefa69, 12 February 2024Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
78 files
- experiments/
cl_fixation.py , Python, 17 lines - experiments/
cl_random_audio_daq.py , Python, 60 lines - experiments/
paired_random_audio_daq. , Python, 50 linespy - experiments/
print_ball_speed.py , Python, 33 lines - experiments/
print_state.py , Python, 11 lines - experiments/
switch_randomly_video1.p , Python, 21 linesy - flyvr/
__init__.py , Python, 1 line - flyvr/
analysis.py , Python, 276 lines - flyvr/
audio/ , Python, 1 line__init__.py - flyvr/
audio/ , Python, 47 linesattenuation.py - flyvr/
audio/ , Python, 659 linesio_task.py - flyvr/
audio/ , Python, 300 linessignal_producer.py - flyvr/
audio/ , Python, 545 linessound_server.py - flyvr/
audio/ , Python, 938 linesstimuli.py - flyvr/
audio/ , Python, 38 linesutil.py - flyvr/
common/ , Python, 415 lines__init__.py - flyvr/
common/ , Python, 318 linesbuild_arg_parser.py - flyvr/
common/ , Python, 152 linesconcurrent_task.py - flyvr/
common/ , Python, 5 linesdottable.py - flyvr/
common/ , Python, 83 linesinputimeout.py - flyvr/
common/ , Python, 226 linesipc.py - flyvr/
common/ , Python, 426 lineslogger.py - flyvr/
common/ , Python, 93 linesmmtimer.py - flyvr/
common/ , Python, 97 linesplot_task.py - flyvr/
common/ , Python, 64 linestools.py - flyvr/
control/ , Python, 1 line__init__.py - flyvr/
control/ , Python, 325 linesexperiment.py - flyvr/
fictrac/ , Python, 1 line__init__.py - flyvr/
fictrac/ , Python, 239 lines, 1 matchfictrac_driver.py - flyvr/
fictrac/ , Python, 159 linesplot_task.py - flyvr/
fictrac/ , Python, 167 linesreplay.py - flyvr/
fictrac/ , Python, 94 linesshmem_transfer_data.py - flyvr/
gui.py , Python, 136 lines - flyvr/
hwio/ , Python, 1 line__init__.py - flyvr/
hwio/ , Python, 225 linesphidget.py - flyvr/
main.py , Python, 180 lines - flyvr/
projector/ , Python, 1 line__init__.py - flyvr/
projector/ , Python, 391 linesdlplc_tcp.py - flyvr/
video/ , Python, 1 line__init__.py - flyvr/
video/ , Python, 595 linescamera_server.py - flyvr/
video/ , Python, 1,234 linesvideo_server.py - matlab/
Im2P_CreateNewMask.m , MATLAB, 202 lines - matlab/
Im2P_VR_FicTracCalibrati , MATLAB, 85 linesonCalc.m - matlab/
attenuateStim.m , MATLAB, 48 lines - matlab/
audio_calibration/ , MATLAB, 161 linesCalibraiteSound_Frequenc ies_flyVR.m - matlab/
audio_calibration/ , MATLAB, 232 lines, 1 matchCalibrateSound_WhiteNois e_flyVR.m - matlab/
audio_calibration/ , MATLAB, 745 linesprvGreyPlayback4.m - matlab/
fictrac_calibration/ , MATLAB, 194 linesCreateNewMask.m - matlab/
fictrac_calibration/ , MATLAB, 191 linesIm2P_CreateFilesFor_flyv r.m - matlab/
projector_calibration/ , MATLAB, 537 linesConfigureProjector.m - matlab/
projector_calibration/ , MATLAB, 376 linesDomeProjection_ForAdam02 082018.m - matlab/
projector_calibration/ , MATLAB, 307 linesInstallMex.m - matlab/
projector_calibration/ , C, 1,429 linesWindowAPI.c - matlab/
projector_calibration/ , MATLAB, 239 linesWindowAPI.m - matlab/
projector_calibration/ , MATLAB, 246 linesdemo_WindowAPI.m - matlab/
projector_calibration/ , MATLAB, 27 linesestimateDensity.m - matlab/
projector_calibration/ , MATLAB, 480 linesgenerateDome.m - matlab/
projector_calibration/ , MATLAB, 389 linesgenerateDome_1.m - matlab/
projector_calibration/ , MATLAB, 321 linesuTest_WindowAPI.m - matlab/
projector_calibration/ , Python, 63 lineswarpStim.py - setup.py, Python, 46 lines
- tests/
audio/ , Python, 60 linestest_attenuator.py - tests/
audio/ , Python, 215 linestest_audio_stim_playlist .py - tests/
audio/ , Python, 32 linestest_matfile_stim.py - tests/
audio/ , Python, 304 linestest_samplechunks.py - tests/
audio/ , Python, 114 linestest_signal_producer.py - tests/
audio/ , Python, 69 linestest_sin_stim.py - tests/
audio/ , Python, 42 linestest_sound_server.py - tests/
audio/ , Python, 11 linestest_square_stim.py - tests/
ball_control_phidget/ , Python, 69 linesball_control.py - tests/
common/ , Python, 13 linestest_arg_parser.py - tests/
common/ , Python, 123 linestest_logger.py - tests/
common/ , Python, 73 linestest_random.py - tests/
conftest.py , Python, 35 lines - tests/
scanimage_triggers.py , Python, 138 lines - tests/
test_daq.py , Python, 33 lines - tests/
test_fictrac.py , Python, 67 lines - README.md, Text, 386 lines
murthylab/lightbead-analysis
55570f4ad028bfd19ab63d5b9b13430803cb277c, 6 April 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
56 files
- analysis_scripts/
Fig3.py , Python, 225 lines - analysis_scripts/
Fig3_aligment.py , Python, 332 lines - analysis_scripts/
Fig4.py , Python, 284 lines - analysis_scripts/
Fig4_alignment.py , Python, 244 lines - analysis_scripts/
ROI_plotter.ipynb , Jupyter, 161 lines - analysis_scripts/
Suppl_figs.py , Python, 243 lines, 2 matches - analysis_scripts/
_aux.py , Python, 290 lines - analysis_scripts/
fig3_preprocessing.py , Python, 360 lines - analysis_scripts/
fig4_preprocessing.py , Python, 229 lines - analysis_scripts/
functions.py , Python, 1,483 lines, 5 matches - analysis_scripts/
volumetric_visualizer.m , MATLAB, 106 lines - dellaserver_processing/
batch_paramoco_AL.sh , Shell, 23 lines - dellaserver_processing/
batch_signal_to_supervox , Shell, 21 linesel_roi_AL.sh - dellaserver_processing/
batch_signal_to_supervox , Shell, 36 linesel_roi_RigE.sh - dellaserver_processing/
batch_tiff_to_dff.sh , Shell, 18 lines - dellaserver_processing/
batch_tiff_to_dff_concat , Shell, 30 lines_RigE.sh - dellaserver_processing/
batch_tiff_to_dff_mc_Rig , Shell, 28 linesE.sh - dellaserver_processing/
batch_tiff_to_dff_mean_b , Shell, 30 linesrain_RigE.sh - dellaserver_processing/
batch_tiff_to_local_atla , Shell, 25 liness_AL.sh - dellaserver_processing/
brainviz/ , Python, not shown here__MACOSX/ brainviz/ ._events.py - dellaserver_processing/
brainviz/ , Python, not shown here__MACOSX/ brainviz/ ._io.py - dellaserver_processing/
brainviz/ , Python, 1 line__init__.py - dellaserver_processing/
brainviz/ , Python, 167 linesevents.py - dellaserver_processing/
brainviz/ , Python, 110 linesfictrac.py - dellaserver_processing/
brainviz/ , Python, 91 linesio.py - dellaserver_processing/
brainviz/ , Python, 124 lineslocalatlas.py - dellaserver_processing/
brainviz/ , Python, 79 linesplotting.py - dellaserver_processing/
brainviz/ , Python, 32 linesprocessing.py - dellaserver_processing/
brainviz/ , Python, 131 lines, 1 matchroi.py - dellaserver_processing/
brainviz/ , Python, 72 linesvideowriter.py - dellaserver_processing/
brainviz/ , Python, 249 linesvolume.py - dellaserver_processing/
flydff/ , Python, not shown here._moco.py - dellaserver_processing/
flydff/ , Python, not shown here._vols.py - dellaserver_processing/
flydff/ , Python, 1 line__init__.py - dellaserver_processing/
flydff/ , Python, 94 linesmoco.py - dellaserver_processing/
flydff/ , Python, 94 linesmoco_LB.py - dellaserver_processing/
flydff/ , Python, 94 linesmoco_RigE.py - dellaserver_processing/
flydff/ , Python, 94 linesmoco_g.py - dellaserver_processing/
flydff/ , Python, 78 linesmoco_greenonly.py - dellaserver_processing/
flydff/ , Python, 51 linespsth2d.py - dellaserver_processing/
flydff/ , Python, 109 linessignal.py - dellaserver_processing/
flydff/ , Python, 148 linesviz.py - dellaserver_processing/
flydff/ , Python, 229 linesvols.py - dellaserver_processing/
flydff/ , Python, 229 linesvols_LB.py - dellaserver_processing/
flydff/ , Python, 401 linesvols_RigE.py - dellaserver_processing/
flydff/ , Python, 208 linesvols_green.py - dellaserver_processing/
signal_to_supervoxel_roi , Python, 157 lines.py - dellaserver_processing/
signal_to_supervoxel_roi , Python, 169 lines_RigE.py - dellaserver_processing/
tiff_to_dff.py , Python, 149 lines - dellaserver_processing/
tiff_to_dff_concat_RigE. , Python, 149 linespy - dellaserver_processing/
tiff_to_dff_mc_RigE.py , Python, 147 lines - dellaserver_processing/
tiff_to_dff_mean_brain_R , Python, 170 linesigE.py - dellaserver_processing/
tiff_to_local_atlas.py , Python, 126 lines - preprocessing/
I2C.m , MATLAB, 43 lines - preprocessing/
Sliding_window.m , MATLAB, 116 lines - README.md, Text, 21 lines
Code availability statement
The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to the authors' code: murthylab/
fly-vr , murthylab/lightbead-analysis
Read it in the paper: doi.org/10.1038/s41467-026-72437-1.
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:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 132 scripts, each with its path and the digest of its content;
- 10 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:17613016, at Zenodo; found in “Data availability”
- zenodo:17618684, at Zenodo; found in “Data availability”
Code and data availability statement
The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to 2 datasets: Zenodo 17613016, Zenodo 17618684
- it points to the authors' code: murthylab/
fly-vr , murthylab/lightbead-analysis
Read it in the paper: doi.org/10.1038/s41467-026-72437-1.
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, 30 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 2 keywords, 9 MeSH terms, 52 references.
Cite
This paper
Gauthey, W., Lin, A., Ahmed, O. M., Leifer, A. M., Murthy, M., & Thiberge, S. Y. (2026). High-speed whole-brain imaging in Drosophila. Nature communications, 17(1), 5810. https://
BibTeX
@article{gauthey2026high
author = {Gauthey, Wayan and Lin, Albert and Ahmed, Osama M and Leifer, Andrew M and Murthy, Mala and Thiberge, Stephan Y},
title = {{High-speed whole-brain imaging in Drosophila}},
journal = {Nature communications},
year = {2026},
month = apr,
volume = {17},
number = {1},
pages = {5810},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42050341},
pmcid = {PMC13328740}
}
RIS
TY - JOUR
AU - Gauthey, Wayan
AU - Lin, Albert
AU - Ahmed, Osama M
AU - Leifer, Andrew M
AU - Murthy, Mala
AU - Thiberge, Stephan Y
TI - High-speed whole-brain imaging in Drosophila
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 5810
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "High-speed whole-brain imaging in Drosophila",
"container-title": "Nature communications",
"author": [
{
"family": "Gauthey",
"given": "Wayan"
},
{
"family": "Lin",
"given": "Albert"
},
{
"family": "Ahmed",
"given": "Osama M"
},
{
"family": "Leifer",
"given": "Andrew M"
},
{
"family": "Murthy",
"given": "Mala"
},
{
"family": "Thiberge",
"given": "Stephan Y"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "5810",
"DOI": "10.1038/
"PMID": "42050341",
"PMCID": "PMC13328740",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
28
]
]
}
}
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.1038/s41586-026-10735-w [code]
- Distributed control circuits across a brain-and-cord connectome.Journal: NatureIn common: Plotly, scikit-image, scikit-learn, 4 other tools, drosophila, 4 references, author Mala Murthy
- [2] doi:10.1038/s41467-026-72057-9 [code]
- Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.Journal: Nature communicationsIn common: OpenCV, scikit-image, h5py, 7 other tools, drosophila, systems, 4 references
- [3] doi:10.1016/j.xpro.2026.104659 [code]
- Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila.Journal: STAR protocolsIn common: OpenCV, h5py, Pillow, 4 other tools, drosophila, 6 references
- [4] doi:10.1038/s41467-026-72710-3 [code]
- A modular multi-color fluorescence microscope for simultaneous tracking of cellular activity and behavior.Journal: Nature communicationsIn common: OpenCV, scikit-image, Pillow, 6 other tools, drosophila, 5 references
- [5] 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: Plotly, scikit-image, h5py, 6 other tools, systems, 4 references
- [6] doi:10.1038/s41592-026-03179-7 [code]
- Voltage imaging of neurons distributed across entire brains of larval zebrafish.Journal: Nature methodsIn common: OpenCV, scikit-image, h5py, 3 other tools, systems, 6 references
- [7] 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: SimpleITK, OpenCV, scikit-image, 5 other tools, systems, 4 references
- [8] doi:10.1002/alz.71649 [code]
- Postmortem brain MRI reveals differential associations of subcortical and limbic volumes with cortical thinning and neurodegenerative pathologies.Journal: Alzheimer's & dementia : the journal of the Alzheimer's AssociationIn common: SimpleITK, ANTs, Plotly, 9 other tools
- [9] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: OpenCV, scikit-image, h5py, 9 other tools, systems
- [10] doi:10.21203/rs.3.rs-9326213/v1 [code]
- Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brainJournal: Research Square (preprint)In common: PsychoPy, ANTs, Plotly, 8 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: 2 repositories of the authors' code, each at its verified commit and with its license, 132 scripts, and 10 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:bb234f65e78e3018…
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.
