Alarm pheromone imaging: Hart, Frank, Lopes et al. (2023), dandiset 000467¶

Paper: Hart T, Frank DD, Lopes LE et al. "Sparse and stereotyped encoding implicates a core glomerulus for ant alarm behavior", Cell 186 (2023), doi:10.1016/j.cell.2023.05.025.

Dataset: DANDI:000467, version 0.261004.1655 (doi:10.48324/dandi.000467/0.261004.1655). The paper cites the dandiset without a version. This is the only published version (published 2026-10-04, "after adding additional meta data"); the draft has the same 14,685 assets and size, so the most recent published version is pinned, as the protocol says when the paper cites none.

Claims file: claims/000467/10.1016-j.cell.2023.05.025.yaml, written from the accepted author manuscript in PubMed Central (PMC10334690) with its supplement. The shared methods M1 to M4 are in its opening comment; the helper al_imaging.py says where each parameter comes from.

This notebook holds the unilateral alarm pheromone experiment (13 ants). The general odorant and bilateral experiments are in general_odorants.ipynb. Claims tested here:

Claim Here
fig4b-vehicle-no-response tested
figs5a-sparse-median-6 tested
figs5b-shared-glomerulus tested
figs5c-mho-rarely-overlaps tested
fig6-shared-panic-pathway imaging half tested
fig4-no-decreases tested at each ant's higher concentration
fig4-large-lasting-responses tested
fig5ab-focal-glomeruli tested
fig5d-stereotyped-positions tested
fig5c-s6-tuning tested
fig5c-6g-slow tested
figs7a-6g-extended tested
figs7c-temporal-lme tested
figs5a-concentration-more-glomeruli in concentration.ipynb
fig3f, fig3fg, figs4a in general_odorants.ipynb
behaviour, transgenesis, confocal and genome claims (15) blocked; see the attempt record

Whole-volume peak maps (M1) are computed for each ant's higher-concentration pheromone trials, its vehicle trial and its isopropanol trial: 182 trials of 33 planes, about 6,000 files of 84 MB, of which only volumes 1 to 5, 8 and 9 to 14 are read. The single-glomerulus traces (M3) read every volume of a band of rows around the focal glomeruli, on five planes, for every pheromone trial.

In [1]:
import time
import warnings

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
from scipy import ndimage, stats

import al_imaging as al

from statsmodels.tools.sm_exceptions import ConvergenceWarning

warnings.filterwarnings("ignore", category=RuntimeWarning)
warnings.filterwarnings("ignore", category=ConvergenceWarning)   # convergence is reported with each fit
warnings.filterwarnings("ignore", message="Random effects covariance is singular")
pd.set_option("display.width", 200)
pd.set_option("display.max_columns", 40)
pd.set_option("display.max_rows", 200)
T0 = time.time()
print("dandiset", al.DANDISET_ID, "version", al.DANDISET_VERSION)
dandiset 000467 version 0.261004.1655
In [2]:
assets = al.list_assets()
trials = al.trials(assets)
print(len(assets), "assets;", assets.blob.nunique(), "distinct blobs; all file names parsed:", assets.parsed.all())
alarm_ants = sorted(trials[trials.odor == "heptanone"].ant.unique())
general_ants = sorted(trials[trials.odor == "3-hexanone"].ant.unique())
bilateral_ants = sorted(set(trials.ant) - set(alarm_ants) - set(general_ants))
print("alarm ants", len(alarm_ants), "general odorant ants", len(general_ants), "bilateral ants", len(bilateral_ants), bilateral_ants)
alarm = trials[trials.ant.isin(alarm_ants)].copy()
conc_pairs = alarm[alarm.odor.isin(al.PHEROMONES)].groupby("ant").conc.agg(lambda c: tuple(sorted(set(c))))
alarm["higher"] = alarm.ant.map(conc_pairs.map(max))
print(conc_pairs.value_counts())
14685 assets; 14685 distinct blobs; all file names parsed: True
alarm ants 13 general odorant ants 6 bilateral ants 3 ['ant2-m2-d10-y2023', 'ant3-m2-d10-y2023', 'ant4-m2-d10-y2023']
conc
(0.03, 0.48)      5
(0.12, 0.48)      3
(0.0075, 0.12)    3
(0.03, 0.12)      2
Name: count, dtype: int64
In [3]:
print("Trials per ant, odor and concentration (alarm experiment; planes per trial:",
      sorted(alarm.n_planes.unique()), "; volumes per trial:", sorted(alarm.nvol.unique()), ")")
print(alarm.groupby(["ant", "conc", "odor"]).size().unstack("odor").fillna(0).astype(int).to_string())
print()
print("Recordings of 120 volumes (144 s):")
print(alarm[alarm.nvol == 120][["ant", "trial", "odor", "conc", "n_planes"]].to_string(index=False))
# Each ant's sessions: date and ant number in the name; one subject per ant
print("subjects:", assets[assets.ant.isin(alarm_ants)]["sub"].nunique(), "; sessions (date):",
      assets[assets.ant.isin(alarm_ants)].date.value_counts().to_dict())
Trials per ant, odor and concentration (alarm experiment; planes per trial: [np.int64(33)] ; volumes per trial: [np.int64(40), np.int64(120)] )
odor                      6MHO  heptanol  heptanone  hexanol  isopropanol  vehicle
ant               conc                                                            
ant1-m4-d21-y2022 0.1200     3         3          3        3            0        0
                  0.4800     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant1-m4-d26-y2022 0.0300     3         3          3        3            0        0
                  0.4800     4         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant1-m4-d27-y2022 0.0075     3         3          3        3            0        0
                  0.1200     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant2-m4-d20-y2022 0.0300     3         3          3        3            0        0
                  0.1200     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant2-m4-d21-y2022 0.1200     3         3          3        3            0        0
                  0.4800     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant2-m4-d26-y2022 0.0300     3         3          3        3            0        0
                  0.4800     4         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant2-m4-d27-y2022 0.0075     3         3          3        3            0        0
                  0.1200     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant3-m4-d21-y2022 0.1200     3         3          3        3            0        0
                  0.4800     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant3-m4-d26-y2022 0.0300     3         3          3        3            0        0
                  0.4800     4         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant3-m4-d27-y2022 0.0075     3         3          3        3            0        0
                  0.1200     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant4-m4-d20-y2022 0.0300     3         3          3        3            0        0
                  0.1200     3         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant4-m4-d26-y2022 0.0300     3         3          3        3            0        0
                  0.4800     4         3          3        3            0        0
                  1.0000     0         0          0        0            1        1
ant5-m4-d26-y2022 0.0300     3         3          3        3            0        0
                  0.4800     4         3          3        3            0        0
                  1.0000     0         0          0        0            1        1

Recordings of 120 volumes (144 s):
              ant  trial odor  conc  n_planes
ant1-m4-d26-y2022    157 6MHO  0.48        33
ant2-m4-d26-y2022    194 6MHO  0.48        33
ant3-m4-d26-y2022    228 6MHO  0.48        33
ant4-m4-d26-y2022    265 6MHO  0.48        33
ant5-m4-d26-y2022    300 6MHO  0.48        33
subjects: 13 ; sessions (date): {'m4-d26-y2022': 4455, 'm4-d27-y2022': 2574, 'm4-d21-y2022': 2574, 'm4-d20-y2022': 1716}
In [4]:
# Metadata from the files themselves: one file per ant, plus all 33 planes of one trial and one 144 s file
meta_rows = []
for ant in alarm_ants:
    r = alarm[(alarm.ant == ant) & (alarm.nvol == 40)].iloc[0]
    m = al.file_metadata(r.blobs[16])
    meta_rows.append({"ant": ant, **{k: m[k] for k in ("shape", "dtype", "chunks", "compression", "offset", "unit",
                                                       "imaging_rate", "grid_spacing", "age", "location")}})
meta = pd.DataFrame(meta_rows)
print(meta.to_string(index=False))
r = alarm[alarm.nvol == 120].iloc[0]
m120 = al.file_metadata(r.blobs[0])
print("144 s file:", m120["shape"], "offset", m120["offset"], "|", m120["series_description"])
print("experiment description of one file:", al.file_metadata(alarm.iloc[0].blobs[0])["experiment_description"])
one = alarm[(alarm.nvol == 40)].iloc[3]
starts = [al.file_metadata(b)["starting_time"] for b in one.blobs]
print("starting_time by plane (s), trial", one.ant, one.trial, ":", np.round(starts, 4).tolist())
print("step between planes", np.round(np.diff(starts).mean(), 5), "s; x 33 =", np.round(np.diff(starts).mean() * 33, 4),
      "s per volume; 1/imaging_rate =", round(1 / 0.83, 4))
PLANE_DT = float(np.diff(starts).mean())   # each plane's volumes start PLANE_DT x plane later
# the raw range reads equal the h5py reads
with al.open_nwb(one.blobs[10]) as f:
    h = f["acquisition/TwoPhotonSeries1/data"][8:10, 100:110, :]
print("range read equals h5py read:", np.array_equal(al.read_frames(one.blobs[10], 8, 10)[:, 100:110, :], h))
print("byte offset is", sorted(set(meta.offset) | {m120["offset"]}), "in every file read; file sizes:",
      assets.groupby("nvol")["size"].agg(["min", "max"]).to_dict("index"))
              ant          shape   dtype chunks compression  offset             unit  imaging_rate              grid_spacing        age                                              location
ant1-m4-d21-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0)  P92D/P99D right antennal lobe, up is lateral, right is anterior
ant1-m4-d26-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P97D/P104D right antennal lobe, up is lateral, right is anterior
ant1-m4-d27-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P98D/P104D right antennal lobe, up is lateral, right is anterior
ant2-m4-d20-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0)  P91D/P98D right antennal lobe, up is lateral, right is anterior
ant2-m4-d21-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0)  P92D/P99D right antennal lobe, up is lateral, right is anterior
ant2-m4-d26-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P97D/P104D right antennal lobe, up is lateral, right is anterior
ant2-m4-d27-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P98D/P104D right antennal lobe, up is lateral, right is anterior
ant3-m4-d21-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0)  P92D/P99D right antennal lobe, up is lateral, right is anterior
ant3-m4-d26-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P97D/P104D right antennal lobe, up is lateral, right is anterior
ant3-m4-d27-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P98D/P104D right antennal lobe, up is lateral, right is anterior
ant4-m4-d20-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0)  P91D/P98D right antennal lobe, up is lateral, right is anterior
ant4-m4-d26-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P97D/P104D right antennal lobe, up is lateral, right is anterior
ant5-m4-d26-y2022 (40, 512, 512) float64   None        None    3464 raw fluorescence          0.83 (0.288867, 0.288867, 5.0) P97D/P104D right antennal lobe, up is lateral, right is anterior
144 s file: (120, 512, 512) offset 3464 | two photon recording, 120 cycles total, recording time 144.0s, odor stimulus triggered at 3s with duration 5s
experiment description of one file: 40 cycle 2photon recording, 5s odor stim triggered at 3s, using 1x paraffin-oil at z depth 0 um in the clonal raider ant antennal lobe, transgenic line expressing GCaMP6s in olfactory sensory neurons
starting_time by plane (s), trial ant1-m4-d21-y2022 126 : [0.1004, 0.1339, 0.1674, 0.2008, 0.2343, 0.2678, 0.3013, 0.3347, 0.3682, 0.4017, 0.4352, 0.4686, 0.5021, 0.5356, 0.5691, 0.6025, 0.636, 0.6695, 0.703, 0.7364, 0.7699, 0.8034, 0.8369, 0.8703, 0.9038, 0.9373, 0.9708, 1.0042, 1.0377, 1.0712, 1.1047, 1.1381, 1.1716]
step between planes 0.03347 s; x 33 = 1.1047 s per volume; 1/imaging_rate = 1.2048
range read equals h5py read: True
byte offset is [3464] in every file read; file sizes: {40: {'min': 84086336, 'max': 84086336}, 120: {'min': 251858496, 'max': 251858496}}

The files give the orientation ("right antennal lobe, up is lateral, right is anterior"), so image rows run lateral (top) to medial and columns posterior (left) to anterior. Pixel size 0.2889 um and 5 um between planes, as the paper states. Ages are given as ranges ("P97D/P104D"). The plane's starting_time grows by about 1.2 s / 33 per plane, so each file's volumes are timed from its plane's place in the volume scan.

In [5]:
# Whole-volume peak maps (M1) for each ant's higher concentration, vehicle and isopropanol
sel = alarm[(alarm.nvol == 40) & ((alarm.conc == alarm.higher) | (alarm.odor.isin(["vehicle", "isopropanol"])))]
sel = sel[sel.odor.isin(al.PHEROMONES + ["vehicle", "isopropanol"])]
print(len(sel), "trials,", sum(len(b) for b in sel.blobs), "plane files")
t = time.time()
maps = al.trial_maps(sel, keep_frames=True, progress=lambda a, b: print(f"{a}/{b} files, {time.time() - t:.0f} s", flush=True) if a % 2000 == 0 else None)
print(f"maps in {time.time() - t:.0f} s")
182 trials, 6006 plane files
2000/6006 files, 262 s
4000/6006 files, 523 s
6000/6006 files, 783 s
maps in 783 s
In [6]:
shift = pd.Series({i: maps[i]["shift"].max() for i in sel.index})
sat = pd.Series({i: maps[i]["saturated"].mean() for i in sel.index})
f0max = pd.Series({i: maps[i]["f0"].max() for i in sel.index})
print("largest frame shift per trial (pixels): median", shift.median(), "max", shift.max(),
      "; trials with a shift over 10 pixels (2.9 um):", int((shift > 10).sum()))
print("fraction of baseline pixels at the detector maximum (8191), mean over planes: median",
      round(sat.median(), 4), "max", round(sat.max(), 4))
print("baseline F0 (4x4 bins) maximum per trial: range", f0max.min().round(0), "to", f0max.max().round(0))
proj_rng = pd.Series({i: (maps[i]["proj"].min(), maps[i]["proj"].max()) for i in sel.index})
print("max projections are clipped to [-1, 4]: min", min(p[0] for p in proj_rng), "max", max(p[1] for p in proj_rng))
# duplicate recordings: identical baseline maps between two trials of an ant
dups = []
for ant, g in sel.groupby("ant"):
    idx = list(g.index)
    for a in range(len(idx)):
        for b in range(a + 1, len(idx)):
            if np.array_equal(maps[idx[a]]["f0"], maps[idx[b]]["f0"]):
                dups.append((ant, sel.loc[idx[a]].trial, sel.loc[idx[b]].trial))
print("pairs of trials with identical baseline volumes:", dups if dups else "none")
largest frame shift per trial (pixels): median 10.0 max 42 ; trials with a shift over 10 pixels (2.9 um): 77
fraction of baseline pixels at the detector maximum (8191), mean over planes: median 0.001 max 0.0046
baseline F0 (4x4 bins) maximum per trial: range 8036.0 to 8191.0
max projections are clipped to [-1, 4]: min -1.0 max 4.0
pairs of trials with identical baseline volumes: none

ROIs and responding glomeruli (M2)¶

For each ant, ROIs are drawn on its higher-concentration pheromone trials (the vehicle and isopropanol have one trial each, so they cannot meet "at least two trials"), inside a mask of the AL: the connected regions of at least 0.2 in each trial's map that recur (overlap by at least half the smaller region) in another trial of the same odor, drawing a keeping all of them and drawing b those of glomerulus size (10 to 20 um, wider ones split). Each claim built on M2 is shown under the eight readings: ROI drawing a or b, ROI value i (mean) or ii (maximum), counting x (trial mean at least 0.2) or y (mean of per-trial counts). The figure shows every ant's ROIs under both drawings.

In [7]:
al_masks = {ant: al.al_mask(maps, sel[sel.ant == ant].index) for ant in alarm_ants}
rois = {}
for ant in alarm_ants:
    hi = sel[(sel.ant == ant) & sel.odor.isin(al.PHEROMONES)]
    for d in "ab":
        rois[ant, d] = al.draw_rois(maps, hi, d, al_masks[ant])
print(pd.DataFrame({d: {ant: f"{len(rois[ant, d])} regions / {al.n_groups(rois[ant, d])} ROIs" for ant in alarm_ants} for d in "ab"}))

ODORS = al.PHEROMONES + ["vehicle", "isopropanol"]
resp, counts, depth = {}, [], {}
for ant in alarm_ants:
    g = sel[sel.ant == ant]
    for d, v, c in al.READINGS:
        for od in ODORS:
            idx = g[g.odor == od].index
            r_, n_ = al.responding(maps, rois[ant, d], idx, v, c)
            resp[ant, d, v, c, od] = r_
            counts.append({"ant": ant, "higher": g.conc.max() if od in al.PHEROMONES else g[g.odor.isin(al.PHEROMONES)].conc.max(),
                           "reading": d + v + c, "odor": od, "count": n_})
    for d in "ab":
        for od in al.PHEROMONES:
            depth[ant, d, od] = al.group_depths(maps, rois[ant, d], g[g.odor == od].index)
counts = pd.DataFrame(counts)
counts["higher"] = counts.ant.map(conc_pairs.map(max))
wide = counts.pivot_table(index=["ant", "higher"], columns=["reading", "odor"], values="count")
print(wide.round(1).to_string())

# ROI sizes and an image of the ROIs of each ant over its max projection (maximum over its
# higher-concentration pheromone trials), for both drawings
size_rows = []
for (ant, d), rr in rois.items():
    for r_ in rr:
        size_rows.append({"drawing": d, "diameter_um": 2 * np.sqrt(r_["mask"].sum() / np.pi) * al.PIXEL_UM * al.BIN})
size_rows = pd.DataFrame(size_rows)
print(size_rows.groupby("drawing").diameter_um.describe(percentiles=[0.1, 0.5, 0.9]).round(1).to_string())
print("regions under 10 um across, by drawing:", size_rows[size_rows.diameter_um < 10].groupby("drawing").size().to_dict())
fig, ax = plt.subplots(2, len(alarm_ants), figsize=(1.9 * len(alarm_ants), 4.2), squeeze=False)
for k, ant in enumerate(alarm_ants):
    hi = sel[(sel.ant == ant) & sel.odor.isin(al.PHEROMONES)]
    bg = np.max([al._bin(maps[i]["proj"], al.BIN) for i in hi.index], axis=0)
    for j, d in enumerate("ab"):
        rgb = np.repeat(((np.clip(bg, -0.2, 1) + 0.2) / 1.2)[..., None], 3, 2)
        edge = np.zeros(bg.shape, bool)
        for m in al.group_masks(rois[ant, d]):
            edge |= m & ~ndimage.binary_erosion(m)
        rgb[edge] = (1, 0, 0)
        rgb[al_masks[ant] & ~ndimage.binary_erosion(al_masks[ant])] = (0, 0.8, 0.8)
        ax[j, k].imshow(rgb); ax[j, k].axis("off")
        ax[j, k].set_title(f"{ant.split('-')[0]}-{ant.split('-')[1]}-{ant.split('-')[2]} {d}: {al.n_groups(rois[ant, d])}", fontsize=6)
plt.suptitle("ROI outlines (red) and AL mask (cyan) over the max projection, clipped to [-0.2, 1]; top drawing a, bottom b", fontsize=8)
plt.tight_layout(); plt.show()
                                      a                     b
ant1-m4-d21-y2022  26 regions / 17 ROIs    7 regions / 4 ROIs
ant1-m4-d26-y2022  32 regions / 23 ROIs    7 regions / 5 ROIs
ant1-m4-d27-y2022  49 regions / 25 ROIs  37 regions / 32 ROIs
ant2-m4-d20-y2022  34 regions / 26 ROIs    6 regions / 5 ROIs
ant2-m4-d21-y2022  31 regions / 26 ROIs    6 regions / 4 ROIs
ant2-m4-d26-y2022  50 regions / 38 ROIs   11 regions / 6 ROIs
ant2-m4-d27-y2022    9 regions / 8 ROIs    3 regions / 3 ROIs
ant3-m4-d21-y2022  22 regions / 18 ROIs    4 regions / 3 ROIs
ant3-m4-d26-y2022  29 regions / 20 ROIs    4 regions / 4 ROIs
ant3-m4-d27-y2022  20 regions / 18 ROIs    8 regions / 7 ROIs
ant4-m4-d20-y2022  41 regions / 34 ROIs    4 regions / 3 ROIs
ant4-m4-d26-y2022  52 regions / 40 ROIs  13 regions / 10 ROIs
ant5-m4-d26-y2022  41 regions / 35 ROIs    7 regions / 4 ROIs
reading                   aiix                                                 aiiy                                                 aix                                                 aiy                                                 biix                                                 biiy                                                 bix                                                 biy                                               
odor                      6MHO heptanol heptanone hexanol isopropanol vehicle  6MHO heptanol heptanone hexanol isopropanol vehicle 6MHO heptanol heptanone hexanol isopropanol vehicle 6MHO heptanol heptanone hexanol isopropanol vehicle  6MHO heptanol heptanone hexanol isopropanol vehicle  6MHO heptanol heptanone hexanol isopropanol vehicle 6MHO heptanol heptanone hexanol isopropanol vehicle 6MHO heptanol heptanone hexanol isopropanol vehicle
ant               higher                                                                                                                                                                                                                                                                                                                                                                                                                                    
ant1-m4-d21-y2022 0.48     6.0      2.0       6.0    12.0         5.0     3.0   5.7      5.3       6.7    10.3         5.0     3.0  1.0      1.0       2.0     5.0         0.0     0.0  2.0      3.0       3.0     6.3         0.0     0.0   3.0      2.0       3.0     4.0         2.0     2.0   2.7      2.3       3.3     4.0         2.0     2.0  1.0      1.0       2.0     3.0         0.0     0.0  0.7      0.7       2.0     3.0         0.0     0.0
ant1-m4-d26-y2022 0.48     3.0      4.0      11.0    17.0         6.0     0.0   4.3      5.0      10.7    17.3         6.0     0.0  2.0      3.0       5.0    11.0         1.0     0.0  2.7      3.0       5.7    12.0         1.0     0.0   1.0      3.0       5.0     5.0         4.0     0.0   1.7      3.7       4.7     5.0         4.0     0.0  1.0      2.0       1.0     3.0         0.0     0.0  0.7      2.0       1.0     3.3         0.0     0.0
ant1-m4-d27-y2022 0.12     8.0     15.0      15.0    16.0         3.0     4.0   8.3     14.0      11.3    17.7         3.0     4.0  1.0      3.0       4.0     6.0         0.0     0.0  2.7      5.3       7.7     9.7         0.0     0.0  32.0     32.0      32.0    32.0        16.0    22.0  25.0     31.0      21.0    28.7        16.0    22.0  1.0      2.0       4.0    10.0         0.0     0.0  7.3      8.0      11.0    14.0         0.0     0.0
ant2-m4-d20-y2022 0.12     2.0      8.0      15.0    21.0        10.0     6.0   3.3      7.3      13.3    19.0        10.0     6.0  0.0      1.0       5.0     5.0         1.0     0.0  0.3      1.7       7.3     8.7         1.0     0.0   1.0      2.0       3.0     5.0         2.0     4.0   1.7      2.3       3.0     4.7         2.0     4.0  0.0      0.0       1.0     3.0         0.0     0.0  0.3      0.0       1.0     2.3         0.0     0.0
ant2-m4-d21-y2022 0.48     9.0     20.0       7.0    14.0         7.0     1.0   9.3     15.0      10.3    12.3         7.0     1.0  0.0      2.0       1.0     4.0         0.0     0.0  2.0      7.3       3.7     5.0         0.0     0.0   3.0      4.0       2.0     4.0         2.0     0.0   2.3      3.3       2.3     4.0         2.0     0.0  0.0      0.0       1.0     2.0         0.0     0.0  0.3      0.3       0.3     2.0         0.0     0.0
ant2-m4-d26-y2022 0.48    12.0     22.0      20.0    21.0         9.0    27.0  14.7     21.3      18.7    18.3         9.0    27.0  0.0      4.0       5.0     8.0         0.0     5.0  3.3     10.7       9.3    11.3         0.0     5.0   5.0      6.0       6.0     6.0         4.0     6.0   4.7      5.7       5.3     5.7         4.0     6.0  0.0      2.0       3.0     5.0         0.0     0.0  0.3      1.0       3.0     4.7         0.0     0.0
ant2-m4-d27-y2022 0.12     3.0      0.0       6.0     6.0         1.0     0.0   3.3      0.0       4.3     6.3         1.0     0.0  2.0      0.0       0.0     2.0         0.0     0.0  1.3      0.0       2.0     2.7         0.0     0.0   2.0      0.0       3.0     3.0         0.0     0.0   2.0      0.0       2.0     3.0         0.0     0.0  1.0      0.0       0.0     2.0         0.0     0.0  0.7      0.0       0.7     2.0         0.0     0.0
ant3-m4-d21-y2022 0.48     1.0      3.0       4.0    10.0        17.0     0.0   2.0      3.0       4.3    12.3        17.0     0.0  0.0      2.0       2.0     4.0        15.0     0.0  0.3      1.7       2.7     7.3        15.0     0.0   1.0      1.0       1.0     2.0         3.0     0.0   1.3      1.0       1.7     2.0         3.0     0.0  0.0      0.0       1.0     2.0         3.0     0.0  0.3      0.3       1.0     2.0         3.0     0.0
ant3-m4-d26-y2022 0.48     6.0      3.0       6.0    11.0         4.0     1.0   7.3      4.0       5.7     9.7         4.0     1.0  3.0      2.0       6.0     7.0         0.0     0.0  3.0      2.7       4.7     8.3         0.0     0.0   1.0      2.0       2.0     4.0         3.0     1.0   1.7      2.0       2.3     4.0         3.0     1.0  0.0      0.0       2.0     3.0         0.0     0.0  0.0      0.3       2.0     3.3         0.0     0.0
ant3-m4-d27-y2022 0.12     3.0      6.0       4.0    14.0         5.0     2.0   2.7      6.7       4.7    13.0         5.0     2.0  0.0      0.0       2.0     8.0         0.0     0.0  0.3      1.3       1.7     8.0         0.0     0.0   3.0      4.0       4.0     7.0         3.0     0.0   3.0      3.3       4.0     6.7         3.0     0.0  0.0      0.0       0.0     5.0         0.0     0.0  0.3      0.0       0.0     4.0         0.0     0.0
ant4-m4-d20-y2022 0.12    12.0     14.0      12.0    14.0        14.0     1.0  16.0     12.7      15.7    11.3        14.0     1.0  2.0      3.0       1.0     4.0         4.0     0.0  6.3      3.0       7.0     6.3         4.0     0.0   3.0      3.0       3.0     3.0         3.0     0.0   3.0      2.3       3.0     3.0         3.0     0.0  1.0      0.0       1.0     2.0         2.0     0.0  0.3      0.0       1.0     2.0         2.0     0.0
ant4-m4-d26-y2022 0.48     6.0     17.0       9.0    32.0         6.0     3.0   6.7     17.3       9.7    31.3         6.0     3.0  2.0      4.0       4.0    12.0         1.0     0.0  1.7      8.7       3.0    18.7         1.0     0.0   9.0      7.0       7.0    10.0         6.0     2.0   7.3      6.7       6.3    10.0         6.0     2.0  2.0      2.0       1.0     8.0         0.0     0.0  1.0      2.0       1.3     6.7         0.0     0.0
ant5-m4-d26-y2022 0.48    13.0      9.0      19.0    14.0         8.0     2.0  18.0      8.7      17.0    16.3         8.0     2.0  1.0      1.0       1.0     5.0         0.0     0.0  5.0      3.0       7.7     7.3         0.0     0.0   4.0      4.0       4.0     4.0         3.0     2.0   3.7      3.0       3.0     4.0         3.0     2.0  0.0      1.0       1.0     3.0         0.0     0.0  0.0      1.0       1.3     2.7         0.0     0.0
         count  mean  std   min   10%   50%   90%   max
drawing                                                
a        436.0   6.2  6.5   1.3   1.8   3.9  12.5  67.5
b        117.0  13.7  3.2  10.1  10.5  13.0  18.8  23.3
regions under 10 um across, by drawing: {'a': 356}
No description has been provided for this image
In [8]:
# Variant used below: leave out trials where most of the AL is at least 0.2 (a non-specific change,
# as with the "large motion artifacts" the Methods excluded without giving a rule)
frac_al = pd.Series({i: (al._bin(maps[i]["proj"], al.BIN)[al_masks[sel.loc[i].ant]] >= al.THRESHOLD).mean() for i in sel.index})
print("fraction of AL bins at least 0.2 per trial: median", frac_al.median().round(3), "max", frac_al.max().round(3))
flagged = sel.loc[frac_al[frac_al > 0.5].index, ["ant", "trial", "odor", "conc"]]
print("trials with more than half the AL at least 0.2:", len(flagged))
print(flagged.to_string(index=False))
veh_idx = sel[sel.odor == "vehicle"].index
print("vehicle trials: fraction of AL bins at least 0.2, and the largest frame shift (pixels):")
print(pd.DataFrame({"ant": sel.loc[veh_idx].ant, "AL fraction >= 0.2": frac_al[veh_idx].round(3),
                    "largest shift": [maps[i]["shift"].max() for i in veh_idx]}).to_string(index=False))

counts_x = []
for ant in alarm_ants:
    g = sel[(sel.ant == ant) & ~sel.index.isin(flagged.index)]
    for d, v, c in al.READINGS:
        for od in al.PHEROMONES:
            idx = g[g.odor == od].index
            counts_x.append({"ant": ant, "reading": d + v + c, "odor": od,
                             "count": al.responding(maps, rois[ant, d], idx, v, c)[1] if len(idx) else np.nan})
counts_x = pd.DataFrame(counts_x)
counts_x["higher"] = counts_x.ant.map(conc_pairs.map(max))
fraction of AL bins at least 0.2 per trial: median 0.029 max 0.571
trials with more than half the AL at least 0.2: 1
              ant  trial      odor  conc
ant1-m4-d27-y2022    140 heptanone  0.12
vehicle trials: fraction of AL bins at least 0.2, and the largest frame shift (pixels):
              ant  AL fraction >= 0.2  largest shift
ant1-m4-d21-y2022               0.011             10
ant1-m4-d26-y2022               0.001             12
ant1-m4-d27-y2022               0.024             14
ant2-m4-d20-y2022               0.016             12
ant2-m4-d21-y2022               0.010             10
ant2-m4-d26-y2022               0.135             12
ant2-m4-d27-y2022               0.004              8
ant3-m4-d21-y2022               0.001              8
ant3-m4-d26-y2022               0.002             12
ant3-m4-d27-y2022               0.008             18
ant4-m4-d20-y2022               0.013             18
ant4-m4-d26-y2022               0.001              8
ant5-m4-d26-y2022               0.012             10

fig4b-vehicle-no-response¶

Sparse, unique subsets of glomeruli respond to each alarm pheromone, while the paraffin oil vehicle generates no responses.

Criterion: no ROI responds in any ant's vehicle trial, and each pheromone has at least one responding ROI in most ants at their higher concentration, under every M2 reading; the largest vehicle ROI value is reported.

In [9]:
rows = []
for d, v, c in al.READINGS:
    k = d + v + c
    sub = counts[counts.reading == k]
    veh = sub[sub.odor == "vehicle"]
    vmax = []
    for ant in alarm_ants:
        vi = sel[(sel.ant == ant) & (sel.odor == "vehicle")].index[0]
        vals = al.roi_values(maps, rois[ant, d], vi)[0 if v == "i" else 1]
        vmax.append(vals.max() if len(vals) else np.nan)
    row = {"reading": k, "ants with a vehicle-responding ROI": int((veh["count"] > 0).sum()),
           "largest vehicle ROI value": round(float(np.nanmax(vmax)), 3)}
    for od in al.PHEROMONES:
        row[f"ants with >=1 {od} ROI"] = int((sub[sub.odor == od]["count"] > 0).sum())
    rows.append(row)
fig4b = pd.DataFrame(rows)
print("of", len(alarm_ants), "ants")
print(fig4b.to_string(index=False))
ok = (fig4b["ants with a vehicle-responding ROI"] == 0) & np.all([fig4b[f"ants with >=1 {od} ROI"] > len(alarm_ants) / 2 for od in al.PHEROMONES], axis=0)
print("criterion met under readings:", list(fig4b.reading[ok]), "; not met under:", list(fig4b.reading[~ok]))
print("by ant, vehicle counts per reading:")
print(counts[counts.odor == "vehicle"].pivot_table(index="ant", columns="reading", values="count").to_string())

# Variant: the flagged trials (most of the AL at least 0.2) left out, vehicle trials included
print("\nVariant without the flagged trials (vehicle trials flagged:", int(veh_idx.isin(flagged.index).sum()), ")")
rows = []
for d, v, c in al.READINGS:
    k = d + v + c
    n_veh = 0
    for ant in alarm_ants:
        vi = sel[(sel.ant == ant) & (sel.odor == "vehicle")].index
        vi = vi[~vi.isin(flagged.index)]
        if len(vi) and al.responding(maps, rois[ant, d], vi, v, c)[1] > 0:
            n_veh += 1
    sub = counts_x[counts_x.reading == k]
    rows.append({"reading": k, "ants with a vehicle-responding ROI": n_veh,
                 **{f"ants with >=1 {od} ROI": int((sub[sub.odor == od]["count"] > 0).sum()) for od in al.PHEROMONES}})
fig4b_x = pd.DataFrame(rows)
print(fig4b_x.to_string(index=False))
of 13 ants
reading  ants with a vehicle-responding ROI  largest vehicle ROI value  ants with >=1 heptanone ROI  ants with >=1 heptanol ROI  ants with >=1 hexanol ROI  ants with >=1 6MHO ROI
    aix                                   1                      0.654                           12                          11                         13                       8
    aiy                                   1                      0.654                           13                          12                         13                      13
   aiix                                  10                      1.531                           13                          12                         13                      13
   aiiy                                  10                      1.531                           13                          12                         13                      13
    bix                                   0                      0.110                           11                           6                         13                       6
    biy                                   0                      0.110                           12                           9                         13                      11
   biix                                   7                      1.531                           13                          12                         13                      13
   biiy                                   7                      1.531                           13                          12                         13                      13
criterion met under readings: ['biy'] ; not met under: ['aix', 'aiy', 'aiix', 'aiiy', 'bix', 'biix', 'biiy']
by ant, vehicle counts per reading:
reading            aiix  aiiy  aix  aiy  biix  biiy  bix  biy
ant                                                          
ant1-m4-d21-y2022   3.0   3.0  0.0  0.0   2.0   2.0  0.0  0.0
ant1-m4-d26-y2022   0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant1-m4-d27-y2022   4.0   4.0  0.0  0.0  22.0  22.0  0.0  0.0
ant2-m4-d20-y2022   6.0   6.0  0.0  0.0   4.0   4.0  0.0  0.0
ant2-m4-d21-y2022   1.0   1.0  0.0  0.0   0.0   0.0  0.0  0.0
ant2-m4-d26-y2022  27.0  27.0  5.0  5.0   6.0   6.0  0.0  0.0
ant2-m4-d27-y2022   0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant3-m4-d21-y2022   0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant3-m4-d26-y2022   1.0   1.0  0.0  0.0   1.0   1.0  0.0  0.0
ant3-m4-d27-y2022   2.0   2.0  0.0  0.0   0.0   0.0  0.0  0.0
ant4-m4-d20-y2022   1.0   1.0  0.0  0.0   0.0   0.0  0.0  0.0
ant4-m4-d26-y2022   3.0   3.0  0.0  0.0   2.0   2.0  0.0  0.0
ant5-m4-d26-y2022   2.0   2.0  0.0  0.0   2.0   2.0  0.0  0.0

Variant without the flagged trials (vehicle trials flagged: 0 )
reading  ants with a vehicle-responding ROI  ants with >=1 heptanone ROI  ants with >=1 heptanol ROI  ants with >=1 hexanol ROI  ants with >=1 6MHO ROI
    aix                                   1                           12                          11                         13                       8
    aiy                                   1                           13                          12                         13                      13
   aiix                                  10                           13                          12                         13                      13
   aiiy                                  10                           13                          12                         13                      13
    bix                                   0                           11                           6                         13                       6
    biy                                   0                           12                           9                         13                      11
   biix                                   7                           13                          12                         13                      13
   biiy                                   7                           13                          12                         13                      13

Result: not reproduced. Only biy meets the criterion. Under bix no ant's vehicle trial has a responding ROI (largest vehicle ROI value 0.110), but heptanol and 6MHO each have a responding ROI in only 6 of 13 ants, not most; under biy the vehicle gives none and the four pheromones give 12, 9, 13 and 11 of 13. Under the readings with the ROI maximum (ii) 7 to 10 ants have a vehicle-responding ROI (largest value 1.531), and under aix and aiy one ant does (ant2-m4-d26-y2022, 5 ROIs), whose vehicle trial has 13.5% of the AL at 0.2 or more, against at most 2.4% in the other vehicle trials. No vehicle trial is flagged as a non-specific change (more than half the AL), so the variant without the flagged trial gives the same vehicle counts.

figs5a-sparse-median-6¶

Each alarm pheromone activates few of the roughly 500 glomeruli; even at the highest concentration the median number of responding glomeruli is at most 6.

Criterion: counts per ant from M2 (each reading); concentration read as (a) 48% (8 ants) or (b) each ant's higher concentration (13 ants). Reproduced when, under a reading, the median over ants is at most 6.6 for every pheromone.

In [10]:
def medians(cnt):
    out = []
    for k in [d + v + c for d, v, c in al.READINGS]:
        for conc_reading, ants in (("48%", [a for a in alarm_ants if conc_pairs[a][-1] == 0.48]), ("higher", alarm_ants)):
            sub = cnt[(cnt.reading == k) & cnt.ant.isin(ants) & cnt.odor.isin(al.PHEROMONES)]
            med = sub.groupby("odor")["count"].median()
            out.append({"M2 reading": k, "concentration": conc_reading, "n ants": len(ants),
                        **{od: med[od] for od in al.PHEROMONES}, "largest median": med.max(),
                        "<= 6.6": bool(med.max() <= 6.6)})
    return pd.DataFrame(out)
s5a = medians(counts)
print(s5a.to_string(index=False))
print("readings with every median at most 6.6:", int(s5a["<= 6.6"].sum()), "of", len(s5a))
s5a_x = medians(counts_x)
print("\nVariant without the", len(flagged), "trials where most of the AL is at least 0.2:")
print(s5a_x[["M2 reading", "concentration", "largest median", "<= 6.6"]].to_string(index=False))
print("\nPer ant, reading bix (b, mean value, Methods counting):")
print(counts[(counts.reading == "bix") & counts.odor.isin(al.PHEROMONES)].pivot_table(index=["ant", "higher"], columns="odor", values="count").to_string())
M2 reading concentration  n ants  heptanone  heptanol   hexanol     6MHO  largest median  <= 6.6
       aix           48%       8   3.000000  2.000000  6.000000 1.000000        6.000000    True
       aix        higher      13   2.000000  2.000000  5.000000 1.000000        5.000000    True
       aiy           48%       8   4.166667  3.000000  7.833333 2.333333        7.833333   False
       aiy        higher      13   4.666667  3.000000  8.000000 2.000000        8.000000   False
      aiix           48%       8   8.000000  6.500000 14.000000 6.000000       14.000000   False
      aiix        higher      13   9.000000  8.000000 14.000000 6.000000       14.000000   False
      aiiy           48%       8  10.000000  7.000000 14.333333 7.000000       14.333333   False
      aiiy        higher      13  10.333333  7.333333 13.000000 6.666667       13.000000   False
       bix           48%       8   1.000000  1.000000  3.000000 0.000000        3.000000    True
       bix        higher      13   1.000000  0.000000  3.000000 0.000000        3.000000    True
       biy           48%       8   1.333333  0.833333  3.166667 0.333333        3.166667    True
       biy        higher      13   1.000000  0.333333  3.000000 0.333333        3.000000    True
      biix           48%       8   3.500000  3.500000  4.000000 3.000000        4.000000    True
      biix        higher      13   3.000000  3.000000  4.000000 3.000000        4.000000    True
      biiy           48%       8   3.166667  3.166667  4.000000 2.500000        4.000000    True
      biiy        higher      13   3.000000  3.000000  4.000000 2.666667        4.000000    True
readings with every median at most 6.6: 10 of 16

Variant without the 1 trials where most of the AL is at least 0.2:
M2 reading concentration  largest median  <= 6.6
       aix           48%        6.000000    True
       aix        higher        5.000000    True
       aiy           48%        7.833333   False
       aiy        higher        8.000000   False
      aiix           48%       14.000000   False
      aiix        higher       14.000000   False
      aiiy           48%       14.333333   False
      aiiy        higher       13.000000   False
       bix           48%        3.000000    True
       bix        higher        3.000000    True
       biy           48%        3.166667    True
       biy        higher        3.000000    True
      biix           48%        4.000000    True
      biix        higher        4.000000    True
      biiy           48%        4.000000    True
      biiy        higher        4.000000    True

Per ant, reading bix (b, mean value, Methods counting):
odor                      6MHO  heptanol  heptanone  hexanol
ant               higher                                    
ant1-m4-d21-y2022 0.48     1.0       1.0        2.0      3.0
ant1-m4-d26-y2022 0.48     1.0       2.0        1.0      3.0
ant1-m4-d27-y2022 0.12     1.0       2.0        4.0     10.0
ant2-m4-d20-y2022 0.12     0.0       0.0        1.0      3.0
ant2-m4-d21-y2022 0.48     0.0       0.0        1.0      2.0
ant2-m4-d26-y2022 0.48     0.0       2.0        3.0      5.0
ant2-m4-d27-y2022 0.12     1.0       0.0        0.0      2.0
ant3-m4-d21-y2022 0.48     0.0       0.0        1.0      2.0
ant3-m4-d26-y2022 0.48     0.0       0.0        2.0      3.0
ant3-m4-d27-y2022 0.12     0.0       0.0        0.0      5.0
ant4-m4-d20-y2022 0.12     1.0       0.0        1.0      2.0
ant4-m4-d26-y2022 0.48     2.0       2.0        1.0      8.0
ant5-m4-d26-y2022 0.48     0.0       1.0        1.0      3.0

Result: reproduced, resting on the adopted M2 reading. Under bix the largest median is 3 (hexanol) at 48% (8 ants) and at each ant's higher concentration (13 ants). The four readings b and aix keep every median at or below 6.6 (largest 3 to 6); aiy (7.8 to 8.0) and aiix and aiiy (13 to 14.3) do not. The readings disagree, so the outcome rests on the adopted reading, the ROI's mean value (ImageJ's default measure) with the Methods' trial-mean rule (x), under which both drawings meet the criterion. Leaving out the one flagged trial changes nothing.

figs5b-shared-glomerulus¶

The three compounds that elicit panic alarm activate partly overlapping sets of glomeruli, with a single glomerulus, PGb, activated by all three.

Criterion: per ant, the number of M2 ROIs responding to all three (heptanone, heptanol, hexanol) at (a) each ant's higher concentration or (b) 48%. Reproduced when, under each M2 reading and under (a) or (b), the median over ants is 1 and more than half of the ants have exactly one such ROI. A response of one ROI to two odors counts as one glomerulus only at the same depth (Methods).

In [11]:
def shared_counts(exclude=None):
    rows = []
    for ant in alarm_ants:
        for d, v, c in al.READINGS:
            if exclude is not None:
                g = sel[(sel.ant == ant) & ~sel.index.isin(exclude)]
                R = {od: al.responding(maps, rois[ant, d], g[g.odor == od].index, v, c)[0] for od in al.PHEROMONES}
            else:
                R = {od: resp[ant, d, v, c, od] for od in al.PHEROMONES}
            D = {od: depth[ant, d, od] for od in al.PHEROMONES}
            three = R["heptanone"] & R["heptanol"] & R["hexanol"] & al.same_depth(D["heptanone"], D["heptanol"]) \
                & al.same_depth(D["heptanone"], D["hexanol"]) & al.same_depth(D["heptanol"], D["hexanol"])
            mho = R["6MHO"] & np.logical_or.reduce([R[o] & al.same_depth(D["6MHO"], D[o]) for o in ("heptanone", "heptanol", "hexanol")])
            mho_hk = R["6MHO"] & np.logical_or.reduce([R[o] & al.same_depth(D["6MHO"], D[o]) for o in ("heptanone", "hexanol")])
            rows.append({"ant": ant, "reading": d + v + c, "shared3": int(three.sum()),
                         "pairs": {f"{a}+{b}": int((R[a] & R[b]).sum()) for a, b in (("heptanone", "heptanol"), ("heptanone", "hexanol"), ("heptanol", "hexanol"))},
                         "mho_any": int(mho.sum()), "mho_heptanone_or_hexanol": int(mho_hk.sum())})
    out = pd.DataFrame(rows)
    out["higher"] = out.ant.map(conc_pairs.map(max))
    return out
sh = shared_counts()

def judge_shared(sh):
    rows = []
    for k in sh.reading.unique():
        for conc_reading in ("higher", "48%"):
            sub = sh[(sh.reading == k) & ((sh.higher == 0.48) if conc_reading == "48%" else True)]
            rows.append({"M2 reading": k, "concentration": conc_reading, "n ants": len(sub),
                         "median": sub.shared3.median(), "ants with exactly 1": int((sub.shared3 == 1).sum()),
                         "ants with 0": int((sub.shared3 == 0).sum()), "ants with >1": int((sub.shared3 > 1).sum()),
                         "met": bool(sub.shared3.median() == 1 and (sub.shared3 == 1).sum() > len(sub) / 2)})
    return pd.DataFrame(rows)
s5b = judge_shared(sh)
print(s5b.to_string(index=False))
print("\nPer ant, ROIs responding to all three:")
print(sh.pivot_table(index=["ant", "higher"], columns="reading", values="shared3").to_string())
print("\nPairwise overlaps (ROIs responding to both), reading bix:")
print(pd.DataFrame(list(sh[sh.reading == "bix"].pairs), index=sh[sh.reading == "bix"].ant).to_string())
sh_x = shared_counts(exclude=flagged.index)
print("\nVariant without the flagged trials:")
print(judge_shared(sh_x)[["M2 reading", "concentration", "median", "ants with exactly 1", "met"]].to_string(index=False))
M2 reading concentration  n ants  median  ants with exactly 1  ants with 0  ants with >1   met
       aix        higher      13     1.0                    5            6             2 False
       aix           48%       8     1.0                    4            2             2 False
       aiy        higher      13     1.0                    6            5             2 False
       aiy           48%       8     1.0                    4            2             2 False
      aiix        higher      13     2.0                    3            1             9 False
      aiix           48%       8     2.5                    1            0             7 False
      aiiy        higher      13     2.0                    2            1            10 False
      aiiy           48%       8     2.0                    1            0             7 False
       bix        higher      13     0.0                    4            8             1 False
       bix           48%       8     1.0                    4            3             1 False
       biy        higher      13     0.0                    5            8             0 False
       biy           48%       8     1.0                    5            3             0  True
      biix        higher      13     2.0                    3            3             7 False
      biix           48%       8     2.0                    2            0             6 False
      biiy        higher      13     2.0                    4            2             7 False
      biiy           48%       8     2.0                    2            0             6 False

Per ant, ROIs responding to all three:
reading                   aiix  aiiy  aix  aiy  biix  biiy  bix  biy
ant               higher                                            
ant1-m4-d21-y2022 0.48     2.0   2.0  1.0  1.0   2.0   2.0  1.0  1.0
ant1-m4-d26-y2022 0.48     3.0   3.0  2.0  2.0   3.0   3.0  1.0  1.0
ant1-m4-d27-y2022 0.12     3.0   2.0  0.0  1.0   5.0   5.0  0.0  0.0
ant2-m4-d20-y2022 0.12     2.0   2.0  0.0  0.0   0.0   0.0  0.0  0.0
ant2-m4-d21-y2022 0.48     2.0   2.0  0.0  0.0   1.0   1.0  0.0  0.0
ant2-m4-d26-y2022 0.48     6.0   4.0  2.0  1.0   4.0   4.0  2.0  1.0
ant2-m4-d27-y2022 0.12     0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant3-m4-d21-y2022 0.48     1.0   1.0  0.0  0.0   1.0   1.0  0.0  0.0
ant3-m4-d26-y2022 0.48     2.0   2.0  1.0  2.0   2.0   2.0  0.0  0.0
ant3-m4-d27-y2022 0.12     1.0   2.0  0.0  0.0   0.0   1.0  0.0  0.0
ant4-m4-d20-y2022 0.12     1.0   1.0  1.0  1.0   1.0   1.0  0.0  0.0
ant4-m4-d26-y2022 0.48     3.0   3.0  1.0  1.0   3.0   3.0  1.0  1.0
ant5-m4-d26-y2022 0.48     3.0   2.0  1.0  1.0   2.0   2.0  1.0  1.0

Pairwise overlaps (ROIs responding to both), reading bix:
                   heptanone+heptanol  heptanone+hexanol  heptanol+hexanol
ant                                                                       
ant1-m4-d21-y2022                   1                  2                 1
ant1-m4-d26-y2022                   1                  1                 1
ant1-m4-d27-y2022                   0                  2                 0
ant2-m4-d20-y2022                   0                  0                 0
ant2-m4-d21-y2022                   0                  0                 0
ant2-m4-d26-y2022                   2                  3                 2
ant2-m4-d27-y2022                   0                  0                 0
ant3-m4-d21-y2022                   0                  1                 0
ant3-m4-d26-y2022                   0                  1                 0
ant3-m4-d27-y2022                   0                  0                 0
ant4-m4-d20-y2022                   0                  1                 0
ant4-m4-d26-y2022                   1                  1                 2
ant5-m4-d26-y2022                   1                  1                 1
Variant without the flagged trials:
M2 reading concentration  median  ants with exactly 1   met
       aix        higher     1.0                    5 False
       aix           48%     1.0                    4 False
       aiy        higher     1.0                    6 False
       aiy           48%     1.0                    4 False
      aiix        higher     2.0                    3 False
      aiix           48%     2.5                    1 False
      aiiy        higher     2.0                    2 False
      aiiy           48%     2.0                    1 False
       bix        higher     0.0                    4 False
       bix           48%     1.0                    4 False
       biy        higher     0.0                    5 False
       biy           48%     1.0                    5  True
      biix        higher     2.0                    3 False
      biix           48%     2.0                    2 False
      biiy        higher     2.0                    4 False
      biiy           48%     2.0                    2 False

Result: not reproduced. Only biy at 48% meets the criterion (median 1, exactly one shared ROI in 5 of 8 ants). Under bix the median is 0 at the higher concentration (exactly one in 4 of 13 ants, none in 8) and 1 at 48% (exactly one in 4 of 8, not more than half). aix and aiy give a median of 1 with exactly one in 5 or 6 of 13 ants, and the readings ii medians of 2 to 2.5. Under bix heptanone and hexanol share an ROI in 9 of 13 ants, so the overlap is partial and the three-way overlap is often missing. Leaving out the flagged trial gives the same.

figs5c-mho-rarely-overlaps¶

6MHO only rarely and inconsistently activates glomeruli shared with 4-methyl-3-heptanone or 4-methyl-3-hexanol.

Criterion: per ant, at (a) its higher concentration or (b) 48%, the number of M2 ROIs responding to 6MHO and to any other pheromone (overlaps at different depths excluded). Reproduced when, under each M2 reading and under (a) or (b), the median over ants is 0 and the count is non-zero in fewer than half of the ants.

In [12]:
def judge_mho(sh, col):
    rows = []
    for k in sh.reading.unique():
        for conc_reading in ("higher", "48%"):
            sub = sh[(sh.reading == k) & ((sh.higher == 0.48) if conc_reading == "48%" else True)]
            rows.append({"M2 reading": k, "concentration": conc_reading, "n ants": len(sub), "median": sub[col].median(),
                         "ants with >0": int((sub[col] > 0).sum()),
                         "met": bool(sub[col].median() == 0 and (sub[col] > 0).sum() < len(sub) / 2)})
    return pd.DataFrame(rows)
s5c = judge_mho(sh, "mho_any")
print("6MHO and any other pheromone (the criterion):")
print(s5c.to_string(index=False))
print("\nThe criterion as written (met under each M2 reading), per concentration reading:")
for conc_reading in ("higher", "48%"):
    s_ = s5c[s5c.concentration == conc_reading]
    print(f"  {conc_reading}: met under {int(s_.met.sum())} of {len(s_)} readings; under every reading: {bool(s_.met.all())};",
          "fails under", ", ".join(s_[~s_.met]["M2 reading"]) or "none")
print("\n6MHO and heptanone or hexanol (the statement's wording):")
print(judge_mho(sh, "mho_heptanone_or_hexanol").to_string(index=False))
print("\nPer ant, ROIs shared by 6MHO and another pheromone:")
print(sh.pivot_table(index=["ant", "higher"], columns="reading", values="mho_any").to_string())
print("\nVariant without the flagged trials:")
print(judge_mho(sh_x, "mho_any")[["M2 reading", "concentration", "median", "ants with >0", "met"]].to_string(index=False))
6MHO and any other pheromone (the criterion):
M2 reading concentration  n ants  median  ants with >0   met
       aix        higher      13     0.0             1  True
       aix           48%       8     0.0             1  True
       aiy        higher      13     0.0             2  True
       aiy           48%       8     0.0             2  True
      aiix        higher      13     2.0            11 False
      aiix           48%       8     3.0             6 False
      aiiy        higher      13     2.0            11 False
      aiiy           48%       8     3.0             6 False
       bix        higher      13     0.0             1  True
       bix           48%       8     0.0             1  True
       biy        higher      13     0.0             0  True
       biy           48%       8     0.0             0  True
      biix        higher      13     1.0            11 False
      biix           48%       8     1.0             6 False
      biiy        higher      13     1.0            11 False
      biiy           48%       8     1.0             6 False

The criterion as written (met under each M2 reading), per concentration reading:
  higher: met under 4 of 8 readings; under every reading: False; fails under aiix, aiiy, biix, biiy
  48%: met under 4 of 8 readings; under every reading: False; fails under aiix, aiiy, biix, biiy

6MHO and heptanone or hexanol (the statement's wording):
M2 reading concentration  n ants  median  ants with >0   met
       aix        higher      13     0.0             1  True
       aix           48%       8     0.0             1  True
       aiy        higher      13     0.0             2  True
       aiy           48%       8     0.0             2  True
      aiix        higher      13     2.0            10 False
      aiix           48%       8     2.0             5 False
      aiiy        higher      13     1.0            10 False
      aiiy           48%       8     1.5             5 False
       bix        higher      13     0.0             1  True
       bix           48%       8     0.0             1  True
       biy        higher      13     0.0             0  True
       biy           48%       8     0.0             0  True
      biix        higher      13     1.0            10 False
      biix           48%       8     1.0             5 False
      biiy        higher      13     1.0            10 False
      biiy           48%       8     1.0             5 False

Per ant, ROIs shared by 6MHO and another pheromone:
reading                   aiix  aiiy  aix  aiy  biix  biiy  bix  biy
ant               higher                                            
ant1-m4-d21-y2022 0.48     2.0   2.0  0.0  1.0   1.0   1.0  0.0  0.0
ant1-m4-d26-y2022 0.48     0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant1-m4-d27-y2022 0.12     6.0   5.0  0.0  0.0  27.0  24.0  0.0  0.0
ant2-m4-d20-y2022 0.12     2.0   2.0  0.0  0.0   1.0   1.0  0.0  0.0
ant2-m4-d21-y2022 0.48     4.0   4.0  0.0  0.0   1.0   1.0  0.0  0.0
ant2-m4-d26-y2022 0.48    10.0  11.0  0.0  0.0   4.0   5.0  0.0  0.0
ant2-m4-d27-y2022 0.12     1.0   1.0  0.0  0.0   1.0   1.0  0.0  0.0
ant3-m4-d21-y2022 0.48     0.0   0.0  0.0  0.0   0.0   0.0  0.0  0.0
ant3-m4-d26-y2022 0.48     2.0   1.0  0.0  0.0   1.0   1.0  0.0  0.0
ant3-m4-d27-y2022 0.12     1.0   1.0  0.0  0.0   2.0   1.0  0.0  0.0
ant4-m4-d20-y2022 0.12     6.0   7.0  0.0  0.0   3.0   3.0  0.0  0.0
ant4-m4-d26-y2022 0.48     4.0   4.0  1.0  1.0   5.0   5.0  1.0  0.0
ant5-m4-d26-y2022 0.48     5.0   6.0  0.0  0.0   2.0   2.0  0.0  0.0

Variant without the flagged trials:
M2 reading concentration  median  ants with >0   met
       aix        higher     0.0             1  True
       aix           48%     0.0             1  True
       aiy        higher     0.0             2  True
       aiy           48%     0.0             2  True
      aiix        higher     2.0            11 False
      aiix           48%     3.0             6 False
      aiiy        higher     2.0            11 False
      aiiy           48%     3.0             6 False
       bix        higher     0.0             1  True
       bix           48%     0.0             1  True
       biy        higher     0.0             0  True
       biy           48%     0.0             0  True
      biix        higher     1.0            11 False
      biix           48%     1.0             6 False
      biiy        higher     1.0            11 False
      biiy           48%     1.0             6 False

Result: not reproduced. The criterion asks for the result under each M2 reading, and the four readings with the ROI's maximum (aiix, aiiy, biix, biiy) fail at both concentration readings (printed above: met under 4 of 8 readings, under every reading False). At the higher concentration their medians are 1 to 3, with shared ROIs in 11 of 13 ants. The four readings with the ROI's mean value (aix, aiy, bix, biy) meet the criterion: under bix the median number of ROIs shared by 6MHO and another pheromone is 0, with any in 1 of 13 ants (1 of 8 at 48%), and under the four mean-value readings shared ROIs occur in 0 to 2 ants. With heptanone or hexanol only (the statement's wording), and without the flagged trial, the result is the same. In round 1 the outcome was reproduced, resting on the ROI mean; that set aside the criterion's own wording, which figs5b-shared-glomerulus and fig4b-vehicle-no-response are judged by, and the third verification round agreed to the change. Under drawing a the maximum-value readings call a response from one pixel of at least 0.2 in mostly sub-glomerular regions, which makes overlaps common (one ant, ant1-m4-d27, has 27 shared ROIs under biix and none under bix); the failure may come from that reading as much as from the data.

fig6-shared-panic-pathway (imaging half)¶

The three pheromones with overlapping AL responses all elicit panic alarm, while 6MHO does not elicit panic and gives a non-overlapping response.

Criterion: reproduced in its imaging half when figs5b-shared-glomerulus and figs5c-mho-rarely-overlaps are reproduced; the behaviour half rests on fig1e-categorical-response, whose data are not in the dataset.

In [13]:
both = s5b.merge(s5c, on=["M2 reading", "concentration"], suffixes=(" shared3", " mho"))
print(both[["M2 reading", "concentration", "met shared3", "met mho"]].to_string(index=False))
print("figs5b met under every M2 reading at the higher concentration:", bool(s5b[s5b.concentration == "higher"].met.all()),
      "; at 48%:", bool(s5b[s5b.concentration == "48%"].met.all()))
print("figs5c met under every M2 reading at the higher concentration:", bool(s5c[s5c.concentration == "higher"].met.all()),
      "; at 48%:", bool(s5c[s5c.concentration == "48%"].met.all()))
M2 reading concentration  met shared3  met mho
       aix        higher        False     True
       aix           48%        False     True
       aiy        higher        False     True
       aiy           48%        False     True
      aiix        higher        False    False
      aiix           48%        False    False
      aiiy        higher        False    False
      aiiy           48%        False    False
       bix        higher        False     True
       bix           48%        False     True
       biy        higher        False     True
       biy           48%         True     True
      biix        higher        False    False
      biix           48%        False    False
      biiy        higher        False    False
      biiy           48%        False    False
figs5b met under every M2 reading at the higher concentration: False ; at 48%: False
figs5c met under every M2 reading at the higher concentration: False ; at 48%: False

Result: not reproduced, imaging half only. figs5b-shared-glomerulus is met under no reading at the higher concentration and only under biy at 48%; figs5c-mho-rarely-overlaps is not reproduced: it is met under the four mean-value readings and fails under the four maximum-value readings. The behaviour half rests on fig1e-categorical-response, whose data are not in the dataset.

fig4-no-decreases¶

Odors cause no fluorescence decreases; only small non-specific decreases from shifts in AL position and photobleaching occur.

Criterion: per-plane peak fold change maps of the first look (stabilization, 1-sigma Gaussian blur); reproduced when no glomerulus-sized region (10 to 20 um) reaches -0.2 or below in at least two trials of the same odor and concentration without doing so in that ant's vehicle trial. Only each ant's higher concentration is mapped here. A region is counted when it is at least 10 um across (the lower end of a glomerulus); larger regions are counted too, and their sizes are shown. "Doing so in the vehicle trial" is read as at least half of the region at -0.2 or below in the vehicle map of the same plane.

In [14]:
min_bins = np.pi * (al.GLOM_MIN_UM / 2) ** 2 / (al.PIXEL_UM * al.BIN) ** 2
dec = []
for ant in alarm_ants:
    g = sel[sel.ant == ant]
    f0mean = np.mean([maps[i]["f0"] for i in g.index], axis=0)
    from skimage.filters import threshold_otsu
    thr = threshold_otsu(np.log1p(f0mean.max(0)))
    vi = g[g.odor == "vehicle"].index[0]
    for od in al.PHEROMONES + ["isopropanol"]:
        idx = g[g.odor == od].index
        if len(idx) < 2:
            continue
        blur = np.stack([maps[i]["blur"] for i in idx])
        for p in range(al.N_PLANES):
            plane_mask = np.log1p(f0mean[p]) > thr
            edge_um = ndimage.distance_transform_edt(plane_mask) * al.PIXEL_UM * al.BIN
            neg = ((blur[:, p] <= -al.THRESHOLD).sum(0) >= 2) & plane_mask
            lab, n = ndimage.label(neg)
            for k in range(1, n + 1):
                reg = lab == k
                if reg.sum() < min_bins:
                    continue
                in_vehicle = (maps[vi]["blur"][p][reg] <= -al.THRESHOLD).mean() >= 0.5
                dec.append({"ant": ant, "odor": od, "plane": p, "diameter_um": 2 * np.sqrt(reg.sum() / np.pi) * al.PIXEL_UM * al.BIN,
                            "mean_value": float(blur[:, p][:, reg].mean()), "in_vehicle": bool(in_vehicle),
                            "edge_um": float(edge_um[reg].min()), "mask": reg})
dec = pd.DataFrame(dec, columns=["ant", "odor", "plane", "diameter_um", "mean_value", "in_vehicle", "edge_um", "mask"])
odor_dec = dec[~dec.in_vehicle]
print("glomerulus-sized regions at -0.2 or below in >=2 trials of one odor:", len(dec),
      "; of these also in the vehicle trial:", int(dec.in_vehicle.sum()), "; odor-specific:", len(odor_dec))
print("odor-specific regions per ant and odor:")
print(odor_dec.groupby(["ant", "odor"]).size().unstack(fill_value=0).to_string() if len(odor_dec) else "none")
if len(odor_dec):
    print("diameters (um): median", odor_dec.diameter_um.median().round(1), "range", odor_dec.diameter_um.min().round(1), "to", odor_dec.diameter_um.max().round(1))
    print("regions of 10 to 20 um:", int(odor_dec.diameter_um.between(10, 20).sum()))
    print("most negative odor-specific region:")
    print(odor_dec.drop(columns="mask").sort_values("mean_value").head(5).round(3).to_string(index=False))
    print("ants with at least one:", odor_dec.ant.nunique(), "of", len(alarm_ants))

# Where the odor-specific negative regions lie: on the AL border (motion moves the edge of the
# fluorescent lobe), and in the same place for other odors (a decrease specific to an odor would not be)
if len(odor_dec):
    other = []
    for k, r in odor_dec.iterrows():
        same = odor_dec[(odor_dec.ant == r.ant) & (odor_dec.plane == r.plane) & (odor_dec.odor != r.odor)]
        u = np.logical_or.reduce(list(same["mask"])) if len(same) else np.zeros_like(r["mask"])
        other.append((u & r["mask"]).sum() / r["mask"].sum())
    odor_dec = odor_dec.assign(overlap_other_odor=other)
    print("odor-specific regions overlapping a negative region of another odor (same ant and plane):",
          int((odor_dec.overlap_other_odor > 0).sum()), "of", len(odor_dec), "; by half or more:", int((odor_dec.overlap_other_odor >= 0.5).sum()))
    print("distance of the region's nearest bin to the AL border (um): median", round(odor_dec.edge_um.median(), 1),
          "; regions touching the border (within one bin, 1.2 um):", int((odor_dec.edge_um <= al.PIXEL_UM * al.BIN).sum()),
          "; within 5 um:", int((odor_dec.edge_um <= 5).sum()), "of", len(odor_dec))
    # reference: how often a 10 um region centred at random in a plane's AL mask comes within 5 um of the border
    ref = []
    for ant in alarm_ants:
        g = sel[sel.ant == ant]
        f0mean = np.mean([maps[i]["f0"] for i in g.index], axis=0)
        thr = threshold_otsu(np.log1p(f0mean.max(0)))
        for p in range(al.N_PLANES):
            pm = np.log1p(f0mean[p]) > thr
            if pm.sum() > 100:
                ref.append((ndimage.distance_transform_edt(pm)[pm] * al.PIXEL_UM * al.BIN <= 5 + al.GLOM_MIN_UM / 2).mean())
    print("reference: fraction of AL bins within 10 um of the border (a 10 um region centred there comes within 5 um), mean over planes:", round(float(np.mean(ref)), 3))
    print("by odor: regions, overlapping another odor's, touching the border")
    print(odor_dec.groupby("odor").agg(n=("plane", "size"), overlap=("overlap_other_odor", lambda s: int((s > 0).sum())),
                                       border=("edge_um", lambda s: int((s <= al.PIXEL_UM * al.BIN).sum()))).to_string())
glomerulus-sized regions at -0.2 or below in >=2 trials of one odor: 762 ; of these also in the vehicle trial: 168 ; odor-specific: 594
odor-specific regions per ant and odor:
odor               6MHO  heptanol  heptanone  hexanol
ant                                                  
ant1-m4-d21-y2022    24         9          6        3
ant1-m4-d26-y2022    33         1         10        7
ant1-m4-d27-y2022    39        40          5        7
ant2-m4-d20-y2022     0         6          0        2
ant2-m4-d21-y2022     2        17         15        7
ant2-m4-d26-y2022    36        23         15        2
ant2-m4-d27-y2022     2         9          7        6
ant3-m4-d21-y2022     6        10          3        0
ant3-m4-d26-y2022     1         0         11        1
ant3-m4-d27-y2022    11        18         18        7
ant4-m4-d20-y2022    17         8          4       15
ant4-m4-d26-y2022     1        41         11       33
ant5-m4-d26-y2022    22        16          4        3
diameters (um): median 12.4 range 10.0 to 33.6
regions of 10 to 20 um: 540
most negative odor-specific region:
              ant     odor  plane  diameter_um  mean_value  in_vehicle  edge_um
ant4-m4-d26-y2022  hexanol      3       22.844      -0.452       False    1.155
ant4-m4-d26-y2022  hexanol      9       22.394      -0.440       False    1.155
ant4-m4-d26-y2022 heptanol      0       18.346      -0.440       False    1.155
ant4-m4-d26-y2022  hexanol      8       24.427      -0.437       False    1.155
ant4-m4-d20-y2022     6MHO      3       24.704      -0.429       False    1.155
ants with at least one: 13 of 13
odor-specific regions overlapping a negative region of another odor (same ant and plane): 384 of 594 ; by half or more: 302
distance of the region's nearest bin to the AL border (um): median 1.2 ; regions touching the border (within one bin, 1.2 um): 487 ; within 5 um: 564 of 594
reference: fraction of AL bins within 10 um of the border (a 10 um region centred there comes within 5 um), mean over planes: 0.83
by odor: regions, overlapping another odor's, touching the border
             n  overlap  border
odor                           
6MHO       194      105     161
heptanol   198      131     165
heptanone  109       72      76
hexanol     93       76      85

Result: not reproduced. 594 regions at least 10 um across (540 of them 10 to 20 um) reach -0.2 or below in at least two trials of one odor without doing so in the vehicle trial, in all 13 ants; the most negative has a mean of -0.452 (ant4-m4-d26-y2022, hexanol, plane 3). Where they lie points to motion, the source the paper names for small non-specific decreases: 487 of the 594 touch the AL border (within one 1.2 um bin) and 564 lie within 5 um of it, against 83% of the AL area expected for a 10 um region placed at random; and 384 overlap a negative region of another odor in the same plane (302 by half or more). The criterion counts every such region, and its vehicle comparison rests on one vehicle trial per ant.

fig4-large-lasting-responses¶

Fluorescence increases to alarm pheromones are often large (1 to 2-fold change) and outlast the 5 s odor pulse.

Criterion: over responding ROI-trials (M2) at each ant's higher concentration, the median ROI peak fold change lies in 0.9 to 2.2, under each ROI value reading (i) and (ii); and in more than half of those ROI-trials dF/F0 at the first volume after the pulse ends (8.4 s, volume 8) is still above the baseline mean plus 3 SD. A responding ROI-trial is an ROI region whose value in that trial is at least 0.2 (counting y). The trace is the ROI's mean raw fluorescence in the plane of its depth.

In [15]:
rows = []
for ant in alarm_ants:
    g = sel[(sel.ant == ant) & sel.odor.isin(al.PHEROMONES)]
    for d in "ab":
        for r_ in rois[ant, d]:
            for i in g.index:
                vi_, vii_ = al.roi_values(maps, [r_], i)
                fr = maps[i]["frames"][r_["depth"]].astype(np.float32)   # volumes 1-5 and 8
                f = fr[:, r_["mask"]].mean(1)
                dff = f / f[:5].mean() - 1
                lasting = dff[5] > dff[:5].mean() + 3 * dff[:5].std(ddof=1)
                rows.append({"ant": ant, "drawing": d, "odor": sel.loc[i].odor, "vi": vi_[0], "vii": vii_[0], "lasting": lasting})
lr = pd.DataFrame(rows)
out = []
for d in "ab":
    for v in ("vi", "vii"):
        sub = lr[(lr.drawing == d) & (lr[v] >= al.THRESHOLD)]
        out.append({"drawing": d, "value": v[1:], "responding ROI-trials": len(sub), "median peak": round(sub[v].median(), 3),
                    "IQR": (round(sub[v].quantile(0.25), 3), round(sub[v].quantile(0.75), 3)),
                    "fraction in 1-2": round(sub[v].between(1, 2).mean(), 3),
                    "fraction lasting past 8 s": round(sub.lasting.mean(), 3), "ants": sub.ant.nunique(),
                    "median in 0.9-2.2": bool(0.9 <= sub[v].median() <= 2.2), "lasting > half": bool(sub.lasting.mean() > 0.5)})
lrt = pd.DataFrame(out)
print(lrt.to_string(index=False))
print("\nBy ant (drawing b, value i):")
sub = lr[(lr.drawing == "b") & (lr.vi >= al.THRESHOLD)]
print(sub.groupby("ant").agg(n=("vi", "size"), median=("vi", "median"), lasting=("lasting", "mean")).round(3).to_string())
drawing value  responding ROI-trials  median peak            IQR  fraction in 1-2  fraction lasting past 8 s  ants  median in 0.9-2.2  lasting > half
      a     i                   1159        0.377 (0.278, 0.544)            0.063                      0.619    13              False            True
      a    ii                   2460        0.573 (0.336, 1.114)            0.189                      0.439    13              False           False
      b     i                    445        0.453 (0.307, 0.638)            0.088                      0.703    13              False            True
      b    ii                   1086        1.016  (0.53, 1.736)            0.327                      0.413    13               True           False

By ant (drawing b, value i):
                     n  median  lasting
ant                                    
ant1-m4-d21-y2022   31   0.393    0.742
ant1-m4-d26-y2022   37   0.659    0.946
ant1-m4-d27-y2022  140   0.417    0.350
ant2-m4-d20-y2022   13   0.453    0.769
ant2-m4-d21-y2022   15   0.313    0.800
ant2-m4-d26-y2022   63   0.479    0.810
ant2-m4-d27-y2022   10   0.354    1.000
ant3-m4-d21-y2022   16   0.333    1.000
ant3-m4-d26-y2022   17   0.425    1.000
ant3-m4-d27-y2022   14   0.411    0.714
ant4-m4-d20-y2022   14   0.430    0.857
ant4-m4-d26-y2022   57   0.642    0.912
ant5-m4-d26-y2022   18   0.334    0.889

Result: not reproduced. No reading meets both parts. With glomerulus-sized ROIs (b) the median peak fold change of responding ROI-trials is 0.453 with the mean value (445 ROI-trials, 70.3% still elevated at 8.4 s) and 1.016 with the maximum (1,086 ROI-trials, 41.3% elevated): the first median is below 0.9 and the second reading fails the lasting part. With drawing a the medians are 0.377 and 0.573. Under the four readings 6.3 to 32.7% of responding ROI-trials lie within 1 to 2.

fig5ab-focal-glomeruli¶

PGb lies in the anterior ventral hemi-lobe next to a gap without glomeruli, about halfway between the dorsal and ventral AL surfaces, with two neighbouring alarm-responsive glomeruli in the same plane; PGb and 6G were found in 13 of 13 ants and PGa in 11 of 13.

Criterion: identification by M4 on each ant's higher-concentration ROIs (M2, each reading). Reproduced when the counts are within tolerance (12 or 13, 12 or 13, 10 to 12) and, in most ants with PGb, its centre plane lies in the middle third of the AL depth (from the top plane to the deepest plane with AL fluorescence). "Adjacent" (M4) is read as centres at most 20 um apart; the deepest plane with AL fluorescence as the deepest where the mean baseline fluorescence inside the AL mask is at least 25% of the brightest plane's (10% and 50% shown).

In [16]:
focal, idrows = {}, []
for ant in alarm_ants:
    hi = sel[(sel.ant == ant) & sel.odor.isin(al.PHEROMONES)]
    f0prof = np.mean([maps[i]["f0"][:, al_masks[ant]].mean(1) for i in sel[sel.ant == ant].index], axis=0)
    deepest = {q: int(np.max(np.where(f0prof >= q * f0prof.max())[0])) for q in (0.1, 0.25, 0.5)}
    for d, v, c in al.READINGS:
        f = al.identify_focal(maps, rois[ant, d], hi, v, c)
        focal[ant, d + v + c] = f
        row = {"ant": ant, "reading": d + v + c, "PGb": f["PGb"] is not None, "6G": f["6G"] is not None,
               "PGa": f["PGa"] is not None, "PGb candidates": f["n_pgb_candidates"]}
        if f["PGb"] is not None:
            p = f["depth"]["PGb"]
            row.update({"PGb plane": p, "AL deepest plane": deepest[0.25], "PGb depth fraction": round(p / deepest[0.25], 2)})
            for q in (0.1, 0.25, 0.5):
                row[f"middle third ({int(q * 100)}%)"] = deepest[q] / 3 <= p <= 2 * deepest[q] / 3
            row["6G plane"] = f["depth"].get("6G")
            row["PGa plane"] = f["depth"].get("PGa")
        idrows.append(row)
ident = pd.DataFrame(idrows)
summ = ident.groupby("reading").agg(PGb=("PGb", "sum"), G6=("6G", "sum"), PGa=("PGa", "sum"),
                                    middle_third=("middle third (25%)", "sum"),
                                    middle_third_10=("middle third (10%)", "sum"), middle_third_50=("middle third (50%)", "sum"))
summ["counts within tolerance"] = (summ.PGb >= 12) & (summ.G6 >= 12) & summ.PGa.between(10, 12)
summ["PGb in middle third in most"] = summ.middle_third > summ.PGb / 2
print(summ.to_string())
print("\nPer ant, reading bix:")
print(ident[ident.reading == "bix"].drop(columns=["reading"]).to_string(index=False))
MAIN = "bix"
# A second identification for the traces: the reading that identifies PGa in the most ants (then PGb and 6G)
ALT = (summ.PGa * 1000 + summ.PGb * 10 + summ.G6).idxmax()
print("\nsingle-glomerulus traces are computed under", MAIN, "(the adopted M2 reading) and", ALT,
      "(the reading that identifies PGa in the most ants):", summ.loc[ALT, ["PGb", "G6", "PGa"]].to_dict())
         PGb  G6  PGa middle_third middle_third_10 middle_third_50  counts within tolerance  PGb in middle third in most
reading                                                                                                                 
aiix      12  10    3            3              11               1                    False                        False
aiiy      12  10    4            3              11               0                    False                        False
aix        7   4    2            1               7               0                    False                        False
aiy        8   3    2            2               7               1                    False                        False
biix      10   9    2            2              10               1                    False                        False
biiy      11   9    2            3              10               2                    False                        False
bix        5   2    0            1               5               0                    False                        False
biy        5   1    0            1               5               0                    False                        False

Per ant, reading bix:
              ant   PGb    6G   PGa  PGb candidates  PGb plane  AL deepest plane  PGb depth fraction middle third (10%) middle third (25%) middle third (50%)  6G plane  PGa plane
ant1-m4-d21-y2022  True  True False               1       19.0              25.0                0.76               True              False              False      20.0        NaN
ant1-m4-d26-y2022  True  True False               1       17.0              22.0                0.77               True              False              False      18.0        NaN
ant1-m4-d27-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant2-m4-d20-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant2-m4-d21-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant2-m4-d26-y2022  True False False               2       18.0              28.0                0.64               True               True              False       NaN        NaN
ant2-m4-d27-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant3-m4-d21-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant3-m4-d26-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant3-m4-d27-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant4-m4-d20-y2022 False False False               0        NaN               NaN                 NaN                NaN                NaN                NaN       NaN        NaN
ant4-m4-d26-y2022  True False False               1       17.0              24.0                0.71               True              False              False       NaN        NaN
ant5-m4-d26-y2022  True False False               1       20.0              26.0                0.77               True              False              False       NaN        NaN

single-glomerulus traces are computed under bix (the adopted M2 reading) and aiiy (the reading that identifies PGa in the most ants): {'PGb': 12, 'G6': 10, 'PGa': 4}

Result: not reproduced. No reading gives all three counts within tolerance: PGb is identified in 5 to 12 of 13 ants, 6G in 1 to 10 and PGa in 0 to 4 (bix 5, 2, 0; aiix 12, 10, 3). With the deepest plane at 25% of the brightest plane's AL fluorescence, PGb lies in the middle third of the depth in 1 to 3 ants per reading (bix 1 of 5, the others at 0.71 to 0.77 of the depth); with a 10% rule, in 5 to 11.

fig5d-stereotyped-positions¶

PGa is always anterior (12.9 +/- 1.9 SD um) and slightly lateral (5.1 +/- 2.9 um) to PGb, and 6G always lateral to PGb (13.1 +/- 2.6 um) at a similar anterior-posterior position (0.6 +/- 2.2 um).

Criterion: one plane per ant (PGb's), PGb at (0, 0); centres (a) the centroid of each glomerulus's pixels of at least 0.2 to its defining odor in that plane, inside its ROI, or (b) its peak pixel; the plane's map from (c) one higher-concentration trial (the first) or (d) the mean over that ant's higher-concentration trials. Reproduced when, under every reading, the four means are within tolerance, PGa is anterior and 6G lateral in every ant, and each SD is below 10 um. Glomeruli are identified by M4 under the main M2 reading (b, i, x: glomerulus-sized ROIs, ImageJ's mean, the Methods' counting); the other M2 readings are shown as variants. M4 identifies PGa and 6G by their direction from PGb's ROI, but the centres here are taken again on the defining odors' maps in PGb's plane, so the "every ant" conditions are tested, not given by construction.

In [17]:
DEFINING = {"PGb": ["heptanone", "heptanol", "hexanol"], "6G": ["6MHO"], "PGa": ["heptanol", "hexanol"]}

def centres(ant, f, rd_centre, rd_plane, d):
    hi = sel[(sel.ant == ant) & sel.odor.isin(al.PHEROMONES)]
    p = f["depth"]["PGb"]
    masks = al.group_masks(rois[ant, d])
    out = {}
    for name, odors in DEFINING.items():
        if f[name] is None:
            continue
        idx = [hi[hi.odor == o].index for o in odors]
        if rd_plane == "c":
            m = np.mean([maps[ix[0]]["planes"][p] for ix in idx], axis=0)
        else:
            m = np.mean([maps[i]["planes"][p] for ix in idx for i in ix], axis=0)
        roi = ndimage.binary_dilation(masks[f[name]], iterations=2)
        if rd_centre == "a":
            px = roi & (m >= al.THRESHOLD)
            if not px.any():
                px = roi
            out[name] = al.to_full(ndimage.center_of_mass(np.where(px, m, 0)))
        else:
            out[name] = al.to_full(np.unravel_index(np.argmax(np.where(roi, m, -np.inf)), m.shape))
    return out

pos = []
for reading_m2 in [d + v + c for d, v, c in al.READINGS]:
    for ant in alarm_ants:
        f = focal[ant, reading_m2]
        if f["PGb"] is None:
            continue
        for rc in "ab":
            for rp in "cd":
                cs = centres(ant, f, rc, rp, reading_m2[0])
                for name in ("PGa", "6G"):
                    if name in cs:
                        a, l = al.offsets_um(cs["PGb"], cs[name])
                        pos.append({"M2": reading_m2, "centre": rc, "plane": rp, "ant": ant, "glom": name, "anterior": a, "lateral": l})
pos = pd.DataFrame(pos)
EXPECTED = {("PGa", "anterior"): (12.9, 1.9), ("PGa", "lateral"): (5.1, 2.9), ("6G", "lateral"): (13.1, 2.6), ("6G", "anterior"): (0.6, 2.2)}

def judge_pos(p):
    rows = []
    for (m2, rc, rp), g in p.groupby(["M2", "centre", "plane"]):
        row = {"M2": m2, "reading": rc + rp}
        ok = True
        for (glom, axis), (val, tol) in EXPECTED.items():
            s = g[g.glom == glom][axis]
            row[f"{glom} {axis}"] = f"{s.mean():.1f} +/- {s.std():.1f} (n={len(s)})"
            ok &= bool(abs(s.mean() - val) <= tol and s.std() < 10) if len(s) > 1 else False
        row["PGa anterior in all"] = bool((g[g.glom == "PGa"].anterior > 0).all())
        row["6G lateral in all"] = bool((g[g.glom == "6G"].lateral > 0).all())
        row["met"] = ok and row["PGa anterior in all"] and row["6G lateral in all"]
        rows.append(row)
    return pd.DataFrame(rows)
jp = judge_pos(pos)
print("Main M2 reading (bix):")
print(jp[jp.M2 == MAIN].to_string(index=False))
print("\nPer ant, bix, centre a, plane d (um):")
print(pos[(pos.M2 == MAIN) & (pos.centre == "a") & (pos.plane == "d")].pivot_table(index="ant", columns="glom", values=["anterior", "lateral"]).round(1).to_string())
print("\nReadings where a direction condition fails (PGa anterior, 6G lateral in every ant):")
print(jp[~(jp["PGa anterior in all"] & jp["6G lateral in all"])][["M2", "reading", "PGa anterior in all", "6G lateral in all"]].to_string(index=False))
print("\nOther M2 readings:")
print(jp[jp.M2 != MAIN].to_string(index=False))

# Orientation check: baseline fluorescence in PGb's plane for one ant where all three are identified
# under ALT, with the centres marked (rows: up is lateral; columns: right is anterior, as the files say)
ant_o = next((a for a in alarm_ants if all(focal[a, ALT][n] is not None for n in ("PGb", "6G", "PGa"))), None)
if ant_o is not None:
    f_o = focal[ant_o, ALT]
    p_o = f_o["depth"]["PGb"]
    img = np.mean([maps[i]["f0"][p_o] for i in sel[sel.ant == ant_o].index], axis=0)
    fig, ax = plt.subplots(1, 2, figsize=(9, 4.5))
    ax[0].imshow(img, cmap="gray")
    hi = sel[(sel.ant == ant_o) & sel.odor.isin(al.PHEROMONES)]
    ax[1].imshow(np.mean([maps[i]["planes"][p_o] for i in hi[hi.odor.isin(["heptanol", "hexanol"])].index], axis=0), cmap="magma", vmin=0, vmax=1)
    for a_ in ax:
        for n, col in (("PGb", "c"), ("6G", "lime"), ("PGa", "y")):
            r_, c_ = f_o["centroid"][n]
            a_.plot(c_ / al.BIN, r_ / al.BIN, "o", mfc="none", mec=col, ms=8)
            a_.text(c_ / al.BIN + 4, r_ / al.BIN, n, color=col, fontsize=8)
        a_.set_xlabel("column (anterior to the right)"); a_.set_ylabel("row (lateral up)")
    ax[0].set_title(f"{ant_o}, plane {p_o}: baseline F0"); ax[1].set_title("heptanol and hexanol peak map (0 to 1)")
    plt.tight_layout(); plt.show()
    print(ant_o, "plane", p_o, "centres (row, column, full-resolution pixels):",
          {n: tuple(round(float(x), 1) for x in f_o["centroid"][n]) for n in ("PGb", "6G", "PGa")})
    print("binned F0 image range", round(float(img.min()), 1), "to", round(float(img.max()), 1))
Main M2 reading (bix):
 M2 reading      PGa anterior       PGa lateral         6G lateral       6G anterior  PGa anterior in all  6G lateral in all   met
bix      ac nan +/- nan (n=0) nan +/- nan (n=0) 14.2 +/- 2.8 (n=2) 0.1 +/- 4.0 (n=2)                 True               True False
bix      ad nan +/- nan (n=0) nan +/- nan (n=0) 12.9 +/- 1.3 (n=2) 1.6 +/- 6.6 (n=2)                 True               True False
bix      bc nan +/- nan (n=0) nan +/- nan (n=0) 15.6 +/- 7.4 (n=2) 1.7 +/- 2.5 (n=2)                 True               True False
bix      bd nan +/- nan (n=0) nan +/- nan (n=0) 13.3 +/- 4.1 (n=2) 3.5 +/- 4.9 (n=2)                 True               True False

Per ant, bix, centre a, plane d (um):
                  anterior lateral
glom                    6G      6G
ant                               
ant1-m4-d21-y2022     -3.0    12.0
ant1-m4-d26-y2022      6.3    13.8

Readings where a direction condition fails (PGa anterior, 6G lateral in every ant):
  M2 reading  PGa anterior in all  6G lateral in all
aiix      bd                 True              False
aiiy      ad                 True              False
aiiy      bc                False               True
aiiy      bd                 True              False
 aiy      ad                 True              False
 aiy      bd                 True              False
biix      ac                 True              False
biix      bd                False               True
biiy      ac                 True              False
biiy      bd                False               True

Other M2 readings:
  M2 reading        PGa anterior        PGa lateral          6G lateral         6G anterior  PGa anterior in all  6G lateral in all   met
aiix      ac  15.7 +/- 2.6 (n=3)  0.6 +/- 6.5 (n=3) 12.5 +/- 3.2 (n=10) -1.3 +/- 6.1 (n=10)                 True               True False
aiix      ad  15.7 +/- 2.6 (n=3)  2.1 +/- 5.4 (n=3) 12.2 +/- 3.7 (n=10) -1.6 +/- 6.2 (n=10)                 True               True False
aiix      bc  18.5 +/- 0.0 (n=3) 0.4 +/- 10.7 (n=3) 12.8 +/- 6.2 (n=10)  1.0 +/- 5.2 (n=10)                 True               True False
aiix      bd  16.9 +/- 2.7 (n=3)  3.9 +/- 8.7 (n=3) 11.0 +/- 6.8 (n=10) -0.2 +/- 5.2 (n=10)                 True              False False
aiiy      ac  12.2 +/- 7.6 (n=4) 1.9 +/- 10.0 (n=4) 11.9 +/- 3.3 (n=10) -1.4 +/- 6.3 (n=10)                 True               True False
aiiy      ad  12.3 +/- 6.5 (n=4) 0.5 +/- 13.6 (n=4) 11.5 +/- 5.1 (n=10) -1.7 +/- 6.3 (n=10)                 True              False False
aiiy      bc 11.3 +/- 10.4 (n=4) 3.8 +/- 10.4 (n=4) 11.8 +/- 5.3 (n=10)  0.7 +/- 6.0 (n=10)                False               True False
aiiy      bd  12.4 +/- 8.2 (n=4) 2.6 +/- 14.0 (n=4) 10.2 +/- 7.7 (n=10) -0.6 +/- 5.8 (n=10)                 True              False False
 aix      ac  17.1 +/- 1.1 (n=2)  2.5 +/- 8.0 (n=2)  10.9 +/- 4.8 (n=4)   2.6 +/- 3.7 (n=4)                 True               True False
 aix      ad  17.2 +/- 0.7 (n=2)  4.7 +/- 4.4 (n=2)  11.3 +/- 2.1 (n=4)   2.8 +/- 4.2 (n=4)                 True               True False
 aix      bc  18.5 +/- 0.0 (n=2) 3.5 +/- 13.1 (n=2)  10.4 +/- 7.4 (n=4)   4.3 +/- 3.8 (n=4)                 True               True False
 aix      bd  18.5 +/- 0.0 (n=2)  6.9 +/- 9.8 (n=2)  10.4 +/- 4.7 (n=4)   4.3 +/- 3.0 (n=4)                 True               True False
 aiy      ac  17.1 +/- 1.1 (n=2)  2.5 +/- 8.0 (n=2)  11.6 +/- 4.2 (n=3) -2.0 +/- 11.3 (n=3)                 True               True False
 aiy      ad  17.2 +/- 0.7 (n=2)  4.7 +/- 4.4 (n=2)   7.6 +/- 8.0 (n=3) -0.2 +/- 10.6 (n=3)                 True              False False
 aiy      bc  18.5 +/- 0.0 (n=2) 3.5 +/- 13.1 (n=2)  12.3 +/- 7.7 (n=3) -1.5 +/- 14.0 (n=3)                 True               True False
 aiy      bd  18.5 +/- 0.0 (n=2)  6.9 +/- 9.8 (n=2)   8.5 +/- 8.8 (n=3) -0.4 +/- 10.7 (n=3)                 True              False False
biix      ac  10.3 +/- 8.5 (n=2)  8.8 +/- 1.0 (n=2)  11.3 +/- 5.6 (n=9)  -0.4 +/- 3.6 (n=9)                 True              False False
biix      ad  10.8 +/- 8.4 (n=2)  9.1 +/- 1.9 (n=2)  11.9 +/- 5.1 (n=9)   0.2 +/- 3.4 (n=9)                 True               True False
biix      bc  13.3 +/- 7.4 (n=2)  9.8 +/- 4.1 (n=2)  13.1 +/- 6.9 (n=9)  -0.9 +/- 5.4 (n=9)                 True               True False
biix      bd  9.2 +/- 13.1 (n=2)  7.5 +/- 9.0 (n=2)  10.9 +/- 5.4 (n=9)   0.1 +/- 5.0 (n=9)                False               True False
biiy      ac  10.3 +/- 8.5 (n=2)  8.8 +/- 1.0 (n=2)  11.3 +/- 5.6 (n=9)  -0.4 +/- 3.6 (n=9)                 True              False False
biiy      ad  10.8 +/- 8.4 (n=2)  9.1 +/- 1.9 (n=2)  11.9 +/- 5.1 (n=9)   0.2 +/- 3.4 (n=9)                 True               True False
biiy      bc  13.3 +/- 7.4 (n=2)  9.8 +/- 4.1 (n=2)  13.1 +/- 6.9 (n=9)  -0.9 +/- 5.4 (n=9)                 True               True False
biiy      bd  9.2 +/- 13.1 (n=2)  7.5 +/- 9.0 (n=2)  10.9 +/- 5.4 (n=9)   0.1 +/- 5.0 (n=9)                False               True False
 biy      ac   nan +/- nan (n=0)  nan +/- nan (n=0)  16.2 +/- nan (n=1)   2.9 +/- nan (n=1)                 True               True False
 biy      ad   nan +/- nan (n=0)  nan +/- nan (n=0)  13.8 +/- nan (n=1)   6.3 +/- nan (n=1)                 True               True False
 biy      bc   nan +/- nan (n=0)  nan +/- nan (n=0)  20.8 +/- nan (n=1)   3.5 +/- nan (n=1)                 True               True False
 biy      bd   nan +/- nan (n=0)  nan +/- nan (n=0)  16.2 +/- nan (n=1)   6.9 +/- nan (n=1)                 True               True False
No description has been provided for this image
ant1-m4-d26-y2022 plane 17 centres (row, column, full-resolution pixels): {'PGb': (272.0, 319.5), '6G': (221.1, 334.6), 'PGa': (253.5, 375.8)}
binned F0 image range 4.4 to 6195.8

Result: not reproduced. No reading meets the criterion, so the readings do not disagree on the outcome. Under bix no PGa is identified, and 6G (2 ants) lies 12.9 to 15.6 um lateral and 0.1 to 3.5 um anterior of PGb on average. Under the other readings PGa (2 to 4 ants) lies 9.2 to 18.5 um anterior of PGb on average (expected 12.9 +/- 1.9) with SDs up to 13.1 um, and 0.4 to 9.8 um lateral with SDs up to 14.0 um; 6G lies 7.6 to 20.8 um lateral (1 to 10 ants) with SDs of 1.3 to 8.8 um. M4 picks PGa and 6G by their direction from PGb, but the centres taken again here do not always keep it: PGa is not anterior in every ant under aiiy bc, biix bd and biiy bd, and 6G is not lateral in every ant under aiix bd, aiiy ad and bd, aiy ad and bd, biix ac and biiy ac. In the baseline image of ant1-m4-d26-y2022, PGa lies to the right of PGb (anterior) and 6G above it (lateral), the arrangement of the paper's Figure 5B under the files' orientation.

Single-glomerulus traces (M3)¶

For each ant where PGb was identified under the main M2 reading (bix) or under the reading that identifies PGa in the most ants (ALT, printed above), every pheromone trial at both concentrations, the vehicle trial and the 144 s recordings are read as row bands around the focal glomeruli, on PGb's, 6G's and PGa's planes +-2. Each plane's band is stabilized as the maps are (phase correlation against the baseline mean), as the claims file's M3 assumes; the unstabilized disc trace is kept to show what this changes. The centre plane of each glomerulus is chosen per trial among its higher-concentration depth +-1, (a) where its area of at least 0.2 is largest or (b) where its disc's peak fold change is highest. The ROI is the claims file's 15 um disc on the centre ("disc") or the region of at least 0.2 there ("region"). Every claim below is shown under both identifications, both plane readings and both ROIs. Volume times are (index - 1) x 1.205 s plus the centre plane's offset within the volume scan (PLANE_DT x plane, from the files' starting_time).

In [18]:
from concurrent.futures import ThreadPoolExecutor
m3_trials = alarm[alarm.odor.isin(al.PHEROMONES + ["vehicle"])]
IDS = [MAIN] + ([ALT] if ALT != MAIN else [])
jobs = []
for ant in alarm_ants:
    fs = {k: focal[ant, k] for k in IDS if focal[ant, k]["PGb"] is not None}
    if not fs:
        continue
    rows_band = al.band_for([c for f in fs.values() for c in f["centroid"].values()])
    for idx, r in m3_trials[m3_trials.ant == ant].iterrows():
        jobs.append((idx, r, rows_band, fs))
print(len(jobs), "trials for M3, from", len({j[1].ant for j in jobs}), "ants")
t = time.time()

def run(job):
    idx, r, rows_band, fs = job
    return idx, al.trial_m3(r.blobs, r.nvol, rows_band, fs)
with ThreadPoolExecutor(6) as ex:
    m3 = dict(ex.map(run, jobs))
print(f"M3 in {time.time() - t:.0f} s")
trace_rows, shift_rows = [], []
for idx, res in m3.items():
    r = m3_trials.loc[idx]
    for p, s_ in res["_shift"].items():
        shift_rows.append({"ant": r.ant, "trial": r.trial, "odor": r.odor, "nvol": r.nvol, "plane": p,
                           "max shift px": int(np.abs(s_).max()), "shifts": s_})
    for ident in IDS:
        for name, rr in res.get(ident, {}).items():
            for (rd, roi), v in rr.items():
                trace_rows.append({"ident": ident, "ant": r.ant, "trial": r.trial, "odor": r.odor, "conc": r.conc, "nvol": r.nvol,
                                   "glom": name, "plane_reading": rd, "roi": roi, "plane": v["plane"], "peak": v["peak"],
                                   "roi_px": v["roi_px"], "t0": v["plane"] * PLANE_DT, "trace": v["trace"]})
tr_all = pd.DataFrame(trace_rows)
shifts_m3 = pd.DataFrame(shift_rows)
tr = tr_all[tr_all.roi != "raw"]
print(tr.groupby(["ident", "glom", "plane_reading", "roi"]).size().unstack(["plane_reading", "roi"]).to_string())
print("ants with traces:", tr.groupby(["ident", "glom"]).ant.nunique().unstack().to_string())
print("region ROI size (pixels; the disc has", int(tr[tr.roi == "disc"].roi_px.iloc[0]), "): median",
      int(tr[tr.roi == "region"].roi_px.median()), "; trials where no disc pixel reached 0.2, so the disc was used:",
      int((tr[tr.roi == "region"].roi_px.values == tr[tr.roi == "disc"].roi_px.values).sum()), "of", int((tr.roi == "region").sum()))
305 trials for M3, from 12 ants
M3 in 630 s
plane_reading    a           b       
roi           disc region disc region
ident glom                           
aiiy  6G       255    255  255    255
      PGa      102    102  102    102
      PGb      305    305  305    305
bix   6G        51     51   51     51
      PGb      129    129  129    129
ants with traces: glom     6G  PGa   PGb
ident                 
aiiy   10.0  4.0  12.0
bix     2.0  NaN   5.0
region ROI size (pixels; the disc has 2119 ): median 565 ; trials where no disc pixel reached 0.2, so the disc was used: 618 of 1684
In [19]:
# Clock check: the odor-evoked rise follows the stimulus at 3 s in pheromone trials and is absent in the
# vehicle trials (PGb, MAIN identification, plane reading b, disc, 40-volume trials at the higher concentration)
t40 = np.arange(40) * al.VOLUME_S
sub = tr[(tr.ident == MAIN) & (tr.glom == "PGb") & (tr.plane_reading == "b") & (tr.roi == "disc") & (tr.nvol == 40)]
odor_tr = np.stack(sub[sub.odor.isin(["heptanone", "heptanol", "hexanol"]) & (sub.conc == sub.ant.map(conc_pairs.map(max)))].trace)
veh_tr = np.stack(sub[sub.odor == "vehicle"].trace)
print("volume time (s):      ", np.round(t40[:14], 1).tolist())
print("PGb mean dF/F0, odor: ", np.round(odor_tr.mean(0)[:14].astype(float), 3).tolist(), "n =", len(odor_tr))
print("PGb mean dF/F0, vehicle:", np.round(veh_tr.mean(0)[:14].astype(float), 3).tolist(), "n =", len(veh_tr))
diff = odor_tr.mean(0) - veh_tr.mean(0)
print("first volume where odor minus vehicle exceeds 0.1:", int(np.argmax(diff > 0.1)) + 1, "at", round(t40[np.argmax(diff > 0.1)], 1), "s")
print("odor minus vehicle over volumes 1-3 (before 3 s):", np.round(diff[:3].astype(float), 3).tolist())

# Motion in the row bands: the shift of each volume against the baseline mean, and what stabilization changes
mx = shifts_m3.groupby(["ant", "trial"])["max shift px"].max()
print("\nlargest shift per trial in the M3 bands (pixels; 0.289 um each): median", mx.median(), "max", mx.max(),
      "; trials over 10 pixels:", int((mx > 10).sum()), "of", len(mx))
fig, ax = plt.subplots(1, 2, figsize=(11, 3))
ex = shifts_m3[shifts_m3.nvol == 40].sort_values("max shift px")
for k in list(range(0, len(ex), max(1, len(ex) // 3)))[:3] + [len(ex) - 1]:
    s_ = ex.iloc[k]
    ax[0].plot(np.arange(1, 41), np.hypot(*s_.shifts.T) * al.PIXEL_UM, label=f"{s_.ant[:9]} t{s_.trial} {s_.odor} plane {s_.plane}")
ax[0].axvspan(3.5, 6.5, color="0.9"); ax[0].set_xlabel("volume"); ax[0].set_ylabel("shift (um)"); ax[0].legend(fontsize=6)
st = tr_all[(tr_all.ident == MAIN) & (tr_all.glom == "PGb") & (tr_all.plane_reading == "b") & tr_all.roi.isin(["disc", "raw"])]
pv = st.pivot_table(index=["ant", "trial"], columns="roi", values="peak")
print("PGb peak fold change (MAIN, plane b, disc), stabilized against not: median absolute difference",
      round(float((pv.disc - pv.raw).abs().median()), 3), "; largest", round(float((pv.disc - pv.raw).abs().max()), 3),
      "; r =", round(float(np.corrcoef(pv.disc, pv.raw)[0, 1]), 3), "; trials on different sides of 0.2:",
      int(((pv.disc >= 0.2) != (pv.raw >= 0.2)).sum()), "of", len(pv))
k_ = (pv.disc - pv.raw).abs().idxmax()
for roi, sty in (("disc", "k"), ("raw", "r--")):
    ax[1].plot(t40, st[(st.ant == k_[0]) & (st.trial == k_[1]) & (st.roi == roi)].trace.iloc[0][:40], sty, label=f"{roi} ({'stabilized' if roi == 'disc' else 'not stabilized'})")
ax[1].set_title(f"PGb, {k_[0][:9]} trial {k_[1]}: the largest difference", fontsize=8); ax[1].set_xlabel("s"); ax[1].legend(fontsize=7)
plt.tight_layout(); plt.show()
volume time (s):       [0.0, 1.2, 2.4, 3.6, 4.8, 6.0, 7.2, 8.4, 9.6, 10.8, 12.0, 13.3, 14.5, 15.7]
PGb mean dF/F0, odor:  [0.006, -0.009, -0.013, -0.009, 0.026, 0.198, 0.39, 0.622, 0.771, 0.808, 0.833, 0.84, 0.811, 0.792] n = 45
PGb mean dF/F0, vehicle: [-0.002, -0.013, 0.049, 0.003, -0.036, -0.031, -0.048, -0.065, -0.095, -0.072, -0.096, -0.117, -0.135, -0.107] n = 5
first volume where odor minus vehicle exceeds 0.1: 6 at 6.0 s
odor minus vehicle over volumes 1-3 (before 3 s): [0.008, 0.004, -0.061]
largest shift per trial in the M3 bands (pixels; 0.289 um each): median 10.0 max 44 ; trials over 10 pixels: 142 of 305
PGb peak fold change (MAIN, plane b, disc), stabilized against not: median absolute difference 0.035 ; largest 0.591 ; r = 0.98 ; trials on different sides of 0.2: 3 of 129
No description has been provided for this image

fig5c-s6-tuning¶

PGb, PGa and 6G each respond to a unique combination of pheromones, with several pheromone and glomerulus pairs giving no response; PGb responds to heptanone, heptanol and hexanol and not to 6MHO, PGa to heptanol and hexanol, and 6G to 6MHO.

Criterion: peak fold change per trial from M3, all concentrations; readings (a) a linear model with individual as a fixed factor, (b) a linear mixed model with a random intercept per ant; concentration log10-transformed. A combination responds when the prediction at 48% is at least 0.2 with its 95% CI above 0, and gives no response when the 95% CI at 48% includes 0. Reproduced when the six named combinations respond, PGb to 6MHO does not, and the three glomeruli's sets of responding pheromones all differ.

In [20]:
x48 = np.log10(0.48)
fits = []
for ident in IDS:
    for roi in ("disc", "region"):
        for rd in "ab":
            for glom in ("PGb", "PGa", "6G"):
                for od in al.PHEROMONES:
                    d_ = tr[(tr.ident == ident) & (tr.roi == roi) & (tr.glom == glom) & (tr.odor == od) & (tr.plane_reading == rd) & (tr.nvol == 40)].copy()
                    d_["lc"] = np.log10(d_.conc)
                    row = {"ident": ident, "roi": roi, "plane": rd, "glom": glom, "odor": od, "n trials": len(d_), "n ants": d_.ant.nunique()}
                    if d_.ant.nunique() < 2:
                        fits.append(row)
                        continue
                    # (a) fixed ant effects, sum coding: intercept + slope*x is the prediction for the average ant
                    fa = smf.ols("peak ~ lc + C(ant, Sum)", d_).fit()
                    xv = np.zeros(len(fa.params)); xv[0] = 1; xv[list(fa.params.index).index("lc")] = x48
                    pa = float(xv @ fa.params); sa = float(np.sqrt(xv @ fa.cov_params().values @ xv))
                    q = stats.t.ppf(0.975, fa.df_resid)
                    row.update({"(a) pred": pa, "(a) lo": pa - q * sa, "(a) hi": pa + q * sa})
                    try:
                        fb = smf.mixedlm("peak ~ lc", d_, groups=d_.ant).fit(reml=True)
                        fe = fb.fe_params; cv = fb.cov_params().loc[fe.index, fe.index].values
                        xb = np.array([1, x48]); pb = float(xb @ fe); sb = float(np.sqrt(xb @ cv @ xb))
                        row.update({"(b) pred": pb, "(b) lo": pb - 1.96 * sb, "(b) hi": pb + 1.96 * sb, "(b) converged": bool(fb.converged)})
                    except Exception as e:  # noqa: BLE001
                        row.update({"(b) pred": np.nan, "(b) lo": np.nan, "(b) hi": np.nan, "error": str(e)[:60]})
                    fits.append(row)
fits = pd.DataFrame(fits)
for m in ("(a)", "(b)"):
    fits[f"{m} responds"] = (fits[f"{m} pred"] >= 0.2) & (fits[f"{m} lo"] > 0)
    fits[f"{m} no response"] = (fits[f"{m} lo"] <= 0) & (fits[f"{m} hi"] >= 0)
print("fits with fewer than 2 ants (not fitted):", int(fits["(a) pred"].isna().sum()), "of", len(fits), "; mixed-model fits that failed:",
      int(fits["error"].notna().sum()) if "error" in fits else 0, "; mixed-model fits not converged:", int((fits["(b) converged"] == False).sum()))
print(fits.drop(columns=[c for c in ["error"] if c in fits]).round(3).to_string(index=False))
NAMED = [("PGb", "heptanone"), ("PGb", "heptanol"), ("PGb", "hexanol"), ("PGa", "heptanol"), ("PGa", "hexanol"), ("6G", "6MHO")]
tun = []
for ident in IDS:
    for roi in ("disc", "region"):
        for rd in "ab":
            for m in ("(a)", "(b)"):
                f_ = fits[(fits.ident == ident) & (fits.roi == roi) & (fits.plane == rd)].set_index(["glom", "odor"]).reindex(
                    pd.MultiIndex.from_product([["PGb", "PGa", "6G"], al.PHEROMONES]))
                f_[[f"{m} responds", f"{m} no response"]] = f_[[f"{m} responds", f"{m} no response"]].fillna(False).astype(bool)
                named = [bool(f_.loc[k, f"{m} responds"]) for k in NAMED]
                pgb_mho = bool(f_.loc[("PGb", "6MHO"), f"{m} no response"])
                sets = {g: frozenset(o for o in al.PHEROMONES if f_.loc[(g, o), f"{m} responds"]) for g in ("PGb", "PGa", "6G")}
                distinct = len(set(sets.values())) == 3
                tun.append({"ident": ident, "roi": roi, "plane": rd, "model": m, "named responding": f"{sum(named)}/6",
                            "which": "".join("1" if x else "0" for x in named), "PGb-6MHO none": pgb_mho,
                            "PGb set": ",".join(sorted(sets["PGb"])), "PGa set": ",".join(sorted(sets["PGa"])),
                            "6G set": ",".join(sorted(sets["6G"])), "sets differ": distinct,
                            "met": all(named) and pgb_mho and distinct})
tun = pd.DataFrame(tun)
print("\n'which' marks the six named combinations in the order", NAMED)
print(tun.to_string(index=False))
fits with fewer than 2 ants (not fitted): 16 of 96 ; mixed-model fits that failed: 0 ; mixed-model fits not converged: 9
ident    roi plane glom      odor  n trials  n ants  (a) pred  (a) lo  (a) hi  (b) pred  (b) lo  (b) hi (b) converged  (a) responds  (a) no response  (b) responds  (b) no response
  bix   disc     a  PGb heptanone        30       5     0.851   0.742   0.960     0.849   0.583   1.115          True          True            False          True            False
  bix   disc     a  PGb  heptanol        30       5     0.678   0.570   0.786     0.676   0.395   0.957          True          True            False          True            False
  bix   disc     a  PGb   hexanol        30       5     0.734   0.653   0.816     0.733   0.453   1.014          True          True            False          True            False
  bix   disc     a  PGb      6MHO        30       5    -0.087  -0.141  -0.032    -0.084  -0.148  -0.021          True         False            False         False            False
  bix   disc     a  PGa heptanone         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     a  PGa  heptanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     a  PGa   hexanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     a  PGa      6MHO         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     a   6G heptanone        12       2    -0.178  -0.229  -0.126    -0.178  -0.221  -0.136         False         False            False         False            False
  bix   disc     a   6G  heptanol        12       2    -0.159  -0.185  -0.132    -0.160  -0.205  -0.114          True         False            False         False            False
  bix   disc     a   6G   hexanol        12       2    -0.093  -0.131  -0.055    -0.095  -0.154  -0.035          True         False            False         False            False
  bix   disc     a   6G      6MHO        12       2     0.335   0.213   0.456     0.328   0.166   0.491          True          True            False          True            False
  bix   disc     b  PGb heptanone        30       5     0.883   0.774   0.992     0.881   0.569   1.194          True          True            False          True            False
  bix   disc     b  PGb  heptanol        30       5     0.684   0.594   0.773     0.683   0.391   0.974          True          True            False          True            False
  bix   disc     b  PGb   hexanol        30       5     0.849   0.760   0.938     0.848   0.492   1.205          True          True            False          True            False
  bix   disc     b  PGb      6MHO        30       5    -0.078  -0.128  -0.028    -0.076  -0.140  -0.012          True         False            False         False            False
  bix   disc     b  PGa heptanone         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     b  PGa  heptanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     b  PGa   hexanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     b  PGa      6MHO         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix   disc     b   6G heptanone        12       2    -0.148  -0.200  -0.096    -0.151  -0.208  -0.094         False         False            False         False            False
  bix   disc     b   6G  heptanol        12       2    -0.110  -0.155  -0.066    -0.115  -0.155  -0.074         False         False            False         False            False
  bix   disc     b   6G   hexanol        12       2    -0.063  -0.112  -0.014    -0.065  -0.149   0.019          True         False            False         False             True
  bix   disc     b   6G      6MHO        12       2     0.359   0.242   0.476     0.355   0.140   0.570          True          True            False          True            False
  bix region     a  PGb heptanone        30       5     1.785   1.661   1.909     1.789   1.542   2.036          True          True            False          True            False
  bix region     a  PGb  heptanol        30       5     1.617   1.416   1.818     1.620   1.218   2.023          True          True            False          True            False
  bix region     a  PGb   hexanol        30       5     1.575   1.418   1.731     1.575   1.392   1.757          True          True            False          True            False
  bix region     a  PGb      6MHO        30       5     0.211  -0.058   0.480     0.219  -0.104   0.542          True         False             True         False             True
  bix region     a  PGa heptanone         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     a  PGa  heptanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     a  PGa   hexanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     a  PGa      6MHO         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     a   6G heptanone        12       2     0.248  -0.666   1.162     0.315  -0.609   1.239          True         False             True         False             True
  bix region     a   6G  heptanol        12       2    -0.159  -0.185  -0.132    -0.160  -0.205  -0.114          True         False            False         False            False
  bix region     a   6G   hexanol        12       2     0.665  -0.229   1.560     0.676  -0.062   1.414          True         False             True         False             True
  bix region     a   6G      6MHO        12       2     0.991   0.573   1.409     0.991   0.646   1.336          True          True            False          True            False
  bix region     b  PGb heptanone        30       5     1.797   1.655   1.938     1.799   1.544   2.055          True          True            False          True            False
  bix region     b  PGb  heptanol        30       5     1.621   1.464   1.778     1.622   1.237   2.008          True          True            False          True            False
  bix region     b  PGb   hexanol        30       5     1.722   1.574   1.870     1.721   1.411   2.032          True          True            False          True            False
  bix region     b  PGb      6MHO        30       5     0.290   0.022   0.558     0.313   0.018   0.607          True          True            False          True            False
  bix region     b  PGa heptanone         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     b  PGa  heptanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     b  PGa   hexanol         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     b  PGa      6MHO         0       0       NaN     NaN     NaN       NaN     NaN     NaN           NaN         False            False         False            False
  bix region     b   6G heptanone        12       2     0.020  -0.660   0.700     0.075  -0.560   0.710         False         False             True         False             True
  bix region     b   6G  heptanol        12       2    -0.126  -0.391   0.139    -0.144  -0.417   0.129          True         False             True         False             True
  bix region     b   6G   hexanol        12       2     0.252  -0.542   1.047     0.263  -0.393   0.918         False         False             True         False             True
  bix region     b   6G      6MHO        12       2     1.058   0.655   1.460     1.056   0.724   1.389         False          True            False          True            False
 aiiy   disc     a  PGb heptanone        72      12     0.596   0.518   0.673     0.605   0.498   0.711          True          True            False          True            False
 aiiy   disc     a  PGb  heptanol        72      12     0.327   0.271   0.384     0.330   0.266   0.393          True          True            False          True            False
 aiiy   disc     a  PGb   hexanol        72      12     0.528   0.472   0.585     0.534   0.433   0.635          True          True            False          True            False
 aiiy   disc     a  PGb      6MHO        72      12    -0.084  -0.119  -0.049    -0.079  -0.113  -0.044         False         False            False         False            False
 aiiy   disc     a  PGa heptanone        24       4     0.028  -0.031   0.087     0.020  -0.054   0.094          True         False             True         False             True
 aiiy   disc     a  PGa  heptanol        24       4     0.274   0.197   0.351     0.272   0.017   0.528          True          True            False          True            False
 aiiy   disc     a  PGa   hexanol        24       4     0.167   0.104   0.229     0.164   0.003   0.324          True         False            False         False            False
 aiiy   disc     a  PGa      6MHO        24       4    -0.059  -0.102  -0.015    -0.069  -0.115  -0.022         False         False            False         False            False
 aiiy   disc     a   6G heptanone        60      10    -0.150  -0.172  -0.127    -0.150  -0.190  -0.110          True         False            False         False            False
 aiiy   disc     a   6G  heptanol        60      10    -0.110  -0.128  -0.092    -0.111  -0.148  -0.074          True         False            False         False            False
 aiiy   disc     a   6G   hexanol        60      10    -0.095  -0.118  -0.071    -0.094  -0.136  -0.051          True         False            False         False            False
 aiiy   disc     a   6G      6MHO        60      10     0.280   0.225   0.335     0.279   0.187   0.371          True          True            False          True            False
 aiiy   disc     b  PGb heptanone        72      12     0.622   0.545   0.699     0.630   0.532   0.729          True          True            False          True            False
 aiiy   disc     b  PGb  heptanol        72      12     0.332   0.282   0.383     0.335   0.276   0.394          True          True            False          True            False
 aiiy   disc     b  PGb   hexanol        72      12     0.598   0.545   0.652     0.601   0.494   0.707          True          True            False          True            False
 aiiy   disc     b  PGb      6MHO        72      12    -0.076  -0.108  -0.044    -0.071  -0.106  -0.036          True         False            False         False            False
 aiiy   disc     b  PGa heptanone        24       4     0.025  -0.034   0.084     0.018  -0.056   0.091          True         False             True         False             True
 aiiy   disc     b  PGa  heptanol        24       4     0.258   0.183   0.334     0.257   0.008   0.507          True          True            False          True            False
 aiiy   disc     b  PGa   hexanol        24       4     0.191   0.122   0.261     0.187   0.019   0.356          True         False            False         False            False
 aiiy   disc     b  PGa      6MHO        24       4    -0.064  -0.092  -0.036    -0.066  -0.104  -0.029          True         False            False         False            False
 aiiy   disc     b   6G heptanone        60      10    -0.130  -0.153  -0.107    -0.130  -0.165  -0.096          True         False            False         False            False
 aiiy   disc     b   6G  heptanol        60      10    -0.085  -0.105  -0.065    -0.086  -0.120  -0.053          True         False            False         False            False
 aiiy   disc     b   6G   hexanol        60      10    -0.074  -0.098  -0.050    -0.073  -0.113  -0.033          True         False            False         False            False
 aiiy   disc     b   6G      6MHO        60      10     0.279   0.221   0.337     0.279   0.179   0.379          True          True            False          True            False
 aiiy region     a  PGb heptanone        72      12     1.412   1.240   1.583     1.427   1.214   1.640          True          True            False          True            False
 aiiy region     a  PGb  heptanol        72      12     1.357   1.137   1.577     1.330   1.095   1.564          True          True            False          True            False
 aiiy region     a  PGb   hexanol        72      12     1.503   1.379   1.626     1.505   1.248   1.762          True          True            False          True            False
 aiiy region     a  PGb      6MHO        72      12     0.375   0.138   0.613     0.340   0.075   0.605         False          True            False          True            False
 aiiy region     a  PGa heptanone        24       4     0.915   0.609   1.222     0.902   0.405   1.398          True          True            False          True            False
 aiiy region     a  PGa  heptanol        24       4     2.313   1.848   2.779     2.331   1.157   3.504          True          True            False          True            False
 aiiy region     a  PGa   hexanol        24       4     1.115   0.585   1.645     1.144   0.472   1.816          True          True            False          True            False
 aiiy region     a  PGa      6MHO        24       4     0.001  -0.351   0.353    -0.011  -0.333   0.311          True         False             True         False             True
 aiiy region     a   6G heptanone        60      10     0.655   0.365   0.946     0.623   0.287   0.960          True          True            False          True            False
 aiiy region     a   6G  heptanol        60      10     0.328   0.133   0.522     0.316   0.058   0.574          True          True            False          True            False
 aiiy region     a   6G   hexanol        60      10     0.725   0.415   1.035     0.660   0.346   0.974          True          True            False          True            False
 aiiy region     a   6G      6MHO        60      10     0.729   0.598   0.860     0.735   0.520   0.949          True          True            False          True            False
 aiiy region     b  PGb heptanone        72      12     1.431   1.251   1.612     1.454   1.263   1.645          True          True            False          True            False
 aiiy region     b  PGb  heptanol        72      12     1.399   1.208   1.591     1.376   1.138   1.614          True          True            False          True            False
 aiiy region     b  PGb   hexanol        72      12     1.532   1.412   1.653     1.534   1.286   1.782          True          True            False          True            False
 aiiy region     b  PGb      6MHO        72      12     0.465   0.211   0.720     0.403   0.131   0.676          True          True            False          True            False
 aiiy region     b  PGa heptanone        24       4     0.943   0.654   1.232     0.935   0.354   1.516          True          True            False          True            False
 aiiy region     b  PGa  heptanol        24       4     1.816   1.394   2.238     1.829   0.955   2.704          True          True            False          True            False
 aiiy region     b  PGa   hexanol        24       4     1.172   0.775   1.569     1.171   0.467   1.876          True          True            False          True            False
 aiiy region     b  PGa      6MHO        24       4     0.010  -0.127   0.147     0.011  -0.368   0.390          True         False             True         False             True
 aiiy region     b   6G heptanone        60      10     0.343   0.091   0.594     0.322   0.047   0.597          True          True            False          True            False
 aiiy region     b   6G  heptanol        60      10     0.200   0.010   0.389     0.228   0.038   0.418          True         False            False          True            False
 aiiy region     b   6G   hexanol        60      10     0.699   0.355   1.042     0.614   0.271   0.957          True          True            False          True            False
 aiiy region     b   6G      6MHO        60      10     0.780   0.647   0.913     0.785   0.564   1.007          True          True            False          True            False

'which' marks the six named combinations in the order [('PGb', 'heptanone'), ('PGb', 'heptanol'), ('PGb', 'hexanol'), ('PGa', 'heptanol'), ('PGa', 'hexanol'), ('6G', '6MHO')]
ident    roi plane model named responding  which  PGb-6MHO none                         PGb set                    PGa set                          6G set  sets differ   met
  bix   disc     a   (a)              4/6 111001          False      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix   disc     a   (b)              4/6 111001          False      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix   disc     b   (a)              4/6 111001          False      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix   disc     b   (b)              4/6 111001          False      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix region     a   (a)              4/6 111001           True      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix region     a   (b)              4/6 111001           True      heptanol,heptanone,hexanol                                                       6MHO         True False
  bix region     b   (a)              4/6 111001          False 6MHO,heptanol,heptanone,hexanol                                                       6MHO         True False
  bix region     b   (b)              4/6 111001          False 6MHO,heptanol,heptanone,hexanol                                                       6MHO         True False
 aiiy   disc     a   (a)              5/6 111101          False      heptanol,heptanone,hexanol                   heptanol                            6MHO         True False
 aiiy   disc     a   (b)              5/6 111101          False      heptanol,heptanone,hexanol                   heptanol                            6MHO         True False
 aiiy   disc     b   (a)              5/6 111101          False      heptanol,heptanone,hexanol                   heptanol                            6MHO         True False
 aiiy   disc     b   (b)              5/6 111101          False      heptanol,heptanone,hexanol                   heptanol                            6MHO         True False
 aiiy region     a   (a)              6/6 111111          False 6MHO,heptanol,heptanone,hexanol heptanol,heptanone,hexanol 6MHO,heptanol,heptanone,hexanol        False False
 aiiy region     a   (b)              6/6 111111          False 6MHO,heptanol,heptanone,hexanol heptanol,heptanone,hexanol 6MHO,heptanol,heptanone,hexanol        False False
 aiiy region     b   (a)              6/6 111111          False 6MHO,heptanol,heptanone,hexanol heptanol,heptanone,hexanol          6MHO,heptanone,hexanol         True False
 aiiy region     b   (b)              6/6 111111          False 6MHO,heptanol,heptanone,hexanol heptanol,heptanone,hexanol 6MHO,heptanol,heptanone,hexanol        False False

Result: partially reproduced, resting on the identification and the disc ROI. With the 15 um disc, PGb responds at 48% to heptanone, heptanol and hexanol under both identifications, both plane readings and both models (plane a, model a: bix 0.851, 0.678, 0.734; aiiy 0.596, 0.327, 0.528), and 6G responds to 6MHO (0.335 and 0.280). PGa is not identified under bix. Under aiiy (4 ants) it responds to heptanol (0.258 to 0.274) and its hexanol prediction (0.164 to 0.191, CI above 0) falls short of 0.2, so 5 of the 6 named combinations respond. PGb's prediction for 6MHO is negative with its CI below 0 (-0.087 and -0.084), which the criterion counts as neither a response nor no response. With the region of at least 0.2, which takes in neighbouring glomeruli within 17.5 um of the centre, PGb responds to 6MHO in some fits and under aiiy the three sets of responding pheromones differ in only one of four fits. No reading meets the whole criterion. The aiiy identification uses the ROI's maximum and drawing a, the reading set aside for the counts of figs5a-sparse-median-6, and 356 of drawing a's 436 regions are under 10 um across; it is used here only because it is the one that finds PGa in the most ants. For the region ROI, 618 of the 1,684 traces had no disc pixel of at least 0.2 and fell back to the disc, so the region and disc readings coincide for those traces.

fig5c-6g-slow¶

Responses in PGb and PGa peak and then decline close to baseline by the end of the 48 s recording, while 6G responses to 6MHO are very slow, with a plateau of tens of seconds.

Criterion: mean M3 traces for heptanone in PGb, heptanol and hexanol in PGb and PGa, 6MHO in 6G; readings (a) 48% (8 ants) or (b) all concentrations pooled. Reproduced when the PGb and PGa mean traces over the last five volumes are below 0.2 and below a quarter of their peak, while the 6G trace stays at or above half its peak for at least 20 s.

In [21]:
COMBOS = [("PGb", "heptanone"), ("PGb", "heptanol"), ("PGb", "hexanol"), ("PGa", "heptanol"), ("PGa", "hexanol"), ("6G", "6MHO")]
rows = []
for ident in IDS:
    for roi in ("disc", "region"):
        for rd in "ab":
            for conc_reading in ("48%", "pooled"):
                for glom, od in COMBOS:
                    s = tr[(tr.ident == ident) & (tr.roi == roi) & (tr.glom == glom) & (tr.odor == od) & (tr.plane_reading == rd) & (tr.nvol == 40)]
                    if conc_reading == "48%":
                        s = s[s.conc == 0.48]
                    if not len(s):
                        continue
                    m = np.stack(s.trace).mean(0)
                    pk = m.max()
                    end = m[-5:].mean()
                    above = m >= pk / 2
                    # longest run of volumes at or above half the peak
                    run_, best = 0, 0
                    for a in above:
                        run_ = run_ + 1 if a else 0
                        best = max(best, run_)
                    rows.append({"ident": ident, "roi": roi, "plane": rd, "conc": conc_reading, "glom": glom, "odor": od, "n": len(s),
                                 "ants": s.ant.nunique(), "peak": round(pk, 3), "peak volume": int(m.argmax()) + 1, "last 5 mean": round(end, 3),
                                 "end/peak": round(end / pk, 3), "s at >= half peak": round(best * al.VOLUME_S, 1)})
slow = pd.DataFrame(rows)
print(slow.to_string(index=False))
slow_j = []
for (ident, roi, rd, cr), g in slow.groupby(["ident", "roi", "plane", "conc"]):
    fast = g[g.glom != "6G"]
    six = g[g.glom == "6G"]
    ok_fast = bool(((fast["last 5 mean"] < 0.2) & (fast["end/peak"] < 0.25)).all())
    ok_six = bool((six["s at >= half peak"] >= 20).all()) if len(six) else False
    slow_j.append({"ident": ident, "roi": roi, "plane": rd, "conc": cr, "PGa tested": bool((fast.glom == "PGa").any()),
                   "PGb/PGa near baseline": ok_fast, "6G >= half peak for >= 20 s": ok_six, "met": ok_fast and ok_six})
slow_j = pd.DataFrame(slow_j)
print(slow_j.to_string(index=False))
ident    roi plane   conc glom      odor  n  ants  peak  peak volume  last 5 mean  end/peak  s at >= half peak
  bix   disc     a    48%  PGb heptanone 15     5 0.894           12        0.044     0.049               20.5
  bix   disc     a    48%  PGb  heptanol 15     5 0.762           15        0.113     0.148               22.9
  bix   disc     a    48%  PGb   hexanol 15     5 0.784           11       -0.105    -0.134               18.1
  bix   disc     a    48%   6G      6MHO  6     2 0.590           23        0.458     0.776               36.1
  bix   disc     a pooled  PGb heptanone 30     5 0.617            9       -0.095    -0.154               16.9
  bix   disc     a pooled  PGb  heptanol 30     5 0.442           12       -0.035    -0.079               20.5
  bix   disc     a pooled  PGb   hexanol 30     5 0.657           10       -0.134    -0.203               15.7
  bix   disc     a pooled   6G      6MHO 12     2 0.442           21        0.311     0.705               36.1
  bix   disc     b    48%  PGb heptanone 15     5 0.922           12        0.040     0.044               20.5
  bix   disc     b    48%  PGb  heptanol 15     5 0.765           15        0.116     0.151               22.9
  bix   disc     b    48%  PGb   hexanol 15     5 0.912           11       -0.068    -0.075               16.9
  bix   disc     b    48%   6G      6MHO  6     2 0.633           24        0.521     0.823               36.1
  bix   disc     b pooled  PGb heptanone 30     5 0.663            9       -0.088    -0.132               16.9
  bix   disc     b pooled  PGb  heptanol 30     5 0.459           12       -0.025    -0.054               20.5
  bix   disc     b pooled  PGb   hexanol 30     5 0.735           10       -0.108    -0.147               16.9
  bix   disc     b pooled   6G      6MHO 12     2 0.497           21        0.377     0.758               36.1
  bix region     a    48%  PGb heptanone 15     5 1.855           10        0.289     0.156               22.9
  bix region     a    48%  PGb  heptanol 15     5 1.806           15        0.472     0.261               24.1
  bix region     a    48%  PGb   hexanol 15     5 1.682           11        0.035     0.021               19.3
  bix region     a    48%   6G      6MHO  6     2 1.392           20        1.105     0.794               37.3
  bix region     a pooled  PGb heptanone 30     5 1.709           10        0.169     0.099               20.5
  bix region     a pooled  PGb  heptanol 30     5 1.447           15        0.331     0.228               22.9
  bix region     a pooled  PGb   hexanol 30     5 1.706           10        0.052     0.030               19.3
  bix region     a pooled   6G      6MHO 12     2 1.376           24        1.157     0.841               38.6
  bix region     b    48%  PGb heptanone 15     5 1.875           10        0.285     0.152               24.1
  bix region     b    48%  PGb  heptanol 15     5 1.805           15        0.466     0.258               24.1
  bix region     b    48%  PGb   hexanol 15     5 1.843           11        0.059     0.032               19.3
  bix region     b    48%   6G      6MHO  6     2 1.574           24        1.289     0.819               37.3
  bix region     b pooled  PGb heptanone 30     5 1.687            9        0.144     0.085               20.5
  bix region     b pooled  PGb  heptanol 30     5 1.502           15        0.339     0.226               24.1
  bix region     b pooled  PGb   hexanol 30     5 1.776           11        0.066     0.037               19.3
  bix region     b pooled   6G      6MHO 12     2 1.524           24        1.273     0.835               37.3
 aiiy   disc     a    48%  PGb heptanone 24     8 0.719           10        0.022     0.031               20.5
 aiiy   disc     a    48%  PGb  heptanol 24     8 0.447           15       -0.014    -0.032               21.7
 aiiy   disc     a    48%  PGb   hexanol 24     8 0.644           11       -0.131    -0.203               14.5
 aiiy   disc     a    48%  PGa  heptanol  9     3 0.328           19        0.046     0.140               24.1
 aiiy   disc     a    48%  PGa   hexanol  9     3 0.129            9       -0.145    -1.123                7.2
 aiiy   disc     a    48%   6G      6MHO 24     8 0.385           21        0.277     0.720               37.3
 aiiy   disc     a pooled  PGb heptanone 72    12 0.369           10       -0.120    -0.324               14.5
 aiiy   disc     a pooled  PGb  heptanol 72    12 0.165           15       -0.136    -0.824               15.7
 aiiy   disc     a pooled  PGb   hexanol 72    12 0.403           10       -0.165    -0.410               13.3
 aiiy   disc     a pooled  PGa  heptanol 24     4 0.220           13       -0.015    -0.068               21.7
 aiiy   disc     a pooled  PGa   hexanol 24     4 0.129            9       -0.145    -1.118                6.0
 aiiy   disc     a pooled   6G      6MHO 60    10 0.351           20        0.250     0.712               37.3
 aiiy   disc     b    48%  PGb heptanone 24     8 0.712           12        0.006     0.009               20.5
 aiiy   disc     b    48%  PGb  heptanol 24     8 0.446           15       -0.021    -0.047               20.5
 aiiy   disc     b    48%  PGb   hexanol 24     8 0.700           11       -0.111    -0.159               14.5
 aiiy   disc     b    48%  PGa  heptanol  9     3 0.295           14        0.036     0.122               22.9
 aiiy   disc     b    48%  PGa   hexanol  9     3 0.141           10       -0.129    -0.917                8.4
 aiiy   disc     b    48%   6G      6MHO 24     8 0.406           21        0.309     0.762               37.3
 aiiy   disc     b pooled  PGb heptanone 72    12 0.394           10       -0.100    -0.255               14.5
 aiiy   disc     b pooled  PGb  heptanol 72    12 0.181           15       -0.133    -0.733               15.7
 aiiy   disc     b pooled  PGb   hexanol 72    12 0.435           10       -0.147    -0.338               13.3
 aiiy   disc     b pooled  PGa  heptanol 24     4 0.213           13       -0.017    -0.081               21.7
 aiiy   disc     b pooled  PGa   hexanol 24     4 0.148            9       -0.149    -1.011                6.0
 aiiy   disc     b pooled   6G      6MHO 60    10 0.363           21        0.268     0.740               36.1
 aiiy region     a    48%  PGb heptanone 24     8 1.677           12        0.313     0.187               24.1
 aiiy region     a    48%  PGb  heptanol 24     8 1.549           15        0.396     0.256               24.1
 aiiy region     a    48%  PGb   hexanol 24     8 1.688           11        0.042     0.025               16.9
 aiiy region     a    48%  PGa  heptanol  9     3 2.941           14        1.178     0.401               28.9
 aiiy region     a    48%  PGa   hexanol  9     3 1.447           11       -0.071    -0.049                9.6
 aiiy region     a    48%   6G      6MHO 24     8 0.959           25        0.773     0.806               37.3
 aiiy region     a pooled  PGb heptanone 72    12 1.473           11        0.169     0.115               18.1
 aiiy region     a pooled  PGb  heptanol 72    12 1.222           14        0.272     0.222               21.7
 aiiy region     a pooled  PGb   hexanol 72    12 1.506           10        0.055     0.037               15.7
 aiiy region     a pooled  PGa  heptanol 24     4 2.546           14        0.908     0.357               28.9
 aiiy region     a pooled  PGa   hexanol 24     4 1.372           11       -0.016    -0.011               10.8
 aiiy region     a pooled   6G      6MHO 60    10 0.936           25        0.716     0.764               38.6
 aiiy region     b    48%  PGb heptanone 24     8 1.658           12        0.286     0.173               24.1
 aiiy region     b    48%  PGb  heptanol 24     8 1.567           15        0.385     0.246               24.1
 aiiy region     b    48%  PGb   hexanol 24     8 1.704           11        0.042     0.025               16.9
 aiiy region     b    48%  PGa  heptanol  9     3 2.452           11        0.850     0.347               26.5
 aiiy region     b    48%  PGa   hexanol  9     3 1.315           11       -0.025    -0.019               13.3
 aiiy region     b    48%   6G      6MHO 24     8 1.104           17        0.793     0.719               37.3
 aiiy region     b pooled  PGb heptanone 72    12 1.449           11        0.161     0.111               18.1
 aiiy region     b pooled  PGb  heptanol 72    12 1.293           14        0.277     0.214               21.7
 aiiy region     b pooled  PGb   hexanol 72    12 1.471           11        0.029     0.020               16.9
 aiiy region     b pooled  PGa  heptanol 24     4 2.177           14        0.667     0.306               25.3
 aiiy region     b pooled  PGa   hexanol 24     4 1.270            9       -0.006    -0.005               10.8
 aiiy region     b pooled   6G      6MHO 60    10 0.987           25        0.731     0.741               38.6
ident    roi plane   conc  PGa tested  PGb/PGa near baseline  6G >= half peak for >= 20 s   met
 aiiy   disc     a    48%        True                   True                         True  True
 aiiy   disc     a pooled        True                   True                         True  True
 aiiy   disc     b    48%        True                   True                         True  True
 aiiy   disc     b pooled        True                   True                         True  True
 aiiy region     a    48%        True                  False                         True False
 aiiy region     a pooled        True                  False                         True False
 aiiy region     b    48%        True                  False                         True False
 aiiy region     b pooled        True                  False                         True False
  bix   disc     a    48%       False                   True                         True  True
  bix   disc     a pooled       False                   True                         True  True
  bix   disc     b    48%       False                   True                         True  True
  bix   disc     b pooled       False                   True                         True  True
  bix region     a    48%       False                  False                         True False
  bix region     a pooled       False                  False                         True False
  bix region     b    48%       False                  False                         True False
  bix region     b pooled       False                  False                         True False

Result: partially reproduced, resting on the identification and the disc ROI. With the disc, the mean PGb traces for heptanone, heptanol and hexanol end (last five volumes) at -0.165 to 0.116, with end over peak at most 0.151, and the mean 6G trace to 6MHO stays at or above half its peak for 36.1 to 37.3 s, under both identifications, both plane readings and both concentration readings. PGa is identified only under aiiy, and its traces (9 trials at 48%, 24 pooled) end at -0.149 to 0.046; its hexanol trace peaks at only 0.129 to 0.148. With the region ROI the PGb heptanol trace ends at 0.272 to 0.472, above 0.2, and the criterion fails. The PGa part rests on 3 or 4 ants under one identification, and with no clear hexanol response in PGa it passes trivially. The aiiy identification uses the ROI's maximum and drawing a, the reading set aside for the counts of figs5a-sparse-median-6, and 356 of drawing a's 436 regions are under 10 um across; it is used here only because it is the one that finds PGa in the most ants. For the region ROI, 618 of the 1,684 traces had no disc pixel of at least 0.2 and fell back to the disc, so the region and disc readings coincide for those traces.

figs7a-6g-extended¶

In 144 s recordings, 6G responses to 6MHO plateau for about 30 s and return to baseline about 80 s after odor presentation, sometimes taking more than 100 s.

Criterion: single 144 s trials from five ants at 48%, M3 traces of 6G, and their mean. Return to baseline as the first volume after the peak where the mean trace falls (a) below baseline mean plus 3 SD or (b) below 0.2; plateau as the time the mean trace stays at or above (c) 90% or (d) 80% of its peak; odor presentation as (e) 3 s or (f) 8 s. Reproduced when, under one reading, both values are within tolerance (30 +/- 3 s, 80 +/- 8 s) and at least one trial takes more than 100 s.

In [22]:
t120 = np.arange(120) * al.VOLUME_S

def plateau_return(tr_, t_):
    pk = int(np.argmax(tr_))
    base = tr_[al.BASE]
    out = {}
    for name, frac in (("c", 0.9), ("d", 0.8)):
        above = tr_ >= frac * tr_[pk]
        a0 = pk
        while a0 > 0 and above[a0 - 1]:
            a0 -= 1
        a1 = pk
        while a1 < len(tr_) - 1 and above[a1 + 1]:
            a1 += 1
        out[name] = (a1 - a0 + 1) * al.VOLUME_S
    for name, bound in (("a", base.mean() + 3 * base.std(ddof=1)), ("b", 0.2)):
        below = np.where(tr_[pk:] < bound)[0]
        out[name] = t_[pk + below[0]] if len(below) else np.nan
    return out
res7 = []
for ident in IDS:
    long = tr[(tr.ident == ident) & (tr.nvol == 120) & (tr.glom == "6G")]
    print(ident, ": 144 s trials with 6G identified:", long.ant.nunique() // 1, "ants of", alarm[alarm.nvol == 120].ant.nunique(),
          "with a 144 s recording; trials", long[(long.roi == "disc") & (long.plane_reading == "a")].shape[0])
    for roi in ("disc", "region"):
        for rd in "ab":
            s = long[(long.plane_reading == rd) & (long.roi == roi)]
            if not len(s):
                continue
            t_ = t120 + float(s.t0.mean())
            mean = np.stack(s.trace).mean(0)
            pm = plateau_return(mean, t_)
            per = [plateau_return(x, t120 + t0) for x, t0 in zip(s.trace, s.t0)]
            for ret in "ab":
                for pl in "cd":
                    for on, t_on in (("e", 3.0), ("f", 8.0)):
                        plateau = pm[pl]
                        back = pm[ret] - t_on
                        per_back = [p[ret] - t_on for p in per]
                        res7.append({"ident": ident, "roi": roi, "plane": rd, "reading": ret + pl + on, "n trials": len(s), "plateau s": round(plateau, 1),
                                     "return s": round(back, 1), "per-trial return s": [round(x, 1) for x in per_back],
                                     "any > 100 s": bool(np.nanmax(per_back) > 100) if np.isfinite(per_back).any() else False,
                                     "within tolerance": bool(abs(plateau - 30) <= 3 and abs(back - 80) <= 8)})
            print(f"  {roi}, plane {rd}: mean 6G trace peak {mean.max():.3f} at {t_[mean.argmax()]:.1f} s; range of the mean trace {mean.min():.3f} to {mean.max():.3f}")
res7 = pd.DataFrame(res7)
res7["met"] = res7["within tolerance"] & res7["any > 100 s"]
print(res7.to_string(index=False))
print("readings meeting the criterion:", int(res7.met.sum()), "of", len(res7))
print("\nTrials returning more than 100 s after odor presentation (onset reading e, plateau reading c):")
for _, r_ in res7[res7.reading.str.endswith("ce")].iterrows():
    over = [x for x in r_["per-trial return s"] if np.isfinite(x) and x > 100]
    print(f"  {r_.ident}, {r_.roi}, plane {r_.plane}, return {r_.reading[0]}: {len(over)} of {r_['n trials']}", [float(x) for x in over])
long = tr[(tr.nvol == 120) & (tr.glom == "6G") & (tr.roi == "disc")]
if len(long):
    fig, ax = plt.subplots(1, len(IDS), figsize=(5 * len(IDS), 3), squeeze=False)
    for k, ident in enumerate(IDS):
        s = long[(long.ident == ident) & (long.plane_reading == "a")]
        if not len(s):
            continue
        for x in s.trace:
            ax[0, k].plot(t120, x, lw=0.7)
        ax[0, k].plot(t120, np.stack(s.trace).mean(0), "k", lw=2)
        ax[0, k].axvspan(3, 8, color="0.9"); ax[0, k].set_title(f"6G, 6MHO 48%, 144 s, {ident}, disc, plane a", fontsize=8); ax[0, k].set_xlabel("s")
    plt.tight_layout(); plt.show()
bix : 144 s trials with 6G identified: 1 ants of 5 with a 144 s recording; trials 1
  disc, plane a: mean 6G trace peak 0.627 at 27.1 s; range of the mean trace -0.153 to 0.627
  disc, plane b: mean 6G trace peak 0.627 at 27.1 s; range of the mean trace -0.153 to 0.627
  region, plane a: mean 6G trace peak 2.435 at 27.1 s; range of the mean trace -0.233 to 2.435
  region, plane b: mean 6G trace peak 2.435 at 27.1 s; range of the mean trace -0.233 to 2.435
aiiy : 144 s trials with 6G identified: 5 ants of 5 with a 144 s recording; trials 5
  disc, plane a: mean 6G trace peak 0.252 at 33.1 s; range of the mean trace -0.122 to 0.252
  disc, plane b: mean 6G trace peak 0.281 at 33.1 s; range of the mean trace -0.108 to 0.281
  region, plane a: mean 6G trace peak 0.828 at 27.0 s; range of the mean trace -0.105 to 0.828
  region, plane b: mean 6G trace peak 0.822 at 27.1 s; range of the mean trace -0.100 to 0.822
ident    roi plane reading  n trials  plateau s  return s               per-trial return s  any > 100 s  within tolerance   met
  bix   disc     a     ace         1        9.6      91.6                           [91.6]        False             False False
  bix   disc     a     acf         1        9.6      86.6                           [86.6]        False             False False
  bix   disc     a     ade         1       22.9      91.6                           [91.6]        False             False False
  bix   disc     a     adf         1       22.9      86.6                           [86.6]        False             False False
  bix   disc     a     bce         1        9.6      83.2                           [83.2]        False             False False
  bix   disc     a     bcf         1        9.6      78.2                           [78.2]        False             False False
  bix   disc     a     bde         1       22.9      83.2                           [83.2]        False             False False
  bix   disc     a     bdf         1       22.9      78.2                           [78.2]        False             False False
  bix   disc     b     ace         1        9.6      91.6                           [91.6]        False             False False
  bix   disc     b     acf         1        9.6      86.6                           [86.6]        False             False False
  bix   disc     b     ade         1       22.9      91.6                           [91.6]        False             False False
  bix   disc     b     adf         1       22.9      86.6                           [86.6]        False             False False
  bix   disc     b     bce         1        9.6      83.2                           [83.2]        False             False False
  bix   disc     b     bcf         1        9.6      78.2                           [78.2]        False             False False
  bix   disc     b     bde         1       22.9      83.2                           [83.2]        False             False False
  bix   disc     b     bdf         1       22.9      78.2                           [78.2]        False             False False
  bix region     a     ace         1        7.2      91.6                           [91.6]        False             False False
  bix region     a     acf         1        7.2      86.6                           [86.6]        False             False False
  bix region     a     ade         1       15.7      91.6                           [91.6]        False             False False
  bix region     a     adf         1       15.7      86.6                           [86.6]        False             False False
  bix region     a     bce         1        7.2     110.9                          [110.9]         True             False False
  bix region     a     bcf         1        7.2     105.9                          [105.9]         True             False False
  bix region     a     bde         1       15.7     110.9                          [110.9]         True             False False
  bix region     a     bdf         1       15.7     105.9                          [105.9]         True             False False
  bix region     b     ace         1        7.2      91.6                           [91.6]        False             False False
  bix region     b     acf         1        7.2      86.6                           [86.6]        False             False False
  bix region     b     ade         1       15.7      91.6                           [91.6]        False             False False
  bix region     b     adf         1       15.7      86.6                           [86.6]        False             False False
  bix region     b     bce         1        7.2     110.9                          [110.9]         True             False False
  bix region     b     bcf         1        7.2     105.9                          [105.9]         True             False False
  bix region     b     bde         1       15.7     110.9                          [110.9]         True             False False
  bix region     b     bdf         1       15.7     105.9                          [105.9]         True             False False
 aiiy   disc     a     ace         5        2.4      56.6   [91.6, 48.3, 33.8, 1.1, 105.7]         True             False False
 aiiy   disc     a     acf         5        2.4      51.6  [86.6, 43.3, 28.8, -3.9, 100.7]         True             False False
 aiiy   disc     a     ade         5        4.8      56.6   [91.6, 48.3, 33.8, 1.1, 105.7]         True             False False
 aiiy   disc     a     adf         5        4.8      51.6  [86.6, 43.3, 28.8, -3.9, 100.7]         True             False False
 aiiy   disc     a     bce         5        2.4      32.5   [83.2, 43.5, 27.8, 1.1, 105.7]         True             False False
 aiiy   disc     a     bcf         5        2.4      27.5  [78.2, 38.5, 22.8, -3.9, 100.7]         True             False False
 aiiy   disc     a     bde         5        4.8      32.5   [83.2, 43.5, 27.8, 1.1, 105.7]         True             False False
 aiiy   disc     a     bdf         5        4.8      27.5  [78.2, 38.5, 22.8, -3.9, 100.7]         True             False False
 aiiy   disc     b     ace         5        3.6      56.6   [91.6, 47.1, 44.7, 1.1, 105.7]         True             False False
 aiiy   disc     b     acf         5        3.6      51.6  [86.6, 42.1, 39.7, -3.9, 100.7]         True             False False
 aiiy   disc     b     ade         5        8.4      56.6   [91.6, 47.1, 44.7, 1.1, 105.7]         True             False False
 aiiy   disc     b     adf         5        8.4      51.6  [86.6, 42.1, 39.7, -3.9, 100.7]         True             False False
 aiiy   disc     b     bce         5        3.6      38.5   [83.2, 51.9, 27.8, 1.1, 105.7]         True             False False
 aiiy   disc     b     bcf         5        3.6      33.5  [78.2, 46.9, 22.8, -3.9, 100.7]         True             False False
 aiiy   disc     b     bde         5        8.4      38.5   [83.2, 51.9, 27.8, 1.1, 105.7]         True             False False
 aiiy   disc     b     bdf         5        8.4      33.5  [78.2, 46.9, 22.8, -3.9, 100.7]         True             False False
 aiiy region     a     ace         5        7.2      75.8   [91.6, 31.4, 35.0, 1.1, 105.7]         True             False False
 aiiy region     a     acf         5        7.2      70.8  [86.6, 26.4, 30.0, -3.9, 100.7]         True             False False
 aiiy region     a     ade         5       15.7      75.8   [91.6, 31.4, 35.0, 1.1, 105.7]         True             False False
 aiiy region     a     adf         5       15.7      70.8  [86.6, 26.4, 30.0, -3.9, 100.7]         True             False False
 aiiy region     a     bce         5        7.2      91.5  [110.9, 44.7, 45.8, 1.1, 105.7]         True             False False
 aiiy region     a     bcf         5        7.2      86.5 [105.9, 39.7, 40.8, -3.9, 100.7]         True             False False
 aiiy region     a     bde         5       15.7      91.5  [110.9, 44.7, 45.8, 1.1, 105.7]         True             False False
 aiiy region     a     bdf         5       15.7      86.5 [105.9, 39.7, 40.8, -3.9, 100.7]         True             False False
 aiiy region     b     ace         5        7.2      83.1   [91.6, 38.7, 19.4, 1.1, 105.7]         True             False False
 aiiy region     b     acf         5        7.2      78.1  [86.6, 33.7, 14.4, -3.9, 100.7]         True             False False
 aiiy region     b     ade         5       15.7      83.1   [91.6, 38.7, 19.4, 1.1, 105.7]         True             False False
 aiiy region     b     adf         5       15.7      78.1  [86.6, 33.7, 14.4, -3.9, 100.7]         True             False False
 aiiy region     b     bce         5        7.2      91.5  [110.9, 45.9, 35.0, 1.1, 105.7]         True             False False
 aiiy region     b     bcf         5        7.2      86.5 [105.9, 40.9, 30.0, -3.9, 100.7]         True             False False
 aiiy region     b     bde         5       15.7      91.5  [110.9, 45.9, 35.0, 1.1, 105.7]         True             False False
 aiiy region     b     bdf         5       15.7      86.5 [105.9, 40.9, 30.0, -3.9, 100.7]         True             False False
readings meeting the criterion: 0 of 64

Trials returning more than 100 s after odor presentation (onset reading e, plateau reading c):
  bix, disc, plane a, return a: 0 of 1 []
  bix, disc, plane a, return b: 0 of 1 []
  bix, disc, plane b, return a: 0 of 1 []
  bix, disc, plane b, return b: 0 of 1 []
  bix, region, plane a, return a: 0 of 1 []
  bix, region, plane a, return b: 1 of 1 [110.9]
  bix, region, plane b, return a: 0 of 1 []
  bix, region, plane b, return b: 1 of 1 [110.9]
  aiiy, disc, plane a, return a: 1 of 5 [105.7]
  aiiy, disc, plane a, return b: 1 of 5 [105.7]
  aiiy, disc, plane b, return a: 1 of 5 [105.7]
  aiiy, disc, plane b, return b: 1 of 5 [105.7]
  aiiy, region, plane a, return a: 1 of 5 [105.7]
  aiiy, region, plane a, return b: 2 of 5 [110.9, 105.7]
  aiiy, region, plane b, return a: 1 of 5 [105.7]
  aiiy, region, plane b, return b: 2 of 5 [110.9, 105.7]
No description has been provided for this image

Result: not reproduced. Under aiiy 6G is identified in all 5 ants with a 144 s recording (under bix in 1). With the disc the mean trace peaks at 0.252 to 0.281 at 33.1 s, stays at or above 90% of its peak for 2.4 to 3.6 s and at or above 80% for 4.8 to 8.4 s (expected about 30 s), and returns 27.5 to 56.6 s after odor presentation (expected 80 +/- 8 s). With the region ROI the return (70.8 to 91.5 s) is near the expected value but the plateau is 7.2 to 15.7 s. One trial returns more than 100 s after odor presentation under the disc (100.7 to 105.7 s, by onset reading); under the region ROI with return reading b two trials do (105.9 to 110.9 s and 100.7 to 105.7 s). For the region ROI, 618 of the 1,684 traces had no disc pixel of at least 0.2 and fell back to the disc, so the region and disc readings coincide for those traces. None of the 64 combinations of identification, ROI, plane and criterion reading meets the criterion.

figs7c-temporal-lme¶

Onset and peak times differ by pheromone and glomerulus; for PGb and PGa responses to heptanol and hexanol, onset time depends on pheromone, glomerulus and their interaction, and time to maximum on pheromone and glomerulus but not their interaction.

Criterion: M3 traces at 48%; onset and time to maximum (Methods); traces that never exceed the bound excluded. Model per parameter: pheromone, glomerulus and their interaction, random effect for trial nested in ant (lme). p-values read as (a) sequential tests in the order the Methods list (here likelihood-ratio tests of nested ML fits, as statsmodels has no anova for mixed models), (b) marginal tests (Wald z of sum-coded effects), (c) the coefficient tests of a treatment-coded summary. Reproduced when, under every reading, the five effects have p < 0.05 and the time-to-maximum interaction has p of 0.05 or more; partially reproduced when three or four of the five hold.

In [23]:
lat = []
for _, r in tr[(tr.conc == 0.48) & (tr.nvol == 40) & tr.glom.isin(["PGb", "PGa"]) & tr.odor.isin(["heptanol", "hexanol"])].iterrows():
    on, mx = al.onset_and_max(r.trace, t40 + r.t0)
    lat.append({"ident": r.ident, "roi": r.roi, "ant": r.ant, "trial": f"{r.ant}-{r.trial}", "odor": r.odor, "glom": r.glom,
                "plane_reading": r.plane_reading, "onset": on, "tmax": mx})
lat = pd.DataFrame(lat)
print("traces per identification, ROI, plane reading and glomerulus:")
print(lat.groupby(["ident", "roi", "plane_reading", "glom"]).size().unstack().to_string())
print("excluded (never above the bound):")
print(lat.groupby(["ident", "roi", "plane_reading", "glom"]).onset.apply(lambda s: int(s.isna().sum())).unstack().to_string())
print(lat.dropna().groupby(["ident", "roi", "plane_reading", "glom", "odor"])[["onset", "tmax"]].agg(["mean", "std", "size"]).round(2).to_string())

def lme_p(d_, y):
    d_ = d_.dropna(subset=[y]).copy()
    vc = {"trial": "0 + C(trial)"}
    out = {}
    # (c) treatment coding, coefficient z-tests
    fc = smf.mixedlm(f"{y} ~ C(odor) * C(glom)", d_, groups=d_.ant, vc_formula=vc).fit(reml=True)
    out["converged"] = bool(fc.converged)
    pv = fc.pvalues
    out["c"] = {"pheromone": pv[[k for k in pv.index if k.startswith("C(odor)") and ":" not in k][0]],
                "glomerulus": pv[[k for k in pv.index if k.startswith("C(glom)") and ":" not in k][0]],
                "interaction": pv[[k for k in pv.index if ":" in k][0]]}
    # (b) sum coding, marginal z-tests
    fb = smf.mixedlm(f"{y} ~ C(odor, Sum) * C(glom, Sum)", d_, groups=d_.ant, vc_formula=vc).fit(reml=True)
    out["converged_b"] = bool(fb.converged)
    pv = fb.pvalues
    out["b"] = {"pheromone": pv[[k for k in pv.index if k.startswith("C(odor") and ":" not in k][0]],
                "glomerulus": pv[[k for k in pv.index if k.startswith("C(glom") and ":" not in k][0]],
                "interaction": pv[[k for k in pv.index if ":" in k][0]]}
    # (a) sequential likelihood-ratio tests (ML)
    forms = ["1", "C(odor)", "C(odor) + C(glom)", "C(odor) * C(glom)"]
    fa = [smf.mixedlm(f"{y} ~ {f}", d_, groups=d_.ant, vc_formula=vc).fit(reml=False) for f in forms]
    out["converged_a"] = bool(all(f_.converged for f_ in fa))
    ll = [f_.llf for f_ in fa]
    out["a"] = {k: float(stats.chi2.sf(2 * (ll[i + 1] - ll[i]), 1)) for i, k in enumerate(["pheromone", "glomerulus", "interaction"])}
    return out
rows, untestable = [], []
for (ident, roi, rd), d_ in lat.groupby(["ident", "roi", "plane_reading"]):
    for y in ("onset", "tmax"):
        levels = d_.dropna(subset=[y]).glom.nunique()
        if levels < 2:
            untestable.append(f"{ident}/{roi}/plane {rd}/{y}: only {sorted(d_.dropna(subset=[y]).glom.unique())} has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted")
            continue
        try:
            ps = lme_p(d_, y)
        except Exception as e:  # noqa: BLE001
            untestable.append(f"{ident}/{roi}/plane {rd}/{y}: fit failed ({e})")
            continue
        conv = {"c": ps.pop("converged"), "b": ps.pop("converged_b"), "a": ps.pop("converged_a")}
        for reading, p in ps.items():
            rows.append({"converged": conv[reading], **{"ident": ident, "roi": roi, "plane": rd, "p reading": reading, "parameter": y, **{k: round(float(v), 4) for k, v in p.items()}}})
for u in untestable:
    print("NOT TESTED:", u)
for ident in IDS:
    if not (lat.ident == ident).any() or (lat[lat.ident == ident].glom == "PGa").sum() == 0:
        print(f"NOT TESTED under {ident}: no PGa traces, so the claim cannot be tested under this identification")
lme = pd.DataFrame(rows, columns=["ident", "roi", "plane", "p reading", "parameter", "pheromone", "glomerulus", "interaction", "converged"])
print(lme.to_string(index=False))
print("fits converged (all four ML fits for reading a):", int(lme.converged.sum()), "of", len(lme))
lme_j = []
for (ident, roi, rd, pr), g in lme.groupby(["ident", "roi", "plane", "p reading"]):
    if set(g.parameter) != {"onset", "tmax"}:
        continue
    on = g[g.parameter == "onset"].iloc[0]; mx = g[g.parameter == "tmax"].iloc[0]
    holds = [on.pheromone < 0.05, on.glomerulus < 0.05, on.interaction < 0.05, mx.pheromone < 0.05, mx.glomerulus < 0.05]
    lme_j.append({"ident": ident, "roi": roi, "plane": rd, "p reading": pr, "significant of 5": sum(holds),
                  "which": "".join("1" if h else "0" for h in holds), "tmax interaction p >= 0.05": bool(mx.interaction >= 0.05)})
lme_j = pd.DataFrame(lme_j)
print("\n'which' marks onset pheromone, glomerulus, interaction, time-to-maximum pheromone, glomerulus")
print(lme_j.to_string(index=False))
traces per identification, ROI, plane reading and glomerulus:
glom                         PGa   PGb
ident roi    plane_reading            
aiiy  disc   a              18.0  48.0
             b              18.0  48.0
      region a              18.0  48.0
             b              18.0  48.0
bix   disc   a               NaN  30.0
             b               NaN  30.0
      region a               NaN  30.0
             b               NaN  30.0
excluded (never above the bound):
glom                        PGa  PGb
ident roi    plane_reading          
aiiy  disc   a              9.0  1.0
             b              8.0  1.0
      region a              2.0  0.0
             b              3.0  0.0
bix   disc   a              NaN  0.0
             b              NaN  0.0
      region a              NaN  0.0
             b              NaN  0.0
                                          onset               tmax            
                                           mean    std size   mean    std size
ident roi    plane_reading glom odor                                          
aiiy  disc   a             PGa  heptanol   6.79   5.11    5  16.18   4.13    5
                                hexanol    5.43   0.71    4   7.84   0.67    4
                           PGb  heptanol   7.00   2.40   23  14.07   2.55   23
                                hexanol    4.95   0.89   24   9.97   2.13   24
             b             PGa  heptanol  12.04  14.14    6  19.87  10.32    6
                                hexanol    5.12   1.16    4   7.84   0.71    4
                           PGb  heptanol   6.73   1.52   23  14.07   2.35   23
                                hexanol    4.80   0.92   24  10.07   2.17   24
      region a             PGa  heptanol   4.22   1.26    8  17.47   8.91    8
                                hexanol    5.71   0.85    8   8.42   1.40    8
                           PGb  heptanol   5.41   1.30   24  15.05   2.48   24
                                hexanol    4.10   0.72   24  10.37   1.96   24
             b             PGa  heptanol   9.03  12.55    8  18.06   9.53    8
                                hexanol    4.97   0.78    7   8.93   1.13    7
                           PGb  heptanol   5.35   0.94   24  15.34   3.24   24
                                hexanol    4.20   1.51   24  10.27   1.83   24
bix   disc   a             PGb  heptanol   6.21   1.21   15  13.92   2.50   15
                                hexanol    4.44   0.78   15   9.74   2.03   15
             b             PGb  heptanol   6.37   1.17   15  14.00   2.49   15
                                hexanol    4.28   0.79   15  10.15   2.14   15
      region a             PGb  heptanol   5.17   0.84   15  14.65   2.35   15
                                hexanol    4.03   0.60   15  10.38   1.88   15
             b             PGb  heptanol   5.24   0.74   15  13.76   2.42   15
                                hexanol    4.28   1.88   15   9.91   1.54   15
NOT TESTED: bix/disc/plane a/onset: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/disc/plane a/tmax: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/disc/plane b/onset: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/disc/plane b/tmax: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/region/plane a/onset: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/region/plane a/tmax: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/region/plane b/onset: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED: bix/region/plane b/tmax: only ['PGb'] has traces above the bound, so the glomerulus factor has one level and the model cannot be fitted
NOT TESTED under bix: no PGa traces, so the claim cannot be tested under this identification
ident    roi plane p reading parameter  pheromone  glomerulus  interaction  converged
 aiiy   disc     a         c     onset     0.3562      0.8432       0.6663       True
 aiiy   disc     a         b     onset     0.0337      0.8733       0.6663       True
 aiiy   disc     a         a     onset     0.0011      0.8981       0.6548       True
 aiiy   disc     a         c      tmax     0.0000      0.0379       0.0103       True
 aiiy   disc     a         b      tmax     0.0000      0.7596       0.0103       True
 aiiy   disc     a         a      tmax     0.0000      0.7255       0.0113       True
 aiiy   disc     b         c     onset     0.0173      0.0101       0.1178       True
 aiiy   disc     b         b     onset     0.0055      0.0775       0.1178       True
 aiiy   disc     b         a     onset     0.0171      0.0415       0.1088       True
 aiiy   disc     b         c      tmax     0.0000      0.0009       0.0027       True
 aiiy   disc     b         b      tmax     0.0000      0.1875       0.0027       True
 aiiy   disc     b         a      tmax     0.0000      0.0818       0.0028       True
 aiiy region     a         c     onset     0.0128      0.0198       0.0000       True
 aiiy region     a         b     onset     0.7938      0.5211       0.0000      False
 aiiy region     a         a     onset     0.0384      0.5346       0.0000      False
 aiiy region     a         c      tmax     0.0000      0.0785       0.0294       True
 aiiy region     a         b      tmax     0.0000      0.7567       0.0294       True
 aiiy region     a         a      tmax     0.0000      0.7694       0.0278       True
 aiiy region     b         c     onset     0.0796      0.0440       0.2729       True
 aiiy region     b         b     onset     0.0493      0.0934       0.2729       True
 aiiy region     b         a     onset     0.0964      0.0793       0.2594       True
 aiiy region     b         c      tmax     0.0000      0.1011       0.0900       True
 aiiy region     b         b      tmax     0.0000      0.5665       0.0900       True
 aiiy region     b         a      tmax     0.0000      0.5044       0.0826       True
fits converged (all four ML fits for reading a): 22 of 24

'which' marks onset pheromone, glomerulus, interaction, time-to-maximum pheromone, glomerulus
ident    roi plane p reading  significant of 5 which  tmax interaction p >= 0.05
 aiiy   disc     a         a                 2 10010                       False
 aiiy   disc     a         b                 2 10010                       False
 aiiy   disc     a         c                 2 00011                       False
 aiiy   disc     b         a                 3 11010                       False
 aiiy   disc     b         b                 2 10010                       False
 aiiy   disc     b         c                 4 11011                       False
 aiiy region     a         a                 3 10110                       False
 aiiy region     a         b                 2 00110                       False
 aiiy region     a         c                 4 11110                       False
 aiiy region     b         a                 1 00010                        True
 aiiy region     b         b                 2 10010                        True
 aiiy region     b         c                 2 01010                        True

Result: not reproduced, resting on the identification. Under bix no PGa is identified, so the models cannot be fitted (the glomerulus factor has one level; listed as NOT TESTED above). Under aiiy, the identification that finds PGa in the most ants (4 of 13, 3 of them at 48%), the models fit with 18 PGa and 48 PGb traces at 48% (with the disc 8 or 9 PGa traces never exceed the bound and are excluded, with the region 2 or 3). Across the 12 combinations of ROI, plane and p-value reading, 1 to 4 of the 5 effects have p < 0.05, and the time-to-maximum interaction has p < 0.05 in 9, where the paper finds none (p = 0.88). One of the 24 sets of fits did not converge: the ML fits of reading a for onset with the region ROI and plane a; the other 23 converged. The evidence is weak: PGa comes from 3 ants at 48%, against the paper's 8. The aiiy identification uses the ROI's maximum and drawing a, the reading set aside for the counts of figs5a-sparse-median-6, and 356 of drawing a's 436 regions are under 10 um across; it is used here only because it is the one that finds PGa in the most ants.

Summary¶

Claim Outcome
fig1cd-new-pheromones-leave-nest blocked (data_missing)
fig1e-categorical-response blocked (data_missing)
fig1f-hexanol-like-native blocked (data_missing)
fig1f-mho-differs blocked (data_missing)
fig1f-vehicle-differs blocked (data_missing)
table1-transgenesis blocked (data_missing)
fig2b-s1a-transgene-fluorescence blocked (data_missing)
fig2cd-gcamp-in-osns blocked (other), elsewhere
figs1-expression-outside-al blocked (other), elsewhere
figs2ab-glomerulus-counts blocked (not_runnable), elsewhere
fig2e-t7-ammc blocked (other), elsewhere
fig2f-ablation blocked (other), elsewhere
ablation-al-volume-trend blocked (not_runnable), elsewhere
figs2cf-gcamp-ants-behavior blocked (data_missing)
figs3-single-insertion blocked (not_runnable), elsewhere
fig3f-general-odorants-unique not reproduced
fig3fg-hexanone-broad reproduced
figs4a-bilateral-symmetry partially reproduced
fig4b-vehicle-no-response not reproduced
fig4-no-decreases not reproduced
fig4-large-lasting-responses not reproduced
figs5a-sparse-median-6 reproduced (assumed)
figs5a-concentration-more-glomeruli reproduced (assumed)
figs5b-shared-glomerulus not reproduced
figs5c-mho-rarely-overlaps not reproduced
fig5ab-focal-glomeruli not reproduced
fig5c-s6-tuning partially reproduced (assumed)
fig5c-6g-slow partially reproduced (assumed)
figs7a-6g-extended not reproduced
figs7c-temporal-lme not reproduced (assumed)
fig5d-stereotyped-positions not reproduced
fig6-shared-panic-pathway not reproduced, imaging half

"(assumed)" marks an outcome that rests on the adopted reading of a method the paper leaves open. The paper drew its ROIs by eye. Of the eight M2 readings of the claims file, the outcomes of figs5a-sparse-median-6 and figs5a-concentration-more-glomeruli rest on the ROI's mean value (ImageJ's default measure) with the Methods' trial-mean rule, under which both ROI drawings meet each criterion. For figs5a-sparse-median-6, aiy, aiix and aiiy fail and biix and biiy meet it; for figs5a-concentration-more-glomeruli, aiix, aiiy and biix fail. figs5c-mho-rarely-overlaps is not reproduced: its criterion asks for every reading, and the four readings with the ROI's maximum fail. The single-glomerulus claims rest on the focal glomeruli identified under bix (the adopted reading, which finds PGb in 5 of 13 ants, 6G in 2 and PGa in none) or under aiiy (the reading that finds PGa in the most ants, 4, which uses the ROI's maximum and drawing a, the reading set aside for the counts), and on the 15 um disc ROI; the claims file's other ROI, the region of at least 0.2, takes in neighbouring glomeruli and is shown everywhere.

How the analysis differs from the paper's: Fiji's Image Stabilizer is replaced by whole-pixel phase correlation on each plane, for the maps and for the single-glomerulus row bands; per-plane maps are binned 4 x 4 (1.16 um); ROIs are drawn by rule inside an AL mask, not by eye, as regions of at least 0.2 that recur in two trials of an odor (drawing a keeps every such region, however small, as the claims file has it; drawing b keeps those of glomerulus size); a recurrence is an overlap of at least half the smaller region; the Methods' exclusion of trials with large motion artifacts has no rule, and a variant leaves out the one trial where most of the AL is at least 0.2.

What was found about the dataset: it holds all three imaging experiments the paper describes (6 general odorant ants with the five analysed odorants, 3 bilateral ants, 13 alarm ants at the stated concentration pairs, and five 144 s recordings), one file per plane and trial, 14,685 files, 1.26 TB of uncompressed float64. No file or trial is deposited twice. The files give orientation, pixel size and ages; the stimulus timing is only in free text. The planes' starting times step by 0.0335 s (1.105 s per 33-plane volume), while the imaging rate field says 0.83 volumes/s (1.205 s) and the paper 27.5 frames/s (1.2 s per volume).

A failure to reproduce here may be a failure of this attempt, not of the paper: see the reasoning and the differences from the paper's method.

Runtime of this notebook:

In [24]:
print(f"notebook ran in {(time.time() - T0) / 60:.1f} min")
notebook ran in 24.9 min