Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior.
The 2 matches
- [1] § MATERIALS AND METHODS › Population trajectory analysis ↔ 2AFC/Modules/reader.py, lines 643–699 · score 0.56 · Savitzky Golay filtering, polynomial, window, neural
- [2] § MATERIALS AND METHODS › Population trajectory analysis ↔ 2p_2AFC_reg_version/Modules/reader.py, lines 640–696 · score 0.56 · Savitzky Golay filtering, polynomial, window, neural
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 · 706 lines · 35 KB · no license · 1 match
- # Liberary for reading session data from multiple directories
- import os
- import numpy as np
- import h5py
- import shutil
- import pandas as pd
- import scipy.io as sio
- from scipy.signal import savgol_filter
- # First fuction to read for reading sessions data from multiple directories
- def read_ops(list_session_data_path):
- """
- Reads and processes operation data from multiple session data paths.
- This function loads operation data stored in 'ops.npy' files from a list of session
- directories, updates each operation dictionary with its corresponding session path,
- and returns a list of these operation dictionaries.
- Args:
- list_session_data_path (list): A list of strings, where each string is a path to
- a directory containing an 'ops.npy' file with operation data.
- Returns:
- list: A list of dictionaries, where each dictionary contains the operation data
- loaded from an 'ops.npy' file, with an additional 'save_path0' key pointing
- to the corresponding session directory.
- Notes:
- - The function uses `numpy.load` with `allow_pickle=True` to load the 'ops.npy'
- files, which are expected to contain pickled Python objects (dictionaries).
- - The 'ops.npy' file must exist in each session directory specified in
- `list_session_data_path`.
- - The function assumes that the loaded `ops` data is a dictionary and modifies it
- by adding or updating the 'save_path0' key with the session directory path.
- Example:
- >>> session_paths = ['/path/to/session1', '/path/to/session2']
- >>> ops_list = read_ops(session_paths)
- >>> print(ops_list[0]['save_path0'])
- '/path/to/session1'
- Raises:
- FileNotFoundError: If an 'ops.npy' file is not found in any of the specified
- session directories.
- ValueError: If the loaded 'ops.npy' file does not contain a dictionary.
- """
- list_ops = []
- for session_data_path in list_session_data_path:
- ops = np.load(
- os.path.join(session_data_path, 'ops.npy'),
- allow_pickle=True).item()
- ops['save_path0'] = os.path.join(session_data_path)
- list_ops.append(ops)
- return list_ops
- def create_memmap(data, dtype, mmap_path):
- """
- Creates a memory-mapped array from input data and saves it to a specified file path.
- This function creates a memory-mapped array with the same shape and data type as the
- input data, writes the input data to the memory-mapped file, and returns the
- memory-mapped array for further use.
- Args:
- data (numpy.ndarray): The input NumPy array whose data will be written to the
- memory-mapped file.
- dtype (numpy.dtype): The data type of the memory-mapped array.
- mmap_path (str): The file path where the memory-mapped array will be stored.
- Returns:
- numpy.memmap: A memory-mapped array with the specified shape and data type,
- containing the data from the input array.
- Notes:
- - The memory-mapped array is created in 'w+' mode, which creates a new file or
- overwrites an existing file for reading and writing.
- - The function uses `numpy.memmap` to create the memory-mapped array, which allows
- efficient handling of large arrays by mapping them directly to disk.
- - The input `data` is copied into the memory-mapped array using slice assignment.
- Example:
- >>> import numpy as np
- >>> data = np.array([[1, 2], [3, 4]], dtype=np.int32)
- >>> mmap_arr = create_memmap(data, np.int32, 'data.mmap')
- >>> print(mmap_arr)
- [[1 2]
- [3 4]]
- Raises:
- ValueError: If the `dtype` does not match the data type of the input `data` or if
- the `mmap_path` is invalid.
- OSError: If there are issues with file creation or access at `mmap_path`.
- """
- memmap_arr = np.memmap(mmap_path, dtype=dtype, mode='w+', shape=data.shape)
- memmap_arr[:] = data[...]
- return memmap_arr
- def get_memmap_path(ops, h5_file_name):
- """
- Generates paths for memory-mapped data and the corresponding HDF5 file.
- This function creates a directory for memory-mapped data based on the provided HDF5
- file name and constructs paths for both the memory-mapped directory and the HDF5 file.
- The memory-mapped directory is created under a 'memmap' subdirectory within the
- session's save path if it does not already exist.
- Args:
- ops (dict): A dictionary containing session metadata, including the key
- 'save_path0' which specifies the base directory path for the session.
- h5_file_name (str): The name of the HDF5 file (e.g., 'data.h5').
- Returns:
- tuple: A tuple containing two strings:
- - mm_path (str): The path to the memory-mapped data directory.
- - file_path (str): The path to the HDF5 file.
- Notes:
- - The memory-mapped directory is named after the HDF5 file name (without its
- extension) and is created under '<save_path0>/memmap/'.
- - If the memory-mapped directory does not exist, it is created automatically.
- - The function assumes that 'save_path0' exists in the `ops` dictionary and is a
- valid directory path.
- Example:
- >>> ops = {'save_path0': '/path/to/session'}
- >>> h5_file_name = 'data.h5'
- >>> mm_path, file_path = get_memmap_path(ops, h5_file_name)
- >>> print(mm_path)
- '/path/to/session/memmap/data'
- >>> print(file_path)
- '/path/to/session/data.h5'
- Raises:
- KeyError: If 'save_path0' is not present in the `ops` dictionary.
- OSError: If there are issues creating the memory-mapped directory or accessing
- the file system.
- """
- mm_folder_name, _ = os.path.splitext(h5_file_name)
- if not os.path.exists(os.path.join(ops['save_path0'], 'memmap', mm_folder_name)):
- os.makedirs(os.path.join(ops['save_path0'], 'memmap', mm_folder_name))
- mm_path = os.path.join(ops['save_path0'], 'memmap', mm_folder_name)
- file_path = os.path.join(ops['save_path0'], h5_file_name)
- return mm_path, file_path
- def read_masks(ops):
- """
- Reads mask-related data from an HDF5 file and creates memory-mapped arrays.
- This function retrieves paths for memory-mapped data and an HDF5 file using
- `get_memmap_path`, then loads specific datasets from the HDF5 file into memory-mapped
- arrays. It handles both functional and anatomical data, with anatomical data loaded
- only if the session has two channels.
- Args:
- ops (dict): A dictionary containing session metadata, including:
- - 'save_path0': The base directory path for the session.
- - 'nchannels': The number of channels in the session (e.g., 1 or 2).
- Returns:
- list: A list containing the following memory-mapped arrays (or None for anatomical
- data if `ops['nchannels'] != 2`):
- - labels (numpy.memmap): Array of integer labels (dtype: int8).
- - masks (numpy.memmap): Functional mask data (dtype: float32).
- - mean_func (numpy.memmap): Mean functional data (dtype: float32).
- - max_func (numpy.memmap): Maximum functional data (dtype: float32).
- - mean_anat (numpy.memmap or None): Mean anatomical data (dtype: float32) if
- `ops['nchannels'] == 2`, otherwise None.
- - masks_anat (numpy.memmap or None): Anatomical mask data (dtype: float32) if
- `ops['nchannels'] == 2`, otherwise None.
- Notes:
- - The function assumes the HDF5 file 'masks.h5' exists in the session directory
- specified by `ops['save_path0']` and contains the datasets 'labels',
- 'masks_func', 'mean_func', 'max_func', and optionally 'mean_anat' and
- 'masks_anat' for two-channel data.
- - Memory-mapped arrays are created using the `create_memmap` function and stored
- in a 'memmap' subdirectory under the session path.
- - The HDF5 file is accessed in read-only mode ('r') using `h5py.File`.
- Example:
- >>> ops = {'save_path0': '/path/to/session', 'nchannels': 2}
- >>> result = read_masks(ops)
- >>> print(result[0]) # Access the labels memory-mapped array
- <numpy.memmap object with dtype=int8>
- Raises:
- KeyError: If required keys ('save_path0', 'nchannels') are missing from `ops` or
- required datasets are missing from the HDF5 file.
- FileNotFoundError: If the 'masks.h5' file does not exist at the specified path.
- OSError: If there are issues creating memory-mapped files or accessing the file
- system.
- """
- mm_path, file_path = get_memmap_path(ops, 'masks.h5')
- with h5py.File(file_path, 'r') as f:
- labels = create_memmap(f['labels'], 'int8', os.path.join(mm_path, 'labels.mmap'))
- masks = create_memmap(f['masks_func'], 'float32', os.path.join(mm_path, 'masks_func.mmap'))
- mean_func = create_memmap(f['mean_func'], 'float32', os.path.join(mm_path, 'mean_func.mmap'))
- max_func = create_memmap(f['max_func'], 'float32', os.path.join(mm_path, 'max_func.mmap'))
- mean_anat = create_memmap(f['mean_anat'], 'float32', os.path.join(mm_path, 'mean_anat.mmap')) if ops['nchannels'] == 2 else None
- masks_anat = create_memmap(f['masks_anat'], 'float32', os.path.join(mm_path, 'masks_anat.mmap')) if ops['nchannels'] == 2 else None
- return [labels, masks, mean_func, max_func, mean_anat, masks_anat]
- def read_raw_voltages(ops):
- """
- Reads raw voltage data from an HDF5 file and creates memory-mapped arrays.
- This function retrieves paths for memory-mapped data and an HDF5 file using
- `get_memmap_path`, then loads specific voltage-related datasets from the HDF5 file
- into memory-mapped arrays. The datasets include timestamps and various voltage signals
- related to experimental events (e.g., visual stimuli, audio stimuli, imaging triggers).
- Args:
- ops (dict): A dictionary containing session metadata, including:
- - 'save_path0': The base directory path for the session.
- Returns:
- list: A list containing the following memory-mapped arrays:
- - vol_time (numpy.memmap): Timestamps for voltage signals (dtype: float32).
- - vol_start (numpy.memmap): Trial start trigger signals (dtype: int8).
- - vol_stim_vis (numpy.memmap): Visual stimulus signals (dtype: int8).
- - vol_img (numpy.memmap): Imaging trigger signals (dtype: int8).
- - vol_hifi (numpy.memmap): HiFi (audio) trigger signals (dtype: int8).
- - vol_stim_aud (numpy.memmap): Audio stimulus signals (dtype: float32).
- - vol_flir (numpy.memmap): FLIR camera trigger signals (dtype: int8).
- - vol_pmt (numpy.memmap): PMT (photomultiplier tube) signals (dtype: int8).
- - vol_led (numpy.memmap): LED trigger signals (dtype: int8).
- Notes:
- - The function assumes the HDF5 file 'raw_voltages.h5' exists in the session
- directory specified by `ops['save_path0']` and contains a 'raw' group with
- datasets: 'vol_time', 'vol_start', 'vol_stim_vis', 'vol_img', 'vol_hifi',
- 'vol_stim_aud', 'vol_flir', 'vol_pmt', and 'vol_led'.
- - Memory-mapped arrays are created using the `create_memmap` function and stored
- in a 'memmap' subdirectory under the session path.
- - The HDF5 file is accessed in read-only mode ('r') using `h5py.File`.
- Example:
- >>> ops = {'save_path0': '/path/to/session'}
- >>> voltages = read_raw_voltages(ops)
- >>> print(voltages[0]) # Access the vol_time memory-mapped array
- <numpy.memmap object with dtype=float32>
- Raises:
- KeyError: If required keys (e.g., 'save_path0') are missing from `ops` or if
- expected datasets are missing in the HDF5 file.
- FileNotFoundError: If the 'raw_voltages.h5' file does not exist at the specified
- path.
- OSError: If there are issues creating memory-mapped files or accessing the file
- system.
- """
- mm_path, file_path = get_memmap_path(ops, 'raw_voltages.h5')
- with h5py.File(file_path, 'r') as f:
- vol_time = create_memmap(f['raw']['vol_time'], 'float32', os.path.join(mm_path, 'vol_time.mmap'))
- vol_start = create_memmap(f['raw']['vol_start'], 'int8', os.path.join(mm_path, 'vol_start.mmap'))
- vol_stim_vis = create_memmap(f['raw']['vol_stim_vis'], 'int8', os.path.join(mm_path, 'vol_stim_vis.mmap'))
- vol_hifi = create_memmap(f['raw']['vol_hifi'], 'int8', os.path.join(mm_path, 'vol_hifi.mmap'))
- vol_img = create_memmap(f['raw']['vol_img'], 'int8', os.path.join(mm_path, 'vol_img.mmap'))
- vol_stim_aud = create_memmap(f['raw']['vol_stim_aud'], 'float32', os.path.join(mm_path, 'vol_stim_aud.mmap'))
- vol_flir = create_memmap(f['raw']['vol_flir'], 'int8', os.path.join(mm_path, 'vol_flir.mmap'))
- vol_pmt = create_memmap(f['raw']['vol_pmt'], 'int8', os.path.join(mm_path, 'vol_pmt.mmap'))
- vol_led = create_memmap(f['raw']['vol_led'], 'int8', os.path.join(mm_path, 'vol_led.mmap'))
- return [vol_time, vol_start, vol_stim_vis, vol_img,
- vol_hifi, vol_stim_aud, vol_flir,
- vol_pmt, vol_led]
- def read_dff(ops):
- """
- Reads dF/F (delta F over F) traces from an HDF5 file and creates a memory-mapped array.
- This function retrieves paths for memory-mapped data and an HDF5 file using
- `get_memmap_path`, then loads the dF/F dataset from the HDF5 file into a
- memory-mapped array.
- Args:
- ops (dict): A dictionary containing session metadata, including:
- - 'save_path0': The base directory path for the session.
- Returns:
- numpy.memmap: A memory-mapped array containing dF/F traces (dtype: float32).
- Notes:
- - The function assumes the HDF5 file 'dff.h5' exists in the session directory
- specified by `ops['save_path0']` and contains a 'dff' dataset.
- - The memory-mapped array is created using the `create_memmap` function and
- stored in a 'memmap' subdirectory under the session path.
- - The HDF5 file is accessed in read-only mode ('r') using `h5py.File`.
- Example:
- >>> ops = {'save_path0': '/path/to/session'}
- >>> dff_data = read_dff(ops)
- >>> print(dff_data) # Access the dF/F memory-mapped array
- <numpy.memmap object with dtype=float32>
- Raises:
- KeyError: If required keys (e.g., 'save_path0') are missing from `ops` or if the
- 'dff' dataset is missing in the HDF5 file.
- FileNotFoundError: If the 'dff.h5' file does not exist at the specified path.
- OSError: If there are issues creating the memory-mapped file or accessing the file
- system.
- """
- mm_path, file_path = get_memmap_path(ops, 'dff.h5')
- with h5py.File(file_path, 'r') as f:
- dff = create_memmap(f['dff'], 'float32', os.path.join(mm_path, 'dff.mmap'))
- return dff
- # Reading bpod from matlab file
- def read_bpod_mat_data(ops, session_start_time):
- """
- Reads and processes Bpod session data from a MATLAB file into a structured DataFrame.
- This function loads behavioral data from a 'bpod_session_data.mat' file, processes
- trial-related information (e.g., trial timings, outcomes, stimulus sequences, and
- licking events), and organizes it into a pandas DataFrame. It handles nested MATLAB
- structures and converts them into Python dictionaries, adjusts timestamps relative to
- a session start time, and labels trial outcomes and states.
- Args:
- ops (dict): A dictionary containing session metadata, including:
- - 'save_path0': The base directory path where the 'bpod_session_data.mat' file
- is located.
- session_start_time (float): The reference start time (in milliseconds) for
- correcting trial timestamps.
- Returns:
- pandas.DataFrame: A DataFrame containing trial-related data with the following
- columns:
- - time_trial_start (float): Trial start timestamps (ms, adjusted to session
- start).
- - time_trial_end (float): Trial end timestamps (ms, adjusted to session start).
- - trial_type (int): Trial type indicator (0-based, derived from raw data).
- - outcome (str): Trial outcome ('punish', 'reward', 'naive_punish',
- 'naive_reward', 'no_choose', or 'other').
- - state_window_choice (array): Timestamps for the 'WindowChoice' state [start, end].
- - state_reward (array): Timestamps for the 'Reward' state [start, end].
- - state_punish (array): Timestamps for the 'Punish' state [start, end].
- - stim_seq (array): Stimulus sequence timestamps [[BNC1High], [BNC1Low]].
- - isi (float): Inter-stimulus interval (ms) between BNC1High and BNC1Low.
- - lick (array): Licking events with [timestamps, direction, correctness, lick_type].
- Notes:
- - The function assumes the 'bpod_session_data.mat' file exists in the directory
- specified by `ops['save_path0']` and contains a 'SessionData' structure with
- fields like 'nTrials', 'RawEvents', 'TrialStartTimestamp', 'TrialEndTimestamp',
- and 'TrialTypes'.
- - Timestamps are converted to milliseconds and adjusted relative to the session
- start time.
- - Nested MATLAB structures are recursively converted to Python dictionaries using
- helper functions `_check_keys`, `_todict`, and `_tolist`.
- - Trial outcomes are determined by the `states_labeling` helper function based on
- the presence of specific states in the trial data.
- - Stimulus sequences and licking events are processed to handle missing or invalid
- data (e.g., NaN values for absent events).
- - The 'yicong_forever' string is used as a temporary placeholder to handle array
- length alignment and is removed before returning the DataFrame.
- Example:
- >>> ops = {'save_path0': '/path/to/session'}
- >>> session_start_time = 1000.0
- >>> df = read_bpod_mat_data(ops, session_start_time)
- >>> print(df[['time_trial_start', 'outcome']].head())
- time_trial_start outcome
- 0 1000.0 reward
- 1 1500.0 punish
- ...
- Raises:
- KeyError: If required keys (e.g., 'save_path0') are missing from `ops` or if
- expected fields are missing in the MATLAB file.
- FileNotFoundError: If the 'bpod_session_data.mat' file does not exist.
- ValueError: If the data structure in the MATLAB file is malformed or incompatible.
- """
- def _check_keys(d):
- """
- Recursively converts MATLAB structs to dictionaries.
- Args:
- d (dict): Input dictionary containing MATLAB struct objects.
- Returns:
- dict: Dictionary with MATLAB structs converted to nested dictionaries.
- """
- for key in d:
- if isinstance(d[key], sio.matlab.mat_struct):
- d[key] = _todict(d[key])
- return d
- def _todict(matobj):
- """
- Converts a MATLAB struct to a Python dictionary.
- Args:
- matobj (sio.matlab.mat_struct): MATLAB struct object.
- Returns:
- dict: Dictionary with field names as keys and converted values.
- """
- d = {}
- for strg in matobj._fieldnames:
- elem = matobj.__dict__[strg]
- if isinstance(elem, sio.matlab.mat_struct):
- d[strg] = _todict(elem)
- elif isinstance(elem, np.ndarray):
- d[strg] = _tolist(elem)
- else:
- d[strg] = elem
- return d
- def _tolist(ndarray):
- """
- Recursively converts NumPy arrays to lists, handling nested MATLAB structs.
- Args:
- ndarray (numpy.ndarray): Input NumPy array.
- Returns:
- list: List of converted elements.
- """
- elem_list = []
- for sub_elem in ndarray:
- if isinstance(sub_elem, sio.matlab.mat_struct):
- elem_list.append(_todict(sub_elem))
- elif isinstance(sub_elem, np.ndarray):
- elem_list.append(_tolist(sub_elem))
- else:
- elem_list.append(sub_elem)
- return elem_list
- def states_labeling(trial_states):
- """
- Labels trial outcomes based on trial states.
- Args:
- trial_states (dict): Dictionary of trial states and their timestamps.
- Returns:
- str: Outcome label ('punish', 'reward', 'naive_punish', 'naive_reward',
- 'no_choose', or 'other').
- """
- if 'Punish' in trial_states.keys() and not np.isnan(trial_states['Punish'][0]):
- outcome = 'punish'
- elif 'Reward' in trial_states.keys() and not np.isnan(trial_states['Reward'][0]):
- outcome = 'reward'
- elif 'PunishNaive' in trial_states.keys() and not np.isnan(trial_states['PunishNaive'][0]):
- outcome = 'naive_punish'
- elif 'RewardNaive' in trial_states.keys() and not np.isnan(trial_states['RewardNaive'][0]):
- outcome = 'naive_reward'
- elif 'DidNotChoose' in trial_states.keys() and not np.isnan(trial_states['DidNotChoose'][0]):
- outcome = 'no_choose'
- else:
- outcome = 'other'
- return outcome
- def get_state(trial_state_dict, target_state, trial_start):
- """
- Retrieves timestamps for a specific trial state.
- Args:
- trial_state_dict (dict): Dictionary of trial states.
- target_state (str): Name of the target state.
- trial_start (float): Trial start timestamp (ms).
- Returns:
- numpy.ndarray: Array of [start, end] timestamps (ms) or [NaN, NaN] if the
- state is not found.
- """
- if target_state in trial_state_dict:
- time_state = 1000 * np.array(trial_state_dict[target_state]) + trial_start
- else:
- time_state = np.array([np.nan, np.nan])
- return time_state
- # Read raw data from MATLAB file
- raw = sio.loadmat(
- os.path.join(ops['save_path0'], 'bpod_session_data.mat'),
- struct_as_record=False, squeeze_me=True)
- raw = _check_keys(raw)['SessionData']
- trial_labels = dict()
- n_trials = raw['nTrials']
- trial_states = [raw['RawEvents']['Trial'][ti]['States'] for ti in range(n_trials)]
- trial_events = [raw['RawEvents']['Trial'][ti]['Events'] for ti in range(n_trials)]
- # Trial start and end timestamps
- trial_labels['time_trial_start'] = 1000 * np.array(raw['TrialStartTimestamp']).reshape(-1)
- trial_labels['time_trial_end'] = 1000 * np.array(raw['TrialEndTimestamp']).reshape(-1)
- trial_labels['time_trial_end'] = trial_labels['time_trial_end'] - trial_labels['time_trial_start'][0] + session_start_time
- trial_labels['time_trial_start'] = trial_labels['time_trial_start'] - trial_labels['time_trial_start'][0] + session_start_time
- # Trial type Block type and outcome
- trial_labels['trial_type'] = np.array(raw['TrialTypes']).reshape(-1) - 1
- trial_labels['block_type'] = np.array(raw['BlockTypes']).reshape(-1)
- trial_labels['outcome'] = np.array([states_labeling(ts) for ts in trial_states], dtype='object')
- # Trial state timings
- trial_labels['state_window_choice'] = np.array([
- get_state(trial_states[ti], 'WindowChoice', trial_labels['time_trial_start'][ti])
- for ti in range(n_trials)] + ['yicong_forever'], dtype='object')[:-1]
- trial_labels['state_reward'] = np.array([
- get_state(trial_states[ti], 'Reward', trial_labels['time_trial_start'][ti])
- for ti in range(n_trials)] + ['yicong_forever'], dtype='object')[:-1]
- trial_labels['state_punish'] = np.array([
- get_state(trial_states[ti], 'Punish', trial_labels['time_trial_start'][ti])
- for ti in range(n_trials)] + ['yicong_forever'], dtype='object')[:-1]
- # Stimulus timing
- trial_isi = []
- trial_stim_seq = []
- for ti in range(n_trials):
- if ('BNC1High' in trial_events[ti].keys() and
- 'BNC1Low' in trial_events[ti].keys() and
- len(np.array(trial_events[ti]['BNC1High']).reshape(-1)) == 2 and
- len(np.array(trial_events[ti]['BNC1Low']).reshape(-1)) == 2):
- stim_seq = 1000 * np.array([trial_events[ti]['BNC1High'], trial_events[ti]['BNC1Low']]) + trial_labels['time_trial_start'][ti]
- stim_seq = np.transpose(stim_seq, [1, 0])
- isi = 1000 * np.array(trial_events[ti]['BNC1High'][1] - trial_events[ti]['BNC1Low'][0])
- else:
- stim_seq = np.array([[np.nan, np.nan], [np.nan, np.nan]])
- isi = np.nan
- trial_stim_seq.append(stim_seq)
- trial_isi.append(isi)
- trial_labels['stim_seq'] = np.array(trial_stim_seq + ['yicong_forever'], dtype='object')[:-1]
- trial_labels['isi'] = np.array(trial_isi + ['yicong_forever'], dtype='object')[:-1]
- # Licking events
- trial_lick = []
- for ti in range(n_trials):
- licking_events = []
- direction = []
- correctness = []
- if 'Port1In' in trial_events[ti].keys():
- lick_left = np.array(trial_events[ti]['Port1In']).reshape(-1)
- licking_events.append(lick_left)
- direction.append(np.zeros_like(lick_left))
- if trial_labels['trial_type'][ti] == 0:
- correctness.append(np.ones_like(lick_left))
- else:
- correctness.append(np.zeros_like(lick_left))
- if 'Port3In' in trial_events[ti].keys():
- lick_right = np.array(trial_events[ti]['Port3In']).reshape(-1)
- licking_events.append(lick_right)
- direction.append(np.ones_like(lick_right))
- if trial_labels['trial_type'][ti] == 1:
- correctness.append(np.ones_like(lick_right))
- else:
- correctness.append(np.zeros_like(lick_right))
- if len(licking_events) > 0:
- licking_events = 1000 * np.concatenate(licking_events).reshape(1, -1) + trial_labels['time_trial_start'][ti]
- direction = np.concatenate(direction).reshape(1, -1)
- correctness = np.concatenate(correctness).reshape(1, -1)
- lick = np.concatenate([licking_events, direction, correctness], axis=0)
- lick = lick[:, np.argsort(lick[0, :])]
- lick = lick[:, lick[0, :] >= trial_labels['state_window_choice'][ti][0]]
- if np.size(lick) != 0:
- lick_type = np.full(lick.shape[1], np.nan)
- lick_type[0] = 1
- if (not np.isnan(trial_labels['state_reward'][ti][1]) and
- len(lick_type) > 1):
- lick_type[1:][lick[0, 1:] > trial_labels['state_reward'][ti][0]] = 0
- lick_type = lick_type.reshape(1, -1)
- lick = np.concatenate([lick, lick_type], axis=0)
- else:
- lick = np.array([[np.nan], [np.nan], [np.nan], [np.nan]])
- else:
- lick = np.array([[np.nan], [np.nan], [np.nan], [np.nan]])
- trial_lick.append(lick)
- trial_labels['lick'] = np.array(trial_lick + ['yicong_forever'], dtype='object')[:-1]
- # Convert to DataFrame
- trial_labels = pd.DataFrame(trial_labels)
- return trial_labels
- # Readiing Trialized data
- def read_trial_label(ops):
- """Read trial label data from a CSV file into a pandas DataFrame.
- This function reads a CSV file containing trial label data and converts specific columns
- into numpy arrays with appropriate data types and shapes. Some columns containing array-like
- data are parsed from string representations back into numpy arrays. The processed data is
- returned as a structured pandas DataFrame.
- Args:
- ops (dict): Dictionary containing configuration options, including 'save_path0' for the
- directory where the 'trial_labels.csv' file is located.
- Returns:
- pandas.DataFrame: A DataFrame containing the trial label data with columns:
- - time_trial_start (float32): Start times of trials.
- - time_trial_end (float32): End times of trials.
- - trial_type (int8): Type of each trial.
- - outcome (object): Outcome of each trial.
- - state_window_choice (object): Array of choice window states per trial.
- - state_reward (object): Array of reward states per trial.
- - state_punish (object): Array of punishment states per trial.
- - stim_seq (object): 2D array of stimulus sequences per trial.
- - isi (float32): Inter-stimulus intervals.
- - lick (object): 2D array of lick data per trial.
- Notes:
- - The CSV file is expected to be located at ops['save_path0']/trial_labels.csv.
- - Columns with array-like data (e.g., state_window_choice, stim_seq, lick) are stored as
- strings in the CSV and are parsed back into numpy arrays with specified shapes.
- - The function uses a helper function, object_parse, to handle array parsing.
- - The 'yicong_forever' string is appended during parsing and then removed to maintain
- data integrity.
- """
- raw_csv = pd.read_csv(os.path.join(ops['save_path0'], 'trial_labels.csv'), index_col=0)
- # recover object numpy array from csv str.
- def object_parse(k, shape):
- arr = np.array(
- [np.fromstring(s.replace('[', '').replace(']', ''), sep=' ').reshape(shape)
- for s in raw_csv[k].to_list()] + ['yicong_forever'],
- dtype='object')[:-1]
- return arr
- # parse all array.
- time_trial_start = raw_csv['time_trial_start'].to_numpy(dtype='float32')
- time_trial_end = raw_csv['time_trial_end'].to_numpy(dtype='float32')
- trial_type = raw_csv['trial_type'].to_numpy(dtype='int8')
- block_type = raw_csv['block_type'].to_numpy(dtype='int8')
- outcome = raw_csv['outcome'].to_numpy(dtype='object')
- state_window_choice = object_parse('state_window_choice', [-1])
- state_reward = object_parse('state_reward', [-1])
- state_punish = object_parse('state_punish', [-1])
- stim_seq = object_parse('stim_seq', [-1, 2])
- isi = raw_csv['isi'].to_numpy(dtype='float32')
- lick = object_parse('lick', [4, -1])
- # convert to dataframe.
- trial_labels = pd.DataFrame({
- 'time_trial_start': time_trial_start,
- 'time_trial_end': time_trial_end,
- 'trial_type': trial_type,
- 'block_type': block_type,
- 'outcome': outcome,
- 'state_window_choice': state_window_choice,
- 'state_reward': state_reward,
- 'state_punish': state_punish,
- 'stim_seq': stim_seq,
- 'isi': isi,
- 'lick': lick,
- })
- return trial_labels
- def read_neural_trials(ops, smooth):
- """Read neural trial data from an HDF5 file and create memory-mapped arrays.
- This function reads neural trial data from an HDF5 file, including fluorescence signals,
- time points, and voltage signals, and optionally applies a Savitzky-Golay filter to smooth
- the fluorescence data. The data is stored as memory-mapped arrays for efficient access and
- returned as a dictionary. Trial labels are read using the read_trial_label function.
- Args:
- ops (dict): Dictionary containing configuration options, including 'save_path0' for the
- directory where the 'neural_trials.h5' file is located.
- smooth (bool): If True, apply Savitzky-Golay smoothing to the fluorescence data (dff).
- Returns:
- dict: A dictionary containing memory-mapped arrays and trial labels:
- - dff (numpy.memmap): Fluorescence signal data (float32).
- - time (numpy.memmap): Corrected neural time points (float32).
- - trial_labels (pandas.DataFrame): Trial label data from read_trial_label.
- - vol_time (numpy.memmap): Voltage signal time points (float32).
- - vol_stim_vis (numpy.memmap): Visual stimulation signals (int8).
- - vol_stim_aud (numpy.memmap): Auditory stimulation signals (float32).
- - vol_flir (numpy.memmap): FLIR camera signals (int8).
- - vol_pmt (numpy.memmap): Photomultiplier tube signals (int8).
- - vol_led (numpy.memmap): LED signals (int8).
- Notes:
- - The HDF5 file is expected to be located at ops['save_path0']/neural_trials.h5.
- - If smooth is True, a Savitzky-Golay filter is applied to the dff data with a window
- length of 9 and polynomial order of 3.
- - Memory-mapped files are created in a directory specified by get_memmap_path.
- - The function assumes the existence of helper functions: get_memmap_path,
- read_trial_label, and create_memmap.
- """
- mm_path, file_path = get_memmap_path(ops, 'neural_trials.h5')
- trial_labels = read_trial_label(ops)
- with h5py.File(file_path, 'r') as f:
- neural_trials = dict()
- dff = np.array(f['neural_trials']['dff'])
- if smooth:
- window_length = 9
- polyorder = 3
- dff = np.apply_along_axis(
- savgol_filter, 1, dff,
- window_length=window_length,
- polyorder=polyorder)
- else:
- pass
- neural_trials['dff'] = create_memmap(dff, 'float32', os.path.join(mm_path, 'dff.mmap'))
- neural_trials['time'] = create_memmap(f['neural_trials']['time'], 'float32', os.path.join(mm_path, 'time.mmap'))
- neural_trials['trial_labels'] = trial_labels
- neural_trials['vol_time'] = create_memmap(f['neural_trials']['vol_time'], 'float32', os.path.join(mm_path, 'vol_time.mmap'))
- neural_trials['vol_stim_vis'] = create_memmap(f['neural_trials']['vol_stim_vis'], 'int8', os.path.join(mm_path, 'vol_stim_vis.mmap'))
- neural_trials['vol_stim_aud'] = create_memmap(f['neural_trials']['vol_stim_aud'], 'float32', os.path.join(mm_path, 'vol_stim_aud.mmap'))
- neural_trials['vol_flir'] = create_memmap(f['neural_trials']['vol_flir'], 'int8', os.path.join(mm_path, 'vol_flir.mmap'))
- neural_trials['vol_pmt'] = create_memmap(f['neural_trials']['vol_pmt'], 'int8', os.path.join(mm_path, 'vol_pmt.mmap'))
- neural_trials['vol_led'] = create_memmap(f['neural_trials']['vol_led'], 'int8', os.path.join(mm_path, 'vol_led.mmap'))
- return neural_trials
- # clean memory mapping files.
- def clean_memap_path(ops):
- try:
- if os.path.exists(os.path.join(ops['save_path0'], 'memmap')):
- shutil.rmtree(os.path.join(ops['save_path0'], 'memmap'))
- except: pass
reader.py at commit 1c0f5de, no license · at the source
Overview
- Wallace H. Coulter Department of Biomedical Engineering, Georgia Institute of Technology and Emory University, Atlanta, GA 30332, USA
- School of Biological Sciences, Georgia Institute of Technology, Atlanta, GA, 30332, USA
Abstract
Ramping neural activity is widely interpreted as a signature of predictive processing, but whether these signals truly reflect predictions or instead emerge from sensory mechanisms remains unclear. To address this question, we used two-photon calcium imaging across multiple cell types in visual and parietal cortex, while awake mice passively received repeated audiovisual stimuli presented under distinct temporal structures. Neurons segregated into two broad response classes: stimulus-activated (ramp-down) and stimulus-inhibited (ramp-up) populations with diverse temporal kinetics. Multiple findings argued against a predictive interpretation: Ramping activity was present in naïve animals, neural responses changed immediately after short/
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 2 matches between paragraphs and lines of code.
najafi-laboratory/2p_imaging
1c0f5de0cc6b9e430169e1a5a27f643dac8f7114, 16 September 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
63 files
- 2AFC/
Main.py , Python, 128 lines - 2AFC/
Main.sh , Shell, 21 lines - 2AFC/
Modules/ , Python, 234 linesAlignment.py - 2AFC/
Modules/ , Python, 655 linesclustering_neurons.py - 2AFC/
Modules/ , Python, 787 linesdecoding copy.py - 2AFC/
Modules/ , Python, 717 linesdecoding_four.py - 2AFC/
Modules/ , Python, 1,111 linesdecoding_multiple_settin gs.py - 2AFC/
Modules/ , Python, 706 lines, 1 matchreader.py - 2AFC/
Modules/ , Python, 253 linestrialize.py - 2AFC/
Plot/ , Python, 436 linesplot_FOV.py - 2AFC/
Plot/ , Python, 513 linesplot_epoch_response.py - 2AFC/
Plot/ , Python, 794 linesplot_events_neural_respo nse.py - 2AFC/
Plot/ , Python, 215 linesplot_licking.py - 2AFC/
Plot/ , Python, 336 linesplot_licking_neural_resp onse.py - 2AFC/
Test_pilot/ , Python, 1 line__init__.py - 2AFC/
Test_pilot/ , Jupyter, 645 linestest.ipynb - 2AFC/
Test_pilot/ , Jupyter, 1,507 linestest_GLM.ipynb - 2AFC/
Test_pilot/ , Jupyter, 113 linestest_LFAD.ipynb - 2AFC/
Test_pilot/ , Jupyter, 229 linestest_TCA_block_transitio n.ipynb - 2AFC/
Test_pilot/ , Jupyter, 3,422 linestest_alignemnts - Copy.ipynb - 2AFC/
Test_pilot/ , Jupyter, 3,422 linestest_alignemnts.ipynb - 2AFC/
Test_pilot/ , Jupyter, 195 linestest_alignemnts_V3.ipynb - 2AFC/
Test_pilot/ , Jupyter, 299 linestest_licking.ipynb - 2AFC/
globus_mc11_download_no_ , Shell, 46 linesraw.sh - 2AFC/
notebook_tools/ , Python, 2 lines__init__.py - 2AFC/
notebook_tools/ , Python, 156 linesalignment.py - 2AFC/
notebook_tools/ , Python, 218 linesalignments.py - 2AFC/
notebook_tools/ , Python, 555 linesclustering.py - 2AFC/
notebook_tools/ , Python, 1,132 linesdecoding.py - 2AFC/
notebook_tools/ , Python, 59 linesdistribution.py - 2AFC/
notebook_tools/ , Python, 157 linesglm.py - 2AFC/
notebook_tools/ , Python, 897 linesio.py - 2AFC/
notebook_tools/ , Python, 102 lineslfads.py - 2AFC/
notebook_tools/ , Python, 244 lineslick.py - 2AFC/
notebook_tools/ , Python, 863 linesplot_viewers.py - 2AFC/
notebook_tools/ , Python, 854 linesstim.py - 2AFC/
notebook_tools/ , Python, 77 linestca.py - 2p_2AFC_double_block_ver
sion/ , Python, 128 linesMain.py - 2p_2AFC_double_block_ver
sion/ , Shell, 21 linesMain.sh - 2p_2AFC_double_block_ver
sion/ , Python, 234 linesModules/ Alignment.py - 2p_2AFC_double_block_ver
sion/ , Python, 655 linesModules/ clustering_neurons.py - 2p_2AFC_double_block_ver
sion/ , Python, 787 linesModules/ decoding copy.py - 2p_2AFC_double_block_ver
sion/ , Python, 779 linesModules/ decoding.py - 2p_2AFC_double_block_ver
sion/ , Python, 717 linesModules/ decoding_four.py - 2p_2AFC_double_block_ver
sion/ , Python, 1,111 linesModules/ decoding_multiple_settin gs.py - 2p_2AFC_double_block_ver
sion/ , Python, 706 linesModules/ reader.py - 2p_2AFC_double_block_ver
sion/ , Python, 253 linesModules/ trialize.py - 2p_2AFC_double_block_ver
sion/ , Python, 436 linesPlot/ plot_FOV.py - 2p_2AFC_double_block_ver
sion/ , Python, 513 linesPlot/ plot_epoch_response.py - 2p_2AFC_double_block_ver
sion/ , Python, 794 linesPlot/ plot_events_neural_respo nse.py - 2p_2AFC_double_block_ver
sion/ , Python, 215 linesPlot/ plot_licking.py - 2p_2AFC_double_block_ver
sion/ , Python, 336 linesPlot/ plot_licking_neural_resp onse.py - 2p_2AFC_double_block_ver
sion/ , Jupyter, 3,138 linestest.ipynb - 2p_2AFC_reg_version/
Main.py , Python, 93 lines - 2p_2AFC_reg_version/
Modules/ , Python, 231 linesAlignment.py - 2p_2AFC_reg_version/
Modules/ , Python, 456 linesclustering_neurons.py - 2p_2AFC_reg_version/
Modules/ , Python, 741 linesdecoding.py - 2p_2AFC_reg_version/
Modules/ , Python, 703 lines, 1 matchreader.py - 2p_2AFC_reg_version/
Modules/ , Python, 253 linestrialize.py - 2p_2AFC_reg_version/
Plot/ , Python, 200 linesplot_FOV.py - 2p_2AFC_reg_version/
Plot/ , Python, 522 linesplot_events_neural_respo nse.py - 2p_2AFC_reg_version/
Plot/ , Python, 336 linesplot_licking_neural_resp onse.py - 2p_2AFC_reg_version/
test.ipynb , Jupyter, 2,817 lines - repository limit reached (2,000 files or 30 MB): the rest is at the source (353 files)
The paper's code and data availability statement is in the Data section.
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;
- 63 scripts, each with its path and the digest of its content;
- 2 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:20350578, at Zenodo; found in “Data, code, and materials availability:”
Data, code, and materials availability
All data and code needed to evaluate and reproduce the results in this paper are present in the paper and/
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 7 MeSH terms, 4 funders, 133 references.
Cite
This paper
Huang, Y., Shamsnia, A., Chen, M., Wu, S., Stamm, T., Medico, S., & Najafi, F. (2026). Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior. Science advances, 12(34), eaed6417. https://
BibTeX
@article{huang2026intrin
author = {Huang, Yicong and Shamsnia, Ali and Chen, Mengze and Wu, Shuang and Stamm, Timothy and Medico, Sophie and Najafi, Farzaneh},
title = {{Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior}},
journal = {Science advances},
year = {2026},
month = aug,
volume = {12},
number = {34},
pages = {eaed6417},
publisher = {American Association for the Advancement of Science},
issn = {2375-2548},
doi = {10.1126/
url = {https://
pmid = {42627925},
pmcid = {PMC13496207}
}
RIS
TY - JOUR
AU - Huang, Yicong
AU - Shamsnia, Ali
AU - Chen, Mengze
AU - Wu, Shuang
AU - Stamm, Timothy
AU - Medico, Sophie
AU - Najafi, Farzaneh
TI - Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior
T2 - Science advances
J2 - Sci Adv
PY - 2026
DA - 2026/
VL - 12
IS - 34
SP - eaed6417
SN - 2375-2548
PB - American Association for the Advancement of Science
DO - 10.1126/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1126/
"type": "article-journal",
"title": "Intrinsic timing, not temporal prediction, underlies ramping dynamics in visual and parietal cortex during passive behavior",
"container-title": "Science advances",
"author": [
{
"family": "Huang",
"given": "Yicong"
},
{
"family": "Shamsnia",
"given": "Ali"
},
{
"family": "Chen",
"given": "Mengze"
},
{
"family": "Wu",
"given": "Shuang"
},
{
"family": "Stamm",
"given": "Timothy"
},
{
"family": "Medico",
"given": "Sophie"
},
{
"family": "Najafi",
"given": "Farzaneh"
}
],
"container-title-short":
"volume": "12",
"issue": "34",
"page": "eaed6417",
"DOI": "10.1126/
"PMID": "42627925",
"PMCID": "PMC13496207",
"ISSN": "2375-2548",
"publisher": "American Association for the Advancement of Science",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
21
]
]
}
}
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/s41467-026-73999-w [code]
- Independence and coherence in temporal sequence computation across the fronto-parietal network.Journal: Nature communicationsIn common: scikit-learn, SciPy, Matplotlib, 1 other tool, systems, mouse, 10 references
- [2] doi:10.1038/s41593-026-02255-7 [code]
- Neural circuits encode prior knowledge of temporal statistics.Journal: Nature neuroscienceIn common: Plotly, scikit-learn, pandas, 3 other tools, systems, mouse, 5 references
- [3] doi:10.1038/s41586-026-10528-1 [code]
- A critical initialization for biological neural networks.Journal: NatureIn common: scikit-learn, SciPy, Matplotlib, 1 other tool, mouse, 8 references
- [4] doi:10.1016/j.neuron.2026.07.016 [code]
- Inferring brain-wide interactions using data-constrained recurrent neural network models.Journal: NeuronIn common: Matplotlib, NumPy, systems, mouse, 8 references
- [5] doi:10.1038/s41467-026-70354-x [code]
- Global error signal guides local optimization in mismatch calculation.Journal: Nature communicationsIn common: seaborn, scikit-learn, SciPy, 2 other tools, mouse, 6 references
- [6] doi:10.1038/s41593-026-02350-9 [code]
- Probing inter-areal computations with a two-photon holographic mesoscope.Journal: Nature neuroscienceIn common: h5py, SciPy, NumPy, systems, mouse, 6 references
- [7] doi:10.1371/journal.pone.0356243 [code]
- Functional organization and natural scene responses across mouse visual cortical areas revealed with encoding manifolds.Journal: PloS oneIn common: Plotly, scikit-learn, pandas, 3 other tools, mouse, 4 references
- [8] doi:10.1371/journal.pbio.3003915 [code]
- Noise-invariant representations of sound emerge along the canonical cortical hierarchy.Journal: PLoS biologyIn common: scikit-image, h5py, seaborn, 5 other tools, mouse, 3 references
- [9] doi:10.1371/journal.pbio.3003831 [code]
- Disinhibitory signaling enables flexible coding of top-down information in cortical networks.Journal: PLoS biologyIn common: h5py, scikit-learn, pandas, 3 other tools, systems, mouse, 4 references
- [10] doi:10.1038/s41467-026-71725-0 [code]
- Interactions across hemispheres in prefrontal cortex reflect global cognitive processing.Journal: Nature communicationsIn common: scikit-learn, pandas, SciPy, 2 other tools, 6 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, 63 scripts, and 2 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:bacfe748b36fc4cf…
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.
