Perception and neural representation of intermittent odor stimuli in mice.
The 5 matches
- [1] § Methods › Behavioral training ↔ Odor_pulses_data_guideline.ipynb, lines 32–108 · score 0.77 · Lick port location, Lick Training phase, water drops, Poisson distributions, day, Block
- [2] § Methods › Behavioral task ↔ Odor_pulses_data_guideline.ipynb, lines 32–108 · score 0.70 · Wait Period, lick port, water drop, Poisson distributed, Go, Odor Pulses
- [3] § Methods › Behavioral apparatus ↔ Behavioral_data_compiler_v2.ipynb, lines 373–425 · score 0.68 · methyl butyrate, clean air, Catch trials, psychometric, behavior, pulse
- [4] § Results › Odor pulse counting task ↔ Behavioral_data_compiler_v2.ipynb, lines 373–425 · score 0.63 · methyl butyrate, clean air, Catch trials, behavioral, pulses
- [5] § Results › Olfactory cortical responses largely reflect sensory information ↔ NComms_Submission/ephys_compiler.ipynb, lines 762–792 · score 0.53 · 9–12, firing rate, 5–8, 1–4, neurons, pulses
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 287 lines · 11 KB · MIT · 2 matches
- # %%
- import pickle
- import pandas as pd
- import numpy as np
- import matplotlib as mpl
- from matplotlib import rcParams
- import matplotlib.pyplot as plt
- import h5py
- import os
- import tqdm
- import scipy
- from scipy import signal
- from scipy.signal import resample
- from tqdm import tnrange
- import seaborn as sns
- from scipy.stats import norm,entropy,linregress
- from scipy.optimize import minimize, curve_fit
- from scipy.io import savemat
- import multiprocess as mp
- from multiprocess import Pool
- import sys
- import warnings
- warnings.filterwarnings('ignore')
- sns.set_context('poster', font_scale=1.1)
- cmap = plt.rcParams['axes.prop_cycle'].by_key()['color']
- modulename = 'multiprocess'
- mpl.rcParams['svg.fonttype'] = 'none'
- sns.set_context('poster', font_scale=1.1)
- path = os.getcwd() + '/Session/'
- # %% [markdown]
- # Loading a session_animalname_date_sessionnum.pickle file gives a single-element list containing a dictionary
- # ============================================================================================================
- #
- # Information about trial outcomes
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **num_trials**: number of trials recorded
- #
- # **high_trials**: boolean saying if the trial had a high number of pulses (above boundary) or not
- #
- # **low_trials**: the opposite of high_trials
- #
- # **high_choice**: boolean saying if animal chose the 'high side' or not
- #
- # **low_choice**: boolean saying if animal chose the 'low side' or not
- #
- # **correct_trials**: boolean saying if the animal got the trial correctly or not. (high_choice==True & high_trials==True and the opposite)
- #
- # **failure_trials**: Opposite of correct trials.
- #
- # **idle_trials**: if True, animal didn't make any binary choice in that trial
- #
- # **performance**: Correct trials/Total trials in the session
- #
- #
- # **Information about session parameters**
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **type**: Whether high and low trials were delivered in blocks (usually true for training phase) or randomly (once animals learnt the task).
- #
- # **lick training**: When True, odors are not delivered. This is only used at the beggining of the training when animals are learning lick port location.
- #
- # **free_drop**: if True, water drop will delivered to the correct side independently of animal action. Only used at the beggining of training.
- #
- # **high_count**: lambda of the poisson distribution used for generating the trials with high number of pulses.
- #
- # **low_count**: lambda of the poisson distribution used for generating the trials with low number of pulses
- #
- # **pulse_time_ms**: duration of the individual odor pulses in ms
- #
- # **refract_time**: duration (in ms) of the waiting period before the sampling period of a trial
- #
- # **delay_time**: duration of the sampling period (period in which odor pulses are delivered)
- #
- # **go_time**: duration of the period in which the animal has to lick (in ms)
- #
- #
- # **Information about odor and sniffing**
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **trial_odor**: array with len equal to delay_time containing the time course of the commands to solenoid valves for odor delivery. 0 = no odor, 100 = odor on. When two or more consecutive pulses overlaps, that is displayed as 100 multiples
- #
- # **trial_pre_breath**: an array containing the sniffing during the refractory period.
- #
- # **trial_breath**: an array containing the sniffing during delay_time(sampling period)+go_time.
- #
- #
- # **Other accesory information**
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **animal**: ID of the animal
- #
- # **year**: year of the session
- #
- # **month**: month of the session
- #
- # **day**: day date of the session
- #
- # **session_id**: number identifying the she specific session for when multiple sessions are conducted on a day
- # %%
- session_fname = path + 'session_' + 'Banner_20220305_3.pickle' #remember to change the path to where your file is
- if os.path.isfile(session_fname):
- with open(session_fname,'rb') as handle:
- session_list = pickle.load(handle)
- session = session_list[0]
- # %%
- #Plotting correct and error trials on a session.
- plt.plot(session_list[0]['correct_trials'].cumsum())
- plt.plot(session_list[0]['failure_trials'].cumsum())
- # %%
- # Plotting breathing an odor in a single trial.
- fig, ax1 = plt.subplots()
- # Plot the first variable on the left y-axis
- ax1.plot(session_list[0]['trial_breath'][2], '-k', linewidth=1)
- ax1.set_ylabel('Sniffing', color='k')
- # Create a second y-axis sharing the same x-axis
- ax2 = ax1.twinx()
- # Plot the second variable on the right y-axis
- ax2.plot(session_list[0]['trial_odor'][2], 'r-')
- ax2.set_ylabel('Odor commands', color='r')
- plt.show()
- # plt.plot(session_list[0]['trial_breath'][2])
- # plt.plot(session_list[0]['trial_odor'][2])
- # plt.show()
- # %% [markdown]
- # Loading a cell_animalname_date_sessionnum.pickle file gives a list, with the length=recorded units. For each element of the list there is a dictionary
- # ============================================================================================================
- #
- # Information about the specific neuron
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **cell_id**: id of the neuron/unit
- #
- # **ks_id**: id of the neuron based on kilosort
- #
- # **sample_rate**: sampling rate used for the acquisition of neural recordings.
- #
- # **spike_times**: timing of the spikes during the recording window. Events can happen in a window which goes from 0 (500 ms after sampling window started) to 135000 (the end of the sampling window). Remember that sampling rate is 30000 Hz
- #
- # **firing_rate**: not sure how this firing rate calculation was done, so I don't use it.
- #
- #
- #
- # **Information about session parameters**
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **num_trials**: number of trials recorded in the session.
- #
- # **delay_time**: duration of the sampling period (period in which odor pulses are delivered)
- #
- # **cut_delay_time**: duration of the part of delay time that was discarded of the analysis (the initial 0.5 s of the sampling periods; spikes were not taken into account due to artifacts)
- #
- # **full_delay_time**: duration of the part of sampling period for which spikes are actually considered (4.5 s)
- #
- #
- # **Other accesory information**
- # ---------------------------------------------------------------------------------------------------------------------------------------
- #
- # **year**: year of the session
- #
- # **month**: month of the session
- #
- # **day**: day date of the session
- #
- # **session**: number identifying the she specific session for when multiple sessions are conducted on a day
- # %%
- new_path = os.getcwd() + '/HW_Data/pack_data_new/' #remember to change the path to where your file is
- cell_fname = new_path + 'cell_' + 'Bengal_20190430_0.pickle'
- if os.path.isfile(cell_fname):
- print('pepe')
- with open(cell_fname,'rb') as handle:
- cell_list = pickle.load(handle)
- cell = cell_list[0]
- # %%
- def convert_to_numpy_arrays(data):
- if isinstance(data, np.ndarray):
- return data
- elif isinstance(data, list):
- return np.array([convert_to_numpy_arrays(item) for item in data])
- else:
- return np.array([data])
- # %%
- # Generating raster plot of a single neuron across all trials on a session
- icell = 8
- cell = cell_list[icell]
- num_trials = cell['num_trials']
- #non_idle_trial_ids = np.argwhere(non_idle_trials).squeeze()
- sp_times = np.array(cell['spike_times'])[:num_trials]#[non_idle_trials]
- spike_times = convert_to_numpy_arrays(sp_times)
- max_counts = 0
- for i in range(len(spike_times)):
- max_counts = max(max_counts,len(spike_times[i]))
- #print(max_counts)
- spike_table =np.zeros((len(spike_times),max_counts))
- #rank_ind = np.argsort(trial_cum_odor)
- for i in range(len(spike_times)):
- spike_time = spike_times[i]
- spike_table[i,:len(spike_time)] = spike_time/30000 + 0.5
- sns.set_style('white')
- fig = plt.figure(figsize=(15,7.5))
- plt.eventplot(spike_table,color='black')
- plt.xlim(0.5,5)
- plt.ylim(0,242)
- plt.xlabel('Time during odor period(s)')
- plt.ylabel('Trial')
- plt.show()
- # %% [markdown]
- # Now, going to the codes and kernels data coming from the DUNL analysis of the ephys and behavior
- # ================================================================================================
- # %%
- codepath = os.getcwd() + '/Bahareh/'
- dat = np.load(codepath+'Bengal_20190508_0_data_all.npy', allow_pickle=True)
- data = dat.tolist() #Maybe this conversion to list is not necessary but I remember having some issues without it.
- print(data.keys())
- # %% [markdown]
- # Loading the data_all.npy file provides a single dictionary with a set of parameters that are repeated for each recorded neuron, indicated by the _0, 1, 2, etc., at the end of the variable.
- #
- # According to Bahareh, these variables represent:
- #
- # y_{#neuron}: spike counts from neuron #
- #
- # Hx_{#neuron}: you can skipa_{#neuron}: baseline
- #
- # rate_hat_{#neuron}: estimated rate for neuron
- #
- # x_est_{#neuron}: This is the code estimate from the trial. This is at the 50 ms binned resolution from each trial.
- #
- # kernel_est_{# neuron}: length of kernel which is 20 samples, with 50 ms res, this is 1 s
- #
- # x: this is the odor onset in the 50 ms bin resolution
- #
- # high_choice: a flag for whether the trial was high or low odor count.
- #
- # 'norm_phase_hist_{#neuron}': This a phase histogram weighted by the code amplitudes
- #
- # You can also get some of this information from the other files, the ones called _kernels.npy, codes_stacked_trial.npy, phase_weighted_by_codes.npy, but I found easier just to load the data_all, since it has it all.
- # %%
- # %%
- test = data['norm_phase_hist_0'] #This contains a vector of length 36, so the phase variations on code amplitude seem to be binned in 36 bins
- #This is the set of transformations that Hao used to do with the norm_phase_hist data coming from Bahareh's analysis.
- #Not completely sure if this is fully correct, but it's the way he was using
- print(test)
- X_upsampled = resample(test,360,axis=0)
- print(X_upsampled)
- X_rolled = np.roll(X_upsampled,50,axis=0)
- print(X_rolled)
- X_resampled = resample(X_rolled,14,axis=0)
- print(X_resampled)
- X_norm = X_resampled/X_resampled.max(axis=0).reshape(-1,1)
- print(X_norm)
- # X_norm_nan_removed = X_norm[~np.isnan(X_norm).any(axis=1)]
- # Xnorm.append(X_norm_nan_removed)
- # %%
Odor_pulses_data_guideline.ipynb at commit 5653ebc, under MIT · at the source
Overview
- Department of Molecular & Cellular Biology, Harvard University, Cambridge, MA USA
- Center for Brain Science, Harvard University, Cambridge, MA USA
- Department of Chemistry & Chemical Biology, Harvard University, Cambridge, MA USA
- Department of Biological Sciences, University of Illinois, Chicago, IL USA
- Department of Psychology, McGill University, Montreal, QC Canada
- Mila - Quebec Artificial Intelligence Institute, Montreal, Canada
- Alberta Machine Intelligence Institute (Amii), Edmonton, AB Canada
- Neuroscience and Mental Health Institute (NMHI), University of Alberta, Edmonton, AB Canada
- John A. Paulson School of Engineering and Applied Sciences, Harvard University, Cambridge, MA USA
- Kempner Institute for the Study of Natural & Artificial Intelligence, Harvard University, Cambridge, MA USA
Abstract
Odor cues in nature are sparse and fluctuating due to turbulent transport. To investigate how animals perceive these intermittent cues, we developed a behavioral task in which mice made binary decisions based on the total number of discrete odor pulses presented stochastically over several seconds. Mice quickly learned this task, placing higher perceptual weight to stimuli arriving during inhalation than exhalation, a phase dependency that strongly correlated with the magnitude of responses in olfactory sensory neurons. Neurons in the anterior piriform cortex responded to odor pulses with varying degrees of dependence on respiration phase. Single cortical neurons responded stochastically and transiently to odor pulses, leading to a representation that carries signatures of sensory evidence, but not its accumulation. Our study reveals that mice can integrate intermittent odor signals across dozens of breaths and that respiratory modulation imposes limits on sensory information acquisition that cortical circuits cannot overcome to improve behavior.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 5 matches between paragraphs and lines of code.
leboero/odor_pulses_task_analysis
5653ebc491961e6ca196b39c18b086ffedf932ac, 17 March 2026Availability: 1 check, the latest on 30 September 2026: the link answers
- 30 September 2026: the link answers
18 files
- Behavioral_Analysis_SfN.
ipynb , Jupyter, 1,572 lines - Behavioral_Analysis_Snif
f_Kernel.ipynb , Jupyter, 1,244 lines - Behavioral_Analysis_Snif
f_Kernel_distribution_an , Jupyter, 2,054 linesalysis.ipynb - Behavioral_data_compiler
_v2.ipynb , Jupyter, 882 lines, 2 matches - Calcium_data_analysis.ip
ynb , Jupyter, 301 lines - Cell_test.ipynb, Jupyter, 2,650 lines
- Learning_analysis.ipynb, Jupyter, 391 lines
- LogReg_Luis.ipynb, Jupyter, 2,122 lines
- LogReg_evol_Noe.ipynb, Jupyter, 996 lines
- LogReg_old.ipynb, Jupyter, 590 lines
- NComms_Submission/
Calcium_data_analysis.ip , Jupyter, 449 linesynb - NComms_Submission/
PID_figures_source_code. , Jupyter, 305 linesipynb - NComms_Submission/
Spectrogram_analysis.ipy , Jupyter, 278 linesnb - NComms_Submission/
Trained_dataset_analysis , Jupyter, 82 lines.ipynb - NComms_Submission/
ephys_compiler.ipynb , Jupyter, 1,472 lines, 1 match - Odor_pulses_data_guideli
ne.ipynb , Jupyter, 287 lines, 2 matches - PID_odor_kernel_analysis
.ipynb , Jupyter, 330 lines - repository limit reached (2,000 files or 30 MB): the rest is at the source (22 files)
- LICENSE, License, 21 lines
Code availability
Code for the analysis of the data is provided at: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
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;
- 17 scripts, each with its path and the digest of its content;
- 5 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
Datasets cited
- figshare:31792705, at figshare; found in “Data availability”
Data Availability Statement
The data generated in this study have been deposited in Figshare under accession code 10.6084/
Code for the analysis of the data is provided at: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 30 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 9 authors, 2 keywords, 13 MeSH terms, 6 funders, 80 references.
Cite
This paper
Boero, L. E., Wu, H., Zak, J. D., Masset, P., Pashakhanloo, F., Jayakumar, S., Tolooshams, B., Ba, D., & Murthy, V. N. (2026). Perception and neural representation of intermittent odor stimuli in mice. Nature communications, 17(1), 5575. https://
BibTeX
@article{boero2026percep
author = {Boero, Luis E and Wu, Hao and Zak, Joseph D and Masset, Paul and Pashakhanloo, Farhad and Jayakumar, Siddharth and Tolooshams, Bahareh and Ba, Demba and Murthy, Venkatesh N},
title = {{Perception and neural representation of intermittent odor stimuli in mice}},
journal = {Nature communications},
year = {2026},
month = apr,
volume = {17},
number = {1},
pages = {5575},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42056108},
pmcid = {PMC13304163}
}
RIS
TY - JOUR
AU - Boero, Luis E
AU - Wu, Hao
AU - Zak, Joseph D
AU - Masset, Paul
AU - Pashakhanloo, Farhad
AU - Jayakumar, Siddharth
AU - Tolooshams, Bahareh
AU - Ba, Demba
AU - Murthy, Venkatesh N
TI - Perception and neural representation of intermittent odor stimuli in mice
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 5575
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Perception and neural representation of intermittent odor stimuli in mice",
"container-title": "Nature communications",
"author": [
{
"family": "Boero",
"given": "Luis E"
},
{
"family": "Wu",
"given": "Hao"
},
{
"family": "Zak",
"given": "Joseph D"
},
{
"family": "Masset",
"given": "Paul"
},
{
"family": "Pashakhanloo",
"given": "Farhad"
},
{
"family": "Jayakumar",
"given": "Siddharth"
},
{
"family": "Tolooshams",
"given": "Bahareh"
},
{
"family": "Ba",
"given": "Demba"
},
{
"family": "Murthy",
"given": "Venkatesh N"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "5575",
"DOI": "10.1038/
"PMID": "42056108",
"PMCID": "PMC13304163",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
29
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1126/sciadv.aed3610 [code]
- Dexterous single sniffs for ethological active olfaction.Journal: Science advancesIn common: Pingouin, seaborn, pandas, 3 other tools, mouse, 6 references
- [2] doi:10.1126/sciadv.aee1002 [code]
- Theta oscillations are an organizational unit of odor processing in the olfactory bulb.Journal: Science advancesIn common: 8 references
- [3] doi:10.1038/s41467-026-70356-9
- A topographical organization in the primary olfactory cortex.Journal: Nature communicationsIn common: mouse, 8 references
- [4] doi:10.1038/s41593-026-02314-z [code]
- Low-dimensional population dynamics in the brainstem gate REM sleep.Journal: Nature neuroscienceIn common: Pingouin, h5py, statsmodels, 6 other tools, mouse, 1 reference
- [5] doi:10.1016/j.isci.2026.116825 [code]
- Social hierarchy shapes behavioral and transcriptional responses to chronic stress and ketamine in male mice.Journal: iScienceIn common: Pingouin, h5py, statsmodels, 7 other tools, mouse
- [6] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: Pingouin, h5py, statsmodels, 7 other tools, mouse
- [7] doi:10.1016/j.isci.2026.117375 [code]
- Motor priming is associated with widespread recruitment into neural ensembles and more rapid ensemble transitions.Journal: iScienceIn common: Pingouin, h5py, statsmodels, 7 other tools
- [8] doi:10.1038/s41562-026-02414-7 [code]
- Optimized feature gains explain and predict successes and failures of human selective listening.Journal: Nature human behaviourIn common: Pingouin, h5py, statsmodels, 7 other tools
- [9] doi:10.7554/elife.92882 [code]
- Fear conditioning biases olfactory sensory neuron frequencies across generations.Journal: eLifeIn common: statsmodels, seaborn, pandas, 3 other tools, mouse, 3 references
- [10] doi:10.1016/j.isci.2026.115897 [code]
- Experience and behavior modulate piriform cortex odor representation in freely moving mice.Journal: iScienceIn common: NumPy, mouse, 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, 17 scripts, and 5 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:25ccf75f4e40e930…
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.
