Sit & Goard (2023) on dandiset 000483: ADN and visual axons, light and dark (Figure 4)¶

Paper. Sit KK, Goard MJ (2023), Nature Communications 14:1992, doi:10.1038/s41467-023-37704-5; claims file written from the version of record without its supplement.

Dataset. DANDI:000483, version 0.230421.2321, the only published version (the paper cites none). See single_cue.ipynb for the data check of all files.

Claim Here
fig4-axons-light-on-shape tested
fig4-axons-light-off tested

The claims of Figure 5 are in somata.ipynb (fig5i-axons-in-clusters uses these axons too); Figures S5 and S6 and Figure 5K are in directions.ipynb, and the decoding of Figure 6 in decoding.ipynb. Activity is inferred spikes computed from the raw fluorescence (no neuropil correction; see rsc.py). Light and dark segments are the code's hard-coded ones, as the files do not mark them.

In [1]:
import os

for _v in ["OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS"]:
    os.environ[_v] = "1"   # one BLAS thread, so that results do not depend on the number of cores
import time
import warnings
from multiprocessing import Pool

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
from scipy import stats
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.metrics import silhouette_score
from sklearn.mixture import GaussianMixture

import rsc

warnings.filterwarnings("ignore")
pd.set_option("display.width", 200)
pd.set_option("display.max_columns", 30)
started = time.time()
cat = rsc.catalog(full=False)
sub = cat[cat.experiment.isin(["adn_axons", "vis_axons"])].reset_index(drop=True)
recs = rsc.load_many(sub.url.tolist())
with Pool(4) as pool:
    for r in recs:
        r["dff"] = rsc.dff(r["F"])
        r["sp"] = rsc.spikes(r["dff"], pool)
        del r["F"]
print(f"dandiset {rsc.DANDISET_ID} version {rsc.DANDISET_VERSION}: {len(sub)} files loaded and deconvolved "
      f"in {time.time() - started:.0f} s")
dandiset 000483 version 0.230421.2321: 24 files loaded and deconvolved in 134 s
In [2]:
rows = []
for m, r in zip(sub.itertuples(), recs):
    L, D = rsc.light_dark_frames(m.n_frames)
    r["seg_l"], r["seg_d"] = rsc.segments_within(r["heading"], L), rsc.segments_within(r["heading"], D)
    r["seg_all"] = rsc.trial_segments(r["heading"])
    step = rsc.wrap180(np.diff(rsc.clean_heading(r["heading"])))
    # activity per segment, to see whether the hard-coded light and dark segments differ
    act = r["sp"].sum(1)
    rows.append(dict(experiment=m.experiment, subject=m.subject, identifier=m.identifier[:6] + " " + m.identifier.split("_")[-1],
                     frames=m.n_frames, rois=m.n_rois, median_step=round(float(np.median(step)), 2),
                     trials_all=len(r["seg_all"]), trials_light=len(r["seg_l"]), trials_dark=len(r["seg_d"]),
                     spikes_light=round(float(np.mean([act[s].mean() for s in L])), 2),
                     spikes_dark=round(float(np.mean([act[s].mean() for s in D])), 2)))
info = pd.DataFrame(rows)
print(info.to_string(index=False))
print(info.groupby("experiment").agg(files=("rois", "size"), mice=("subject", "nunique"), rois=("rois", "sum")).to_string())
experiment subject                       identifier  frames  rois  median_step  trials_all  trials_light  trials_dark  spikes_light  spikes_dark
 adn_axons SKKS092         210217 SKKS092-axons-001    7550    27         1.93          38            17           19          0.15         0.09
 adn_axons SKKS092         210217 SKKS092-axons-002    7550    60         1.93          38            19           18          0.45         0.24
 adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-001    7550    25         1.90          38            18           15          0.61         0.74
 adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-002    7550    26         1.89          39            18           19          0.47         0.65
 adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-003    7550    20         1.87          37            17           18          0.39         0.56
 adn_axons SKKS093         210217 SKKS093-axons-001    7550    76         1.95          38            19           17          0.64         0.48
 adn_axons SKKS093         210217 SKKS093-axons-002    7550    60         1.94          39            18           19          0.50         0.20
 adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-001    7550    67         1.89          39            18           20          0.90         0.64
 adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-002    7550    58         1.90          37            18           19          0.78         0.41
 adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-003    7550    79         1.89          39            18           18          0.82         0.72
 adn_axons SKKS094         210217 SKKS094-axons-001    7550    66         1.88          37            17           20          0.81         0.56
 adn_axons SKKS094         210217 SKKS094-axons-002    7550    75         1.88          38            17           18          0.82         0.47
 adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-001    7550   112         1.88          37            18           17          1.53         1.70
 adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-002    7550   110         1.84          35            18           17          1.55         1.53
 adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-003    7550   120         1.62          23            13            9          1.64         1.13
 adn_axons SKKS095         210217 SKKS095-axons-001    7550    44         1.90          39            20           18          0.47         0.28
 adn_axons SKKS095         210217 SKKS095-axons-002    7550    59         1.89          37            15           18          0.41         0.35
 vis_axons SKKS097         210217 SKKS097-axons-001    7550    68         1.73          34            15           16          1.66         0.25
 vis_axons SKKS097         210217 SKKS097-axons-002    7550    47         1.87          37            17           17          0.96         0.12
 vis_axons SKKS098         210217 SKKS098-axons-001    7550    36         1.92          39            21           18          0.62         0.25
 vis_axons SKKS098         210217 SKKS098-axons-002    7550    26         1.93          39            17           18          0.43         0.13
 vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-001    7550    37         0.11          11             8            3          0.76         0.45
 vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-002    7550    28         1.87          37            17           15          0.57         0.49
 vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-003    7550     5         1.89          38            18           17          0.03         0.01
            files  mice  rois
experiment                   
adn_axons      17     4  1084
vis_axons       7     3   247

The files do not say when the lights were on. The code hard-codes the segments (Separator.m): 950-frame light and dark halves for 1950-frame recordings, three light/dark repeats of 1250 frames for 7550-frame ones. The summed activity per segment is shown above as a check that is consistent with, but does not prove, the segmentation. Several 1950-frame recordings of 2021-01-18 stop rotating early and have few trials.

Clock check. As in rotation_remapping.ipynb, files that rotate one way at constant speed cannot show a time offset between heading and activity; the clock is checked only on the files that rotate both ways (single_cue.ipynb), and is not established for these files.

Heading-selection test (two-term Gaussian)¶

On all trials, light and dark together (the code's default, claims file), with the centred fit adopted in single_cue.ipynb. The code's uncentred fit is also run, and the test on light-off trials alone (the code's 'dark' option) for fig4-axons-light-off.

In [3]:
t0 = time.time()
trials_all = [rsc.trial_tuning(r["sp"], r["heading"], r["seg_all"]) for r in recs]
ax_idx = [i for i in range(len(recs)) if "axons" in sub.experiment[i]]
with Pool(4) as pool:
    sel = rsc.selection_many(trials_all, "double", 301, pool, centre=True)   # all files here are axons
    sel_ax_code = rsc.selection_many([trials_all[i] for i in ax_idx], "double", 302, pool, centre=False)
    sel_ax_dark = rsc.selection_many([rsc.trial_tuning(recs[i]["sp"], recs[i]["heading"], recs[i]["seg_d"]) for i in ax_idx],
                                     "double", 303, pool, centre=True)
info["selective"] = [s.is_hd.sum() for s in sel]
print(f"selection tests in {time.time() - t0:.0f} s")
print(info.groupby("experiment")[["rois", "selective"]].sum().to_string())
selection tests in 159 s
            rois  selective
experiment                 
adn_axons   1084        135
vis_axons    247        106
In [4]:
# Light and dark tuning, aligned with the light-on cross-validated shifts (Methods).
def light_dark_aligned(i):
    r = recs[i]
    lt = rsc.trial_tuning(r["sp"], r["heading"], r["seg_l"])
    dt = rsc.trial_tuning(r["sp"], r["heading"], r["seg_d"])
    if lt.shape[2] == 0:
        return None, None
    sh = rsc.cv_shifts(lt) if lt.shape[2] > 1 else (rsc.SHIFT_POINT - lt[:, :, 0].argmax(1))[:, None]
    la = rsc.apply_shifts(lt, sh).mean(2)
    # light-off trials take the shift of the mean light-on curve (all light trials)
    sh_all = (rsc.SHIFT_POINT - lt.mean(2).argmax(1))[:, None]
    da = rsc.apply_shifts(dt, np.repeat(sh_all, max(dt.shape[2], 1), 1)).mean(2) if dt.shape[2] else np.full_like(la, np.nan)
    return la, da

aligned = [light_dark_aligned(i) for i in range(len(recs))]
print("Recordings without light-on trials (left out of the light/dark analyses):",
      [info.identifier[i] for i in range(len(recs)) if aligned[i][0] is None])
print("Recordings without light-off trials:", [info.identifier[i] for i in range(len(recs)) if aligned[i][0] is not None and np.isnan(aligned[i][1]).all()])
Recordings without light-on trials (left out of the light/dark analyses): []
Recordings without light-off trials: []

fig4-axons-light-on-shape¶

In the light-on condition with two symmetric cues, ADN axons in RSC show unimodal heading tuning curves, while visual cortical (AM/PM) axons show bimodal tuning curves with peaks about 180 degrees apart.

Readings of the claims file: (a) axons passing the two-term test, (b) all axons. Flip score of the mean aligned light-on curve (ADN below 0, visual above 0), and the flip scores of single axons, visual above ADN by a two-sample KS test. The fit readings of the selection (centred, code) are both shown for (a).

In [5]:
def axon_sets(selection_by_rec):
    out = {}
    for kind in ["adn_axons", "vis_axons"]:
        la, da, mice, recid = [], [], [], []
        for k, i in enumerate(ax_idx):
            if sub.experiment[i] != kind or aligned[i][0] is None:
                continue
            m = selection_by_rec(k, i)
            la.append(aligned[i][0][m]); da.append(aligned[i][1][m])
            mice += [sub.subject[i]] * int(m.sum()); recid += [i] * int(m.sum())
        out[kind] = (np.vstack(la), np.vstack(da), np.array(mice), np.array(recid))
    return out

readings = {"(a) selective, centred": axon_sets(lambda k, i: sel[i].is_hd.values),
            "(a) selective, code fit": axon_sets(lambda k, i: sel_ax_code[k].is_hd.values),
            "(b) all axons": axon_sets(lambda k, i: np.ones(len(sel[i]), bool))}
light_shape = {}
for name, s in readings.items():
    (la_a, da_a, mice_a, _), (la_v, da_v, mice_v, _) = s["adn_axons"], s["vis_axons"]
    fa, fv = rsc.flip_score(la_a), rsc.flip_score(la_v)
    fa, fv = fa[np.isfinite(fa)], fv[np.isfinite(fv)]
    mean_a, mean_v = rsc.flip_score(np.nanmean(la_a, 0)[None])[0], rsc.flip_score(np.nanmean(la_v, 0)[None])[0]
    ks = stats.ks_2samp(fv, fa)
    light_shape[name] = (mean_a, mean_v, fa.mean(), fv.mean(), ks.pvalue)
    print(f"== {name}: ADN n={len(la_a)} ({len(set(mice_a))} mice), visual n={len(la_v)} ({len(set(mice_v))} mice)")
    print(f"   flip score of the mean light-on curve: ADN {mean_a:.3f}, visual {mean_v:.3f}")
    print(f"   single axons: ADN mean {fa.mean():.3f} (median {np.median(fa):.3f}), visual mean {fv.mean():.3f} (median {np.median(fv):.3f}); KS p = {ks.pvalue:.2g}")
    for kind, mice, f in [("ADN", mice_a, rsc.flip_score(la_a)), ("visual", mice_v, rsc.flip_score(la_v))]:
        print(f"   {kind} by mouse:", {m: round(float(np.nanmean(f[mice == m])), 3) for m in np.unique(mice)})

fig, ax = plt.subplots(1, 2, figsize=(8, 3))
s = readings["(a) selective, centred"]
xdeg = np.arange(60) * 6 - 84
for a, kind, title in zip(ax, ["adn_axons", "vis_axons"], ["ADN axons", "visual axons"]):
    la, da = rsc.rescale(s[kind][0]), rsc.rescale(s[kind][1])
    a.plot(xdeg, np.nanmean(la, 0), label="light on"); a.plot(xdeg, np.nanmean(da, 0), label="light off")
    a.set_title(title); a.set_xlabel("heading from aligned peak (deg)")
ax[0].set_ylabel("mean rescaled response"); ax[0].legend(); plt.tight_layout(); plt.show()
print("Curves are min-max rescaled per axon before averaging, so all values lie in 0 to 1.")
== (a) selective, centred: ADN n=135 (4 mice), visual n=106 (3 mice)
   flip score of the mean light-on curve: ADN 0.277, visual 1.814
   single axons: ADN mean -0.072 (median -0.279), visual mean 1.073 (median 1.146); KS p = 1.7e-21
   ADN by mouse: {np.str_('SKKS092'): 0.48, np.str_('SKKS093'): -0.034, np.str_('SKKS094'): -0.196, np.str_('SKKS095'): -0.461}
   visual by mouse: {np.str_('SKKS097'): 1.185, np.str_('SKKS098'): 1.026, np.str_('SKKS103'): 0.408}
== (a) selective, code fit: ADN n=73 (4 mice), visual n=50 (3 mice)
   flip score of the mean light-on curve: ADN 1.132, visual 1.903
   single axons: ADN mean 0.080 (median -0.100), visual mean 0.882 (median 1.021); KS p = 1.1e-06
   ADN by mouse: {np.str_('SKKS092'): 0.423, np.str_('SKKS093'): -0.158, np.str_('SKKS094'): 0.125, np.str_('SKKS095'): -0.387}
   visual by mouse: {np.str_('SKKS097'): 1.127, np.str_('SKKS098'): 0.578, np.str_('SKKS103'): 0.441}
== (b) all axons: ADN n=1084 (4 mice), visual n=247 (3 mice)
   flip score of the mean light-on curve: ADN -0.207, visual 1.875
   single axons: ADN mean -0.109 (median -0.258), visual mean 0.506 (median 0.589); KS p = 1.6e-24
   ADN by mouse: {np.str_('SKKS092'): -0.001, np.str_('SKKS093'): -0.12, np.str_('SKKS094'): -0.133, np.str_('SKKS095'): -0.127}
   visual by mouse: {np.str_('SKKS097'): 0.867, np.str_('SKKS098'): 0.544, np.str_('SKKS103'): -0.121}
No description has been provided for this image
Curves are min-max rescaled per axon before averaging, so all values lie in 0 to 1.

Result: reproduced under reading (b), all axons. The mean aligned light-on curve has a flip score of -0.207 for ADN axons (unimodal) and 1.875 for visual axons (bimodal). Single visual axons have higher flip scores than ADN axons (means 0.506 and -0.109, KS p = 1.6e-24). Under reading (a), selective axons, the visual part holds (1.814; single axons 1.073 against -0.072, KS p = 1.7e-21). The ADN mean curve, however, has a flip score of 0.277 (centred fit) or 1.132 (code fit), above 0. One ADN mouse, SKKS092, has a mean flip score above 0 under reading (a) (0.48 and 0.42) and about 0 under (b) (-0.001).

fig4-axons-light-off¶

In the light-off condition ADN axons keep their heading tuning for several trials, while visual cortical axons lose their tuning.

Criterion: the fraction of axons selective by the test on light-off trials alone is higher for ADN than for visual axons (per-recording fractions, Mann-Whitney; Fisher's exact test on pooled counts as a variant), and the mean aligned light-off curve of ADN axons peaks within 18 degrees (3 bins) of the light-on peak (bin 15 after alignment) while that of visual axons has a peak-to-trough range under half its light-on range.

The fraction selective in light-off is computed under reading (b), over all axons, and under reading (a), over the axons selective on all trials.

In [6]:
fr = pd.DataFrame({"kind": [sub.experiment[i] for i in ax_idx], "subject": [sub.subject[i] for i in ax_idx],
                   "file": [info.identifier[i] for i in ax_idx], "dark_trials": [len(recs[i]["seg_d"]) for i in ax_idx],
                   "axons": [len(sel[i]) for i in ax_idx], "dark_selective": [s.is_hd.sum() for s in sel_ax_dark]})
fr["fraction"] = fr.dark_selective / fr.axons
print(fr.to_string(index=False))
fa_, fv_ = fr[fr.kind == "adn_axons"].fraction, fr[fr.kind == "vis_axons"].fraction
mw = stats.mannwhitneyu(fa_, fv_, alternative="two-sided")
pooled = fr.groupby("kind")[["dark_selective", "axons"]].sum()
odds, pf = stats.fisher_exact([[pooled.loc["adn_axons", "dark_selective"], pooled.loc["adn_axons", "axons"] - pooled.loc["adn_axons", "dark_selective"]],
                               [pooled.loc["vis_axons", "dark_selective"], pooled.loc["vis_axons", "axons"] - pooled.loc["vis_axons", "dark_selective"]]])
print(f"\nper-recording fraction selective in light-off: ADN median {fa_.median():.3f} (mean {fa_.mean():.3f}), "
      f"visual median {fv_.median():.3f} (mean {fv_.mean():.3f}); Mann-Whitney p = {mw.pvalue:.2g}")
print(f"pooled: ADN {pooled.loc['adn_axons', 'dark_selective']}/{pooled.loc['adn_axons', 'axons']}, visual "
      f"{pooled.loc['vis_axons', 'dark_selective']}/{pooled.loc['vis_axons', 'axons']}; Fisher p = {pf:.2g}, odds ratio {odds:.2f}")
# reading (a): among the axons selective on all trials (light-on selection), the share also selective in light-off
fr["a_selective"] = [sel[i].is_hd.sum() for i in ax_idx]
fr["a_dark_selective"] = [int((sel[i].is_hd.values & s.is_hd.values).sum()) for i, s in zip(ax_idx, sel_ax_dark)]
fr["a_fraction"] = fr.a_dark_selective / fr.a_selective.where(fr.a_selective > 0)
fa_a, fv_a = fr[fr.kind == "adn_axons"].a_fraction.dropna(), fr[fr.kind == "vis_axons"].a_fraction.dropna()
mw_a = stats.mannwhitneyu(fa_a, fv_a, alternative="two-sided")
pa = fr.groupby("kind")[["a_dark_selective", "a_selective"]].sum()
odds_a, pf_a = stats.fisher_exact([[pa.loc["adn_axons", "a_dark_selective"], pa.loc["adn_axons", "a_selective"] - pa.loc["adn_axons", "a_dark_selective"]],
                                   [pa.loc["vis_axons", "a_dark_selective"], pa.loc["vis_axons", "a_selective"] - pa.loc["vis_axons", "a_dark_selective"]]])
print(f"\nreading (a), selective axons: per-recording share also selective in light-off, ADN median {fa_a.median():.3f} "
      f"(n = {len(fa_a)} recordings), visual median {fv_a.median():.3f} (n = {len(fv_a)}); Mann-Whitney p = {mw_a.pvalue:.2g}; pooled ADN "
      f"{pa.loc['adn_axons', 'a_dark_selective']}/{pa.loc['adn_axons', 'a_selective']}, visual {pa.loc['vis_axons', 'a_dark_selective']}/"
      f"{pa.loc['vis_axons', 'a_selective']}, Fisher p = {pf_a:.2g}, odds ratio {odds_a:.2f}")
print("recordings without selective axons are left out of the per-recording comparison:", int(fr.a_fraction.isna().sum()))
for name, s in readings.items():
    (la_a, da_a, _, _), (la_v, da_v, _, _) = s["adn_axons"], s["vis_axons"]
    ma, mda = np.nanmean(la_a, 0), np.nanmean(da_a, 0)
    mv, mdv = np.nanmean(la_v, 0), np.nanmean(da_v, 0)
    pk_off = int(np.argmax(mda)); off_bins = int(abs(rsc.wrap180((pk_off - rsc.SHIFT_POINT) * 6)) // 6)
    ratio_v = np.ptp(mdv) / np.ptp(mv)
    print(f"== {name}: ADN mean light-off peak at bin {pk_off + 1} ({off_bins * 6} deg from the light-on peak bin 15); "
          f"ADN light-off/light-on range {np.ptp(mda) / np.ptp(ma):.2f}; visual light-off/light-on range {ratio_v:.2f}")
     kind subject                             file  dark_trials  axons  dark_selective  fraction
adn_axons SKKS092         210217 SKKS092-axons-001           19     27               0  0.000000
adn_axons SKKS092         210217 SKKS092-axons-002           18     60               4  0.066667
adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-001           15     25               2  0.080000
adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-002           19     26               0  0.000000
adn_axons SKKS092 210304 SKKS092-ADNAxons-3rpm-003           18     20               0  0.000000
adn_axons SKKS093         210217 SKKS093-axons-001           17     76              13  0.171053
adn_axons SKKS093         210217 SKKS093-axons-002           19     60               3  0.050000
adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-001           20     67              14  0.208955
adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-002           19     58               3  0.051724
adn_axons SKKS093 210304 SKKS093-ADNAxons-3rpm-003           18     79               3  0.037975
adn_axons SKKS094         210217 SKKS094-axons-001           20     66               6  0.090909
adn_axons SKKS094         210217 SKKS094-axons-002           18     75               0  0.000000
adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-001           17    112               2  0.017857
adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-002           17    110              16  0.145455
adn_axons SKKS094 210304 SKKS094-ADNAxons-3rpm-003            9    120              26  0.216667
adn_axons SKKS095         210217 SKKS095-axons-001           18     44               0  0.000000
adn_axons SKKS095         210217 SKKS095-axons-002           18     59               1  0.016949
vis_axons SKKS097         210217 SKKS097-axons-001           16     68               1  0.014706
vis_axons SKKS097         210217 SKKS097-axons-002           17     47               7  0.148936
vis_axons SKKS098         210217 SKKS098-axons-001           18     36               2  0.055556
vis_axons SKKS098         210217 SKKS098-axons-002           18     26               1  0.038462
vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-001            3     37               0  0.000000
vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-002           15     28               0  0.000000
vis_axons SKKS103 210304 SKKS103-VISAxons-3rpm-003           17      5               0  0.000000

per-recording fraction selective in light-off: ADN median 0.050 (mean 0.068), visual median 0.015 (mean 0.037); Mann-Whitney p = 0.35
pooled: ADN 93/1084, visual 11/247; Fisher p = 0.035, odds ratio 2.01

reading (a), selective axons: per-recording share also selective in light-off, ADN median 0.134 (n = 14 recordings), visual median 0.000 (n = 7); Mann-Whitney p = 0.15; pooled ADN 39/135, visual 5/106, Fisher p = 5.4e-07, odds ratio 8.21
recordings without selective axons are left out of the per-recording comparison: 3
== (a) selective, centred: ADN mean light-off peak at bin 19 (24 deg from the light-on peak bin 15); ADN light-off/light-on range 0.86; visual light-off/light-on range 0.05
== (a) selective, code fit: ADN mean light-off peak at bin 18 (18 deg from the light-on peak bin 15); ADN light-off/light-on range 0.80; visual light-off/light-on range 0.11
== (b) all axons: ADN mean light-off peak at bin 20 (30 deg from the light-on peak bin 15); ADN light-off/light-on range 0.72; visual light-off/light-on range 0.10

Result: partially reproduced. Visual axons lose their tuning in light-off: their mean light-off curve keeps 5 to 11% of the light-on range, under 50%. The ADN part of the criterion fails. Over all axons (reading b) the per-recording fraction selective in light-off is not significantly higher for ADN than for visual axons (medians 0.050 and 0.015, Mann-Whitney p = 0.35); pooled counts differ (93 of 1084 against 11 of 247, Fisher p = 0.035, the variant). Among selective axons (reading a) the share also selective in light-off is higher for ADN, but not significantly per recording (medians 0.134 and 0.000, Mann-Whitney p = 0.15, 14 and 7 recordings); pooled, 39 of 135 against 5 of 106 (Fisher p = 5.4e-07). The ADN mean light-off curve keeps 72 to 86% of its light-on range, but its peak lies 24 degrees (reading a, centred fit) or 30 degrees (reading b) from the light-on peak, beyond the 18 degrees of the criterion; under the code fit it lies at 18 degrees.

Summary¶

Claim Outcome
fig4-axons-light-on-shape reproduced (reading b, all axons)
fig4-axons-light-off partially reproduced: visual axons lose tuning; the ADN tests of the criterion fail

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.

How the analysis differs from the paper's: inferred spikes from raw fluorescence (the paper used Suite2P traces for axons, not in the files); light and dark segments are the code's hard-coded ones; light-off curves take the shift of the mean light-on curve, as the code's aligner (leaveOneOutAligner) is not public.

What was found about the dataset: the files do not mark light-on and light-off periods; axon recordings are described as soma recordings.