OSCR

Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish.

Code ↔ Paper

19 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 19 matches
  1. [1] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for vagal experiments ↔ Analysis/Figure 2/1_extract_neurons.ipynb, lines 125–188 · score 0.98 · gSig, merge_thr, SNR_lowest, cnn_lowest, min_SNR, min_cnn_thr
  2. [2] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for vagal experiments ↔ Analysis/Figure 3/1_extract_neurons.ipynb, lines 124–195 · score 0.98 · gSig, merge_thr, SNR_lowest, cnn_lowest, min_SNR, min_cnn_thr
  3. [3] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for hindbrain experiments ↔ Analysis/Figure 2/1_extract_neurons.ipynb, lines 125–188 · score 0.97 · gSig, merge_thr, SNR_lowest, cnn_lowest, min_SNR, min_cnn_thr
  4. [4] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for hindbrain experiments ↔ Analysis/Figure 3/1_extract_neurons.ipynb, lines 124–195 · score 0.97 · gSig, merge_thr, SNR_lowest, cnn_lowest, min_SNR, min_cnn_thr
  5. [5] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for vagal experiments ↔ Analysis/Figure 3/4_vagal_alignment.ipynb, lines 47–159 · score 0.90 · flipped horizontally, left vagal ganglion, right vagal ganglion, reverse alignment, scalePenalty, landmarks
  6. [6] § STAR★Methods › Quantification and statistical analysis ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 309–338 · score 0.86 · beta regression, generalized linear model, 0–100 %, upper bound, GLM, variables
  7. [7] § Results › Chemosensory encoding of AITC features slow-onset dynamics ↔ notebooks/hindbrain_alignment_to_mapzebrain.ipynb, lines 402–445 · score 0.86 · inferior dorsal medulla, intermediate dorsal medulla, vagus motor nucleus, vagal sensory lobe, area postrema, oblongata
  8. [8] § Results › Gut distension broadly activates dorsal hindbrain circuits ↔ notebooks/hindbrain_alignment_to_mapzebrain.ipynb, lines 402–445 · score 0.81 · inferior dorsal medulla, intermediate dorsal medulla, vagus motor nucleus, vagal sensory lobe, oblongata, hindbrain
  9. [9] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for vagal experiments ↔ notebooks/vagal_alignment.ipynb, lines 50–70 · score 0.76 · flipped horizontally, left vagal ganglion, right vagal ganglion, anatomy, summed, pixel
  10. [10] § STAR★Methods › Quantification and statistical analysis ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 452–480 · score 0.68 · fold change, Gamma, GLM, genotypes, EECDTA, WT
  11. [11] § STAR★Methods › Method details › Feeding behavior data processing and analysis ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 91–123 · score 0.67 · Kolmogorov Smirnov, feeding behavior, bootstrapping, unfiltered, median, raw
  12. [12] § STAR★Methods › Method details › Slice tensor component analysis (sliceTCA) › Cross-validated model selection ↔ slicetca/run/grid_search.py, lines 15–78 · score 0.66 · cross validated loss, seeds, training, masked, components, models
  13. [13] § STAR★Methods › Method details › Two-photon data processing and analysis ↔ Analysis/Figure 3/1_extract_neurons.ipynb, lines 70–122 · score 0.65 · motion correction, NoRMCorre, CaImAn, mesmerize, gavage, volumes
  14. [14] § STAR★Methods › Method details › Slice tensor component analysis (sliceTCA) › Cross-validated model selection ↔ slicetca/run/decompose.py, lines 11–72 · score 0.65 · SliceTCA, seeds, training, loss, optimize, models
  15. [15] § Results › EEC depletion alters food approach and nutrient-specific feeding ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 348–378 · score 0.64 · feeding behavior, NaCl, Ctrl, EECDTA, alanine, empty
  16. [16] § Results › Amino acids increase gut-mediated feeding ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 189–233 · score 0.60 · NaCl, batch control, Gut fluorescence, Gly, Ala, Glu
  17. [17] § Results › Amino acids increase gut-mediated feeding ↔ Analysis/Figure 1/1_feeding_behavior_analysis.ipynb, lines 348–378 · score 0.56 · NaCl, Gly, Glu, Ctrl, alanine, empty
  18. [18] § STAR★Methods › Method details › Two-photon calcium imaging ↔ Analysis/Figure 3/4_vagal_alignment.ipynb, lines 47–159 · score 0.54 · right vagal ganglion, landmarks, anatomy, pixels, elavl3, stack
  19. [19] § STAR★Methods › Method details › Two-photon data processing and analysis › Parameters for hindbrain experiments ↔ notebooks/hindbrain_alignment_to_mapzebrain.ipynb, lines 65–106 · score 0.50 · scalePenalty, mapZebrain, Alignment, iterations, hindbrain

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 · 533 lines · 20 KB · no license · 6 matches

  1. # %%
  2. %load_ext autoreload
  3. %autoreload 2
  4. import matplotlib.pyplot as plt
  5. import numpy as np
  6. import os
  7. import pandas as pd
  8. from random import sample
  9. from scipy.stats import ks_2samp
  10. import statsmodels.api as sm
  11. import statsmodels.formula.api as smf
  12. from statsmodels.othermod.betareg import BetaModel
  13. import sys
  14. # Change this folder to where the cloned `caImageAnalysis` repository is
  15. sys.path.insert(0, '/Users/minelarinel/caImageAnalysis')
  16. # Set the base path to the arinel-et-al_2025 repository
  17. arinel_repo_path = r'/Users/minelarinel/arinel-et-al_2025'
  18. from caImageAnalysis.statistics import *
  19. # %%
  20. # Set the `data_folder` variable to the path of the folder containing the data files
  21. data_folder = fr'{arinel_repo_path}/Data/Fig1/Feeding behavior experiments'
  22. # %%
  23. # Process experimental data from multiple folders and compile it into a list of DataFrames.
  24. # 1. Initialize an empty list `exp_folders` to store paths of experiment folders.
  25. exp_folders = list()
  26. # 2. Iterate through the entries in the `data_folder` directory:
  27. # - If an entry is a directory and its name starts with '202', add its path to `exp_folders`.
  28. with os.scandir(data_folder) as entries:
  29. for entry in entries:
  30. if os.path.isdir(entry.path) and entry.name.startswith('202'):
  31. exp_folders.append(entry.path)
  32. # 3. Initialize an empty list `dfs` to store DataFrames.
  33. dfs = list()
  34. # 4. Iterate through the experiment folders in `exp_folders`
  35. for exp_folder in exp_folders:
  36. # Read the 'Results.csv' file into a DataFrame `df`.
  37. df = pd.read_csv(os.path.join(exp_folder, 'Results.csv'))
  38. # Add columns 'Experiment', 'filtered', and 'ablated' to `df`.
  39. df['Experiment'] = exp_folder[exp_folder.rfind('/')+1:]
  40. df['filtered'] = False
  41. df['ablated'] = False
  42. # For the '20231005_filtered(ala_emp_glu)' experiment, since only 3 of the stimuli were filtered,
  43. # mark rows with those specific stimuli as filtered.
  44. if '20231005_filtered(ala_emp_glu)' in exp_folder:
  45. filtered_stims = ['alanine', 'empty', 'glucose']
  46. for i, row in df.iterrows():
  47. if row['Stimulus'] in filtered_stims:
  48. df.loc[i, 'filtered'] = True
  49. # If the folder name contains 'filtered', mark all rows as filtered.
  50. elif 'filtered' in exp_folder:
  51. df['filtered'] = True
  52. # If the folder name contains 'ABLATED', mark all rows as ablated.
  53. if 'ABLATED' in exp_folder:
  54. df['ablated'] = True
  55. # Since an experiment can have multiple plates that got the same stimulus,
  56. # also keep track of the plate identity.
  57. df["plate"] = 0
  58. stims = list()
  59. for i, row in df.iterrows():
  60. if row["Stimulus"] != 'None' and row["Stimulus"] not in stims:
  61. pass
  62. elif row["Stimulus"] != 'None' and row["Stimulus"] in stims:
  63. df.loc[i, "plate"] = stims.count(row["Stimulus"])
  64. elif row["Stimulus"] == 'None':
  65. stims.append(df.loc[i-1, "Stimulus"])
  66. dfs.append(df)
  67. # Concatenate the DataFrames in `dfs` into a single DataFrame `df`.
  68. df = pd.concat(dfs, ignore_index=True)
  69. df = df[df.Stimulus != 'None']
  70. df
  71. # %% [markdown]
  72. # ### Check if the filtering has an effect
  73. # %%
  74. # Perform a bootstrapping analysis to determine if there is a statistically significant difference
  75. # between unfiltered and filtered datasets for 'empty' stimulus conditions using the Kolmogorov-Smirnov test.
  76. # Set the number of bootstrap iterations to perform.
  77. n_iterations = 1000
  78. empty_nofilter_vals = df[(df.filtered == False) & (df.Stimulus == 'empty') & (df.RawIntDen != 0)].RawIntDen.values
  79. empty_filter_vals = df[(df.filtered == True) & (df.Stimulus == 'empty') & (df.RawIntDen != 0)].RawIntDen.values
  80. # Check if the filtered dataset has more data points than the unfiltered dataset.
  81. # If true, perform bootstrapping.
  82. if len(empty_filter_vals) > len(empty_nofilter_vals):
  83. sample_pvals = list()
  84. for i in range(n_iterations):
  85. # Randomly sample data points from the filtered dataset to match the number of data points
  86. # in the unfiltered dataset.
  87. y = sample(list(empty_filter_vals), len(empty_nofilter_vals))
  88. # Perform the Kolmogorov-Smirnov test between the sampled filtered data and the unfiltered data.
  89. # Store the p-value from each iteration.
  90. _, p_value = kolmogorov_smirnov_test(y, empty_nofilter_vals, verbose=False)
  91. sample_pvals.append(p_value)
  92. # Calculate and print the median p-value from all iterations.
  93. # If the median p-value is larger than 0.05, the difference is statistically significant and
  94. # the two datasets need to be treated separately.
  95. print(np.median(sample_pvals))
  96. # %% [markdown]
  97. # Since the p-value is larger than 0.05, we can combine the filtered and unfiltered datasets.
  98. # %% [markdown]
  99. # ## Gut fluorescence - WT
  100. # %%
  101. # Combine the raw gut fluorescence for each experiment and store it in a dictionary.
  102. # Define the list of stimuli
  103. stims = ['empty', 'NaCl', 'glucose', 'glycine', 'alanine']
  104. aggregate = {stim: [] for stim in stims}
  105. normal_df = df[(df.ablated == False) & (df.RawIntDen != 0)]
  106. # Calculate gut fluorescence for each stimulus
  107. for stim in stims:
  108. stimdf = normal_df[normal_df.Stimulus == stim]
  109. aggregate[stim].extend(stimdf.RawIntDen.values)
  110. aggregate = dict([(k,pd.Series(v)) for k,v in aggregate.items()])
  111. # Rename the columns of the DataFrame
  112. df_WT = pd.DataFrame(aggregate)
  113. df_WT.columns = ["Ctrl", "NaCl", "Glu", "Gly", "Ala"]
  114. # Print and copy the combined DataFrame to clipboard
  115. print(df_WT)
  116. df_WT.to_clipboard()
  117. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  118. # data table `WT total gut fluorescence`, for plotting the graph.
  119. # %% [markdown]
  120. # ### Check if there are differences across batches
  121. # %%
  122. # Determine whether fish from different batches eat in different amounts.
  123. # Filter out ablated data
  124. normal_df = df[df.ablated == False]
  125. # Initialize a dictionary to store 'empty' stimulus data per experiment
  126. empty_per_exp = dict()
  127. # Iterate over unique experiments
  128. for i, exp in enumerate(normal_df.Experiment.unique()):
  129. # Filter data for the current experiment with 'empty' stimulus and non-zero RawIntDen
  130. exp_df = normal_df[(normal_df.Experiment == exp) & (normal_df.Stimulus == 'empty') & (normal_df.RawIntDen != 0)]
  131. # Store RawIntDen values in the dictionary
  132. empty_per_exp[f'batch {i+1}'] = exp_df.RawIntDen.values
  133. print(f'batch {i+1}: {exp}')
  134. # Convert dictionary values to pandas Series
  135. empty_per_exp = dict([(k, pd.Series(v)) for k, v in empty_per_exp.items()])
  136. # Print the DataFrame created from the dictionary
  137. print(pd.DataFrame(empty_per_exp))
  138. # Copy the DataFrame to clipboard
  139. pd.DataFrame(empty_per_exp).to_clipboard()
  140. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  141. # data table `WT batch differences`, for further analysis.
  142. # %% [markdown]
  143. # Due to significant differences in feeding across the controls of batches, all the raw intensity values are normalized to the batch control.
  144. # %% [markdown]
  145. # ### Fold change in gut fluorescence intensity
  146. # %%
  147. # Calculate the fold change in Raw Integrated Density (RawIntDen) for different stimuli
  148. # compared to the 'empty' condition. The results are stored in a dictionary and then
  149. # converted to a DataFrame for further analysis or copying to the clipboard.
  150. stims = ['empty', 'NaCl', 'glucose', 'glycine', 'alanine']
  151. aggregate_foldchange = {stim: [] for stim in stims}
  152. # Filter the DataFrame to include only relevant data.
  153. normal_df = df[(df.ablated == False) & (df.RawIntDen != 0)]
  154. for exp in normal_df.Experiment.unique():
  155. exp_df = normal_df[normal_df.Experiment == exp]
  156. # Median RawIntDen for the 'empty' condition.
  157. empty_fei = 0
  158. for stim in stims:
  159. stimdf = exp_df[exp_df.Stimulus == stim]
  160. if stim == 'empty':
  161. # For each experiment, calculate the median RawIntDen for the 'empty' condition.
  162. empty_fei = np.median(stimdf.RawIntDen.values)
  163. # Calculate the fold change for each stimulus compared to the 'empty' condition.
  164. aggregate_foldchange[stim].extend((stimdf.RawIntDen.values - empty_fei)/empty_fei)
  165. aggregate_foldchange = dict([(k,pd.Series(v)) for k,v in aggregate_foldchange.items()])
  166. # Rename the columns of the DataFrame
  167. df_foldchange = pd.DataFrame(aggregate_foldchange)
  168. df_foldchange.columns = ["Ctrl", "NaCl", "Glu", "Gly", "Ala"]
  169. # Print the DataFrame and copy it to the clipboard
  170. print(df_foldchange)
  171. df_foldchange.to_clipboard()
  172. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  173. # data table `WT fold change in fluorescence`, for further analysis.
  174. # %% [markdown]
  175. # ## Feeding incidence
  176. # %%
  177. # Calculate the feeding incidence for each experiment and store it in a dictionary.
  178. # Define the list of stimuli
  179. stims = ['empty', 'NaCl', 'glucose', 'glycine', 'alanine']
  180. feeding_incidence = {stim: [] for stim in stims}
  181. feeding_incidence_ablated = {stim: [] for stim in stims}
  182. normal_df = df[df.ablated == False]
  183. ablated_df = df[df.ablated == True]
  184. # Calculate feeding incidence for each stimulus
  185. for stim in stims:
  186. stimdf = normal_df[normal_df.Stimulus == stim]
  187. ablated_stimdf = ablated_df[ablated_df.Stimulus == stim]
  188. for exp in stimdf.Experiment.unique():
  189. for plate in stimdf[stimdf.Experiment == exp].plate.unique():
  190. expdf = stimdf[(stimdf.Experiment == exp) & (stimdf.plate == plate)]
  191. zeros = len(expdf[expdf.RawIntDen == 0])
  192. n_fish = len(expdf)
  193. feeding_incidence[stim].append((n_fish - zeros) / n_fish * 100)
  194. for exp in ablated_stimdf.Experiment.unique():
  195. for plate in ablated_stimdf[ablated_stimdf.Experiment == exp].plate.unique():
  196. expdf = ablated_stimdf[(ablated_stimdf.Experiment == exp) & (ablated_stimdf.plate == plate)]
  197. zeros = len(expdf[expdf.RawIntDen == 0])
  198. n_fish = len(expdf)
  199. feeding_incidence_ablated[stim].append((n_fish - zeros) / n_fish * 100)
  200. # Find the maximum number of columns needed for each stimulus.
  201. max_values_per_stim = {stim: max([len(feeding_incidence[stim]),
  202. len(feeding_incidence_ablated[stim])])
  203. for stim in stims}
  204. max_columns = int(max(max_values_per_stim.values()))
  205. print(f"Number of columns: {max_columns}")
  206. # Organize the feeding incidence data into a single DataFrame.
  207. organized_results = {stim: [] for stim in stims}
  208. for stim, values in feeding_incidence.items():
  209. organized_results[stim] = values + [np.nan] * (max_columns - len(values))
  210. organized_results_ablated = {stim: [] for stim in stims}
  211. for stim, values in feeding_incidence_ablated.items():
  212. organized_results_ablated[stim] = values + [np.nan] * (max_columns - len(values))
  213. # Create a DataFrame from the organized feeding incidence data.
  214. df_results = pd.DataFrame(organized_results)
  215. df_results_ablated = pd.DataFrame(organized_results_ablated)
  216. # Combine the two DataFrames into a single DataFrame.
  217. feeding_incidence_combined = pd.concat([df_results.transpose(), df_results_ablated.transpose()], axis=1)
  218. # Rename the columns of the DataFrame
  219. feeding_incidence_combined.index = ["Ctrl", "NaCl", "Glu", "Gly", "Ala"]
  220. # Print and copy the combined DataFrame to clipboard
  221. print(feeding_incidence_combined)
  222. feeding_incidence_combined.to_clipboard()
  223. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  224. # data table `feeding incidence`, for plotting the graph.
  225. # %%
  226. # Check if the feeding incidence data is normally distributed.
  227. for stim in stims:
  228. check_normality(feeding_incidence[stim], feeding_incidence_ablated[stim])
  229. # %% [markdown]
  230. # Since there are arrays that are not normally distributed, we need to run a test that does not assume normality to determine whether there are any genotype and stimulus effects. Therefore, we transition to Generalized Linear Models (GLMs). Since feeding incidences have a lower and upper bound (0-100), run the GLM with a beta regression.
  231. # %%
  232. # Prepare the data for the GLM
  233. # Combine the feeding incidence data into a single DataFrame
  234. data = []
  235. for stim in stims:
  236. for val in feeding_incidence[stim]:
  237. data.append({'Stimulus': stim, 'Value': val, 'Genotype': 'WT'})
  238. for val in feeding_incidence_ablated[stim]:
  239. data.append({'Stimulus': stim, 'Value': val, 'Genotype': 'EECDTA'})
  240. data = pd.DataFrame(data)
  241. # Beta regression requires the response variable to be in the range (0, 1), without any
  242. # zeros or ones.
  243. # To avoid this, we will transform the data according to Smithson and Verkuilen (2006).
  244. data["Value"] = data["Value"] / 100
  245. N = len(data)
  246. s = 0.5 # constant between 0 and 1
  247. for i, row in data.iterrows():
  248. data.at[i, "Value"] = ((row["Value"] * (N - 1)) + s) / N
  249. # Fit full model with interaction
  250. model = BetaModel.from_formula(f"Value ~ C(Stimulus, Treatment(reference='empty')) * C(Genotype, Treatment(reference='WT'))",
  251. data)
  252. result = model.fit(reml=True)
  253. print(result.summary())
  254. # %% [markdown]
  255. # We see a genotype effect in the GLM, so we combine stimuli.
  256. # %%
  257. # Check the variability of the feeding incidences between WT and EECDTA fish
  258. fligner_test(np.concatenate(list(feeding_incidence.values())),
  259. np.concatenate(list(feeding_incidence_ablated.values())))
  260. # %% [markdown]
  261. # EEC<sup>DTA</sup> fish have significantly lower and more variable feeding incidence values compared to WT.
  262. # %% [markdown]
  263. # ## Gut fluorescence - EEC<sup>DTA</sup>
  264. # %%
  265. # Combine the raw gut fluorescence for each experiment and store it in a dictionary.
  266. # Define the list of stimuli
  267. stims = ['empty', 'NaCl', 'glucose', 'glycine', 'alanine']
  268. aggregate_ablated = {stim: [] for stim in stims}
  269. ablated_df = df[(df.ablated == True) & (df.RawIntDen != 0)]
  270. # Calculate gut fluorescence for each stimulus
  271. for stim in stims:
  272. stimdf = ablated_df[ablated_df.Stimulus == stim]
  273. aggregate_ablated[stim].extend(stimdf.RawIntDen.values)
  274. aggregate_ablated = dict([(k,pd.Series(v)) for k,v in aggregate_ablated.items()])
  275. # Rename the columns of the DataFrame
  276. df_EECDTA = pd.DataFrame(aggregate_ablated)
  277. df_EECDTA.columns = ["Ctrl", "NaCl", "Glu", "Gly", "Ala"]
  278. # Print and copy the combined DataFrame to clipboard
  279. print(df_EECDTA)
  280. df_EECDTA.to_clipboard()
  281. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  282. # data table `EEC-DTA total gut fluorescence`, for plotting the graph.
  283. # %% [markdown]
  284. # ### Check if there are differences across batches
  285. # %%
  286. # Determine whether fish from different batches eat in different amounts.
  287. # Filter out WT data
  288. ablated_df = df[df.ablated == True]
  289. # Initialize a dictionary to store 'empty' stimulus data per experiment
  290. empty_per_exp = dict()
  291. # Iterate over unique experiments
  292. for i, exp in enumerate(ablated_df.Experiment.unique()):
  293. # Filter data for the current experiment with 'empty' stimulus and non-zero RawIntDen
  294. exp_df = ablated_df[(ablated_df.Experiment == exp) & (ablated_df.Stimulus == 'empty') & (ablated_df.RawIntDen != 0)]
  295. # Store RawIntDen values in the dictionary
  296. empty_per_exp[f'batch {i+1}'] = exp_df.RawIntDen.values
  297. print(f'batch {i+1}: {exp}')
  298. # Convert dictionary values to pandas Series
  299. empty_per_exp = dict([(k, pd.Series(v)) for k, v in empty_per_exp.items()])
  300. # Print the DataFrame created from the dictionary
  301. print(pd.DataFrame(empty_per_exp))
  302. # Copy the DataFrame to clipboard
  303. pd.DataFrame(empty_per_exp).to_clipboard()
  304. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  305. # data table `EEC-DTA batch differences`, for further analysis.
  306. # %% [markdown]
  307. # ### Fold change in gut fluorescence intensity
  308. # %%
  309. # Calculate the fold change in Raw Integrated Density (RawIntDen) for different stimuli
  310. # compared to the 'empty' condition. The results are stored in a dictionary and then
  311. # converted to a DataFrame for further analysis or copying to the clipboard.
  312. stims = ['empty', 'NaCl', 'glucose', 'glycine', 'alanine']
  313. aggregate_foldchange_ablated = {stim: [] for stim in stims}
  314. # Filter the DataFrame to include only relevant data.
  315. ablated_df = df[(df.ablated == True) & (df.RawIntDen != 0)]
  316. for exp in ablated_df.Experiment.unique():
  317. exp_df = ablated_df[ablated_df.Experiment == exp]
  318. # Median RawIntDen for the 'empty' condition.
  319. empty_fei = 0
  320. for stim in stims:
  321. stimdf = exp_df[exp_df.Stimulus == stim]
  322. if stim == 'empty':
  323. # For each experiment, calculate the median RawIntDen for the 'empty' condition.
  324. empty_fei = np.median(stimdf.RawIntDen.values)
  325. # Calculate the fold change for each stimulus compared to the 'empty' condition.
  326. aggregate_foldchange_ablated[stim].extend((stimdf.RawIntDen.values - empty_fei)/empty_fei)
  327. # %% [markdown]
  328. # #### Check whether there is a difference between the two genotypes across stimuli
  329. # %%
  330. # Check if the gut fluorescence data is normally distributed.
  331. for stim in stims:
  332. normality = check_normality(aggregate_foldchange[stim], aggregate_foldchange_ablated[stim], verbose=False)
  333. if False in normality:
  334. print(f"Data for {stim} is not normally distributed.")
  335. # %% [markdown]
  336. # Since we have arrays that are not normally distributed, we need to run Generalized Linear Models (GLMs). Since fold changes are heavily skewed, continuous data, run a GLM with a gamma distribution.
  337. # %%
  338. # Prepare the data for generalized linear model (GLM)
  339. # Combine the fold change data into a single DataFrame
  340. data = []
  341. for stim in stims:
  342. for val in aggregate_foldchange[stim]:
  343. data.append({'Stimulus': stim, 'FoldChange': val, 'Genotype': 'WT'})
  344. for val in aggregate_foldchange_ablated[stim]:
  345. data.append({'Stimulus': stim, 'FoldChange': val, 'Genotype': 'EECDTA'})
  346. data = pd.DataFrame(data)
  347. glm_data = pd.DataFrame(data)
  348. # Gamma regression requires the response variable to be positive.
  349. # To avoid this, we will transform the data by adding a small constant to ensure all values
  350. # are positive.
  351. glm_data["FoldChange"] = glm_data["FoldChange"] + np.abs(glm_data["FoldChange"].min()) + 0.001
  352. # Fit Gamma Regression Model
  353. model = smf.glm(
  354. formula="FoldChange ~ C(Genotype, Treatment(reference='WT')) * C(Stimulus, Treatment(reference='empty'))",
  355. data=glm_data,
  356. family=sm.families.Gamma(link=sm.families.links.log())
  357. )
  358. result = model.fit()
  359. print(result.summary())
  360. # %%
  361. sidak_results = sidak_multiple_comparisons_test(data, between=["Stimulus", "Genotype"], between_reference=["empty", "WT"],
  362. interaction=True, melt_df=False, dv='FoldChange',
  363. parametric=False)
  364. sidak_results = add_statistical_asterisks(sidak_results[sidak_results["p-corr"] < 0.1])
  365. sidak_results
  366. # %% [markdown]
  367. # Since there are no significant differences between WT and EECDTA fish across stimuli, we decided to run Kruskal-Wallis tests to determine stimulus effects per genotype.
  368. # %%
  369. # Convert the EECDTA fold change data into a dataframe and copy it to the clipboard.
  370. aggregate_foldchange_ablated = dict([(k,pd.Series(v)) for k,v in aggregate_foldchange_ablated.items()])
  371. # Rename the columns of the DataFrame
  372. df_foldchange_ablated = pd.DataFrame(aggregate_foldchange_ablated)
  373. df_foldchange_ablated.columns = ["Ctrl", "NaCl", "Glu", "Gly", "Ala"]
  374. # Print the DataFrame and copy it to the clipboard
  375. print(df_foldchange_ablated)
  376. df_foldchange_ablated.to_clipboard()
  377. # Paste the copied DataFrame into the feeding_behavior_analysis Prism file,
  378. # data table `EEC-DTA fold change in fluorescence`, for further analysis.
  379. # %% [markdown]
  380. # #### Compare fold changes between genotypes
  381. # %%
  382. stimulus = 'Glu'
  383. wt_median = np.median(df_foldchange[stimulus][df_foldchange[stimulus].notnull()])
  384. eecdta_median = np.median(df_foldchange_ablated[stimulus][df_foldchange_ablated[stimulus].notnull()])
  385. fold_change_ratio = eecdta_median / wt_median
  386. print(f"EEC-DTA fold change in fluorescence is {fold_change_ratio} times higher than WT with {stimulus} particles")
  387. # %%
  388. stimulus = 'Gly'
  389. wt_median = np.median(df_foldchange[stimulus][df_foldchange[stimulus].notnull()])
  390. eecdta_median = np.median(df_foldchange_ablated[stimulus][df_foldchange_ablated[stimulus].notnull()])
  391. fold_change_ratio = eecdta_median / wt_median
  392. print(f"EEC-DTA fold change in fluorescence is {fold_change_ratio} times higher than WT with {stimulus} particles")
  393. # %%
  394. stimulus = 'Ala'
  395. wt_median = np.median(df_foldchange[stimulus][df_foldchange[stimulus].notnull()])
  396. eecdta_median = np.median(df_foldchange_ablated[stimulus][df_foldchange_ablated[stimulus].notnull()])
  397. fold_change_ratio = eecdta_median / wt_median
  398. print(f"EEC-DTA fold change in fluorescence is {fold_change_ratio} times higher than WT with {stimulus} particles")

1_feeding_behavior_analysis.ipynb at commit adf1dab, no license · at the source

Overview

Authors: Minel Arinel1, John A Atkinson1, Karina M Matos-Fernández1, Evan P Drage1, Matt Hawkyard2, Eva A Naumann1,3,4,5
ORCID iDs: Eva A Naumann
  1. Department of Neurobiology, Duke University School of Medicine, Durham, NC, USA
  2. Aquaculture Research Institute, University of Maine, Orono, ME, USA
  3. Department of Cell Biology, Duke University School of Medicine, Durham, NC, USA
  4. Department of Psychology & Neuroscience, Duke University, Durham, NC, USA
  5. Department of Biomedical Engineering, Duke University, Durham, NC, USA
Institutions: Duke University (United States); Duke Medical Center (United States); University of Maine (United States)
Journal: iScience, volume 29, issue 6, article 116206
Dates: received 14 January 2026; accepted 15 May 2026; published online 11 June 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1016/j.isci.2026.116206 · PMID 42325267 · PMCID PMC13276583 · OpenAlex W4412561386
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: optical imaging (calcium, voltage, 2-photon) (modality), zebrafish (organism), systems (subfield)
Methods: Spectral & time-frequency, Statistics, Machine learning, Evoked potentials, Connectivity, fMRI & imaging, Single-unit activity, calcium imaging
Keywords: Interoception, Gut-brain, Calcium imaging, Zebrafish
Topic: Zebrafish Biomedical Research Applications (Cell Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: National Science Foundation Directorate for STEM Education; Boehringer Ingelheim Fonds; Duke Institute for Brain Sciences, Duke University; Boehringer Ingelheim Foundation; National Science Foundation; Alfred P. Sloan Foundation
Citations: cited by 1 paper (Europe PMC); 167 references in the paper
Research resources: pGP-AAV-syn-FLEX-jGCaMP8m-WPRE RRID:Addgene_162378, Tol2-elavl3-H2B-GCaMP6s RRID:Addgene_59530, Fiji RRID:SCR_002285, Prism v10.4.1 RRID:SCR_002798, Prairie View RRID:SCR_017142, CaImAn RRID:SCR_021152, SimpleITK RRID:SCR_024693, Zebrafish: Tg(isl1:EGFP)rw0 RRID:ZFIN_ZDB-ALT-030919-2, Zebrafish: Tg(-5kbneurod1:TagRFP)w69 RRID:ZFIN_ZDB-GENO-120113-1

Abstract

Animals sense food quantity and quality to regulate feeding behaviors. Enteroendocrine cells (EECs) in the gut epithelium detect luminal distension and nutrients, signaling this information to the brain via vagal sensory neurons. However, how mechanosensory and chemosensory signals are dynamically encoded by gut-brain circuits remains unclear, particularly during early development. Using larval zebrafish, we developed a feeding assay with particles releasing specific nutrients after consumption, demonstrating that EECs regulate nutrient-specific feeding days after gut formation. To determine how post-ingestive signals are encoded along gut-brain circuitry, we developed a microgavage method enabling simultaneous gut stimulation and two-photon imaging. Gut distension alone drove widespread activation and suppression in vagal and dorsal hindbrain neurons. Although nutrient-evoked responses shared temporal features with distension, allyl isothiocyanate elicited slower-onset dynamics. These findings reveal that fast gut-to-brain communication emerges early in life, with distension and aversive chemical cues encoded through distinct temporal dynamics in developing interoceptive circuits.

Reproduced under the paper's license (CC BY-NC), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 19 matches between paragraphs and lines of code.

gitlab.oit.duke.edu/ean26/arinel-et-al_2026

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: adf1daba06a24e51cae6960ae9d149413b2a92b5, 23 June 2026
Languages: Jupyter (30)
Size: 3,764 files, 30 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: 30 notebooks
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (7 files), NumPy (6 files), tifffile (5 files), pandas (4 files), SciPy (2 files), statsmodels (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
7 files

naumann-lab/caimageanalysis

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 497918684f1aafd0066b06af651537937eae38d3, 18 December 2023
Languages: Python (30), Jupyter (5)
Size: 49 files, 35 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, 5 notebooks
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (31 files), pandas (16 files), Matplotlib (13 files), tifffile (13 files), CaImAn (11 files), SciPy (8 files), OpenCV (7 files), Suite2p (6 files), seaborn (5 files), Pillow (2 files), scikit-learn (2 files), SimpleITK (2 files), NetworkX (1 file), scikit-image (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
36 files

Naumann-Lab/alignment

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 4e5c3f58b3974446581ea5f92bf1c21f6ad3c8db, 16 May 2026
Languages: Python (4), Jupyter (3)
Size: 11 files, 7 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, license file, environment (environment.yml), 3 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (7 files), tifffile (5 files), Matplotlib (3 files), pandas (3 files), OpenCV (1 file), SimpleITK (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
9 files

Zenodo 20127931

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the resources table
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)

Zenodo 20128341

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the resources table
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (7 files), tifffile (5 files), Matplotlib (3 files), pandas (3 files), OpenCV (1 file), SimpleITK (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
9 files
At the source:

arthur-pe/slicetca

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 61d77c6f8326ab20b2c92f9c9ea7f6e9d9095b36, 7 March 2025
Languages: Python (22), Jupyter (1)
Size: 28 files, 23 scripts
Software Heritage: not archived
Found in: the resources table
Holds: README, license file, environment (setup.cfg, setup.py), tests, 1 notebook
Not found: CITATION.cff, continuous integration, documentation
Tools: PyTorch (13 files), NumPy (10 files), Matplotlib (4 files), SciPy (3 files), pandas (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
25 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:

  • 6 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 79 scripts, each with its path and the digest of its content;
  • 19 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

No dataset and no data link were found in the paper.

Data and code availability

All data reported in this paper will be shared by the lead contact upon request.

All original code has been deposited at https://gitlab.oit.duke.edu/ean26/arinel-et-al_2026 and is publicly available as of the date of publication. For calcium imaging analysis, the notebooks utilize our custom-written module at https://github.com/Naumann-Lab/caImageAnalysis/tree/arinel-et-al_2026. The notebooks for alignment use another environment and repository, which can be found at https://github.com/Naumann-Lab/alignment. Both repositories are publicly available as of the date of publication. Accession numbers are listed in the key resources table.

Any additional information required to reanalyze the data reported in this paper is available from the lead contact upon request.

Reproduced under the paper's license (CC BY-NC), 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, 6 authors, 4 keywords, 6 funders, 165 references, 9 RRIDs.

Cite

This paper

Arinel, M., Atkinson, J. A., Matos-Fernández, K. M., Drage, E. P., Hawkyard, M., & Naumann, E. A. (2026). Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish. iScience, 29(6), 116206. https://doi.org/10.1016/j.isci.2026.116206

BibTeX

@article{arinel2026gut,
author = {Arinel, Minel and Atkinson, John A and Matos-Fernández, Karina M and Drage, Evan P and Hawkyard, Matt and Naumann, Eva A},
title = {{Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish}},
journal = {iScience},
year = {2026},
month = jun,
volume = {29},
number = {6},
pages = {116206},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.116206},
url = {https://doi.org/10.1016/j.isci.2026.116206},
pmid = {42325267},
pmcid = {PMC13276583}
}

RIS

TY - JOUR
AU - Arinel, Minel
AU - Atkinson, John A
AU - Matos-Fernández, Karina M
AU - Drage, Evan P
AU - Hawkyard, Matt
AU - Naumann, Eva A
TI - Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/06/11
VL - 29
IS - 6
SP - 116206
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.116206
UR - https://doi.org/10.1016/j.isci.2026.116206
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.116206",
"type": "article-journal",
"title": "Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish",
"container-title": "iScience",
"author": [
{
"family": "Arinel",
"given": "Minel"
},
{
"family": "Atkinson",
"given": "John A"
},
{
"family": "Matos-Fernández",
"given": "Karina M"
},
{
"family": "Drage",
"given": "Evan P"
},
{
"family": "Hawkyard",
"given": "Matt"
},
{
"family": "Naumann",
"given": "Eva A"
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "6",
"page": "116206",
"DOI": "10.1016/j.isci.2026.116206",
"PMID": "42325267",
"PMCID": "PMC13276583",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.116206",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
11
]
]
}
}

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-76242-8 [code]
Whole-brain, all-optical interrogation of neuronal dynamics underlying gut and vascular interoception in zebrafish.
Journal: Nature communications
In common: SimpleITK, tifffile, NetworkX, 7 other tools, zebrafish, systems, 14 references
[2] doi:10.1016/j.cpblue.2026.100072 [code]
NodoMap: A single-cell and spatial transcriptomic atlas of the mouse nodose ganglion.
Journal: Cell press blue
In common: 13 references
[3] doi:10.1016/j.isci.2026.117359 [code]
Connectomic mapping of pharyngeal and gut sensory circuits in adult &lt;i&gt;Drosophila&lt;/i&gt;.
Journal: iScience
In common: Pillow, statsmodels, seaborn, 4 other tools, systems, 7 references
[4] doi:10.1126/sciadv.adv3770 [code]
Evolution of a central dopamine circuit underlies adaptation of a light-evoked sensorimotor response in the blind cavefish.
Journal: Science advances
In common: CaImAn, tifffile, OpenCV, 8 other tools, systems, 1 reference
[5] doi:10.1016/j.molmet.2026.102371
A distinct vagus-beta cell neural circuit senses glucose and modulates insulin secretion.
Journal: Molecular metabolism
In common: optical imaging (calcium, voltage, 2-photon), 9 references
[6] doi:10.1038/s41467-026-69633-4 [code]
Visuomotor decision-making through multifeature convergence in the larval zebrafish hindbrain.
Journal: Nature communications
In common: Pillow, seaborn, scikit-learn, 4 other tools, zebrafish, 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 neuroscience
In common: SimpleITK, tifffile, OpenCV, 6 other tools, optical imaging (calcium, voltage, 2-photon), systems, 2 references
[8] 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 protocols
In common: CaImAn, tifffile, OpenCV, 6 other tools, optical imaging (calcium, voltage, 2-photon), 2 references
[9] doi:10.1371/journal.pcbi.1014571 [code]
SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.
Journal: PLoS computational biology
In common: tifffile, NetworkX, OpenCV, 10 other tools
[10] doi:10.1364/boe.600665 [code]
NeuroSeg-MF: robust neuron segmentation in two-photon Ca&lt;sup&gt;2+&lt;/sup&gt; imaging using multi-feature fusion and detection-guided SAM.
Journal: Biomedical optics express
In common: tifffile, OpenCV, Pillow, 6 other tools, optical imaging (calcium, voltage, 2-photon), 3 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.

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.